src/joystick/winrt/SDL_xinputjoystick.c
changeset 8552 abd934eac415
parent 8502 78d341895e9f
child 8562 f1aac2d56c76
     1.1 --- a/src/joystick/winrt/SDL_xinputjoystick.c	Tue Dec 24 21:08:11 2013 -0500
     1.2 +++ b/src/joystick/winrt/SDL_xinputjoystick.c	Tue Dec 24 21:28:31 2013 -0500
     1.3 @@ -38,16 +38,18 @@
     1.4  #include "../SDL_joystick_c.h"
     1.5  #include "SDL_events.h"
     1.6  #include "../../events/SDL_events_c.h"
     1.7 +#include "SDL_timer.h"
     1.8  
     1.9  #include <Windows.h>
    1.10  #include <Xinput.h>
    1.11  
    1.12  struct joystick_hwdata {
    1.13 -    //Uint8 bXInputHaptic; // Supports force feedback via XInput.
    1.14 -    DWORD userIndex;    // The XInput device index, in the range [0, XUSER_MAX_COUNT-1] (probably [0,3]).
    1.15 -    XINPUT_STATE XInputState;   // the last-read in XInputState, kept around to compare old and new values
    1.16 -    SDL_bool isDeviceConnected; // was the device connected (on the last polling, or during backend-initialization)?
    1.17 -    SDL_bool isDeviceRemovalEventPending;   // was the device removed, and is the associated removal event pending?
    1.18 +    //Uint8 bXInputHaptic; // Supports force feedback via XInput.
    1.19 +    DWORD userIndex;    // The XInput device index, in the range [0, XUSER_MAX_COUNT-1] (probably [0,3]).
    1.20 +    XINPUT_STATE XInputState;   // the last-read in XInputState, kept around to compare old and new values
    1.21 +    SDL_bool isDeviceConnected; // was the device connected (on the last detection-polling, or during backend-initialization)?
    1.22 +    SDL_bool isDeviceConnectionEventPending;    // was a device added, and is the associated add-event pending?
    1.23 +    SDL_bool isDeviceRemovalEventPending;   // was the device removed, and is the associated remove-event pending?
    1.24  };
    1.25  
    1.26  /* Keep track of data on all XInput devices, regardless of whether or not
    1.27 @@ -55,6 +57,79 @@
    1.28   */
    1.29  static struct joystick_hwdata g_XInputData[XUSER_MAX_COUNT];
    1.30  
    1.31 +/* Device detection can be extremely costly performance-wise, in some cases.
    1.32 +   In particular, if no devices are connected, calls to detect a single device,
    1.33 +   via either XInputGetState() or XInputGetCapabilities(), can take upwards of
    1.34 +   20 ms on a 1st generation Surface RT, more if devices are detected across
    1.35 +   all of of XInput's four device slots.  WinRT and XInput do not appear to
    1.36 +   have callback-based APIs to notify an app when a device is connected, at
    1.37 +   least as of Windows 8.1.  The synchronous XInput calls must be used.
    1.38 +
    1.39 +   Once a device is connected, calling XInputGetState() is a much less costly
    1.40 +   operation, with individual calls costing well under 1 ms, and often under
    1.41 +   0.1 ms [on a 1st gen Surface RT].
    1.42 +
    1.43 +   With XInput's performance limitations in mind, a separate device-detection
    1.44 +   thread will be utilized (by SDL) to try to move costly XInput calls off the
    1.45 +   main thread.  Polling of active devices still, however, occurs on the main
    1.46 +   thread.
    1.47 + */
    1.48 +static SDL_Thread * g_DeviceDetectionThread = NULL;
    1.49 +static SDL_mutex * g_DeviceInfoLock = NULL;
    1.50 +static SDL_bool g_DeviceDetectionQuit = SDL_FALSE;
    1.51 +
    1.52 +/* Main function for the device-detection thread.
    1.53 + */
    1.54 +static int
    1.55 +DeviceDetectionThreadMain(void * _data)
    1.56 +{
    1.57 +    DWORD result;
    1.58 +    XINPUT_CAPABILITIES tempXInputCaps;
    1.59 +    int i;
    1.60 +
    1.61 +    while (1) {
    1.62 +        /* See if the device-detection thread is being asked to shutdown.
    1.63 +         */
    1.64 +        SDL_LockMutex(g_DeviceInfoLock);
    1.65 +        if (g_DeviceDetectionQuit) {
    1.66 +            SDL_UnlockMutex(g_DeviceInfoLock);
    1.67 +            break;
    1.68 +        }
    1.69 +        SDL_UnlockMutex(g_DeviceInfoLock);
    1.70 +
    1.71 +        /* Add a short delay to prevent the device-detection thread from eating
    1.72 +           up too much CPU time:
    1.73 +         */
    1.74 +        SDL_Delay(300);
    1.75 +
    1.76 +        /* TODO, WinRT: try making the device-detection thread wakeup sooner from its CPU-preserving SDL_Delay, if the thread was asked to quit.
    1.77 +         */
    1.78 +
    1.79 +        /* See if any new devices are connected. */
    1.80 +        for (i = 0; i < XUSER_MAX_COUNT; ++i) {
    1.81 +            if (!g_XInputData[i].isDeviceConnected &&
    1.82 +                !g_XInputData[i].isDeviceConnectionEventPending &&
    1.83 +                !g_XInputData[i].isDeviceRemovalEventPending)
    1.84 +            {
    1.85 +                result = XInputGetCapabilities(i, 0, &tempXInputCaps);
    1.86 +                if (result == ERROR_SUCCESS) {
    1.87 +                    /* Yes, a device is connected.  Mark it as such.
    1.88 +                       Others will be told about this (via an
    1.89 +                       SDL_JOYDEVICEADDED event) in the next call to
    1.90 +                       SDL_SYS_JoystickDetect.
    1.91 +                     */
    1.92 +                    SDL_LockMutex(g_DeviceInfoLock);
    1.93 +                    g_XInputData[i].isDeviceConnected = SDL_TRUE;
    1.94 +                    g_XInputData[i].isDeviceConnectionEventPending = SDL_TRUE;
    1.95 +                    SDL_UnlockMutex(g_DeviceInfoLock);
    1.96 +                }
    1.97 +            }
    1.98 +        }
    1.99 +    }
   1.100 +
   1.101 +    return 0;
   1.102 +}
   1.103 +
   1.104  /* Function to scan the system for joysticks.
   1.105   * It should return 0, or -1 on an unrecoverable fatal error.
   1.106   */
   1.107 @@ -76,6 +151,12 @@
   1.108          }
   1.109      }
   1.110  
   1.111 +    /* Start up the device-detection thread.
   1.112 +     */
   1.113 +    g_DeviceDetectionQuit = SDL_FALSE;
   1.114 +    g_DeviceInfoLock = SDL_CreateMutex();
   1.115 +    g_DeviceDetectionThread = SDL_CreateThread(DeviceDetectionThreadMain, "SDL_joystick", NULL);
   1.116 +
   1.117      return (0);
   1.118  }
   1.119  
   1.120 @@ -87,11 +168,13 @@
   1.121      /* Iterate through each possible XInput device and see if something
   1.122         was connected (at joystick init, or during the last polling).
   1.123       */
   1.124 +    SDL_LockMutex(g_DeviceInfoLock);
   1.125      for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   1.126          if (g_XInputData[i].isDeviceConnected) {
   1.127              ++joystickCount;
   1.128          }
   1.129      }
   1.130 +    SDL_UnlockMutex(g_DeviceInfoLock);
   1.131  
   1.132      return joystickCount;
   1.133  }
   1.134 @@ -99,36 +182,28 @@
   1.135  void SDL_SYS_JoystickDetect()
   1.136  {
   1.137      DWORD i;
   1.138 -    XINPUT_STATE tempXInputState;
   1.139 -    HRESULT result;
   1.140      SDL_Event event;
   1.141  
   1.142      /* Iterate through each possible XInput device, seeing if any devices
   1.143         have been connected, or if they were removed.
   1.144       */
   1.145 +    SDL_LockMutex(g_DeviceInfoLock);
   1.146      for (i = 0; i < XUSER_MAX_COUNT; ++i) {
   1.147          /* See if any new devices are connected. */
   1.148 -        if (!g_XInputData[i].isDeviceConnected && !g_XInputData[i].isDeviceRemovalEventPending) {
   1.149 -            result = XInputGetState(i, &tempXInputState);
   1.150 -            if (result == ERROR_SUCCESS) {
   1.151 -                /* Yup, a device is connected.  Mark the device as connected,
   1.152 -                   then tell others about it (via an SDL_JOYDEVICEADDED event.)
   1.153 -                 */
   1.154 -                g_XInputData[i].isDeviceConnected = SDL_TRUE;
   1.155 -
   1.156 +        if (g_XInputData[i].isDeviceConnectionEventPending) {
   1.157  #if !SDL_EVENTS_DISABLED
   1.158 -                SDL_zero(event);
   1.159 -                event.type = SDL_JOYDEVICEADDED;
   1.160 -                
   1.161 -                if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   1.162 -                    event.jdevice.which = i;
   1.163 -                    if ((SDL_EventOK == NULL)
   1.164 -                        || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   1.165 -                        SDL_PushEvent(&event);
   1.166 -                    }
   1.167 +            SDL_zero(event);
   1.168 +            event.type = SDL_JOYDEVICEADDED;
   1.169 +                
   1.170 +            if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   1.171 +                event.jdevice.which = i;
   1.172 +                if ((SDL_EventOK == NULL)
   1.173 +                    || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   1.174 +                    SDL_PushEvent(&event);
   1.175                  }
   1.176 +            }
   1.177  #endif
   1.178 -            }
   1.179 +            g_XInputData[i].isDeviceConnectionEventPending = SDL_FALSE;
   1.180          } else if (g_XInputData[i].isDeviceRemovalEventPending) {
   1.181              /* A device was previously marked as removed (by
   1.182                 SDL_SYS_JoystickUpdate).  Tell others about the device removal.
   1.183 @@ -137,19 +212,20 @@
   1.184              g_XInputData[i].isDeviceRemovalEventPending = SDL_FALSE;
   1.185  
   1.186  #if !SDL_EVENTS_DISABLED
   1.187 -            SDL_zero(event);
   1.188 -            event.type = SDL_JOYDEVICEREMOVED;
   1.189 -                
   1.190 -            if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   1.191 -                event.jdevice.which = i; //joystick->hwdata->userIndex;
   1.192 -                if ((SDL_EventOK == NULL)
   1.193 -                    || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   1.194 -                    SDL_PushEvent(&event);
   1.195 -                }
   1.196 +            SDL_zero(event);
   1.197 +            event.type = SDL_JOYDEVICEREMOVED;
   1.198 +                
   1.199 +            if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   1.200 +                event.jdevice.which = i; //joystick->hwdata->userIndex;
   1.201 +                if ((SDL_EventOK == NULL)
   1.202 +                    || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   1.203 +                    SDL_PushEvent(&event);
   1.204 +                }
   1.205              }
   1.206 -#endif
   1.207 +#endif
   1.208          }
   1.209      }
   1.210 +    SDL_UnlockMutex(g_DeviceInfoLock);
   1.211  }
   1.212  
   1.213  SDL_bool SDL_SYS_JoystickNeedsPolling()
   1.214 @@ -276,31 +352,35 @@
   1.215              device_index, (unsigned int)deviceCaps.Flags);
   1.216      }
   1.217  
   1.218 -    /* Create the joystick data structure */
   1.219 -    joystick->instance_id = device_index;
   1.220 -    joystick->hwdata = &g_XInputData[device_index];
   1.221 -
   1.222 -    // The XInput API has a hard coded button/axis mapping, so we just match it
   1.223 -    joystick->naxes = 6;
   1.224 -    joystick->nbuttons = 15;
   1.225 -    joystick->nballs = 0;
   1.226 -    joystick->nhats = 0;
   1.227 -
   1.228 -    /* We're done! */
   1.229 +    /* Create the joystick data structure */
   1.230 +    joystick->instance_id = device_index;
   1.231 +    joystick->hwdata = &g_XInputData[device_index];
   1.232 +
   1.233 +    // The XInput API has a hard coded button/axis mapping, so we just match it
   1.234 +    joystick->naxes = 6;
   1.235 +    joystick->nbuttons = 15;
   1.236 +    joystick->nballs = 0;
   1.237 +    joystick->nhats = 0;
   1.238 +
   1.239 +    /* We're done! */
   1.240      return (0);
   1.241  }
   1.242  
   1.243  /* Function to determine is this joystick is attached to the system right now */
   1.244  SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
   1.245  {
   1.246 -    return joystick->hwdata->isDeviceConnected;
   1.247 +    SDL_bool isDeviceConnected;
   1.248 +    SDL_LockMutex(g_DeviceInfoLock);
   1.249 +    isDeviceConnected = joystick->hwdata->isDeviceConnected;
   1.250 +    SDL_UnlockMutex(g_DeviceInfoLock);
   1.251 +    return isDeviceConnected;
   1.252  }
   1.253  
   1.254 -/* Function to return > 0 if a bit array of buttons differs after applying a mask
   1.255 -*/
   1.256 -static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
   1.257 -{
   1.258 -    return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
   1.259 +/* Function to return > 0 if a bit array of buttons differs after applying a mask
   1.260 +*/
   1.261 +static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
   1.262 +{
   1.263 +    return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
   1.264  }
   1.265  
   1.266  /* Function to update the state of a joystick - called as a device poll.
   1.267 @@ -311,70 +391,76 @@
   1.268  void
   1.269  SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
   1.270  {
   1.271 -    HRESULT result;
   1.272 -
   1.273 -    /* Before polling for new data, make note of the old data */
   1.274 -    XINPUT_STATE prevXInputState = joystick->hwdata->XInputState;
   1.275 -
   1.276 -    /* Poll for new data */
   1.277 -    result = XInputGetState(joystick->hwdata->userIndex, &joystick->hwdata->XInputState);
   1.278 -    if (result == ERROR_DEVICE_NOT_CONNECTED) {
   1.279 -        if (joystick->hwdata->isDeviceConnected) {
   1.280 -            joystick->hwdata->isDeviceConnected = SDL_FALSE;
   1.281 -            joystick->hwdata->isDeviceRemovalEventPending = SDL_TRUE;
   1.282 -            /* TODO, WinRT: make sure isDeviceRemovalEventPending gets cleared as appropriate, and that quick re-plugs don't cause trouble */
   1.283 -        }
   1.284 -        return;
   1.285 -    }
   1.286 -
   1.287 -    /* Make sure the device is marked as connected */
   1.288 -    joystick->hwdata->isDeviceConnected = SDL_TRUE;
   1.289 -
   1.290 -    // only fire events if the data changed from last time
   1.291 -    if ( joystick->hwdata->XInputState.dwPacketNumber != 0 
   1.292 -        && joystick->hwdata->XInputState.dwPacketNumber != prevXInputState.dwPacketNumber )
   1.293 -    {
   1.294 -        XINPUT_STATE *pXInputState = &joystick->hwdata->XInputState;
   1.295 -        XINPUT_STATE *pXInputStatePrev = &prevXInputState;
   1.296 -
   1.297 -        SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
   1.298 -        SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) );
   1.299 -        SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
   1.300 -        SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) );
   1.301 -        SDL_PrivateJoystickAxis(joystick, 4, (Sint16)((int)pXInputState->Gamepad.bLeftTrigger*32767/255) );
   1.302 -        SDL_PrivateJoystickAxis(joystick, 5, (Sint16)((int)pXInputState->Gamepad.bRightTrigger*32767/255) );
   1.303 -
   1.304 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
   1.305 -            SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED :	SDL_RELEASED );
   1.306 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
   1.307 -            SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED :	SDL_RELEASED );
   1.308 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
   1.309 -            SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED :	SDL_RELEASED );
   1.310 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
   1.311 -            SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED :	SDL_RELEASED );
   1.312 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
   1.313 -            SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED :	SDL_RELEASED );
   1.314 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
   1.315 -            SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED :	SDL_RELEASED );
   1.316 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
   1.317 -            SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
   1.318 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
   1.319 -            SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
   1.320 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
   1.321 -            SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
   1.322 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
   1.323 -            SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
   1.324 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
   1.325 -            SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED :	SDL_RELEASED );
   1.326 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
   1.327 -            SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED :	SDL_RELEASED );
   1.328 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
   1.329 -            SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED :	SDL_RELEASED );
   1.330 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
   1.331 -            SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED :	SDL_RELEASED );
   1.332 -        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons,  0x400 ) )
   1.333 -            SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED :	SDL_RELEASED ); // 0x400 is the undocumented code for the guide button
   1.334 +    HRESULT result;
   1.335 +    XINPUT_STATE prevXInputState;
   1.336 +
   1.337 +    SDL_LockMutex(g_DeviceInfoLock);
   1.338 +
   1.339 +    /* Before polling for new data, make note of the old data */
   1.340 +    prevXInputState = joystick->hwdata->XInputState;
   1.341 +
   1.342 +    /* Poll for new data */
   1.343 +    result = XInputGetState(joystick->hwdata->userIndex, &joystick->hwdata->XInputState);
   1.344 +    if (result == ERROR_DEVICE_NOT_CONNECTED) {
   1.345 +        if (joystick->hwdata->isDeviceConnected) {
   1.346 +            joystick->hwdata->isDeviceConnected = SDL_FALSE;
   1.347 +            joystick->hwdata->isDeviceRemovalEventPending = SDL_TRUE;
   1.348 +            /* TODO, WinRT: make sure isDeviceRemovalEventPending gets cleared as appropriate, and that quick re-plugs don't cause trouble */
   1.349 +        }
   1.350 +        SDL_UnlockMutex(g_DeviceInfoLock);
   1.351 +        return;
   1.352      }
   1.353 +
   1.354 +    /* Make sure the device is marked as connected */
   1.355 +    joystick->hwdata->isDeviceConnected = SDL_TRUE;
   1.356 +
   1.357 +    // only fire events if the data changed from last time
   1.358 +    if ( joystick->hwdata->XInputState.dwPacketNumber != 0 
   1.359 +        && joystick->hwdata->XInputState.dwPacketNumber != prevXInputState.dwPacketNumber )
   1.360 +    {
   1.361 +        XINPUT_STATE *pXInputState = &joystick->hwdata->XInputState;
   1.362 +        XINPUT_STATE *pXInputStatePrev = &prevXInputState;
   1.363 +
   1.364 +        SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
   1.365 +        SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) );
   1.366 +        SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
   1.367 +        SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) );
   1.368 +        SDL_PrivateJoystickAxis(joystick, 4, (Sint16)((int)pXInputState->Gamepad.bLeftTrigger*32767/255) );
   1.369 +        SDL_PrivateJoystickAxis(joystick, 5, (Sint16)((int)pXInputState->Gamepad.bRightTrigger*32767/255) );
   1.370 +
   1.371 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
   1.372 +            SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED :	SDL_RELEASED );
   1.373 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
   1.374 +            SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED :	SDL_RELEASED );
   1.375 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
   1.376 +            SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED :	SDL_RELEASED );
   1.377 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
   1.378 +            SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED :	SDL_RELEASED );
   1.379 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
   1.380 +            SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED :	SDL_RELEASED );
   1.381 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
   1.382 +            SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED :	SDL_RELEASED );
   1.383 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
   1.384 +            SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
   1.385 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
   1.386 +            SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
   1.387 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
   1.388 +            SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
   1.389 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
   1.390 +            SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
   1.391 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
   1.392 +            SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED :	SDL_RELEASED );
   1.393 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
   1.394 +            SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED :	SDL_RELEASED );
   1.395 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
   1.396 +            SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED :	SDL_RELEASED );
   1.397 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
   1.398 +            SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED :	SDL_RELEASED );
   1.399 +        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons,  0x400 ) )
   1.400 +            SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED :	SDL_RELEASED ); // 0x400 is the undocumented code for the guide button
   1.401 +    }
   1.402 +
   1.403 +    SDL_UnlockMutex(g_DeviceInfoLock);
   1.404  }
   1.405  
   1.406  /* Function to close a joystick after use */
   1.407 @@ -382,7 +468,9 @@
   1.408  SDL_SYS_JoystickClose(SDL_Joystick * joystick)
   1.409  {
   1.410      /* Clear cached button data on the joystick */
   1.411 +    SDL_LockMutex(g_DeviceInfoLock);
   1.412      SDL_zero(joystick->hwdata->XInputState);
   1.413 +    SDL_UnlockMutex(g_DeviceInfoLock);
   1.414  
   1.415      /* There's need to free 'hwdata', as it's a pointer to a global array.
   1.416         The field will be cleared anyways, just to indicate that it's not
   1.417 @@ -395,6 +483,18 @@
   1.418  void
   1.419  SDL_SYS_JoystickQuit(void)
   1.420  {
   1.421 +    /* Tell the joystick detection thread to stop, then wait for it to finish */
   1.422 +    SDL_LockMutex(g_DeviceInfoLock);
   1.423 +    g_DeviceDetectionQuit = SDL_TRUE;
   1.424 +    SDL_UnlockMutex(g_DeviceInfoLock);
   1.425 +    SDL_WaitThread(g_DeviceDetectionThread, NULL);
   1.426 +
   1.427 +    /* Clean up device-detection stuff */
   1.428 +    SDL_DestroyMutex(g_DeviceInfoLock);
   1.429 +    g_DeviceInfoLock = NULL;
   1.430 +    g_DeviceDetectionThread = NULL;
   1.431 +    g_DeviceDetectionQuit = SDL_FALSE;
   1.432 +
   1.433      return;
   1.434  }
   1.435  
   1.436 @@ -419,15 +519,15 @@
   1.437      return guid;
   1.438  }
   1.439  
   1.440 -SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
   1.441 -{
   1.442 -    /* The XInput-capable DirectInput joystick backend implements the same
   1.443 -       function (SDL_SYS_IsXInputDeviceIndex), however in that case, not all
   1.444 -       joystick devices are XInput devices.  In this case, with the
   1.445 -       WinRT-enabled XInput-only backend, all "joystick" devices are XInput
   1.446 -       devices.
   1.447 -     */
   1.448 -    return SDL_TRUE;
   1.449 +SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
   1.450 +{
   1.451 +    /* The XInput-capable DirectInput joystick backend implements the same
   1.452 +       function (SDL_SYS_IsXInputDeviceIndex), however in that case, not all
   1.453 +       joystick devices are XInput devices.  In this case, with the
   1.454 +       WinRT-enabled XInput-only backend, all "joystick" devices are XInput
   1.455 +       devices.
   1.456 +     */
   1.457 +    return SDL_TRUE;
   1.458  }
   1.459  
   1.460  #endif /* SDL_JOYSTICK_XINPUT */