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