| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> |
| | | |
| | | This software is provided 'as-is', without any express or implied |
| | | warranty. In no event will the authors be held liable for any damages |
| | |
| | | * Internal stuff. |
| | | */ |
| | | static SDL_bool s_bXInputEnabled = SDL_TRUE; |
| | | static char *s_arrXInputDevicePath[XUSER_MAX_COUNT]; |
| | | |
| | | |
| | | static SDL_bool |
| | | SDL_XInputUseOldJoystickMapping() |
| | | { |
| | | #ifdef __WINRT__ |
| | | /* TODO: remove this __WINRT__ block, but only after integrating with UWP/WinRT's HID API */ |
| | | /* FIXME: Why are Win8/10 different here? -flibit */ |
| | | return (NTDDI_VERSION < NTDDI_WIN10); |
| | | #else |
| | | static int s_XInputUseOldJoystickMapping = -1; |
| | | if (s_XInputUseOldJoystickMapping < 0) { |
| | | s_XInputUseOldJoystickMapping = SDL_GetHintBoolean(SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING, SDL_FALSE); |
| | | } |
| | | return (s_XInputUseOldJoystickMapping > 0); |
| | | #endif |
| | | } |
| | | |
| | | SDL_bool SDL_XINPUT_Enabled(void) |
| | |
| | | return SDL_strdup(name); |
| | | } |
| | | |
| | | /* We can't really tell what device is being used for XInput, but we can guess |
| | | and we'll be correct for the case where only one device is connected. |
| | | */ |
| | | static void |
| | | AddXInputDevice(const Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext) |
| | | GuessXInputDevice(Uint8 userid, Uint16 *pVID, Uint16 *pPID, Uint16 *pVersion) |
| | | { |
| | | #ifndef __WINRT__ /* TODO: remove this ifndef __WINRT__ block, but only after integrating with UWP/WinRT's HID API */ |
| | | |
| | | PRAWINPUTDEVICELIST devices = NULL; |
| | | UINT i, j, device_count = 0; |
| | | |
| | | if ((GetRawInputDeviceList(NULL, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!device_count)) { |
| | | return; /* oh well. */ |
| | | } |
| | | |
| | | devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * device_count); |
| | | if (devices == NULL) { |
| | | return; |
| | | } |
| | | |
| | | if (GetRawInputDeviceList(devices, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) { |
| | | SDL_free(devices); |
| | | return; /* oh well. */ |
| | | } |
| | | |
| | | for (i = 0; i < device_count; i++) { |
| | | RID_DEVICE_INFO rdi; |
| | | char devName[128]; |
| | | UINT rdiSize = sizeof(rdi); |
| | | UINT nameSize = SDL_arraysize(devName); |
| | | |
| | | rdi.cbSize = sizeof(rdi); |
| | | if ((devices[i].dwType == RIM_TYPEHID) && |
| | | (GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) && |
| | | (GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) && |
| | | (SDL_strstr(devName, "IG_") != NULL)) { |
| | | SDL_bool found = SDL_FALSE; |
| | | for (j = 0; j < SDL_arraysize(s_arrXInputDevicePath); ++j) { |
| | | if (j == userid) { |
| | | continue; |
| | | } |
| | | if (!s_arrXInputDevicePath[j]) { |
| | | continue; |
| | | } |
| | | if (SDL_strcmp(devName, s_arrXInputDevicePath[j]) == 0) { |
| | | found = SDL_TRUE; |
| | | break; |
| | | } |
| | | } |
| | | if (found) { |
| | | /* We already have this device in our XInput device list */ |
| | | continue; |
| | | } |
| | | |
| | | /* We don't actually know if this is the right device for this |
| | | * userid, but we'll record it so we'll at least be consistent |
| | | * when the raw device list changes. |
| | | */ |
| | | *pVID = (Uint16)rdi.hid.dwVendorId; |
| | | *pPID = (Uint16)rdi.hid.dwProductId; |
| | | *pVersion = (Uint16)rdi.hid.dwVersionNumber; |
| | | if (s_arrXInputDevicePath[userid]) { |
| | | SDL_free(s_arrXInputDevicePath[userid]); |
| | | } |
| | | s_arrXInputDevicePath[userid] = SDL_strdup(devName); |
| | | break; |
| | | } |
| | | } |
| | | SDL_free(devices); |
| | | #endif /* ifndef __WINRT__ */ |
| | | } |
| | | |
| | | static void |
| | | AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext) |
| | | { |
| | | JoyStick_DeviceData *pPrevJoystick = NULL; |
| | | JoyStick_DeviceData *pNewJoystick = *pContext; |
| | |
| | | if (SDL_XInputUseOldJoystickMapping()) { |
| | | SDL_zero(pNewJoystick->guid); |
| | | } else { |
| | | pNewJoystick->guid.data[0] = 'x'; |
| | | pNewJoystick->guid.data[1] = 'i'; |
| | | pNewJoystick->guid.data[2] = 'n'; |
| | | pNewJoystick->guid.data[3] = 'p'; |
| | | pNewJoystick->guid.data[4] = 'u'; |
| | | pNewJoystick->guid.data[5] = 't'; |
| | | pNewJoystick->guid.data[6] = SubType; |
| | | const Uint16 BUS_USB = 0x03; |
| | | Uint16 vendor = 0; |
| | | Uint16 product = 0; |
| | | Uint16 version = 0; |
| | | Uint16 *guid16 = (Uint16 *)pNewJoystick->guid.data; |
| | | |
| | | GuessXInputDevice(userid, &vendor, &product, &version); |
| | | |
| | | *guid16++ = SDL_SwapLE16(BUS_USB); |
| | | *guid16++ = 0; |
| | | *guid16++ = SDL_SwapLE16(vendor); |
| | | *guid16++ = 0; |
| | | *guid16++ = SDL_SwapLE16(product); |
| | | *guid16++ = 0; |
| | | *guid16++ = SDL_SwapLE16(version); |
| | | *guid16++ = 0; |
| | | |
| | | /* Note that this is an XInput device and what subtype it is */ |
| | | pNewJoystick->guid.data[14] = 'x'; |
| | | pNewJoystick->guid.data[15] = SubType; |
| | | } |
| | | pNewJoystick->SubType = SubType; |
| | | pNewJoystick->XInputUserId = userid; |
| | | |
| | | if (SDL_ShouldIgnoreGameController(pNewJoystick->joystickname, pNewJoystick->guid)) { |
| | | SDL_free(pNewJoystick); |
| | | return; |
| | | } |
| | | |
| | | SDL_SYS_AddJoystickDevice(pNewJoystick); |
| | | } |
| | | |
| | |
| | | static void |
| | | UpdateXInputJoystickBatteryInformation(SDL_Joystick * joystick, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation) |
| | | { |
| | | if ( pBatteryInformation->BatteryType != BATTERY_TYPE_UNKNOWN ) |
| | | { |
| | | if (pBatteryInformation->BatteryType != BATTERY_TYPE_UNKNOWN) { |
| | | SDL_JoystickPowerLevel ePowerLevel = SDL_JOYSTICK_POWER_UNKNOWN; |
| | | if (pBatteryInformation->BatteryType == BATTERY_TYPE_WIRED) { |
| | | ePowerLevel = SDL_JOYSTICK_POWER_WIRED; |
| | | } else { |
| | | switch ( pBatteryInformation->BatteryLevel ) |
| | | { |
| | | switch (pBatteryInformation->BatteryLevel) { |
| | | case BATTERY_LEVEL_EMPTY: |
| | | ePowerLevel = SDL_JOYSTICK_POWER_EMPTY; |
| | | break; |
| | |
| | | } |
| | | } |
| | | |
| | | SDL_PrivateJoystickBatteryLevel( joystick, ePowerLevel ); |
| | | SDL_PrivateJoystickBatteryLevel(joystick, ePowerLevel); |
| | | } |
| | | } |
| | | |
| | |
| | | SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED); |
| | | } |
| | | |
| | | UpdateXInputJoystickBatteryInformation( joystick, pBatteryInformation ); |
| | | UpdateXInputJoystickBatteryInformation(joystick, pBatteryInformation); |
| | | } |
| | | |
| | | static void |
| | |
| | | } |
| | | SDL_PrivateJoystickHat(joystick, 0, hat); |
| | | |
| | | UpdateXInputJoystickBatteryInformation( joystick, pBatteryInformation ); |
| | | UpdateXInputJoystickBatteryInformation(joystick, pBatteryInformation); |
| | | } |
| | | |
| | | void |
| | |
| | | |
| | | result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState); |
| | | if (result == ERROR_DEVICE_NOT_CONNECTED) { |
| | | Uint8 userid = joystick->hwdata->userid; |
| | | |
| | | joystick->hwdata->send_remove_event = SDL_TRUE; |
| | | joystick->hwdata->removed = SDL_TRUE; |
| | | if (s_arrXInputDevicePath[userid]) { |
| | | SDL_free(s_arrXInputDevicePath[userid]); |
| | | s_arrXInputDevicePath[userid] = NULL; |
| | | } |
| | | return; |
| | | } |
| | | |
| | | SDL_zero( XBatteryInformation ); |
| | | if ( XINPUTGETBATTERYINFORMATION ) |
| | | { |
| | | result = XINPUTGETBATTERYINFORMATION( joystick->hwdata->userid, BATTERY_DEVTYPE_GAMEPAD, &XBatteryInformation ); |
| | | SDL_zero(XBatteryInformation); |
| | | if (XINPUTGETBATTERYINFORMATION) { |
| | | result = XINPUTGETBATTERYINFORMATION(joystick->hwdata->userid, BATTERY_DEVTYPE_GAMEPAD, &XBatteryInformation); |
| | | } |
| | | |
| | | /* only fire events if the data changed from last time */ |