src/joystick/windows/SDL_xinputjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 11 Dec 2019 19:24:40 -0800
changeset 13334 82b3162e2724
parent 13333 88aec26a0ad0
permissions -rw-r--r--
Added names for official Microsoft controllers, since they don't have descriptive product names
     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     const char *name;
   224     JoyStick_DeviceData *pPrevJoystick = NULL;
   225     JoyStick_DeviceData *pNewJoystick = *pContext;
   226 
   227     if (SDL_XInputUseOldJoystickMapping() && SubType != XINPUT_DEVSUBTYPE_GAMEPAD)
   228         return;
   229 
   230     if (SubType == XINPUT_DEVSUBTYPE_UNKNOWN)
   231         return;
   232 
   233     while (pNewJoystick) {
   234         if (pNewJoystick->bXInputDevice && (pNewJoystick->XInputUserId == userid) && (pNewJoystick->SubType == SubType)) {
   235             /* if we are replacing the front of the list then update it */
   236             if (pNewJoystick == *pContext) {
   237                 *pContext = pNewJoystick->pNext;
   238             } else if (pPrevJoystick) {
   239                 pPrevJoystick->pNext = pNewJoystick->pNext;
   240             }
   241 
   242             pNewJoystick->pNext = SYS_Joystick;
   243             SYS_Joystick = pNewJoystick;
   244             return;   /* already in the list. */
   245         }
   246 
   247         pPrevJoystick = pNewJoystick;
   248         pNewJoystick = pNewJoystick->pNext;
   249     }
   250 
   251     pNewJoystick = (JoyStick_DeviceData *)SDL_calloc(1, sizeof(JoyStick_DeviceData));
   252     if (!pNewJoystick) {
   253         return; /* better luck next time? */
   254     }
   255 
   256     pNewJoystick->bXInputDevice = SDL_TRUE;
   257     if (!SDL_XInputUseOldJoystickMapping()) {
   258         Uint16 *guid16 = (Uint16 *)pNewJoystick->guid.data;
   259 
   260         GuessXInputDevice(userid, &vendor, &product, &version);
   261 
   262         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
   263         *guid16++ = 0;
   264         *guid16++ = SDL_SwapLE16(vendor);
   265         *guid16++ = 0;
   266         *guid16++ = SDL_SwapLE16(product);
   267         *guid16++ = 0;
   268         *guid16++ = SDL_SwapLE16(version);
   269         *guid16++ = 0;
   270 
   271         /* Note that this is an XInput device and what subtype it is */
   272         pNewJoystick->guid.data[14] = 'x';
   273         pNewJoystick->guid.data[15] = SubType;
   274     }
   275     pNewJoystick->SubType = SubType;
   276     pNewJoystick->XInputUserId = userid;
   277 
   278     name = SDL_GetCustomJoystickName(vendor, product);
   279     if (name) {
   280         pNewJoystick->joystickname = SDL_strdup(name);
   281     } else {
   282         pNewJoystick->joystickname = GetXInputName(userid, SubType);
   283     }
   284     if (!pNewJoystick->joystickname) {
   285         SDL_free(pNewJoystick);
   286         return; /* better luck next time? */
   287     }
   288 
   289     if (SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)) {
   290         SDL_free(pNewJoystick);
   291         return;
   292     }
   293 
   294 #ifdef SDL_JOYSTICK_HIDAPI
   295     if (HIDAPI_IsDevicePresent(vendor, product, version, pNewJoystick->joystickname)) {
   296         /* The HIDAPI driver is taking care of this device */
   297         SDL_free(pNewJoystick);
   298         return;
   299     }
   300 #endif
   301 
   302     WINDOWS_AddJoystickDevice(pNewJoystick);
   303 }
   304 
   305 static void
   306 DelXInputDevice(Uint8 userid)
   307 {
   308     if (s_arrXInputDevicePath[userid]) {
   309         SDL_free(s_arrXInputDevicePath[userid]);
   310         s_arrXInputDevicePath[userid] = NULL;
   311     }
   312 }
   313 
   314 void
   315 SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
   316 {
   317     int iuserid;
   318 
   319     if (!s_bXInputEnabled) {
   320         return;
   321     }
   322 
   323     /* iterate in reverse, so these are in the final list in ascending numeric order. */
   324     for (iuserid = XUSER_MAX_COUNT - 1; iuserid >= 0; iuserid--) {
   325         const Uint8 userid = (Uint8)iuserid;
   326         XINPUT_CAPABILITIES capabilities;
   327         if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
   328             AddXInputDevice(userid, capabilities.SubType, pContext);
   329         } else {
   330             DelXInputDevice(userid);
   331         }
   332     }
   333 }
   334 
   335 int
   336 SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
   337 {
   338     const Uint8 userId = joystickdevice->XInputUserId;
   339     XINPUT_CAPABILITIES capabilities;
   340     XINPUT_VIBRATION state;
   341 
   342     SDL_assert(s_bXInputEnabled);
   343     SDL_assert(XINPUTGETCAPABILITIES);
   344     SDL_assert(XINPUTSETSTATE);
   345     SDL_assert(userId < XUSER_MAX_COUNT);
   346 
   347     joystick->player_index = userId;
   348 
   349     joystick->hwdata->bXInputDevice = SDL_TRUE;
   350 
   351     if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) {
   352         SDL_free(joystick->hwdata);
   353         joystick->hwdata = NULL;
   354         return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?");
   355     }
   356     SDL_zero(state);
   357     joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS);
   358     joystick->hwdata->userid = userId;
   359 
   360     /* The XInput API has a hard coded button/axis mapping, so we just match it */
   361     if (SDL_XInputUseOldJoystickMapping()) {
   362         joystick->naxes = 6;
   363         joystick->nbuttons = 15;
   364     } else {
   365         joystick->naxes = 6;
   366         joystick->nbuttons = 11;
   367         joystick->nhats = 1;
   368     }
   369     return 0;
   370 }
   371 
   372 static void 
   373 UpdateXInputJoystickBatteryInformation(SDL_Joystick * joystick, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
   374 {
   375     if (pBatteryInformation->BatteryType != BATTERY_TYPE_UNKNOWN) {
   376         SDL_JoystickPowerLevel ePowerLevel = SDL_JOYSTICK_POWER_UNKNOWN;
   377         if (pBatteryInformation->BatteryType == BATTERY_TYPE_WIRED) {
   378             ePowerLevel = SDL_JOYSTICK_POWER_WIRED;
   379         } else {
   380             switch (pBatteryInformation->BatteryLevel) {
   381             case BATTERY_LEVEL_EMPTY:
   382                 ePowerLevel = SDL_JOYSTICK_POWER_EMPTY;
   383                 break;
   384             case BATTERY_LEVEL_LOW:
   385                 ePowerLevel = SDL_JOYSTICK_POWER_LOW;
   386                 break;
   387             case BATTERY_LEVEL_MEDIUM:
   388                 ePowerLevel = SDL_JOYSTICK_POWER_MEDIUM;
   389                 break;
   390             default:
   391             case BATTERY_LEVEL_FULL:
   392                 ePowerLevel = SDL_JOYSTICK_POWER_FULL;
   393                 break;
   394             }
   395         }
   396 
   397         SDL_PrivateJoystickBatteryLevel(joystick, ePowerLevel);
   398     }
   399 }
   400 
   401 static void
   402 UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
   403 {
   404     static WORD s_XInputButtons[] = {
   405         XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT,
   406         XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
   407         XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER,
   408         XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
   409         XINPUT_GAMEPAD_GUIDE
   410     };
   411     WORD wButtons = pXInputState->Gamepad.wButtons;
   412     Uint8 button;
   413 
   414     SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
   415     SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
   416     SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX);
   417     SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
   418     SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
   419     SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
   420 
   421     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
   422         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
   423     }
   424 
   425     UpdateXInputJoystickBatteryInformation(joystick, pBatteryInformation);
   426 }
   427 
   428 static void
   429 UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
   430 {
   431     static WORD s_XInputButtons[] = {
   432         XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
   433         XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START,
   434         XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
   435         XINPUT_GAMEPAD_GUIDE
   436     };
   437     WORD wButtons = pXInputState->Gamepad.wButtons;
   438     Uint8 button;
   439     Uint8 hat = SDL_HAT_CENTERED;
   440 
   441     SDL_PrivateJoystickAxis(joystick, 0, pXInputState->Gamepad.sThumbLX);
   442     SDL_PrivateJoystickAxis(joystick, 1, ~pXInputState->Gamepad.sThumbLY);
   443     SDL_PrivateJoystickAxis(joystick, 2, ((int)pXInputState->Gamepad.bLeftTrigger * 257) - 32768);
   444     SDL_PrivateJoystickAxis(joystick, 3, pXInputState->Gamepad.sThumbRX);
   445     SDL_PrivateJoystickAxis(joystick, 4, ~pXInputState->Gamepad.sThumbRY);
   446     SDL_PrivateJoystickAxis(joystick, 5, ((int)pXInputState->Gamepad.bRightTrigger * 257) - 32768);
   447 
   448     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
   449         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
   450     }
   451 
   452     if (wButtons & XINPUT_GAMEPAD_DPAD_UP) {
   453         hat |= SDL_HAT_UP;
   454     }
   455     if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {
   456         hat |= SDL_HAT_DOWN;
   457     }
   458     if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {
   459         hat |= SDL_HAT_LEFT;
   460     }
   461     if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {
   462         hat |= SDL_HAT_RIGHT;
   463     }
   464     SDL_PrivateJoystickHat(joystick, 0, hat);
   465 
   466     UpdateXInputJoystickBatteryInformation(joystick, pBatteryInformation);
   467 }
   468 
   469 int
   470 SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   471 {
   472     XINPUT_VIBRATION XVibration;
   473 
   474     if (!XINPUTSETSTATE) {
   475         return SDL_Unsupported();
   476     }
   477 
   478     XVibration.wLeftMotorSpeed = low_frequency_rumble;
   479     XVibration.wRightMotorSpeed = high_frequency_rumble;
   480     if (XINPUTSETSTATE(joystick->hwdata->userid, &XVibration) != ERROR_SUCCESS) {
   481         return SDL_SetError("XInputSetState() failed");
   482     }
   483 
   484     if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
   485         joystick->hwdata->rumble_expiration = SDL_GetTicks() + duration_ms;
   486     } else {
   487         joystick->hwdata->rumble_expiration = 0;
   488     }
   489     return 0;
   490 }
   491 
   492 void
   493 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
   494 {
   495     HRESULT result;
   496     XINPUT_STATE_EX XInputState;
   497     XINPUT_BATTERY_INFORMATION_EX XBatteryInformation;
   498 
   499     if (!XINPUTGETSTATE)
   500         return;
   501 
   502     result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState);
   503     if (result == ERROR_DEVICE_NOT_CONNECTED) {
   504         return;
   505     }
   506 
   507     SDL_zero(XBatteryInformation);
   508     if (XINPUTGETBATTERYINFORMATION) {
   509         result = XINPUTGETBATTERYINFORMATION(joystick->hwdata->userid, BATTERY_DEVTYPE_GAMEPAD, &XBatteryInformation);
   510     }
   511 
   512     /* only fire events if the data changed from last time */
   513     if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) {
   514         if (SDL_XInputUseOldJoystickMapping()) {
   515             UpdateXInputJoystickState_OLD(joystick, &XInputState, &XBatteryInformation);
   516         } else {
   517             UpdateXInputJoystickState(joystick, &XInputState, &XBatteryInformation);
   518         }
   519         joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
   520     }
   521 
   522     if (joystick->hwdata->rumble_expiration) {
   523         Uint32 now = SDL_GetTicks();
   524         if (SDL_TICKS_PASSED(now, joystick->hwdata->rumble_expiration)) {
   525             SDL_XINPUT_JoystickRumble(joystick, 0, 0, 0);
   526         }
   527     }
   528 }
   529 
   530 void
   531 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
   532 {
   533 }
   534 
   535 void
   536 SDL_XINPUT_JoystickQuit(void)
   537 {
   538     if (s_bXInputEnabled) {
   539         WIN_UnloadXInputDLL();
   540     }
   541 }
   542 
   543 #else /* !SDL_JOYSTICK_XINPUT */
   544 
   545 typedef struct JoyStick_DeviceData JoyStick_DeviceData;
   546 
   547 SDL_bool SDL_XINPUT_Enabled(void)
   548 {
   549     return SDL_FALSE;
   550 }
   551 
   552 int
   553 SDL_XINPUT_JoystickInit(void)
   554 {
   555     return 0;
   556 }
   557 
   558 void
   559 SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
   560 {
   561 }
   562 
   563 int
   564 SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
   565 {
   566     return SDL_Unsupported();
   567 }
   568 
   569 int
   570 SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   571 {
   572     return SDL_Unsupported();
   573 }
   574 
   575 void
   576 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
   577 {
   578 }
   579 
   580 void
   581 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
   582 {
   583 }
   584 
   585 void
   586 SDL_XINPUT_JoystickQuit(void)
   587 {
   588 }
   589 
   590 #endif /* SDL_JOYSTICK_XINPUT */
   591 
   592 /* vi: set ts=4 sw=4 expandtab: */