WinRT: moved ill-performing XInput device-detection calls to a separate thread

main
David Ludwig 2013-12-24 21:28:31 -05:00
parent 27e79b93ec
commit 3f1e3c303e
1 changed files with 224 additions and 124 deletions

View File

@ -38,16 +38,18 @@
#include "../SDL_joystick_c.h" #include "../SDL_joystick_c.h"
#include "SDL_events.h" #include "SDL_events.h"
#include "../../events/SDL_events_c.h" #include "../../events/SDL_events_c.h"
#include "SDL_timer.h"
#include <Windows.h> #include <Windows.h>
#include <Xinput.h> #include <Xinput.h>
struct joystick_hwdata { struct joystick_hwdata {
//Uint8 bXInputHaptic; // Supports force feedback via XInput. //Uint8 bXInputHaptic; // Supports force feedback via XInput.
DWORD userIndex; // The XInput device index, in the range [0, XUSER_MAX_COUNT-1] (probably [0,3]). DWORD userIndex; // The XInput device index, in the range [0, XUSER_MAX_COUNT-1] (probably [0,3]).
XINPUT_STATE XInputState; // the last-read in XInputState, kept around to compare old and new values XINPUT_STATE XInputState; // the last-read in XInputState, kept around to compare old and new values
SDL_bool isDeviceConnected; // was the device connected (on the last polling, or during backend-initialization)? SDL_bool isDeviceConnected; // was the device connected (on the last detection-polling, or during backend-initialization)?
SDL_bool isDeviceRemovalEventPending; // was the device removed, and is the associated removal event pending? SDL_bool isDeviceConnectionEventPending; // was a device added, and is the associated add-event pending?
SDL_bool isDeviceRemovalEventPending; // was the device removed, and is the associated remove-event pending?
}; };
/* Keep track of data on all XInput devices, regardless of whether or not /* Keep track of data on all XInput devices, regardless of whether or not
@ -55,6 +57,79 @@ struct joystick_hwdata {
*/ */
static struct joystick_hwdata g_XInputData[XUSER_MAX_COUNT]; static struct joystick_hwdata g_XInputData[XUSER_MAX_COUNT];
/* Device detection can be extremely costly performance-wise, in some cases.
In particular, if no devices are connected, calls to detect a single device,
via either XInputGetState() or XInputGetCapabilities(), can take upwards of
20 ms on a 1st generation Surface RT, more if devices are detected across
all of of XInput's four device slots. WinRT and XInput do not appear to
have callback-based APIs to notify an app when a device is connected, at
least as of Windows 8.1. The synchronous XInput calls must be used.
Once a device is connected, calling XInputGetState() is a much less costly
operation, with individual calls costing well under 1 ms, and often under
0.1 ms [on a 1st gen Surface RT].
With XInput's performance limitations in mind, a separate device-detection
thread will be utilized (by SDL) to try to move costly XInput calls off the
main thread. Polling of active devices still, however, occurs on the main
thread.
*/
static SDL_Thread * g_DeviceDetectionThread = NULL;
static SDL_mutex * g_DeviceInfoLock = NULL;
static SDL_bool g_DeviceDetectionQuit = SDL_FALSE;
/* Main function for the device-detection thread.
*/
static int
DeviceDetectionThreadMain(void * _data)
{
DWORD result;
XINPUT_CAPABILITIES tempXInputCaps;
int i;
while (1) {
/* See if the device-detection thread is being asked to shutdown.
*/
SDL_LockMutex(g_DeviceInfoLock);
if (g_DeviceDetectionQuit) {
SDL_UnlockMutex(g_DeviceInfoLock);
break;
}
SDL_UnlockMutex(g_DeviceInfoLock);
/* Add a short delay to prevent the device-detection thread from eating
up too much CPU time:
*/
SDL_Delay(300);
/* TODO, WinRT: try making the device-detection thread wakeup sooner from its CPU-preserving SDL_Delay, if the thread was asked to quit.
*/
/* See if any new devices are connected. */
for (i = 0; i < XUSER_MAX_COUNT; ++i) {
if (!g_XInputData[i].isDeviceConnected &&
!g_XInputData[i].isDeviceConnectionEventPending &&
!g_XInputData[i].isDeviceRemovalEventPending)
{
result = XInputGetCapabilities(i, 0, &tempXInputCaps);
if (result == ERROR_SUCCESS) {
/* Yes, a device is connected. Mark it as such.
Others will be told about this (via an
SDL_JOYDEVICEADDED event) in the next call to
SDL_SYS_JoystickDetect.
*/
SDL_LockMutex(g_DeviceInfoLock);
g_XInputData[i].isDeviceConnected = SDL_TRUE;
g_XInputData[i].isDeviceConnectionEventPending = SDL_TRUE;
SDL_UnlockMutex(g_DeviceInfoLock);
}
}
}
}
return 0;
}
/* Function to scan the system for joysticks. /* Function to scan the system for joysticks.
* It should return 0, or -1 on an unrecoverable fatal error. * It should return 0, or -1 on an unrecoverable fatal error.
*/ */
@ -76,6 +151,12 @@ SDL_SYS_JoystickInit(void)
} }
} }
/* Start up the device-detection thread.
*/
g_DeviceDetectionQuit = SDL_FALSE;
g_DeviceInfoLock = SDL_CreateMutex();
g_DeviceDetectionThread = SDL_CreateThread(DeviceDetectionThreadMain, "SDL_joystick", NULL);
return (0); return (0);
} }
@ -87,11 +168,13 @@ int SDL_SYS_NumJoysticks()
/* Iterate through each possible XInput device and see if something /* Iterate through each possible XInput device and see if something
was connected (at joystick init, or during the last polling). was connected (at joystick init, or during the last polling).
*/ */
SDL_LockMutex(g_DeviceInfoLock);
for (i = 0; i < XUSER_MAX_COUNT; ++i) { for (i = 0; i < XUSER_MAX_COUNT; ++i) {
if (g_XInputData[i].isDeviceConnected) { if (g_XInputData[i].isDeviceConnected) {
++joystickCount; ++joystickCount;
} }
} }
SDL_UnlockMutex(g_DeviceInfoLock);
return joystickCount; return joystickCount;
} }
@ -99,36 +182,28 @@ int SDL_SYS_NumJoysticks()
void SDL_SYS_JoystickDetect() void SDL_SYS_JoystickDetect()
{ {
DWORD i; DWORD i;
XINPUT_STATE tempXInputState;
HRESULT result;
SDL_Event event; SDL_Event event;
/* Iterate through each possible XInput device, seeing if any devices /* Iterate through each possible XInput device, seeing if any devices
have been connected, or if they were removed. have been connected, or if they were removed.
*/ */
SDL_LockMutex(g_DeviceInfoLock);
for (i = 0; i < XUSER_MAX_COUNT; ++i) { for (i = 0; i < XUSER_MAX_COUNT; ++i) {
/* See if any new devices are connected. */ /* See if any new devices are connected. */
if (!g_XInputData[i].isDeviceConnected && !g_XInputData[i].isDeviceRemovalEventPending) { if (g_XInputData[i].isDeviceConnectionEventPending) {
result = XInputGetState(i, &tempXInputState);
if (result == ERROR_SUCCESS) {
/* Yup, a device is connected. Mark the device as connected,
then tell others about it (via an SDL_JOYDEVICEADDED event.)
*/
g_XInputData[i].isDeviceConnected = SDL_TRUE;
#if !SDL_EVENTS_DISABLED #if !SDL_EVENTS_DISABLED
SDL_zero(event); SDL_zero(event);
event.type = SDL_JOYDEVICEADDED; event.type = SDL_JOYDEVICEADDED;
if (SDL_GetEventState(event.type) == SDL_ENABLE) { if (SDL_GetEventState(event.type) == SDL_ENABLE) {
event.jdevice.which = i; event.jdevice.which = i;
if ((SDL_EventOK == NULL) if ((SDL_EventOK == NULL)
|| (*SDL_EventOK) (SDL_EventOKParam, &event)) { || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
SDL_PushEvent(&event); SDL_PushEvent(&event);
}
} }
#endif
} }
#endif
g_XInputData[i].isDeviceConnectionEventPending = SDL_FALSE;
} else if (g_XInputData[i].isDeviceRemovalEventPending) { } else if (g_XInputData[i].isDeviceRemovalEventPending) {
/* A device was previously marked as removed (by /* A device was previously marked as removed (by
SDL_SYS_JoystickUpdate). Tell others about the device removal. SDL_SYS_JoystickUpdate). Tell others about the device removal.
@ -137,19 +212,20 @@ void SDL_SYS_JoystickDetect()
g_XInputData[i].isDeviceRemovalEventPending = SDL_FALSE; g_XInputData[i].isDeviceRemovalEventPending = SDL_FALSE;
#if !SDL_EVENTS_DISABLED #if !SDL_EVENTS_DISABLED
SDL_zero(event); SDL_zero(event);
event.type = SDL_JOYDEVICEREMOVED; event.type = SDL_JOYDEVICEREMOVED;
if (SDL_GetEventState(event.type) == SDL_ENABLE) { if (SDL_GetEventState(event.type) == SDL_ENABLE) {
event.jdevice.which = i; //joystick->hwdata->userIndex; event.jdevice.which = i; //joystick->hwdata->userIndex;
if ((SDL_EventOK == NULL) if ((SDL_EventOK == NULL)
|| (*SDL_EventOK) (SDL_EventOKParam, &event)) { || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
SDL_PushEvent(&event); SDL_PushEvent(&event);
} }
} }
#endif #endif
} }
} }
SDL_UnlockMutex(g_DeviceInfoLock);
} }
SDL_bool SDL_SYS_JoystickNeedsPolling() SDL_bool SDL_SYS_JoystickNeedsPolling()
@ -276,31 +352,35 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
device_index, (unsigned int)deviceCaps.Flags); device_index, (unsigned int)deviceCaps.Flags);
} }
/* Create the joystick data structure */ /* Create the joystick data structure */
joystick->instance_id = device_index; joystick->instance_id = device_index;
joystick->hwdata = &g_XInputData[device_index]; joystick->hwdata = &g_XInputData[device_index];
// The XInput API has a hard coded button/axis mapping, so we just match it // The XInput API has a hard coded button/axis mapping, so we just match it
joystick->naxes = 6; joystick->naxes = 6;
joystick->nbuttons = 15; joystick->nbuttons = 15;
joystick->nballs = 0; joystick->nballs = 0;
joystick->nhats = 0; joystick->nhats = 0;
/* We're done! */ /* We're done! */
return (0); return (0);
} }
/* Function to determine is this joystick is attached to the system right now */ /* Function to determine is this joystick is attached to the system right now */
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick) SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
{ {
return joystick->hwdata->isDeviceConnected; SDL_bool isDeviceConnected;
SDL_LockMutex(g_DeviceInfoLock);
isDeviceConnected = joystick->hwdata->isDeviceConnected;
SDL_UnlockMutex(g_DeviceInfoLock);
return isDeviceConnected;
} }
/* Function to return > 0 if a bit array of buttons differs after applying a mask /* Function to return > 0 if a bit array of buttons differs after applying a mask
*/ */
static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask ) static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
{ {
return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask ); return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
} }
/* Function to update the state of a joystick - called as a device poll. /* Function to update the state of a joystick - called as a device poll.
@ -311,70 +391,76 @@ static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
void void
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
{ {
HRESULT result; HRESULT result;
XINPUT_STATE prevXInputState;
/* Before polling for new data, make note of the old data */
XINPUT_STATE prevXInputState = joystick->hwdata->XInputState; SDL_LockMutex(g_DeviceInfoLock);
/* Poll for new data */ /* Before polling for new data, make note of the old data */
result = XInputGetState(joystick->hwdata->userIndex, &joystick->hwdata->XInputState); prevXInputState = joystick->hwdata->XInputState;
if (result == ERROR_DEVICE_NOT_CONNECTED) {
if (joystick->hwdata->isDeviceConnected) { /* Poll for new data */
joystick->hwdata->isDeviceConnected = SDL_FALSE; result = XInputGetState(joystick->hwdata->userIndex, &joystick->hwdata->XInputState);
joystick->hwdata->isDeviceRemovalEventPending = SDL_TRUE; if (result == ERROR_DEVICE_NOT_CONNECTED) {
/* TODO, WinRT: make sure isDeviceRemovalEventPending gets cleared as appropriate, and that quick re-plugs don't cause trouble */ if (joystick->hwdata->isDeviceConnected) {
} joystick->hwdata->isDeviceConnected = SDL_FALSE;
return; joystick->hwdata->isDeviceRemovalEventPending = SDL_TRUE;
} /* TODO, WinRT: make sure isDeviceRemovalEventPending gets cleared as appropriate, and that quick re-plugs don't cause trouble */
}
/* Make sure the device is marked as connected */ SDL_UnlockMutex(g_DeviceInfoLock);
joystick->hwdata->isDeviceConnected = SDL_TRUE; return;
// only fire events if the data changed from last time
if ( joystick->hwdata->XInputState.dwPacketNumber != 0
&& joystick->hwdata->XInputState.dwPacketNumber != prevXInputState.dwPacketNumber )
{
XINPUT_STATE *pXInputState = &joystick->hwdata->XInputState;
XINPUT_STATE *pXInputStatePrev = &prevXInputState;
SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) );
SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) );
SDL_PrivateJoystickAxis(joystick, 4, (Sint16)((int)pXInputState->Gamepad.bLeftTrigger*32767/255) );
SDL_PrivateJoystickAxis(joystick, 5, (Sint16)((int)pXInputState->Gamepad.bRightTrigger*32767/255) );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, 0x400 ) )
SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED : SDL_RELEASED ); // 0x400 is the undocumented code for the guide button
} }
/* Make sure the device is marked as connected */
joystick->hwdata->isDeviceConnected = SDL_TRUE;
// only fire events if the data changed from last time
if ( joystick->hwdata->XInputState.dwPacketNumber != 0
&& joystick->hwdata->XInputState.dwPacketNumber != prevXInputState.dwPacketNumber )
{
XINPUT_STATE *pXInputState = &joystick->hwdata->XInputState;
XINPUT_STATE *pXInputStatePrev = &prevXInputState;
SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) );
SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) );
SDL_PrivateJoystickAxis(joystick, 4, (Sint16)((int)pXInputState->Gamepad.bLeftTrigger*32767/255) );
SDL_PrivateJoystickAxis(joystick, 5, (Sint16)((int)pXInputState->Gamepad.bRightTrigger*32767/255) );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED : SDL_RELEASED );
if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, 0x400 ) )
SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED : SDL_RELEASED ); // 0x400 is the undocumented code for the guide button
}
SDL_UnlockMutex(g_DeviceInfoLock);
} }
/* Function to close a joystick after use */ /* Function to close a joystick after use */
@ -382,7 +468,9 @@ void
SDL_SYS_JoystickClose(SDL_Joystick * joystick) SDL_SYS_JoystickClose(SDL_Joystick * joystick)
{ {
/* Clear cached button data on the joystick */ /* Clear cached button data on the joystick */
SDL_LockMutex(g_DeviceInfoLock);
SDL_zero(joystick->hwdata->XInputState); SDL_zero(joystick->hwdata->XInputState);
SDL_UnlockMutex(g_DeviceInfoLock);
/* There's need to free 'hwdata', as it's a pointer to a global array. /* There's need to free 'hwdata', as it's a pointer to a global array.
The field will be cleared anyways, just to indicate that it's not The field will be cleared anyways, just to indicate that it's not
@ -395,6 +483,18 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick)
void void
SDL_SYS_JoystickQuit(void) SDL_SYS_JoystickQuit(void)
{ {
/* Tell the joystick detection thread to stop, then wait for it to finish */
SDL_LockMutex(g_DeviceInfoLock);
g_DeviceDetectionQuit = SDL_TRUE;
SDL_UnlockMutex(g_DeviceInfoLock);
SDL_WaitThread(g_DeviceDetectionThread, NULL);
/* Clean up device-detection stuff */
SDL_DestroyMutex(g_DeviceInfoLock);
g_DeviceInfoLock = NULL;
g_DeviceDetectionThread = NULL;
g_DeviceDetectionQuit = SDL_FALSE;
return; return;
} }
@ -419,15 +519,15 @@ SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
return guid; return guid;
} }
SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index) SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
{ {
/* The XInput-capable DirectInput joystick backend implements the same /* The XInput-capable DirectInput joystick backend implements the same
function (SDL_SYS_IsXInputDeviceIndex), however in that case, not all function (SDL_SYS_IsXInputDeviceIndex), however in that case, not all
joystick devices are XInput devices. In this case, with the joystick devices are XInput devices. In this case, with the
WinRT-enabled XInput-only backend, all "joystick" devices are XInput WinRT-enabled XInput-only backend, all "joystick" devices are XInput
devices. devices.
*/ */
return SDL_TRUE; return SDL_TRUE;
} }
#endif /* SDL_JOYSTICK_XINPUT */ #endif /* SDL_JOYSTICK_XINPUT */