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