src/joystick/winrt/SDL_xinputjoystick.c
author David Ludwig <dludwig@pobox.com>
Tue, 24 Dec 2013 21:28:31 -0500
changeset 8552 abd934eac415
parent 8502 78d341895e9f
child 8562 f1aac2d56c76
permissions -rw-r--r--
WinRT: moved ill-performing XInput device-detection calls to a separate thread
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_config.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         for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   110             if (!g_XInputData[i].isDeviceConnected &&
   111                 !g_XInputData[i].isDeviceConnectionEventPending &&
   112                 !g_XInputData[i].isDeviceRemovalEventPending)
   113             {
   114                 result = XInputGetCapabilities(i, 0, &tempXInputCaps);
   115                 if (result == ERROR_SUCCESS) {
   116                     /* Yes, a device is connected.  Mark it as such.
   117                        Others will be told about this (via an
   118                        SDL_JOYDEVICEADDED event) in the next call to
   119                        SDL_SYS_JoystickDetect.
   120                      */
   121                     SDL_LockMutex(g_DeviceInfoLock);
   122                     g_XInputData[i].isDeviceConnected = SDL_TRUE;
   123                     g_XInputData[i].isDeviceConnectionEventPending = SDL_TRUE;
   124                     SDL_UnlockMutex(g_DeviceInfoLock);
   125                 }
   126             }
   127         }
   128     }
   129 
   130     return 0;
   131 }
   132 
   133 /* Function to scan the system for joysticks.
   134  * It should return 0, or -1 on an unrecoverable fatal error.
   135  */
   136 int
   137 SDL_SYS_JoystickInit(void)
   138 {
   139     HRESULT result = S_OK;
   140     XINPUT_STATE tempXInputState;
   141     int i;
   142 
   143     SDL_zero(g_XInputData);
   144 
   145     /* Make initial notes on whether or not devices are connected (or not).
   146      */
   147     for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   148         result = XInputGetState(i, &tempXInputState);
   149         if (result == ERROR_SUCCESS) {
   150             g_XInputData[i].isDeviceConnected = SDL_TRUE;
   151         }
   152     }
   153 
   154     /* Start up the device-detection thread.
   155      */
   156     g_DeviceDetectionQuit = SDL_FALSE;
   157     g_DeviceInfoLock = SDL_CreateMutex();
   158     g_DeviceDetectionThread = SDL_CreateThread(DeviceDetectionThreadMain, "SDL_joystick", NULL);
   159 
   160     return (0);
   161 }
   162 
   163 int SDL_SYS_NumJoysticks()
   164 {
   165     int joystickCount = 0;
   166     DWORD i;
   167 
   168     /* Iterate through each possible XInput device and see if something
   169        was connected (at joystick init, or during the last polling).
   170      */
   171     SDL_LockMutex(g_DeviceInfoLock);
   172     for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   173         if (g_XInputData[i].isDeviceConnected) {
   174             ++joystickCount;
   175         }
   176     }
   177     SDL_UnlockMutex(g_DeviceInfoLock);
   178 
   179     return joystickCount;
   180 }
   181 
   182 void SDL_SYS_JoystickDetect()
   183 {
   184     DWORD i;
   185     SDL_Event event;
   186 
   187     /* Iterate through each possible XInput device, seeing if any devices
   188        have been connected, or if they were removed.
   189      */
   190     SDL_LockMutex(g_DeviceInfoLock);
   191     for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   192         /* See if any new devices are connected. */
   193         if (g_XInputData[i].isDeviceConnectionEventPending) {
   194 #if !SDL_EVENTS_DISABLED
   195             SDL_zero(event);
   196             event.type = SDL_JOYDEVICEADDED;
   197                 
   198             if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   199                 event.jdevice.which = i;
   200                 if ((SDL_EventOK == NULL)
   201                     || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   202                     SDL_PushEvent(&event);
   203                 }
   204             }
   205 #endif
   206             g_XInputData[i].isDeviceConnectionEventPending = SDL_FALSE;
   207         } else if (g_XInputData[i].isDeviceRemovalEventPending) {
   208             /* A device was previously marked as removed (by
   209                SDL_SYS_JoystickUpdate).  Tell others about the device removal.
   210             */
   211 
   212             g_XInputData[i].isDeviceRemovalEventPending = SDL_FALSE;
   213 
   214 #if !SDL_EVENTS_DISABLED
   215             SDL_zero(event);
   216             event.type = SDL_JOYDEVICEREMOVED;
   217                 
   218             if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   219                 event.jdevice.which = i; //joystick->hwdata->userIndex;
   220                 if ((SDL_EventOK == NULL)
   221                     || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   222                     SDL_PushEvent(&event);
   223                 }
   224             }
   225 #endif
   226         }
   227     }
   228     SDL_UnlockMutex(g_DeviceInfoLock);
   229 }
   230 
   231 SDL_bool SDL_SYS_JoystickNeedsPolling()
   232 {
   233     /* Since XInput, or WinRT, provides any events to indicate when a game
   234        controller gets connected, and instead indicates device availability
   235        solely through polling, we'll poll (for new devices).
   236      */
   237     return SDL_TRUE;
   238 }
   239 
   240 /* Internal function to retreive device capabilities.
   241    This function will return an SDL-standard value of 0 on success
   242    (a device is connected, and data on it was retrieved), or -1
   243    on failure (no device was connected, or some other error
   244    occurred.  SDL_SetError() will be invoked to set an appropriate
   245    error message.
   246  */
   247 static int
   248 SDL_XInput_GetDeviceCapabilities(int device_index, XINPUT_CAPABILITIES * pDeviceCaps)
   249 {
   250     HRESULT dwResult;
   251 
   252     /* Make sure that the device index is a valid one.  If not, return to the
   253        caller with an error.
   254      */
   255     if (device_index < 0 || device_index >= XUSER_MAX_COUNT) {
   256         return SDL_SetError("invalid/unavailable device index");
   257     }
   258 
   259     /* See if a device exists, and if so, what its capabilities are.  If a
   260        device is not available, return to the caller with an error.
   261      */
   262     switch ((dwResult = XInputGetCapabilities(device_index, 0, pDeviceCaps))) {
   263         case ERROR_SUCCESS:
   264             /* A device is available, and its capabilities were retrieved! */
   265             return 0;
   266         case ERROR_DEVICE_NOT_CONNECTED:
   267             return SDL_SetError("no device is connected at joystick index, %d", device_index);
   268         default:
   269             return SDL_SetError("an unknown error occurred when retrieving info on a device at joystick index, %d", device_index);
   270     }
   271 }
   272 
   273 /* Function to get the device-dependent name of a joystick */
   274 const char *
   275 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   276 {
   277     XINPUT_CAPABILITIES deviceCaps;
   278 
   279     if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) {
   280         /* Uh oh.  Device capabilities couldn't be retrieved.  Return to the
   281            caller.  SDL_SetError() has already been invoked (with relevant
   282            information).
   283          */
   284         return NULL;
   285     }
   286 
   287     switch (deviceCaps.SubType) {
   288         default:
   289             if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) {
   290                 return "Undefined game controller";
   291             } else {
   292                 return "Undefined controller";
   293             }
   294         case XINPUT_DEVSUBTYPE_UNKNOWN:
   295             if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) {
   296                 return "Unknown game controller";
   297             } else {
   298                 return "Unknown controller";
   299             }
   300         case XINPUT_DEVSUBTYPE_GAMEPAD:
   301             return "Gamepad controller";
   302         case XINPUT_DEVSUBTYPE_WHEEL:
   303             return "Racing wheel controller";
   304         case XINPUT_DEVSUBTYPE_ARCADE_STICK:
   305             return "Arcade stick controller";
   306         case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
   307             return "Flight stick controller";
   308         case XINPUT_DEVSUBTYPE_DANCE_PAD:
   309             return "Dance pad controller";
   310         case XINPUT_DEVSUBTYPE_GUITAR:
   311             return "Guitar controller";
   312         case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
   313             return "Guitar controller, Alternate";
   314         case XINPUT_DEVSUBTYPE_GUITAR_BASS:
   315             return "Guitar controller, Bass";
   316         case XINPUT_DEVSUBTYPE_DRUM_KIT:
   317             return "Drum controller";
   318         case XINPUT_DEVSUBTYPE_ARCADE_PAD:
   319             return "Arcade pad controller";
   320     }
   321 }
   322 
   323 /* Function to perform the mapping from device index to the instance id for this index */
   324 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   325 {
   326     return device_index;
   327 }
   328 
   329 /* Function to open a joystick for use.
   330    The joystick to open is specified by the index field of the joystick.
   331    This should fill the nbuttons and naxes fields of the joystick structure.
   332    It returns 0, or -1 if there is an error.
   333  */
   334 int
   335 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
   336 {
   337     XINPUT_CAPABILITIES deviceCaps;
   338 
   339     if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) {
   340         /* Uh oh.  Device capabilities couldn't be retrieved.  Return to the
   341            caller.  SDL_SetError() has already been invoked (with relevant
   342            information).
   343          */
   344         return -1;
   345     }
   346 
   347     /* For now, only game pads are supported.  If the device is something other
   348        than that, return an error to the caller.
   349      */
   350     if (deviceCaps.Type != XINPUT_DEVTYPE_GAMEPAD) {
   351         return SDL_SetError("a device is connected (at joystick index, %d), but it is of an unknown device type (deviceCaps.Flags=%ul)",
   352             device_index, (unsigned int)deviceCaps.Flags);
   353     }
   354 
   355     /* Create the joystick data structure */
   356     joystick->instance_id = device_index;
   357     joystick->hwdata = &g_XInputData[device_index];
   358 
   359     // The XInput API has a hard coded button/axis mapping, so we just match it
   360     joystick->naxes = 6;
   361     joystick->nbuttons = 15;
   362     joystick->nballs = 0;
   363     joystick->nhats = 0;
   364 
   365     /* We're done! */
   366     return (0);
   367 }
   368 
   369 /* Function to determine is this joystick is attached to the system right now */
   370 SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
   371 {
   372     SDL_bool isDeviceConnected;
   373     SDL_LockMutex(g_DeviceInfoLock);
   374     isDeviceConnected = joystick->hwdata->isDeviceConnected;
   375     SDL_UnlockMutex(g_DeviceInfoLock);
   376     return isDeviceConnected;
   377 }
   378 
   379 /* Function to return > 0 if a bit array of buttons differs after applying a mask
   380 */
   381 static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
   382 {
   383     return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
   384 }
   385 
   386 /* Function to update the state of a joystick - called as a device poll.
   387  * This function shouldn't update the joystick structure directly,
   388  * but instead should call SDL_PrivateJoystick*() to deliver events
   389  * and update joystick device state.
   390  */
   391 void
   392 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
   393 {
   394     HRESULT result;
   395     XINPUT_STATE prevXInputState;
   396 
   397     SDL_LockMutex(g_DeviceInfoLock);
   398 
   399     /* Before polling for new data, make note of the old data */
   400     prevXInputState = joystick->hwdata->XInputState;
   401 
   402     /* Poll for new data */
   403     result = XInputGetState(joystick->hwdata->userIndex, &joystick->hwdata->XInputState);
   404     if (result == ERROR_DEVICE_NOT_CONNECTED) {
   405         if (joystick->hwdata->isDeviceConnected) {
   406             joystick->hwdata->isDeviceConnected = SDL_FALSE;
   407             joystick->hwdata->isDeviceRemovalEventPending = SDL_TRUE;
   408             /* TODO, WinRT: make sure isDeviceRemovalEventPending gets cleared as appropriate, and that quick re-plugs don't cause trouble */
   409         }
   410         SDL_UnlockMutex(g_DeviceInfoLock);
   411         return;
   412     }
   413 
   414     /* Make sure the device is marked as connected */
   415     joystick->hwdata->isDeviceConnected = SDL_TRUE;
   416 
   417     // only fire events if the data changed from last time
   418     if ( joystick->hwdata->XInputState.dwPacketNumber != 0 
   419         && joystick->hwdata->XInputState.dwPacketNumber != prevXInputState.dwPacketNumber )
   420     {
   421         XINPUT_STATE *pXInputState = &joystick->hwdata->XInputState;
   422         XINPUT_STATE *pXInputStatePrev = &prevXInputState;
   423 
   424         SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
   425         SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) );
   426         SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
   427         SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) );
   428         SDL_PrivateJoystickAxis(joystick, 4, (Sint16)((int)pXInputState->Gamepad.bLeftTrigger*32767/255) );
   429         SDL_PrivateJoystickAxis(joystick, 5, (Sint16)((int)pXInputState->Gamepad.bRightTrigger*32767/255) );
   430 
   431         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
   432             SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED :	SDL_RELEASED );
   433         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
   434             SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED :	SDL_RELEASED );
   435         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
   436             SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED :	SDL_RELEASED );
   437         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
   438             SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED :	SDL_RELEASED );
   439         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
   440             SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED :	SDL_RELEASED );
   441         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
   442             SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED :	SDL_RELEASED );
   443         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
   444             SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
   445         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
   446             SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
   447         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
   448             SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
   449         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
   450             SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
   451         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
   452             SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED :	SDL_RELEASED );
   453         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
   454             SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED :	SDL_RELEASED );
   455         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
   456             SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED :	SDL_RELEASED );
   457         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
   458             SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED :	SDL_RELEASED );
   459         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons,  0x400 ) )
   460             SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED :	SDL_RELEASED ); // 0x400 is the undocumented code for the guide button
   461     }
   462 
   463     SDL_UnlockMutex(g_DeviceInfoLock);
   464 }
   465 
   466 /* Function to close a joystick after use */
   467 void
   468 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
   469 {
   470     /* Clear cached button data on the joystick */
   471     SDL_LockMutex(g_DeviceInfoLock);
   472     SDL_zero(joystick->hwdata->XInputState);
   473     SDL_UnlockMutex(g_DeviceInfoLock);
   474 
   475     /* There's need to free 'hwdata', as it's a pointer to a global array.
   476        The field will be cleared anyways, just to indicate that it's not
   477        currently needed.
   478      */
   479     joystick->hwdata = NULL;
   480 }
   481 
   482 /* Function to perform any system-specific joystick related cleanup */
   483 void
   484 SDL_SYS_JoystickQuit(void)
   485 {
   486     /* Tell the joystick detection thread to stop, then wait for it to finish */
   487     SDL_LockMutex(g_DeviceInfoLock);
   488     g_DeviceDetectionQuit = SDL_TRUE;
   489     SDL_UnlockMutex(g_DeviceInfoLock);
   490     SDL_WaitThread(g_DeviceDetectionThread, NULL);
   491 
   492     /* Clean up device-detection stuff */
   493     SDL_DestroyMutex(g_DeviceInfoLock);
   494     g_DeviceInfoLock = NULL;
   495     g_DeviceDetectionThread = NULL;
   496     g_DeviceDetectionQuit = SDL_FALSE;
   497 
   498     return;
   499 }
   500 
   501 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
   502 {
   503     SDL_JoystickGUID guid;
   504     // the GUID is just the first 16 chars of the name for now
   505     const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
   506     SDL_zero( guid );
   507     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   508     return guid;
   509 }
   510 
   511 
   512 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
   513 {
   514     SDL_JoystickGUID guid;
   515     // the GUID is just the first 16 chars of the name for now
   516     const char *name = joystick->name;
   517     SDL_zero( guid );
   518     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   519     return guid;
   520 }
   521 
   522 SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
   523 {
   524     /* The XInput-capable DirectInput joystick backend implements the same
   525        function (SDL_SYS_IsXInputDeviceIndex), however in that case, not all
   526        joystick devices are XInput devices.  In this case, with the
   527        WinRT-enabled XInput-only backend, all "joystick" devices are XInput
   528        devices.
   529      */
   530     return SDL_TRUE;
   531 }
   532 
   533 #endif /* SDL_JOYSTICK_XINPUT */
   534 
   535 /* vi: set ts=4 sw=4 expandtab: */