sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_xinputjoystick.c (20677B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 */
     21 #include "../../SDL_internal.h"
     22 
     23 #include "../SDL_sysjoystick.h"
     24 
     25 #if SDL_JOYSTICK_XINPUT
     26 
     27 #include "SDL_hints.h"
     28 #include "SDL_timer.h"
     29 #include "SDL_windowsjoystick_c.h"
     30 #include "SDL_xinputjoystick_c.h"
     31 #include "SDL_rawinputjoystick_c.h"
     32 #include "../hidapi/SDL_hidapijoystick_c.h"
     33 
     34 /*
     35  * Internal stuff.
     36  */
     37 static SDL_bool s_bXInputEnabled = SDL_TRUE;
     38 static char *s_arrXInputDevicePath[XUSER_MAX_COUNT];
     39 
     40 
     41 static SDL_bool
     42 SDL_XInputUseOldJoystickMapping()
     43 {
     44 #ifdef __WINRT__
     45     /* TODO: remove this __WINRT__ block, but only after integrating with UWP/WinRT's HID API */
     46     /* FIXME: Why are Win8/10 different here? -flibit */
     47     return (NTDDI_VERSION < NTDDI_WIN10);
     48 #else
     49     static int s_XInputUseOldJoystickMapping = -1;
     50     if (s_XInputUseOldJoystickMapping < 0) {
     51         s_XInputUseOldJoystickMapping = SDL_GetHintBoolean(SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING, SDL_FALSE);
     52     }
     53     return (s_XInputUseOldJoystickMapping > 0);
     54 #endif
     55 }
     56 
     57 SDL_bool SDL_XINPUT_Enabled(void)
     58 {
     59     return s_bXInputEnabled;
     60 }
     61 
     62 int
     63 SDL_XINPUT_JoystickInit(void)
     64 {
     65     s_bXInputEnabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE);
     66 
     67 #ifdef SDL_JOYSTICK_RAWINPUT
     68     if (RAWINPUT_IsEnabled()) {
     69         /* The raw input driver handles more than 4 controllers, so prefer that when available */
     70         s_bXInputEnabled = SDL_FALSE;
     71     }
     72 #endif
     73 
     74     if (s_bXInputEnabled && WIN_LoadXInputDLL() < 0) {
     75         s_bXInputEnabled = SDL_FALSE;  /* oh well. */
     76     }
     77     return 0;
     78 }
     79 
     80 static const char *
     81 GetXInputName(const Uint8 userid, BYTE SubType)
     82 {
     83     static char name[32];
     84 
     85     if (SDL_XInputUseOldJoystickMapping()) {
     86         SDL_snprintf(name, sizeof(name), "X360 Controller #%u", 1 + userid);
     87     } else {
     88         switch (SubType) {
     89         case XINPUT_DEVSUBTYPE_GAMEPAD:
     90             SDL_snprintf(name, sizeof(name), "XInput Controller #%u", 1 + userid);
     91             break;
     92         case XINPUT_DEVSUBTYPE_WHEEL:
     93             SDL_snprintf(name, sizeof(name), "XInput Wheel #%u", 1 + userid);
     94             break;
     95         case XINPUT_DEVSUBTYPE_ARCADE_STICK:
     96             SDL_snprintf(name, sizeof(name), "XInput ArcadeStick #%u", 1 + userid);
     97             break;
     98         case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
     99             SDL_snprintf(name, sizeof(name), "XInput FlightStick #%u", 1 + userid);
    100             break;
    101         case XINPUT_DEVSUBTYPE_DANCE_PAD:
    102             SDL_snprintf(name, sizeof(name), "XInput DancePad #%u", 1 + userid);
    103             break;
    104         case XINPUT_DEVSUBTYPE_GUITAR:
    105         case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
    106         case XINPUT_DEVSUBTYPE_GUITAR_BASS:
    107             SDL_snprintf(name, sizeof(name), "XInput Guitar #%u", 1 + userid);
    108             break;
    109         case XINPUT_DEVSUBTYPE_DRUM_KIT:
    110             SDL_snprintf(name, sizeof(name), "XInput DrumKit #%u", 1 + userid);
    111             break;
    112         case XINPUT_DEVSUBTYPE_ARCADE_PAD:
    113             SDL_snprintf(name, sizeof(name), "XInput ArcadePad #%u", 1 + userid);
    114             break;
    115         default:
    116             SDL_snprintf(name, sizeof(name), "XInput Device #%u", 1 + userid);
    117             break;
    118         }
    119     }
    120     return name;
    121 }
    122 
    123 /* We can't really tell what device is being used for XInput, but we can guess
    124    and we'll be correct for the case where only one device is connected.
    125  */
    126 static void
    127 GuessXInputDevice(Uint8 userid, Uint16 *pVID, Uint16 *pPID, Uint16 *pVersion)
    128 {
    129 #ifndef __WINRT__   /* TODO: remove this ifndef __WINRT__ block, but only after integrating with UWP/WinRT's HID API */
    130 
    131     PRAWINPUTDEVICELIST devices = NULL;
    132     UINT i, j, device_count = 0;
    133 
    134     if ((GetRawInputDeviceList(NULL, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!device_count)) {
    135         return;  /* oh well. */
    136     }
    137 
    138     devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * device_count);
    139     if (devices == NULL) {
    140         return;
    141     }
    142 
    143     if (GetRawInputDeviceList(devices, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) {
    144         SDL_free(devices);
    145         return;  /* oh well. */
    146     }
    147 
    148     /* First see if we have a cached entry for this index */
    149     if (s_arrXInputDevicePath[userid]) {
    150         for (i = 0; i < device_count; i++) {
    151             RID_DEVICE_INFO rdi;
    152             char devName[128];
    153             UINT rdiSize = sizeof(rdi);
    154             UINT nameSize = SDL_arraysize(devName);
    155 
    156             rdi.cbSize = sizeof(rdi);
    157             if (devices[i].dwType == RIM_TYPEHID &&
    158                 GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != (UINT)-1 &&
    159                 GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != (UINT)-1) {
    160                 if (SDL_strcmp(devName, s_arrXInputDevicePath[userid]) == 0) {
    161                     *pVID = (Uint16)rdi.hid.dwVendorId;
    162                     *pPID = (Uint16)rdi.hid.dwProductId;
    163                     *pVersion = (Uint16)rdi.hid.dwVersionNumber;
    164                     SDL_free(devices);
    165                     return;
    166                 }
    167             }
    168         }
    169     }
    170 
    171     for (i = 0; i < device_count; i++) {
    172         RID_DEVICE_INFO rdi;
    173         char devName[MAX_PATH];
    174         UINT rdiSize = sizeof(rdi);
    175         UINT nameSize = SDL_arraysize(devName);
    176 
    177         rdi.cbSize = sizeof(rdi);
    178         if (devices[i].dwType == RIM_TYPEHID &&
    179             GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != (UINT)-1 &&
    180             GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != (UINT)-1) {
    181 #ifdef DEBUG_JOYSTICK
    182             SDL_Log("Raw input device: VID = 0x%x, PID = 0x%x, %s\n", rdi.hid.dwVendorId, rdi.hid.dwProductId, devName);
    183 #endif
    184             if (SDL_strstr(devName, "IG_") != NULL) {
    185                 SDL_bool found = SDL_FALSE;
    186                 for (j = 0; j < SDL_arraysize(s_arrXInputDevicePath); ++j) {
    187                     if (!s_arrXInputDevicePath[j]) {
    188                         continue;
    189                     }
    190                     if (SDL_strcmp(devName, s_arrXInputDevicePath[j]) == 0) {
    191                         found = SDL_TRUE;
    192                         break;
    193                     }
    194                 }
    195                 if (found) {
    196                     /* We already have this device in our XInput device list */
    197                     continue;
    198                 }
    199 
    200                 /* We don't actually know if this is the right device for this
    201                  * userid, but we'll record it so we'll at least be consistent
    202                  * when the raw device list changes.
    203                  */
    204                 *pVID = (Uint16)rdi.hid.dwVendorId;
    205                 *pPID = (Uint16)rdi.hid.dwProductId;
    206                 *pVersion = (Uint16)rdi.hid.dwVersionNumber;
    207                 if (s_arrXInputDevicePath[userid]) {
    208                     SDL_free(s_arrXInputDevicePath[userid]);
    209                 }
    210                 s_arrXInputDevicePath[userid] = SDL_strdup(devName);
    211                 SDL_free(devices);
    212                 return;
    213             }
    214         }
    215     }
    216     SDL_free(devices);
    217 #endif  /* !__WINRT__ */
    218 
    219     /* The device wasn't in the raw HID device list, it's probably Bluetooth */
    220     *pVID = 0x045e; /* Microsoft */
    221     *pPID = 0x02fd; /* XBox One S Bluetooth */
    222     *pVersion = 0;
    223 }
    224 
    225 static void
    226 AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
    227 {
    228     Uint16 vendor = 0;
    229     Uint16 product = 0;
    230     Uint16 version = 0;
    231     JoyStick_DeviceData *pPrevJoystick = NULL;
    232     JoyStick_DeviceData *pNewJoystick = *pContext;
    233 
    234     if (SDL_XInputUseOldJoystickMapping() && SubType != XINPUT_DEVSUBTYPE_GAMEPAD)
    235         return;
    236 
    237     if (SubType == XINPUT_DEVSUBTYPE_UNKNOWN)
    238         return;
    239 
    240     while (pNewJoystick) {
    241         if (pNewJoystick->bXInputDevice && (pNewJoystick->XInputUserId == userid) && (pNewJoystick->SubType == SubType)) {
    242             /* if we are replacing the front of the list then update it */
    243             if (pNewJoystick == *pContext) {
    244                 *pContext = pNewJoystick->pNext;
    245             } else if (pPrevJoystick) {
    246                 pPrevJoystick->pNext = pNewJoystick->pNext;
    247             }
    248 
    249             pNewJoystick->pNext = SYS_Joystick;
    250             SYS_Joystick = pNewJoystick;
    251             return;   /* already in the list. */
    252         }
    253 
    254         pPrevJoystick = pNewJoystick;
    255         pNewJoystick = pNewJoystick->pNext;
    256     }
    257 
    258     pNewJoystick = (JoyStick_DeviceData *)SDL_calloc(1, sizeof(JoyStick_DeviceData));
    259     if (!pNewJoystick) {
    260         return; /* better luck next time? */
    261     }
    262 
    263     pNewJoystick->bXInputDevice = SDL_TRUE;
    264     if (!SDL_XInputUseOldJoystickMapping()) {
    265         Uint16 *guid16 = (Uint16 *)pNewJoystick->guid.data;
    266 
    267         GuessXInputDevice(userid, &vendor, &product, &version);
    268 
    269         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
    270         *guid16++ = 0;
    271         *guid16++ = SDL_SwapLE16(vendor);
    272         *guid16++ = 0;
    273         *guid16++ = SDL_SwapLE16(product);
    274         *guid16++ = 0;
    275         *guid16++ = SDL_SwapLE16(version);
    276         *guid16++ = 0;
    277 
    278         /* Note that this is an XInput device and what subtype it is */
    279         pNewJoystick->guid.data[14] = 'x';
    280         pNewJoystick->guid.data[15] = SubType;
    281     }
    282     pNewJoystick->SubType = SubType;
    283     pNewJoystick->XInputUserId = userid;
    284     pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, NULL, GetXInputName(userid, SubType));
    285     if (!pNewJoystick->joystickname) {
    286         SDL_free(pNewJoystick);
    287         return; /* better luck next time? */
    288     }
    289 
    290     if (SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)) {
    291         SDL_free(pNewJoystick);
    292         return;
    293     }
    294 
    295 #ifdef SDL_JOYSTICK_HIDAPI
    296     /* Since we're guessing about the VID/PID, use a hard-coded VID/PID to represent XInput */
    297     if (HIDAPI_IsDevicePresent(USB_VENDOR_MICROSOFT, USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER, version, pNewJoystick->joystickname)) {
    298         /* The HIDAPI driver is taking care of this device */
    299         SDL_free(pNewJoystick);
    300         return;
    301     }
    302 #endif
    303 
    304 #ifdef SDL_JOYSTICK_RAWINPUT
    305     if (RAWINPUT_IsDevicePresent(vendor, product, version, pNewJoystick->joystickname)) {
    306         /* The RAWINPUT driver is taking care of this device */
    307         SDL_free(pNewJoystick);
    308         return;
    309     }
    310 #endif
    311 
    312     WINDOWS_AddJoystickDevice(pNewJoystick);
    313 }
    314 
    315 static void
    316 DelXInputDevice(Uint8 userid)
    317 {
    318     if (s_arrXInputDevicePath[userid]) {
    319         SDL_free(s_arrXInputDevicePath[userid]);
    320         s_arrXInputDevicePath[userid] = NULL;
    321     }
    322 }
    323 
    324 void
    325 SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
    326 {
    327     int iuserid;
    328 
    329     if (!s_bXInputEnabled) {
    330         return;
    331     }
    332 
    333     /* iterate in reverse, so these are in the final list in ascending numeric order. */
    334     for (iuserid = XUSER_MAX_COUNT - 1; iuserid >= 0; iuserid--) {
    335         const Uint8 userid = (Uint8)iuserid;
    336         XINPUT_CAPABILITIES capabilities;
    337         if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
    338             /* Adding a new device, must handle all removes first, or GuessXInputDevice goes terribly wrong (returns
    339               a product/vendor ID that is not even attached to the system) when we get a remove and add on the same tick
    340               (e.g. when disconnecting a device and the OS reassigns which userid an already-attached controller is)
    341             */
    342             int iuserid2;
    343             for (iuserid2 = iuserid - 1; iuserid2 >= 0; iuserid2--) {
    344                 const Uint8 userid2 = (Uint8)iuserid2;
    345                 XINPUT_CAPABILITIES capabilities2;
    346                 if (XINPUTGETCAPABILITIES(userid2, XINPUT_FLAG_GAMEPAD, &capabilities2) != ERROR_SUCCESS) {
    347                     DelXInputDevice(userid2);
    348                 }
    349             }
    350             AddXInputDevice(userid, capabilities.SubType, pContext);
    351         } else {
    352             DelXInputDevice(userid);
    353         }
    354     }
    355 }
    356 
    357 int
    358 SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
    359 {
    360     const Uint8 userId = joystickdevice->XInputUserId;
    361     XINPUT_CAPABILITIES capabilities;
    362     XINPUT_VIBRATION state;
    363 
    364     SDL_assert(s_bXInputEnabled);
    365     SDL_assert(XINPUTGETCAPABILITIES);
    366     SDL_assert(XINPUTSETSTATE);
    367     SDL_assert(userId < XUSER_MAX_COUNT);
    368 
    369     joystick->hwdata->bXInputDevice = SDL_TRUE;
    370 
    371     if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) {
    372         SDL_free(joystick->hwdata);
    373         joystick->hwdata = NULL;
    374         return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?");
    375     }
    376     SDL_zero(state);
    377     joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS);
    378     joystick->hwdata->userid = userId;
    379 
    380     /* The XInput API has a hard coded button/axis mapping, so we just match it */
    381     if (SDL_XInputUseOldJoystickMapping()) {
    382         joystick->naxes = 6;
    383         joystick->nbuttons = 15;
    384     } else {
    385         joystick->naxes = 6;
    386         joystick->nbuttons = 11;
    387         joystick->nhats = 1;
    388     }
    389     return 0;
    390 }
    391 
    392 static void 
    393 UpdateXInputJoystickBatteryInformation(SDL_Joystick * joystick, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
    394 {
    395     if (pBatteryInformation->BatteryType != BATTERY_TYPE_UNKNOWN) {
    396         SDL_JoystickPowerLevel ePowerLevel = SDL_JOYSTICK_POWER_UNKNOWN;
    397         if (pBatteryInformation->BatteryType == BATTERY_TYPE_WIRED) {
    398             ePowerLevel = SDL_JOYSTICK_POWER_WIRED;
    399         } else {
    400             switch (pBatteryInformation->BatteryLevel) {
    401             case BATTERY_LEVEL_EMPTY:
    402                 ePowerLevel = SDL_JOYSTICK_POWER_EMPTY;
    403                 break;
    404             case BATTERY_LEVEL_LOW:
    405                 ePowerLevel = SDL_JOYSTICK_POWER_LOW;
    406                 break;
    407             case BATTERY_LEVEL_MEDIUM:
    408                 ePowerLevel = SDL_JOYSTICK_POWER_MEDIUM;
    409                 break;
    410             default:
    411             case BATTERY_LEVEL_FULL:
    412                 ePowerLevel = SDL_JOYSTICK_POWER_FULL;
    413                 break;
    414             }
    415         }
    416 
    417         SDL_PrivateJoystickBatteryLevel(joystick, ePowerLevel);
    418     }
    419 }
    420 
    421 static void
    422 UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
    423 {
    424     static WORD s_XInputButtons[] = {
    425         XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT,
    426         XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
    427         XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER,
    428         XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
    429         XINPUT_GAMEPAD_GUIDE
    430     };
    431     WORD wButtons = pXInputState->Gamepad.wButtons;
    432     Uint8 button;
    433 
    434     SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
    435     SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
    436     SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX);
    437     SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
    438     SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
    439     SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
    440 
    441     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
    442         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
    443     }
    444 
    445     UpdateXInputJoystickBatteryInformation(joystick, pBatteryInformation);
    446 }
    447 
    448 static void
    449 UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
    450 {
    451     static WORD s_XInputButtons[] = {
    452         XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
    453         XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START,
    454         XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
    455         XINPUT_GAMEPAD_GUIDE
    456     };
    457     WORD wButtons = pXInputState->Gamepad.wButtons;
    458     Uint8 button;
    459     Uint8 hat = SDL_HAT_CENTERED;
    460 
    461     SDL_PrivateJoystickAxis(joystick, 0, pXInputState->Gamepad.sThumbLX);
    462     SDL_PrivateJoystickAxis(joystick, 1, ~pXInputState->Gamepad.sThumbLY);
    463     SDL_PrivateJoystickAxis(joystick, 2, ((int)pXInputState->Gamepad.bLeftTrigger * 257) - 32768);
    464     SDL_PrivateJoystickAxis(joystick, 3, pXInputState->Gamepad.sThumbRX);
    465     SDL_PrivateJoystickAxis(joystick, 4, ~pXInputState->Gamepad.sThumbRY);
    466     SDL_PrivateJoystickAxis(joystick, 5, ((int)pXInputState->Gamepad.bRightTrigger * 257) - 32768);
    467 
    468     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
    469         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
    470     }
    471 
    472     if (wButtons & XINPUT_GAMEPAD_DPAD_UP) {
    473         hat |= SDL_HAT_UP;
    474     }
    475     if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {
    476         hat |= SDL_HAT_DOWN;
    477     }
    478     if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {
    479         hat |= SDL_HAT_LEFT;
    480     }
    481     if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {
    482         hat |= SDL_HAT_RIGHT;
    483     }
    484     SDL_PrivateJoystickHat(joystick, 0, hat);
    485 
    486     UpdateXInputJoystickBatteryInformation(joystick, pBatteryInformation);
    487 }
    488 
    489 int
    490 SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
    491 {
    492     XINPUT_VIBRATION XVibration;
    493 
    494     if (!XINPUTSETSTATE) {
    495         return SDL_Unsupported();
    496     }
    497 
    498     XVibration.wLeftMotorSpeed = low_frequency_rumble;
    499     XVibration.wRightMotorSpeed = high_frequency_rumble;
    500     if (XINPUTSETSTATE(joystick->hwdata->userid, &XVibration) != ERROR_SUCCESS) {
    501         return SDL_SetError("XInputSetState() failed");
    502     }
    503     return 0;
    504 }
    505 
    506 void
    507 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
    508 {
    509     HRESULT result;
    510     XINPUT_STATE_EX XInputState;
    511     XINPUT_BATTERY_INFORMATION_EX XBatteryInformation;
    512 
    513     if (!XINPUTGETSTATE)
    514         return;
    515 
    516     result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState);
    517     if (result == ERROR_DEVICE_NOT_CONNECTED) {
    518         return;
    519     }
    520 
    521     SDL_zero(XBatteryInformation);
    522     if (XINPUTGETBATTERYINFORMATION) {
    523         result = XINPUTGETBATTERYINFORMATION(joystick->hwdata->userid, BATTERY_DEVTYPE_GAMEPAD, &XBatteryInformation);
    524     }
    525 
    526     /* only fire events if the data changed from last time */
    527     if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) {
    528         if (SDL_XInputUseOldJoystickMapping()) {
    529             UpdateXInputJoystickState_OLD(joystick, &XInputState, &XBatteryInformation);
    530         } else {
    531             UpdateXInputJoystickState(joystick, &XInputState, &XBatteryInformation);
    532         }
    533         joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
    534     }
    535 }
    536 
    537 void
    538 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
    539 {
    540 }
    541 
    542 void
    543 SDL_XINPUT_JoystickQuit(void)
    544 {
    545     if (s_bXInputEnabled) {
    546         WIN_UnloadXInputDLL();
    547     }
    548 }
    549 
    550 #else /* !SDL_JOYSTICK_XINPUT */
    551 
    552 typedef struct JoyStick_DeviceData JoyStick_DeviceData;
    553 
    554 SDL_bool SDL_XINPUT_Enabled(void)
    555 {
    556     return SDL_FALSE;
    557 }
    558 
    559 int
    560 SDL_XINPUT_JoystickInit(void)
    561 {
    562     return 0;
    563 }
    564 
    565 void
    566 SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
    567 {
    568 }
    569 
    570 int
    571 SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
    572 {
    573     return SDL_Unsupported();
    574 }
    575 
    576 int
    577 SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
    578 {
    579     return SDL_Unsupported();
    580 }
    581 
    582 void
    583 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
    584 {
    585 }
    586 
    587 void
    588 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
    589 {
    590 }
    591 
    592 void
    593 SDL_XINPUT_JoystickQuit(void)
    594 {
    595 }
    596 
    597 #endif /* SDL_JOYSTICK_XINPUT */
    598 
    599 /* vi: set ts=4 sw=4 expandtab: */