src/joystick/winrt/SDL_xinputjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 13 Mar 2014 00:40:08 -0700
changeset 8615 097646deaef2
parent 8609 d59f0f12bf07
child 8860 c4133d635375
permissions -rw-r--r--
Fixed the copyright date on files contributed by David Ludwig
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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 #if SDL_JOYSTICK_XINPUT
    24 
    25 /* SDL_xinputjoystick.c implements an XInput-only joystick and game controller
    26    backend that is suitable for use on WinRT.  SDL's DirectInput backend, also
    27    XInput-capable, was not used as DirectInput is not available on WinRT (or,
    28    at least, it isn't a public API).  Some portions of this XInput backend
    29    may copy parts of the XInput-using code from the DirectInput backend.
    30    Refactoring the common parts into one location may be good to-do at some
    31    point.
    32 
    33    TODO, WinRT: add hotplug support for XInput based game controllers
    34 */
    35 
    36 #include "SDL_joystick.h"
    37 #include "../SDL_sysjoystick.h"
    38 #include "../SDL_joystick_c.h"
    39 #include "SDL_events.h"
    40 #include "../../events/SDL_events_c.h"
    41 #include "SDL_timer.h"
    42 
    43 #include <Windows.h>
    44 #include <Xinput.h>
    45 
    46 struct joystick_hwdata {
    47     //Uint8 bXInputHaptic; // Supports force feedback via XInput.
    48     DWORD userIndex;    // The XInput device index, in the range [0, XUSER_MAX_COUNT-1] (probably [0,3]).
    49     XINPUT_STATE XInputState;   // the last-read in XInputState, kept around to compare old and new values
    50     SDL_bool isDeviceConnected; // was the device connected (on the last detection-polling, or during backend-initialization)?
    51     SDL_bool isDeviceConnectionEventPending;    // was a device added, and is the associated add-event pending?
    52     SDL_bool isDeviceRemovalEventPending;   // was the device removed, and is the associated remove-event pending?
    53 };
    54 
    55 /* Keep track of data on all XInput devices, regardless of whether or not
    56    they've been opened (via SDL_JoystickOpen).
    57  */
    58 static struct joystick_hwdata g_XInputData[XUSER_MAX_COUNT];
    59 
    60 /* Device detection can be extremely costly performance-wise, in some cases.
    61    In particular, if no devices are connected, calls to detect a single device,
    62    via either XInputGetState() or XInputGetCapabilities(), can take upwards of
    63    20 ms on a 1st generation Surface RT, more if devices are detected across
    64    all of of XInput's four device slots.  WinRT and XInput do not appear to
    65    have callback-based APIs to notify an app when a device is connected, at
    66    least as of Windows 8.1.  The synchronous XInput calls must be used.
    67 
    68    Once a device is connected, calling XInputGetState() is a much less costly
    69    operation, with individual calls costing well under 1 ms, and often under
    70    0.1 ms [on a 1st gen Surface RT].
    71 
    72    With XInput's performance limitations in mind, a separate device-detection
    73    thread will be utilized (by SDL) to try to move costly XInput calls off the
    74    main thread.  Polling of active devices still, however, occurs on the main
    75    thread.
    76  */
    77 static SDL_Thread * g_DeviceDetectionThread = NULL;
    78 static SDL_mutex * g_DeviceInfoLock = NULL;
    79 static SDL_bool g_DeviceDetectionQuit = SDL_FALSE;
    80 
    81 /* Main function for the device-detection thread.
    82  */
    83 static int
    84 DeviceDetectionThreadMain(void * _data)
    85 {
    86     DWORD result;
    87     XINPUT_CAPABILITIES tempXInputCaps;
    88     int i;
    89 
    90     while (1) {
    91         /* See if the device-detection thread is being asked to shutdown.
    92          */
    93         SDL_LockMutex(g_DeviceInfoLock);
    94         if (g_DeviceDetectionQuit) {
    95             SDL_UnlockMutex(g_DeviceInfoLock);
    96             break;
    97         }
    98         SDL_UnlockMutex(g_DeviceInfoLock);
    99 
   100         /* Add a short delay to prevent the device-detection thread from eating
   101            up too much CPU time:
   102          */
   103         SDL_Delay(300);
   104 
   105         /* TODO, WinRT: try making the device-detection thread wakeup sooner from its CPU-preserving SDL_Delay, if the thread was asked to quit.
   106          */
   107 
   108         /* See if any new devices are connected. */
   109         SDL_LockMutex(g_DeviceInfoLock);
   110         for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   111             if (!g_XInputData[i].isDeviceConnected &&
   112                 !g_XInputData[i].isDeviceConnectionEventPending &&
   113                 !g_XInputData[i].isDeviceRemovalEventPending)
   114             {
   115                 SDL_UnlockMutex(g_DeviceInfoLock);
   116                 result = XInputGetCapabilities(i, 0, &tempXInputCaps);
   117                 SDL_LockMutex(g_DeviceInfoLock);
   118                 if (result == ERROR_SUCCESS) {
   119                     /* Yes, a device is connected.  Mark it as such.
   120                        Others will be told about this (via an
   121                        SDL_JOYDEVICEADDED event) in the next call to
   122                        SDL_SYS_JoystickDetect.
   123                      */
   124                     g_XInputData[i].isDeviceConnected = SDL_TRUE;
   125                     g_XInputData[i].isDeviceConnectionEventPending = SDL_TRUE;
   126                 }
   127             }
   128         }
   129         SDL_UnlockMutex(g_DeviceInfoLock);
   130     }
   131 
   132     return 0;
   133 }
   134 
   135 /* Function to scan the system for joysticks.
   136  * It should return 0, or -1 on an unrecoverable fatal error.
   137  */
   138 int
   139 SDL_SYS_JoystickInit(void)
   140 {
   141     HRESULT result = S_OK;
   142     XINPUT_STATE tempXInputState;
   143     int i;
   144 
   145     SDL_zero(g_XInputData);
   146 
   147     /* Make initial notes on whether or not devices are connected (or not).
   148      */
   149     for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   150         result = XInputGetState(i, &tempXInputState);
   151         if (result == ERROR_SUCCESS) {
   152             g_XInputData[i].isDeviceConnected = SDL_TRUE;
   153         }
   154     }
   155 
   156     /* Start up the device-detection thread.
   157      */
   158     g_DeviceDetectionQuit = SDL_FALSE;
   159     g_DeviceInfoLock = SDL_CreateMutex();
   160     g_DeviceDetectionThread = SDL_CreateThread(DeviceDetectionThreadMain, "SDL_joystick", NULL);
   161 
   162     return (0);
   163 }
   164 
   165 int SDL_SYS_NumJoysticks()
   166 {
   167     int joystickCount = 0;
   168     DWORD i;
   169 
   170     /* Iterate through each possible XInput device and see if something
   171        was connected (at joystick init, or during the last polling).
   172      */
   173     SDL_LockMutex(g_DeviceInfoLock);
   174     for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   175         if (g_XInputData[i].isDeviceConnected) {
   176             ++joystickCount;
   177         }
   178     }
   179     SDL_UnlockMutex(g_DeviceInfoLock);
   180 
   181     return joystickCount;
   182 }
   183 
   184 void SDL_SYS_JoystickDetect()
   185 {
   186     DWORD i;
   187     SDL_Event event;
   188 
   189     /* Iterate through each possible XInput device, seeing if any devices
   190        have been connected, or if they were removed.
   191      */
   192     SDL_LockMutex(g_DeviceInfoLock);
   193     for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   194         /* See if any new devices are connected. */
   195         if (g_XInputData[i].isDeviceConnectionEventPending) {
   196 #if !SDL_EVENTS_DISABLED
   197             SDL_zero(event);
   198             event.type = SDL_JOYDEVICEADDED;
   199                 
   200             if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   201                 event.jdevice.which = i;
   202                 if ((SDL_EventOK == NULL)
   203                     || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   204                     SDL_PushEvent(&event);
   205                 }
   206             }
   207 #endif
   208             g_XInputData[i].isDeviceConnectionEventPending = SDL_FALSE;
   209         } else if (g_XInputData[i].isDeviceRemovalEventPending) {
   210             /* A device was previously marked as removed (by
   211                SDL_SYS_JoystickUpdate).  Tell others about the device removal.
   212             */
   213 
   214             g_XInputData[i].isDeviceRemovalEventPending = SDL_FALSE;
   215 
   216 #if !SDL_EVENTS_DISABLED
   217             SDL_zero(event);
   218             event.type = SDL_JOYDEVICEREMOVED;
   219                 
   220             if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   221                 event.jdevice.which = i; //joystick->hwdata->userIndex;
   222                 if ((SDL_EventOK == NULL)
   223                     || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   224                     SDL_PushEvent(&event);
   225                 }
   226             }
   227 #endif
   228         }
   229     }
   230     SDL_UnlockMutex(g_DeviceInfoLock);
   231 }
   232 
   233 SDL_bool SDL_SYS_JoystickNeedsPolling()
   234 {
   235     /* Since XInput, or WinRT, provides any events to indicate when a game
   236        controller gets connected, and instead indicates device availability
   237        solely through polling, we'll poll (for new devices).
   238      */
   239     return SDL_TRUE;
   240 }
   241 
   242 /* Internal function to retreive device capabilities.
   243    This function will return an SDL-standard value of 0 on success
   244    (a device is connected, and data on it was retrieved), or -1
   245    on failure (no device was connected, or some other error
   246    occurred.  SDL_SetError() will be invoked to set an appropriate
   247    error message.
   248  */
   249 static int
   250 SDL_XInput_GetDeviceCapabilities(int device_index, XINPUT_CAPABILITIES * pDeviceCaps)
   251 {
   252     HRESULT dwResult;
   253 
   254     /* Make sure that the device index is a valid one.  If not, return to the
   255        caller with an error.
   256      */
   257     if (device_index < 0 || device_index >= XUSER_MAX_COUNT) {
   258         return SDL_SetError("invalid/unavailable device index");
   259     }
   260 
   261     /* See if a device exists, and if so, what its capabilities are.  If a
   262        device is not available, return to the caller with an error.
   263      */
   264     switch ((dwResult = XInputGetCapabilities(device_index, 0, pDeviceCaps))) {
   265         case ERROR_SUCCESS:
   266             /* A device is available, and its capabilities were retrieved! */
   267             return 0;
   268         case ERROR_DEVICE_NOT_CONNECTED:
   269             return SDL_SetError("no device is connected at joystick index, %d", device_index);
   270         default:
   271             return SDL_SetError("an unknown error occurred when retrieving info on a device at joystick index, %d", device_index);
   272     }
   273 }
   274 
   275 /* Function to get the device-dependent name of a joystick */
   276 const char *
   277 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   278 {
   279     XINPUT_CAPABILITIES deviceCaps;
   280 
   281     if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) {
   282         /* Uh oh.  Device capabilities couldn't be retrieved.  Return to the
   283            caller.  SDL_SetError() has already been invoked (with relevant
   284            information).
   285          */
   286         return NULL;
   287     }
   288 
   289     switch (deviceCaps.SubType) {
   290         default:
   291             if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) {
   292                 return "Undefined game controller";
   293             } else {
   294                 return "Undefined controller";
   295             }
   296         case XINPUT_DEVSUBTYPE_UNKNOWN:
   297             if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) {
   298                 return "Unknown game controller";
   299             } else {
   300                 return "Unknown controller";
   301             }
   302         case XINPUT_DEVSUBTYPE_GAMEPAD:
   303             return "Gamepad controller";
   304         case XINPUT_DEVSUBTYPE_WHEEL:
   305             return "Racing wheel controller";
   306         case XINPUT_DEVSUBTYPE_ARCADE_STICK:
   307             return "Arcade stick controller";
   308         case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
   309             return "Flight stick controller";
   310         case XINPUT_DEVSUBTYPE_DANCE_PAD:
   311             return "Dance pad controller";
   312         case XINPUT_DEVSUBTYPE_GUITAR:
   313             return "Guitar controller";
   314         case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
   315             return "Guitar controller, Alternate";
   316         case XINPUT_DEVSUBTYPE_GUITAR_BASS:
   317             return "Guitar controller, Bass";
   318         case XINPUT_DEVSUBTYPE_DRUM_KIT:
   319             return "Drum controller";
   320         case XINPUT_DEVSUBTYPE_ARCADE_PAD:
   321             return "Arcade pad controller";
   322     }
   323 }
   324 
   325 /* Function to perform the mapping from device index to the instance id for this index */
   326 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   327 {
   328     return device_index;
   329 }
   330 
   331 /* Function to open a joystick for use.
   332    The joystick to open is specified by the index field of the joystick.
   333    This should fill the nbuttons and naxes fields of the joystick structure.
   334    It returns 0, or -1 if there is an error.
   335  */
   336 int
   337 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
   338 {
   339     XINPUT_CAPABILITIES deviceCaps;
   340 
   341     if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) {
   342         /* Uh oh.  Device capabilities couldn't be retrieved.  Return to the
   343            caller.  SDL_SetError() has already been invoked (with relevant
   344            information).
   345          */
   346         return -1;
   347     }
   348 
   349     /* For now, only game pads are supported.  If the device is something other
   350        than that, return an error to the caller.
   351      */
   352     if (deviceCaps.Type != XINPUT_DEVTYPE_GAMEPAD) {
   353         return SDL_SetError("a device is connected (at joystick index, %d), but it is of an unknown device type (deviceCaps.Flags=%ul)",
   354             device_index, (unsigned int)deviceCaps.Flags);
   355     }
   356 
   357     /* Create the joystick data structure */
   358     joystick->instance_id = device_index;
   359     joystick->hwdata = &g_XInputData[device_index];
   360 
   361     // The XInput API has a hard coded button/axis mapping, so we just match it
   362     joystick->naxes = 6;
   363     joystick->nbuttons = 15;
   364     joystick->nballs = 0;
   365     joystick->nhats = 0;
   366 
   367     /* We're done! */
   368     return (0);
   369 }
   370 
   371 /* Function to determine is this joystick is attached to the system right now */
   372 SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
   373 {
   374     SDL_bool isDeviceConnected;
   375     SDL_LockMutex(g_DeviceInfoLock);
   376     isDeviceConnected = joystick->hwdata->isDeviceConnected;
   377     SDL_UnlockMutex(g_DeviceInfoLock);
   378     return isDeviceConnected;
   379 }
   380 
   381 /* Function to return > 0 if a bit array of buttons differs after applying a mask
   382 */
   383 static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
   384 {
   385     return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
   386 }
   387 
   388 /* Function to update the state of a joystick - called as a device poll.
   389  * This function shouldn't update the joystick structure directly,
   390  * but instead should call SDL_PrivateJoystick*() to deliver events
   391  * and update joystick device state.
   392  */
   393 void
   394 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
   395 {
   396     HRESULT result;
   397     XINPUT_STATE prevXInputState;
   398 
   399     SDL_LockMutex(g_DeviceInfoLock);
   400 
   401     /* Before polling for new data, make note of the old data */
   402     prevXInputState = joystick->hwdata->XInputState;
   403 
   404     /* Poll for new data */
   405     result = XInputGetState(joystick->hwdata->userIndex, &joystick->hwdata->XInputState);
   406     if (result == ERROR_DEVICE_NOT_CONNECTED) {
   407         if (joystick->hwdata->isDeviceConnected) {
   408             joystick->hwdata->isDeviceConnected = SDL_FALSE;
   409             joystick->hwdata->isDeviceRemovalEventPending = SDL_TRUE;
   410             /* TODO, WinRT: make sure isDeviceRemovalEventPending gets cleared as appropriate, and that quick re-plugs don't cause trouble */
   411         }
   412         SDL_UnlockMutex(g_DeviceInfoLock);
   413         return;
   414     }
   415 
   416     /* Make sure the device is marked as connected */
   417     joystick->hwdata->isDeviceConnected = SDL_TRUE;
   418 
   419     // only fire events if the data changed from last time
   420     if ( joystick->hwdata->XInputState.dwPacketNumber != 0 
   421         && joystick->hwdata->XInputState.dwPacketNumber != prevXInputState.dwPacketNumber )
   422     {
   423         XINPUT_STATE *pXInputState = &joystick->hwdata->XInputState;
   424         XINPUT_STATE *pXInputStatePrev = &prevXInputState;
   425 
   426         SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
   427         SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) );
   428         SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
   429         SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) );
   430         SDL_PrivateJoystickAxis(joystick, 4, (Sint16)((int)pXInputState->Gamepad.bLeftTrigger*32767/255) );
   431         SDL_PrivateJoystickAxis(joystick, 5, (Sint16)((int)pXInputState->Gamepad.bRightTrigger*32767/255) );
   432 
   433         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
   434             SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED :	SDL_RELEASED );
   435         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
   436             SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED :	SDL_RELEASED );
   437         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
   438             SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED :	SDL_RELEASED );
   439         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
   440             SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED :	SDL_RELEASED );
   441         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
   442             SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED :	SDL_RELEASED );
   443         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
   444             SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED :	SDL_RELEASED );
   445         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
   446             SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
   447         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
   448             SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
   449         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
   450             SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
   451         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
   452             SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
   453         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
   454             SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED :	SDL_RELEASED );
   455         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
   456             SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED :	SDL_RELEASED );
   457         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
   458             SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED :	SDL_RELEASED );
   459         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
   460             SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED :	SDL_RELEASED );
   461         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons,  0x400 ) )
   462             SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED :	SDL_RELEASED ); // 0x400 is the undocumented code for the guide button
   463     }
   464 
   465     SDL_UnlockMutex(g_DeviceInfoLock);
   466 }
   467 
   468 /* Function to close a joystick after use */
   469 void
   470 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
   471 {
   472     /* Clear cached button data on the joystick */
   473     SDL_LockMutex(g_DeviceInfoLock);
   474     SDL_zero(joystick->hwdata->XInputState);
   475     SDL_UnlockMutex(g_DeviceInfoLock);
   476 
   477     /* There's need to free 'hwdata', as it's a pointer to a global array.
   478        The field will be cleared anyways, just to indicate that it's not
   479        currently needed.
   480      */
   481     joystick->hwdata = NULL;
   482 }
   483 
   484 /* Function to perform any system-specific joystick related cleanup */
   485 void
   486 SDL_SYS_JoystickQuit(void)
   487 {
   488     /* Tell the joystick detection thread to stop, then wait for it to finish */
   489     SDL_LockMutex(g_DeviceInfoLock);
   490     g_DeviceDetectionQuit = SDL_TRUE;
   491     SDL_UnlockMutex(g_DeviceInfoLock);
   492     SDL_WaitThread(g_DeviceDetectionThread, NULL);
   493 
   494     /* Clean up device-detection stuff */
   495     SDL_DestroyMutex(g_DeviceInfoLock);
   496     g_DeviceInfoLock = NULL;
   497     g_DeviceDetectionThread = NULL;
   498     g_DeviceDetectionQuit = SDL_FALSE;
   499 
   500     return;
   501 }
   502 
   503 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
   504 {
   505     SDL_JoystickGUID guid;
   506     // the GUID is just the first 16 chars of the name for now
   507     const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
   508     SDL_zero( guid );
   509     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   510     return guid;
   511 }
   512 
   513 
   514 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
   515 {
   516     SDL_JoystickGUID guid;
   517     // the GUID is just the first 16 chars of the name for now
   518     const char *name = joystick->name;
   519     SDL_zero( guid );
   520     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   521     return guid;
   522 }
   523 
   524 SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
   525 {
   526     /* The XInput-capable DirectInput joystick backend implements the same
   527        function (SDL_SYS_IsXInputDeviceIndex), however in that case, not all
   528        joystick devices are XInput devices.  In this case, with the
   529        WinRT-enabled XInput-only backend, all "joystick" devices are XInput
   530        devices.
   531      */
   532     return SDL_TRUE;
   533 }
   534 
   535 #endif /* SDL_JOYSTICK_XINPUT */
   536 
   537 /* vi: set ts=4 sw=4 expandtab: */