diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c index abb0ff296..af623c8ca 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -31,6 +31,7 @@ #include "../../core/windows/SDL_windows.h" #define COBJMACROS #include "windows.gaming.input.h" +#include struct joystick_hwdata @@ -123,17 +124,68 @@ SDL_IsXInputDevice(Uint16 vendor, Uint16 product) char devName[MAX_PATH]; UINT rdiSize = sizeof(rdi); UINT nameSize = SDL_arraysize(devName); + DEVINST devNode; + char devVidPidString[32]; + int j; rdi.cbSize = sizeof(rdi); - if ((raw_devices[i].dwType == RIM_TYPEHID) && - (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) && - (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) && - (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) && - (SDL_strstr(devName, "IG_") != NULL)) { + + if ((raw_devices[i].dwType != RIM_TYPEHID) || + (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) == ((UINT)-1)) || + (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) == ((UINT)-1)) || + (SDL_strstr(devName, "IG_") == NULL)) { + /* Skip non-XInput devices */ + continue; + } + + /* First check for a simple VID/PID match. This will work for Xbox 360 controllers. */ + if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) { + SDL_free(raw_devices); return SDL_TRUE; } + + /* For Xbox One controllers, Microsoft doesn't propagate the VID/PID down to the HID stack. + * We'll have to walk the device tree upwards searching for a match for our VID/PID. */ + + /* Make sure the device interface string is something we know how to parse */ + /* Example: \\?\HID#VID_045E&PID_02FF&IG_00#9&2c203035&2&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} */ + if ((SDL_strstr(devName, "\\\\?\\") != devName) || (SDL_strstr(devName, "#{") == NULL)) { + continue; + } + + /* Unescape the backslashes in the string and terminate before the GUID portion */ + for (j = 0; devName[j] != '\0'; j++) { + if (devName[j] == '#') { + if (devName[j + 1] == '{') { + devName[j] = '\0'; + break; + } else { + devName[j] = '\\'; + } + } + } + + /* We'll be left with a string like this: \\?\HID\VID_045E&PID_02FF&IG_00\9&2c203035&2&0000 + * Simply skip the \\?\ prefix and we'll have a properly formed device instance ID */ + if (CM_Locate_DevNodeA(&devNode, &devName[4], CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) { + continue; + } + + SDL_snprintf(devVidPidString, sizeof(devVidPidString), "VID_%04X&PID_%04X", vendor, product); + + while (CM_Get_Parent(&devNode, devNode, 0) == CR_SUCCESS) { + char deviceId[MAX_DEVICE_ID_LEN]; + + if ((CM_Get_Device_IDA(devNode, deviceId, SDL_arraysize(deviceId), 0) == CR_SUCCESS) && + (SDL_strstr(deviceId, devVidPidString) != NULL)) { + /* The VID/PID matched a parent device */ + SDL_free(raw_devices); + return SDL_TRUE; + } + } } + SDL_free(raw_devices); return SDL_FALSE; }