/* ** ** **--------------------------------------------------------------------------- ** Copyright 2005-2016 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ // HEADER FILES ------------------------------------------------------------ #define WIN32_LEAN_AND_MEAN #include #include "i_input.h" #include "d_event.h" #include "templates.h" #include "gameconfigfile.h" #include "m_argv.h" #include "cmdlib.h" #include "keydef.h" // MACROS ------------------------------------------------------------------ #define DEFAULT_DEADZONE 0.25f #define STATUS_SWITCH_TIME 3 #define VID_PLAY_COM 0x0b43 #define PID_EMS_USB2_PS2_CONTROLLER_ADAPTER 0x0003 #define VID_GREENASIA 0x0e8f #define PID_DRAGON_PS2_CONTROLLER_ADAPTER 0x0003 #define PID_PANTHERLORD_PS2_CONTROLLER_ADAPTER 0x0029 #define VID_PERSONAL_COMMUNICATION_SYSTEMS 0x0810 #define PID_TWIN_USB_VIBRATION_GAMEPAD 0x0001 #define STATUS_DISCONNECTED 0xFF #define STATUS_DIGITAL 0x41 #define STATUS_ANALOG 0x73 // TYPES ------------------------------------------------------------------- enum EAdapterType { ADAPTER_EMSUSB2, ADAPTER_DragonPlus, ADAPTER_PantherLord, ADAPTER_TwinUSB, ADAPTER_Unknown }; struct FAdapterHandle { HANDLE Handle; EAdapterType Type; int ControllerNumber; FString DeviceID; }; class FRawPS2Controller : public IJoystickConfig { public: FRawPS2Controller(HANDLE handle, EAdapterType type, int sequence, int controller, FString devid); ~FRawPS2Controller(); bool ProcessInput(RAWHID *raw, int code); void AddAxes(float axes[NUM_JOYAXIS]); bool IsConnected() { return Connected; } // IJoystickConfig interface FString GetName(); float GetSensitivity(); virtual void SetSensitivity(float scale); int GetNumAxes(); float GetAxisDeadZone(int axis); EJoyAxis GetAxisMap(int axis); const char *GetAxisName(int axis); float GetAxisScale(int axis); void SetAxisDeadZone(int axis, float deadzone); void SetAxisMap(int axis, EJoyAxis gameaxis); void SetAxisScale(int axis, float scale); bool IsSensitivityDefault(); bool IsAxisDeadZoneDefault(int axis); bool IsAxisMapDefault(int axis); bool IsAxisScaleDefault(int axis); void SetDefaultConfig(); FString GetIdentifier(); protected: struct AxisInfo { float Value; float DeadZone; float Multiplier; EJoyAxis GameAxis; uint8_t ButtonValue; }; struct DefaultAxisConfig { EJoyAxis GameAxis; float Multiplier; }; enum { AXIS_ThumbLX, AXIS_ThumbLY, AXIS_ThumbRX, AXIS_ThumbRY, NUM_AXES }; HANDLE Handle; FString DeviceID; int ControllerNumber; int Sequence; DWORD DisconnectCount; EAdapterType Type; float Multiplier; AxisInfo Axes[NUM_AXES]; static DefaultAxisConfig DefaultAxes[NUM_AXES]; int LastButtons; bool Connected; bool Marked; bool Active; void Attached(); void Detached(); void NeutralInput(); static void ProcessThumbstick(int value1, AxisInfo *axis1, int value2, AxisInfo *axis2, int base); friend class FRawPS2Manager; }; class FRawPS2Manager : public FJoystickCollection { public: FRawPS2Manager(); ~FRawPS2Manager(); bool GetDevice(); bool ProcessRawInput(RAWINPUT *raw, int code); void AddAxes(float axes[NUM_JOYAXIS]); void GetDevices(TArray &sticks); IJoystickConfig *Rescan(); protected: TArray Devices; bool Registered; void DoRegister(); FRawPS2Controller *EnumDevices(); static int DeviceSort(const void *a, const void *b); }; // Each entry is an offset to the corresponding data field in the // adapter's data packet. Some of these fields are optional and are // assigned negative values if the adapter does not include them. struct PS2Descriptor { const char *AdapterName; uint8_t PacketSize; int8_t ControllerNumber; int8_t ControllerStatus; uint8_t LeftX; uint8_t LeftY; uint8_t RightX; uint8_t RightY; int8_t DPadHat; uint8_t DPadButtonsNibble:1; int8_t DPadButtons:7; // up, right, down, left uint8_t ButtonSet1:7; // triangle, circle, cross, square uint8_t ButtonSet1Nibble:1; uint8_t ButtonSet2:7; // L2, R2, L1, R1 uint8_t ButtonSet2Nibble:1; uint8_t ButtonSet3:7; // select, start, lthumb, rthumb uint8_t ButtonSet3Nibble:1; }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern HWND Window; // PUBLIC DATA DEFINITIONS ------------------------------------------------- CUSTOM_CVAR(Bool, joy_ps2raw, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL) { I_StartupRawPS2(); event_t ev = { EV_DeviceChange }; D_PostEvent(&ev); } // PRIVATE DATA DEFINITIONS ------------------------------------------------ static const int ButtonKeys[16] = { KEY_PAD_DPAD_UP, KEY_PAD_DPAD_RIGHT, KEY_PAD_DPAD_DOWN, KEY_PAD_DPAD_LEFT, KEY_PAD_Y, // triangle KEY_PAD_B, // circle KEY_PAD_A, // cross KEY_PAD_X, // square KEY_PAD_LTRIGGER, // L2 KEY_PAD_RTRIGGER, // R2 KEY_PAD_LSHOULDER, // L1 KEY_PAD_RSHOULDER, // R1 KEY_PAD_BACK, // select KEY_PAD_START, KEY_PAD_LTHUMB, KEY_PAD_RTHUMB }; static const uint8_t HatButtons[16] = { 1, 1+2, 2, 2+4, 4, 4+8, 8, 8+1, 0, 0, 0, 0, 0, 0, 0, 0 }; static const PS2Descriptor Descriptors[ADAPTER_Unknown] = { { // ADAPTER_EMS_USB2 "EMS USB2 Adapter", 8, // packet size 0, // controller number 7, // controller status 3, // left x 4, // left y 5, // right x 6, // right y -1, // hat 1, // d-pad buttons nibble 2, // d-pad buttons 1, // buttons 1 0, // buttons 1 nibble 1, // buttons 2 1, // buttons 2 nibble 2, // buttons 3 0, // buttons 3 nibble }, { // ADAPTER_DragonPlus "Dragon+ Adapter", 9, // packet size -1, // controller number -1, // controller status 3, // left x 4, // left y 2, // right x 1, // right y 6, // hat 0, // d-pad buttons nibble -1, // d-pad buttons 6, // buttons 1 1, // buttons 1 nibble 7, // buttons 2 0, // buttons 2 nibble 7, // buttons 3 1, // buttons 3 nibble }, { // ADAPTER_PantherLord // Windows indentifies it as a ga451-USB device, but I call it a PantherLord // because there's a box in the middle of the cable with a PantherLord sticker // on it. "PantherLord Adapter", 9, // packet size -1, // controller number -1, // controller status 4, // left x 5, // left y 3, // right x 2, // right y -1, // hat ... This device has both a hat and d-pad buttons. 0, // d-pad buttons nibble Since buttons are better, we just read those. 8, // d-pad buttons 6, // buttons 1 1, // buttons 1 nibble 7, // buttons 2 0, // buttons 2 nibble 7, // buttons 3 1, // buttons 3 nibble }, { // ADAPTER_TwinUSB "Twin USB Gamepad", 8, // packet size 0, // controller number -1, // controller status 3, // left x 4, // left y 2, // right x 1, // right y 5, // hat 0, // d-pad buttons nibble -1, // d-pad buttons 5, // buttons 1 1, // buttons 1 nibble 6, // buttons 2 0, // buttons 2 nibble 6, // buttons 3 1, // buttons 3 nibble } }; static const char *AxisNames[] = { "Left Thumb X Axis", "Left Thumb Y Axis", "Right Thumb X Axis", "Right Thumb Y Axis", }; FRawPS2Controller::DefaultAxisConfig FRawPS2Controller::DefaultAxes[NUM_AXES] = { // Game axis, multiplier { JOYAXIS_Side, 1 }, // ThumbLX { JOYAXIS_Forward, 1 }, // ThumbLY { JOYAXIS_Yaw, 1 }, // ThumbRX { JOYAXIS_Pitch, 0.75 }, // ThumbRY }; // CODE -------------------------------------------------------------------- //========================================================================== // // FRawPS2Controller - Constructor // // handle: The Raw Input handle for this device // type: The adapter type // sequence: The seqeunce number, for attaching numbers to names // controller: The controller to check, for multi-controller adapters // //========================================================================== FRawPS2Controller::FRawPS2Controller(HANDLE handle, EAdapterType type, int sequence, int controller, FString devid) { Handle = handle; Type = type; ControllerNumber = controller; Sequence = sequence; DeviceID = devid; // The EMS USB2 controller provides attachment status. The others do not. Connected = (Descriptors[type].ControllerStatus < 0); if (Connected) { Attached(); } M_LoadJoystickConfig(this); } //========================================================================== // // FRawPS2Controller - Destructor // //========================================================================== FRawPS2Controller::~FRawPS2Controller() { // Make sure to send any key ups before destroying this. NeutralInput(); M_SaveJoystickConfig(this); } //========================================================================== // // FRawPS2Controller :: ProcessInput // //========================================================================== bool FRawPS2Controller::ProcessInput(RAWHID *raw, int code) { // w32api has an incompatible definition of bRawData. // (But the version that comes with MinGW64 is fine.) #if defined(__GNUC__) && !defined(__MINGW64_VERSION_MAJOR) uint8_t *rawdata = &raw->bRawData; #else uint8_t *rawdata = raw->bRawData; #endif const PS2Descriptor *desc = &Descriptors[Type]; bool digital; // Ensure packet size is what we expect. if (raw->dwSizeHid != desc->PacketSize) { return false; } #if 0 // If this is a multi-controller device, check that this packet // is for the controller we were created for. We probably don't // need to do this, because the multi-controller adapters // send data for each controller to seperate device instances. if (desc->ControllerNumber >= 0 && raw->bRawData[desc->ControllerNumber] != ControllerNumber) { return false; } #endif // Check for disconnected controller if (desc->ControllerStatus >= 0) { if (rawdata[desc->ControllerStatus] == STATUS_DISCONNECTED) { // When you press the Analog button on a controller, the EMS // USB2 will briefly report the controller as disconnected. if (++DisconnectCount < STATUS_SWITCH_TIME) { NeutralInput(); return true; } if (Connected) { Detached(); } return true; } if (!Connected) { Attached(); } } DisconnectCount = 0; if (code == RIM_INPUTSINK) { NeutralInput(); return true; } // Check for digital controller digital = false; if (desc->ControllerStatus >= 0) { // The EMS USB2 is nice enough to actually tell us what type of // controller is attached. digital = (rawdata[desc->ControllerStatus] == STATUS_DIGITAL); } else { // The other adapters don't bother to tell us, but we can still // make an educated guess. In analog mode, the axes center at 0x80. // In digital mode, they center at 0x7F, and the right stick is // fixed at center because it gets translated to presses of the // four face buttons instead. digital = (rawdata[desc->RightX] == 0x7F && rawdata[desc->RightY] == 0x7F); } // Convert axes to floating point and cancel out deadzones. ProcessThumbstick(rawdata[desc->LeftX], &Axes[AXIS_ThumbLX], rawdata[desc->LeftY], &Axes[AXIS_ThumbLY], KEY_PAD_LTHUMB_RIGHT); // If we know we are digital, ignore the right stick. if (digital) { ProcessThumbstick(0x80, &Axes[AXIS_ThumbRX], 0x80, &Axes[AXIS_ThumbRY], KEY_PAD_RTHUMB_RIGHT); } else { ProcessThumbstick(rawdata[desc->RightX], &Axes[AXIS_ThumbRX], rawdata[desc->RightY], &Axes[AXIS_ThumbRY], KEY_PAD_RTHUMB_RIGHT); } // Generate events for buttons that have changed. int buttons = 0; // If we know we are digital, ignore the D-Pad. if (!digital) { if (desc->DPadButtons >= 0) { buttons = rawdata[desc->DPadButtons] >> (4 * desc->DPadButtonsNibble); } else if (desc->DPadHat >= 0) { buttons = HatButtons[rawdata[desc->DPadHat] & 15]; } } buttons |= ((rawdata[desc->ButtonSet1] >> (4 * desc->ButtonSet1Nibble)) & 15) << 4; buttons |= ((rawdata[desc->ButtonSet2] >> (4 * desc->ButtonSet2Nibble)) & 15) << 8; buttons |= ((rawdata[desc->ButtonSet3] >> (4 * desc->ButtonSet3Nibble)) & 15) << 12; Joy_GenerateButtonEvents(LastButtons, buttons, 16, ButtonKeys); LastButtons = buttons; return true; } //========================================================================== // // FRawPS2Controller :: ProcessThumbstick STATIC // // Converts both axie of a thumb stick to floating point, cancels out the // deadzone, and generates button up/down events for them. // //========================================================================== void FRawPS2Controller::ProcessThumbstick(int value1, AxisInfo *axis1, int value2, AxisInfo *axis2, int base) { uint8_t buttonstate; double axisval1, axisval2; axisval1 = value1 * (2.0 / 255) - 1.0; axisval2 = value2 * (2.0 / 255) - 1.0; axisval1 = Joy_RemoveDeadZone(axisval1, axis1->DeadZone, NULL); axisval2 = Joy_RemoveDeadZone(axisval2, axis2->DeadZone, NULL); axis1->Value = float(axisval1); axis2->Value = float(axisval2); // We store all four buttons in the first axis and ignore the second. buttonstate = Joy_XYAxesToButtons(axisval1, axisval2); Joy_GenerateButtonEvents(axis1->ButtonValue, buttonstate, 4, base); axis1->ButtonValue = buttonstate; } //========================================================================== // // FRawPS2Controller :: Attached // // This controller was just attached. Set all buttons and axes to 0. // //========================================================================== void FRawPS2Controller::Attached() { int i; Connected = true; DisconnectCount = 0; LastButtons = 0; for (i = 0; i < NUM_AXES; ++i) { Axes[i].Value = 0; Axes[i].ButtonValue = 0; } UpdateJoystickMenu(this); } //========================================================================== // // FRawPS2Controller :: Detached // // This controller was just detached. Send button ups for buttons that // were pressed the last time we got input from it. // //========================================================================== void FRawPS2Controller::Detached() { Connected = false; NeutralInput(); UpdateJoystickMenu(NULL); } //========================================================================== // // FRawPS2Controller :: NeutralInput // // Sets the controller's state to a neutral one. Either because the // controller is disconnected or because we are in the background. // //========================================================================== void FRawPS2Controller::NeutralInput() { for (int i = 0; i < NUM_AXES; i += 2) { ProcessThumbstick(0x80, &Axes[i], 0x80, &Axes[i+1], KEY_PAD_LTHUMB_RIGHT + i*2); } Joy_GenerateButtonEvents(LastButtons, 0, 16, ButtonKeys); LastButtons = 0; } //========================================================================== // // FRawPS2Controller :: AddAxes // // Add the values of each axis to the game axes. // //========================================================================== void FRawPS2Controller::AddAxes(float axes[NUM_JOYAXIS]) { // Add to game axes. for (int i = 0; i < NUM_AXES; ++i) { axes[Axes[i].GameAxis] -= float(Axes[i].Value * Multiplier * Axes[i].Multiplier); } } //========================================================================== // // FRawPS2Controller :: SetDefaultConfig // //========================================================================== void FRawPS2Controller::SetDefaultConfig() { Multiplier = 1; for (int i = 0; i < NUM_AXES; ++i) { Axes[i].DeadZone = DEFAULT_DEADZONE; Axes[i].GameAxis = DefaultAxes[i].GameAxis; Axes[i].Multiplier = DefaultAxes[i].Multiplier; } } //========================================================================== // // FRawPS2Controller :: GetIdentifier // //========================================================================== FString FRawPS2Controller::GetIdentifier() { FString id = "PS2:"; id += DeviceID; return id; } //========================================================================== // // FRawPS2Controller :: GetName // //========================================================================== FString FRawPS2Controller::GetName() { FString res = Descriptors[Type].AdapterName; if (Sequence != 0) { res.AppendFormat(" #%d", Sequence); } return res; } //========================================================================== // // FRawPS2Controller :: GetSensitivity // //========================================================================== float FRawPS2Controller::GetSensitivity() { return Multiplier; } //========================================================================== // // FRawPS2Controller :: SetSensitivity // //========================================================================== void FRawPS2Controller::SetSensitivity(float scale) { Multiplier = scale; } //========================================================================== // // FRawPS2Controller :: IsSensitivityDefault // //========================================================================== bool FRawPS2Controller::IsSensitivityDefault() { return Multiplier == 1; } //========================================================================== // // FRawPS2Controller :: GetNumAxes // //========================================================================== int FRawPS2Controller::GetNumAxes() { return NUM_AXES; } //========================================================================== // // FRawPS2Controller :: GetAxisDeadZone // //========================================================================== float FRawPS2Controller::GetAxisDeadZone(int axis) { if (unsigned(axis) < NUM_AXES) { return Axes[axis].DeadZone; } return 0; } //========================================================================== // // FRawPS2Controller :: GetAxisMap // //========================================================================== EJoyAxis FRawPS2Controller::GetAxisMap(int axis) { if (unsigned(axis) < NUM_AXES) { return Axes[axis].GameAxis; } return JOYAXIS_None; } //========================================================================== // // FRawPS2Controller :: GetAxisName // //========================================================================== const char *FRawPS2Controller::GetAxisName(int axis) { if (unsigned(axis) < NUM_AXES) { return AxisNames[axis]; } return "Invalid"; } //========================================================================== // // FRawPS2Controller :: GetAxisScale // //========================================================================== float FRawPS2Controller::GetAxisScale(int axis) { if (unsigned(axis) < NUM_AXES) { return Axes[axis].Multiplier; } return 0; } //========================================================================== // // FRawPS2Controller :: SetAxisDeadZone // //========================================================================== void FRawPS2Controller::SetAxisDeadZone(int axis, float deadzone) { if (unsigned(axis) < NUM_AXES) { Axes[axis].DeadZone = clamp(deadzone, 0.f, 1.f); } } //========================================================================== // // FRawPS2Controller :: SetAxisMap // //========================================================================== void FRawPS2Controller::SetAxisMap(int axis, EJoyAxis gameaxis) { if (unsigned(axis) < NUM_AXES) { Axes[axis].GameAxis = (unsigned(gameaxis) < NUM_JOYAXIS) ? gameaxis : JOYAXIS_None; } } //========================================================================== // // FRawPS2Controller :: SetAxisScale // //========================================================================== void FRawPS2Controller::SetAxisScale(int axis, float scale) { if (unsigned(axis) < NUM_AXES) { Axes[axis].Multiplier = scale; } } //=========================================================================== // // FRawPS2Controller :: IsAxisDeadZoneDefault // //=========================================================================== bool FRawPS2Controller::IsAxisDeadZoneDefault(int axis) { if (unsigned(axis) < NUM_AXES) { return Axes[axis].DeadZone == DEFAULT_DEADZONE; } return true; } //=========================================================================== // // FRawPS2Controller :: IsAxisScaleDefault // //=========================================================================== bool FRawPS2Controller::IsAxisScaleDefault(int axis) { if (unsigned(axis) < NUM_AXES) { return Axes[axis].Multiplier == DefaultAxes[axis].Multiplier; } return true; } //=========================================================================== // // FRawPS2Controller :: IsAxisMapDefault // //=========================================================================== bool FRawPS2Controller::IsAxisMapDefault(int axis) { if (unsigned(axis) < NUM_AXES) { return Axes[axis].GameAxis == DefaultAxes[axis].GameAxis; } return true; } //========================================================================== // // FRawPS2Manager - Constructor // //========================================================================== FRawPS2Manager::FRawPS2Manager() { Registered = false; } //========================================================================== // // FRawPS2Manager - Destructor // //========================================================================== FRawPS2Manager::~FRawPS2Manager() { for (unsigned i = 0; i < Devices.Size(); ++i) { if (Devices[i] != NULL) { delete Devices[i]; } } } //========================================================================== // // FRawPS2Manager :: GetDevice // //========================================================================== bool FRawPS2Manager::GetDevice() { RAWINPUTDEVICE rid; rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE; rid.usUsage = HID_GDP_JOYSTICK; rid.dwFlags = RIDEV_INPUTSINK; rid.hwndTarget = Window; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { return false; } rid.dwFlags = RIDEV_REMOVE; rid.hwndTarget = NULL; // Must be NULL for RIDEV_REMOVE. RegisterRawInputDevices(&rid, 1, sizeof(rid)); EnumDevices(); return true; } //=========================================================================== // // FRawPS2Manager :: AddAxes // // Adds the state of all attached device axes to the passed array. // //=========================================================================== void FRawPS2Manager::AddAxes(float axes[NUM_JOYAXIS]) { for (unsigned i = 0; i < Devices.Size(); ++i) { if (Devices[i]->IsConnected()) { Devices[i]->AddAxes(axes); } } } //=========================================================================== // // FRawPS2Manager :: GetJoysticks // // Adds the IJoystick interfaces for each device we created to the sticks // array, if they are detected as connected. // //=========================================================================== void FRawPS2Manager::GetDevices(TArray &sticks) { for (unsigned i = 0; i < Devices.Size(); ++i) { if (Devices[i]->IsConnected()) { sticks.Push(Devices[i]); } } } //=========================================================================== // // FRawPS2Manager :: ProcessRawInput // //=========================================================================== bool FRawPS2Manager::ProcessRawInput(RAWINPUT *raw, int code) { if (raw->header.dwType != RIM_TYPEHID) { return false; } for (unsigned i = 0; i < Devices.Size(); ++i) { if (Devices[i]->Handle == raw->header.hDevice) { if (Devices[i]->ProcessInput(&raw->data.hid, code)) { return true; } } } return false; } //=========================================================================== // // FRawPS2Manager :: Rescan // //=========================================================================== IJoystickConfig *FRawPS2Manager::Rescan() { return EnumDevices(); } //=========================================================================== // // FRawPS2Manager :: EnumDevices // // Find out what PS2 adaptors we understand are on the system and create // FRawPS2Controller objects for them. May return a pointer to the first new // device found. // //=========================================================================== FRawPS2Controller *FRawPS2Manager::EnumDevices() { UINT nDevices, numDevices; RAWINPUTDEVICELIST *devices; UINT i, j; if (GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { return NULL; } if ((devices = (RAWINPUTDEVICELIST *)malloc(sizeof(RAWINPUTDEVICELIST) * nDevices)) == NULL) { return NULL; } if ((numDevices = GetRawInputDeviceList(devices, &nDevices, sizeof(RAWINPUTDEVICELIST))) == (UINT)-1) { free(devices); return NULL; } TArray adapters; for (i = 0; i < numDevices; ++i) { if (devices[i].dwType == RIM_TYPEHID) { RID_DEVICE_INFO rdi; UINT cbSize; cbSize = rdi.cbSize = sizeof(rdi); if ((INT)GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) >= 0) { // All the PS2 adapters report themselves as joysticks. // (By comparison, the 360 controller reports itself as a gamepad.) if (rdi.hid.usUsagePage == HID_GENERIC_DESKTOP_PAGE && rdi.hid.usUsage == HID_GDP_JOYSTICK) { EAdapterType type = ADAPTER_Unknown; // Check vendor and product IDs to see if we know what this is. if (rdi.hid.dwVendorId == VID_PLAY_COM) { if (rdi.hid.dwProductId == PID_EMS_USB2_PS2_CONTROLLER_ADAPTER) { type = ADAPTER_EMSUSB2; } } else if (rdi.hid.dwVendorId == VID_GREENASIA) { if (rdi.hid.dwProductId == PID_DRAGON_PS2_CONTROLLER_ADAPTER) { type = ADAPTER_DragonPlus; } else if (rdi.hid.dwProductId == PID_PANTHERLORD_PS2_CONTROLLER_ADAPTER) { type = ADAPTER_PantherLord; } } else if (rdi.hid.dwVendorId == VID_PERSONAL_COMMUNICATION_SYSTEMS) { if (rdi.hid.dwProductId == PID_TWIN_USB_VIBRATION_GAMEPAD) { type = ADAPTER_TwinUSB; } } if (type != ADAPTER_Unknown) { // Get the device name. Part of this is a path under HKLM\CurrentControlSet\Enum // with \ characters replaced by # characters. It is not a human-friendly name. // The layout for the name string is: // ## // The Device ID has multiple #-seperated parts and uniquely identifies // this device and which USB port it is connected to. wchar_t name[256]; UINT namelen = countof(name); wchar_t *devid, *devidend; if (GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, name, &namelen) == (UINT)-1) { // Can't get name. Skip it, since there's stuff in there we need for config. continue; } devid = wcschr(name, '#'); if (devid == NULL) { // Should not happen continue; } devidend = wcsrchr(++devid, '#'); if (devidend != NULL) { *devidend = '\0'; } FAdapterHandle handle = { devices[i].hDevice, type, 0, FString(devid) }; // Adapters that support more than one controller have a seperate device // entry for each controller. We can examine the name to determine which // controller this device is for. if (Descriptors[type].ControllerNumber >= 0) { wchar_t *col = wcsstr(devid, L"&Col"); if (col != NULL) { // I have no idea if this number is base 16 or base 10. Every // other number in the name is base 16, so I assume this one is // too, but since I don't have anything that goes higher than 02, // I can't be sure. handle.ControllerNumber = wcstoul(col + 4, NULL, 16); } } adapters.Push(handle); } } } } } free(devices); // Sort the found devices so that we have a consistant ordering. qsort(&adapters[0], adapters.Size(), sizeof(FAdapterHandle), DeviceSort); // Compare the new list of devices with the one we previously instantiated. // Every device we currently hold is marked 0. Then scan through the new // list and try to find it there, if it's found, it is marked 1. At the end // of this, devices marked 1 existed before and are left alone. Devices // marked 0 are no longer present and should be destroyed. If a device is // present in the new list that we have not yet instantiated, we // instantiate it now. FRawPS2Controller *newone = NULL; EAdapterType lasttype = ADAPTER_Unknown; int sequence = 0; // Resets to 0 or 1 each time the adapter type changes for (j = 0; j < Devices.Size(); ++j) { Devices[j]->Marked = false; } for (i = 0; i < adapters.Size(); ++i) { FAdapterHandle *adapter = &adapters[i]; if (adapter->Type != lasttype) { lasttype = adapter->Type; // Peak ahead. If the next adapter has the same type, use 1. // Otherwise, use 0. (0 means to not append a number to // the device name.) if (i == adapters.Size() - 1 || adapter->Type != adapters[i+1].Type) { sequence = 0; } else { sequence = 1; } } for (j = 0; j < Devices.Size(); ++j) { if (Devices[j]->Handle == adapter->Handle) { Devices[j]->Marked = true; break; } } if (j == Devices.Size()) { // Not found. Add it. FRawPS2Controller *device = new FRawPS2Controller(adapter->Handle, adapter->Type, sequence++, adapter->ControllerNumber, adapter->DeviceID); device->Marked = true; Devices.Push(device); if (newone == NULL) { newone = device; } } } // Remove detached devices and avoid holes in the list. for (i = j = 0; j < Devices.Size(); ++j) { if (!Devices[j]->Marked) { delete Devices[j]; } else { if (i != j) { Devices[i] = Devices[j]; } ++i; } } Devices.Resize(i); DoRegister(); return newone; } //=========================================================================== // // FRawPS2Manager :: DeviceSort STATIC // // Sorts first by device type, then by ID, then by controller number. // //=========================================================================== int FRawPS2Manager::DeviceSort(const void *a, const void *b) { const FAdapterHandle *ha = (const FAdapterHandle *)a; const FAdapterHandle *hb = (const FAdapterHandle *)b; int lex = ha->Type - hb->Type; if (lex == 0) { // Skip device part of the ID and sort the connection part const char *ca = strchr(ha->DeviceID, '#'); const char *cb = strchr(hb->DeviceID, '#'); const char *ea, *eb; // The last bit looks like a controller number. Strip it out to be safe // if this is a multi-controller adapter. if (ha->ControllerNumber != 0) { ea = strrchr(ca, '&'); eb = strrchr(cb, '&'); } else { ea = strlen(ca) + ca; eb = strlen(cb) + cb; } for (; ca < ea && cb < eb && lex == 0; ++ca, ++cb) { lex = *ca - *cb; } if (lex == 0) { lex = ha->ControllerNumber - hb->ControllerNumber; } } return lex; } //=========================================================================== // // FRawPS2Manager :: DoRegister // // Ensure that we are only listening for input if devices we care about // are attached to the system. // //=========================================================================== void FRawPS2Manager::DoRegister() { RAWINPUTDEVICE rid; rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE; rid.usUsage = HID_GDP_JOYSTICK; if (Devices.Size() == 0) { if (Registered) { rid.dwFlags = RIDEV_REMOVE; rid.hwndTarget = NULL; if (RegisterRawInputDevices(&rid, 1, sizeof(rid))) { Registered = false; } } } else { if (!Registered) { rid.dwFlags = RIDEV_INPUTSINK; rid.hwndTarget = Window; if (RegisterRawInputDevices(&rid, 1, sizeof(rid))) { Registered = true; } } } } //=========================================================================== // // I_StartupRawPS2 // //=========================================================================== void I_StartupRawPS2() { if (!joy_ps2raw || !use_joystick || Args->CheckParm("-nojoy")) { if (JoyDevices[INPUT_RawPS2] != NULL) { delete JoyDevices[INPUT_RawPS2]; JoyDevices[INPUT_RawPS2] = NULL; UpdateJoystickMenu(NULL); } } else { if (JoyDevices[INPUT_RawPS2] == NULL) { FJoystickCollection *joys = new FRawPS2Manager; if (joys->GetDevice()) { JoyDevices[INPUT_RawPS2] = joys; } else { delete joys; } } } } //=========================================================================== // // I_IsPS2Adapter // // The Data1 part of a DirectInput product GUID contains the device's vendor // and product IDs. Returns true if we know what this device is. // //=========================================================================== bool I_IsPS2Adapter(DWORD vidpid) { return (vidpid == MAKELONG(VID_PLAY_COM, PID_EMS_USB2_PS2_CONTROLLER_ADAPTER) || vidpid == MAKELONG(VID_GREENASIA, PID_DRAGON_PS2_CONTROLLER_ADAPTER) || vidpid == MAKELONG(VID_GREENASIA, PID_PANTHERLORD_PS2_CONTROLLER_ADAPTER) || vidpid == MAKELONG(VID_PERSONAL_COMMUNICATION_SYSTEMS, PID_TWIN_USB_VIBRATION_GAMEPAD)); }