Reworked XInput and DirectInput joystick code.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 28 Aug 2013 16:43:47 -0400
changeset 770737e02f8fcfa8
parent 7706 8cc29a668223
child 7708 d5aa9910b1f7
Reworked XInput and DirectInput joystick code.

Now multiple XInput controllers map correctly to device indexes instead of grabbing
the first available userid, and are completely separated out from DirectInput.

Also, the hardcoded limitation on number of DirectInput devices is gone. I don't
expect there to really ever be more than eight joysticks plugged into a machine, but
it was a leftover limitation for a static array we didn't actually use anymore.

Fixes Bugzilla #1984. (etc?)
src/joystick/SDL_gamecontroller.c
src/joystick/windows/SDL_dxjoystick.c
     1.1 --- a/src/joystick/SDL_gamecontroller.c	Wed Aug 28 16:35:32 2013 -0400
     1.2 +++ b/src/joystick/SDL_gamecontroller.c	Wed Aug 28 16:43:47 2013 -0400
     1.3 @@ -851,9 +851,6 @@
     1.4      SDL_GameController *gamecontroller;
     1.5      SDL_GameController *gamecontrollerlist;
     1.6      ControllerMapping_t *pSupportedController = NULL;
     1.7 -#ifdef SDL_JOYSTICK_DINPUT
     1.8 -	SDL_bool bIsXinputDevice;
     1.9 -#endif
    1.10  
    1.11      if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
    1.12          SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
    1.13 @@ -886,11 +883,6 @@
    1.14          return NULL;
    1.15      }
    1.16  
    1.17 -#ifdef SDL_JOYSTICK_DINPUT
    1.18 -	/* check if we think we should open this device in XInput mode */
    1.19 -	bIsXinputDevice = SDL_SYS_IsXInputDeviceIndex(device_index);
    1.20 -#endif
    1.21 -
    1.22      SDL_memset(gamecontroller, 0, (sizeof *gamecontroller));
    1.23      gamecontroller->joystick = SDL_JoystickOpen(device_index);
    1.24      if ( !gamecontroller->joystick ) {
    1.25 @@ -898,19 +890,6 @@
    1.26          return NULL;
    1.27      }
    1.28  
    1.29 -#ifdef SDL_JOYSTICK_DINPUT
    1.30 -	if ( !SDL_SYS_IsXInputJoystick( gamecontroller->joystick ) && bIsXinputDevice )
    1.31 -	{
    1.32 -		/* we tried to open the controller in XInput mode and failed, so get the mapping again for the direct input variant if possible */
    1.33 -		SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID( device_index );
    1.34 -		pSupportedController = SDL_PrivateGetControllerMappingForGUID(&jGUID);
    1.35 -		if ( !pSupportedController ) {
    1.36 -			SDL_SetError("Failed to open device in XInput mode (%d)", device_index );
    1.37 -			return (NULL);
    1.38 -		}
    1.39 -	}
    1.40 -#endif
    1.41 -
    1.42      SDL_PrivateLoadButtonMapping( &gamecontroller->mapping, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping );
    1.43  
    1.44      /* Add joystick to list */
     2.1 --- a/src/joystick/windows/SDL_dxjoystick.c	Wed Aug 28 16:35:32 2013 -0400
     2.2 +++ b/src/joystick/windows/SDL_dxjoystick.c	Wed Aug 28 16:43:47 2013 -0400
     2.3 @@ -55,7 +55,6 @@
     2.4  
     2.5  
     2.6  #define INPUT_QSIZE 32      /* Buffer up to 32 input messages */
     2.7 -#define MAX_JOYSTICKS 8
     2.8  #define AXIS_MIN    -32768  /* minimum value for axis coordinate */
     2.9  #define AXIS_MAX    32767   /* maximum value for axis coordinate */
    2.10  #define JOY_AXIS_THRESHOLD  (((AXIS_MAX)-(AXIS_MIN))/100)   /* 1% motion */
    2.11 @@ -70,7 +69,6 @@
    2.12  static SDL_bool s_bDeviceAdded = SDL_FALSE;
    2.13  static SDL_bool s_bDeviceRemoved = SDL_FALSE;
    2.14  static SDL_JoystickID s_nInstanceID = -1;
    2.15 -static GUID *s_pKnownJoystickGUIDs = NULL;
    2.16  static SDL_cond *s_condJoystickThread = NULL;
    2.17  static SDL_mutex *s_mutexJoyStickEnum = NULL;
    2.18  static SDL_Thread *s_threadJoystick = NULL;
    2.19 @@ -481,10 +479,10 @@
    2.20      HWND messageWindow = 0;
    2.21      HDEVNOTIFY hNotify = 0;
    2.22      DEV_BROADCAST_DEVICEINTERFACE dbh;
    2.23 -    SDL_bool bOpenedXInputDevices[4];
    2.24 +    SDL_bool bOpenedXInputDevices[SDL_XINPUT_MAX_DEVICES];
    2.25      WNDCLASSEX wincl;
    2.26  
    2.27 -    SDL_memset( bOpenedXInputDevices, 0x0, sizeof(bOpenedXInputDevices) );
    2.28 +    SDL_zero(bOpenedXInputDevices);
    2.29  
    2.30      WIN_CoInitialize();
    2.31  
    2.32 @@ -505,7 +503,7 @@
    2.33          return SDL_SetError("Failed to create message window for joystick autodetect.", GetLastError());
    2.34      }
    2.35  
    2.36 -    SDL_memset(&dbh, 0x0, sizeof(dbh));
    2.37 +    SDL_zero(dbh);
    2.38  
    2.39      dbh.dbcc_size = sizeof(dbh);
    2.40      dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    2.41 @@ -521,9 +519,8 @@
    2.42      while ( s_bJoystickThreadQuit == SDL_FALSE )
    2.43      {
    2.44          MSG messages;
    2.45 -        Uint8 userId;
    2.46 -        int nCurrentOpenedXInputDevices = 0;
    2.47 -        int nNewOpenedXInputDevices = 0;
    2.48 +        SDL_bool bXInputChanged = SDL_FALSE;
    2.49 +
    2.50          SDL_CondWaitTimeout( s_condJoystickThread, s_mutexJoyStickEnum, 300 );
    2.51  
    2.52          while ( s_bJoystickThreadQuit == SDL_FALSE && PeekMessage(&messages, messageWindow, 0, 0, PM_NOREMOVE) )
    2.53 @@ -534,33 +531,24 @@
    2.54              }
    2.55          }
    2.56  
    2.57 -        if ( s_bXInputEnabled && XINPUTGETCAPABILITIES )
    2.58 -        {
    2.59 +        if ( s_bXInputEnabled && XINPUTGETCAPABILITIES ) {
    2.60              /* scan for any change in XInput devices */
    2.61 -            for ( userId = 0; userId < 4; userId++ )
    2.62 -            {
    2.63 +            Uint8 userId;
    2.64 +            for (userId = 0; userId < SDL_XINPUT_MAX_DEVICES; userId++) {
    2.65                  XINPUT_CAPABILITIES capabilities;
    2.66 -                DWORD result;
    2.67 -
    2.68 -                if ( bOpenedXInputDevices[userId] == SDL_TRUE )
    2.69 -                    nCurrentOpenedXInputDevices++;
    2.70 -
    2.71 -                result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
    2.72 -                if ( result == ERROR_SUCCESS )
    2.73 -                {
    2.74 -                    bOpenedXInputDevices[userId] = SDL_TRUE;
    2.75 -                    nNewOpenedXInputDevices++;
    2.76 -                }
    2.77 -                else
    2.78 -                {
    2.79 -                    bOpenedXInputDevices[userId] = SDL_FALSE;
    2.80 +                const DWORD result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
    2.81 +                const SDL_bool available = (result == ERROR_SUCCESS);
    2.82 +                if (bOpenedXInputDevices[userId] != available) {
    2.83 +                    bXInputChanged = SDL_TRUE;
    2.84 +                    bOpenedXInputDevices[userId] = available;
    2.85                  }
    2.86              }
    2.87          }
    2.88  
    2.89 -        if ( s_pKnownJoystickGUIDs && ( s_bWindowsDeviceChanged || nNewOpenedXInputDevices != nCurrentOpenedXInputDevices ) )
    2.90 -        {
    2.91 +        if (s_bWindowsDeviceChanged || bXInputChanged) {
    2.92 +            SDL_UnlockMutex( s_mutexJoyStickEnum );  /* let main thread go while we SDL_Delay(). */
    2.93              SDL_Delay( 300 ); /* wait for direct input to find out about this device */
    2.94 +            SDL_LockMutex( s_mutexJoyStickEnum );
    2.95  
    2.96              s_bDeviceRemoved = SDL_TRUE;
    2.97              s_bDeviceAdded = SDL_TRUE;
    2.98 @@ -625,15 +613,16 @@
    2.99          return SetDIerror("IDirectInput::Initialize", result);
   2.100      }
   2.101  
   2.102 +    if ((s_bXInputEnabled) && (WIN_LoadXInputDLL() == -1)) {
   2.103 +        s_bXInputEnabled = SDL_FALSE;  /* oh well. */
   2.104 +    }
   2.105 +
   2.106      s_mutexJoyStickEnum = SDL_CreateMutex();
   2.107      s_condJoystickThread = SDL_CreateCond();
   2.108      s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
   2.109 +
   2.110      SDL_SYS_JoystickDetect();
   2.111  
   2.112 -    if ((s_bXInputEnabled) && (WIN_LoadXInputDLL() == -1)) {
   2.113 -        s_bXInputEnabled = SDL_FALSE;  /* oh well. */
   2.114 -    }
   2.115 -
   2.116      if ( !s_threadJoystick )
   2.117      {
   2.118          s_bJoystickThreadQuit = SDL_FALSE;
   2.119 @@ -662,15 +651,17 @@
   2.120      return nJoysticks;
   2.121  }
   2.122  
   2.123 -static int s_iNewGUID = 0;
   2.124 -
   2.125  /* helper function for direct input, gets called for each connected joystick */
   2.126  static BOOL CALLBACK
   2.127      EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
   2.128  {
   2.129      JoyStick_DeviceData *pNewJoystick;
   2.130      JoyStick_DeviceData *pPrevJoystick = NULL;
   2.131 -    SDL_bool bXInputDevice;
   2.132 +
   2.133 +    if (SDL_IsXInputDevice( &pdidInstance->guidProduct )) {
   2.134 +        return DIENUM_CONTINUE;  /* ignore XInput devices here, keep going. */
   2.135 +    }
   2.136 +
   2.137      pNewJoystick = *(JoyStick_DeviceData **)pContext;
   2.138      while ( pNewJoystick )
   2.139      {
   2.140 @@ -689,58 +680,107 @@
   2.141              pNewJoystick->pNext = SYS_Joystick;
   2.142              SYS_Joystick = pNewJoystick;
   2.143  
   2.144 -            s_pKnownJoystickGUIDs[ s_iNewGUID ] = pdidInstance->guidInstance;
   2.145 -            s_iNewGUID++;
   2.146 -            if ( s_iNewGUID < MAX_JOYSTICKS )
   2.147 -                return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
   2.148 -            else
   2.149 -                return DIENUM_STOP;
   2.150 +            return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
   2.151          }
   2.152  
   2.153          pPrevJoystick = pNewJoystick;
   2.154          pNewJoystick = pNewJoystick->pNext;
   2.155      }
   2.156  
   2.157 -    s_bDeviceAdded = SDL_TRUE;
   2.158 +    pNewJoystick = (JoyStick_DeviceData *)SDL_malloc( sizeof(JoyStick_DeviceData) );
   2.159 +    if (!pNewJoystick) {
   2.160 +        return DIENUM_CONTINUE; /* better luck next time? */
   2.161 +    }
   2.162  
   2.163 -    bXInputDevice = SDL_IsXInputDevice( &pdidInstance->guidProduct );
   2.164 -
   2.165 -    pNewJoystick = (JoyStick_DeviceData *)SDL_malloc( sizeof(JoyStick_DeviceData) );
   2.166 -
   2.167 -    if ( bXInputDevice )
   2.168 -    {
   2.169 -        pNewJoystick->bXInputDevice = SDL_TRUE;
   2.170 -        pNewJoystick->XInputUserId = INVALID_XINPUT_USERID;
   2.171 -    }
   2.172 -    else
   2.173 -    {
   2.174 -        pNewJoystick->bXInputDevice = SDL_FALSE;
   2.175 +    SDL_zerop(pNewJoystick);
   2.176 +    pNewJoystick->joystickname = WIN_StringToUTF8(pdidInstance->tszProductName);
   2.177 +    if (!pNewJoystick->joystickname) {
   2.178 +        SDL_free(pNewJoystick);
   2.179 +        return DIENUM_CONTINUE; /* better luck next time? */
   2.180      }
   2.181  
   2.182      SDL_memcpy(&(pNewJoystick->dxdevice), pdidInstance,
   2.183          sizeof(DIDEVICEINSTANCE));
   2.184  
   2.185 -    pNewJoystick->joystickname = WIN_StringToUTF8(pdidInstance->tszProductName);
   2.186 +    pNewJoystick->XInputUserId = INVALID_XINPUT_USERID;
   2.187      pNewJoystick->send_add_event = 1;
   2.188      pNewJoystick->nInstanceID = ++s_nInstanceID;
   2.189      SDL_memcpy( &pNewJoystick->guid, &pdidInstance->guidProduct, sizeof(pNewJoystick->guid) );
   2.190 -    pNewJoystick->pNext = NULL;
   2.191 -
   2.192 -    if ( SYS_Joystick )
   2.193 -    {
   2.194 -        pNewJoystick->pNext = SYS_Joystick;
   2.195 -    }
   2.196 +    pNewJoystick->pNext = SYS_Joystick;
   2.197      SYS_Joystick = pNewJoystick;
   2.198  
   2.199 -    s_pKnownJoystickGUIDs[ s_iNewGUID ] = pdidInstance->guidInstance;
   2.200 -    s_iNewGUID++;
   2.201 +    s_bDeviceAdded = SDL_TRUE;
   2.202  
   2.203 -    if ( s_iNewGUID < MAX_JOYSTICKS )
   2.204 -        return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
   2.205 -    else
   2.206 -        return DIENUM_STOP;
   2.207 +    return DIENUM_CONTINUE; /* get next device, please */
   2.208  }
   2.209  
   2.210 +static void
   2.211 +AddXInputDevice(const Uint8 userid, JoyStick_DeviceData **pContext)
   2.212 +{
   2.213 +    char name[32];
   2.214 +    JoyStick_DeviceData *pPrevJoystick = NULL;
   2.215 +    JoyStick_DeviceData *pNewJoystick = *pContext;
   2.216 +
   2.217 +    while (pNewJoystick) {
   2.218 +        if ((pNewJoystick->bXInputDevice) && (pNewJoystick->XInputUserId == userid)) {
   2.219 +            /* if we are replacing the front of the list then update it */
   2.220 +            if (pNewJoystick == *pContext) {
   2.221 +                *pContext = pNewJoystick->pNext;
   2.222 +            } else if (pPrevJoystick) {
   2.223 +                pPrevJoystick->pNext = pNewJoystick->pNext;
   2.224 +            }
   2.225 +
   2.226 +            pNewJoystick->pNext = SYS_Joystick;
   2.227 +            SYS_Joystick = pNewJoystick;
   2.228 +        }
   2.229 +
   2.230 +        pPrevJoystick = pNewJoystick;
   2.231 +        pNewJoystick = pNewJoystick->pNext;
   2.232 +        return;   /* already in the list. */
   2.233 +    }
   2.234 +
   2.235 +    pNewJoystick = (JoyStick_DeviceData *) SDL_malloc(sizeof (JoyStick_DeviceData));
   2.236 +    if (!pNewJoystick) {
   2.237 +        return; /* better luck next time? */
   2.238 +    }
   2.239 +    SDL_zerop(pNewJoystick);
   2.240 +
   2.241 +    SDL_snprintf(name, sizeof (name), "XInput Controller #%d", (int) userid);
   2.242 +    pNewJoystick->joystickname = SDL_strdup(name);
   2.243 +    if (!pNewJoystick->joystickname) {
   2.244 +        SDL_free(pNewJoystick);
   2.245 +        return; /* better luck next time? */
   2.246 +    }
   2.247 +
   2.248 +    pNewJoystick->bXInputDevice = SDL_TRUE;
   2.249 +    pNewJoystick->XInputUserId = userid;
   2.250 +    pNewJoystick->send_add_event = 1;
   2.251 +    pNewJoystick->nInstanceID = ++s_nInstanceID;
   2.252 +    pNewJoystick->pNext = SYS_Joystick;
   2.253 +    SYS_Joystick = pNewJoystick;
   2.254 +
   2.255 +    s_bDeviceAdded = SDL_TRUE;
   2.256 +}
   2.257 +
   2.258 +static void
   2.259 +EnumXInputDevices(JoyStick_DeviceData **pContext)
   2.260 +{
   2.261 +    if (s_bXInputEnabled) {
   2.262 +        Uint8 userid;
   2.263 +        for (userid = 0; userid < SDL_XINPUT_MAX_DEVICES; userid++) {
   2.264 +            XINPUT_CAPABILITIES capabilities;
   2.265 +            if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
   2.266 +                /* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
   2.267 +                /* !!! FIXME: we might want to support steering wheels or guitars or whatever laster. */
   2.268 +                if (capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD) {
   2.269 +                    AddXInputDevice(userid, pContext);
   2.270 +                }
   2.271 +            }
   2.272 +        }
   2.273 +    }
   2.274 +}
   2.275 +
   2.276 +
   2.277  /* detect any new joysticks being inserted into the system */
   2.278  void SDL_SYS_JoystickDetect()
   2.279  {
   2.280 @@ -748,27 +788,26 @@
   2.281      /* only enum the devices if the joystick thread told us something changed */
   2.282      if ( s_bDeviceAdded || s_bDeviceRemoved )
   2.283      {
   2.284 +        SDL_LockMutex( s_mutexJoyStickEnum );
   2.285 +
   2.286          s_bDeviceAdded = SDL_FALSE;
   2.287          s_bDeviceRemoved = SDL_FALSE;
   2.288  
   2.289          pCurList = SYS_Joystick;
   2.290          SYS_Joystick = NULL;
   2.291 -        s_iNewGUID = 0;
   2.292 -        SDL_LockMutex( s_mutexJoyStickEnum );
   2.293  
   2.294 -        if ( !s_pKnownJoystickGUIDs )
   2.295 -            s_pKnownJoystickGUIDs = SDL_malloc( sizeof(GUID)*MAX_JOYSTICKS );
   2.296 +        /* Look for XInput devices... */
   2.297 +        EnumXInputDevices(&pCurList);
   2.298  
   2.299 -        SDL_memset( s_pKnownJoystickGUIDs, 0x0, sizeof(GUID)*MAX_JOYSTICKS );
   2.300 -
   2.301 -        /* Look for joysticks, wheels, head trackers, gamepads, etc.. */
   2.302 +        /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
   2.303          IDirectInput8_EnumDevices(dinput,
   2.304              DI8DEVCLASS_GAMECTRL,
   2.305              EnumJoysticksCallback,
   2.306              &pCurList, DIEDFL_ATTACHEDONLY);
   2.307  
   2.308 -        SDL_free(SDL_RawDevList);  /* in case we used this. */
   2.309 +        SDL_free(SDL_RawDevList);  /* in case we used this in DirectInput enumerator. */
   2.310          SDL_RawDevList = NULL;
   2.311 +        SDL_RawDevListCount = 0;
   2.312  
   2.313          SDL_UnlockMutex( s_mutexJoyStickEnum );
   2.314      }
   2.315 @@ -872,17 +911,11 @@
   2.316  SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
   2.317  {
   2.318      HRESULT result;
   2.319 -    LPDIRECTINPUTDEVICE8 device;
   2.320 -    DIPROPDWORD dipdw;
   2.321      JoyStick_DeviceData *joystickdevice = SYS_Joystick;
   2.322  
   2.323      for (; device_index > 0; device_index--)
   2.324          joystickdevice = joystickdevice->pNext;
   2.325  
   2.326 -    SDL_memset(&dipdw, 0, sizeof(DIPROPDWORD));
   2.327 -    dipdw.diph.dwSize = sizeof(DIPROPDWORD);
   2.328 -    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
   2.329 -
   2.330      /* allocate memory for system specific hardware data */
   2.331      joystick->instance_id = joystickdevice->nInstanceID;
   2.332      joystick->closed = 0;
   2.333 @@ -891,97 +924,50 @@
   2.334      if (joystick->hwdata == NULL) {
   2.335          return SDL_OutOfMemory();
   2.336      }
   2.337 -    SDL_memset(joystick->hwdata, 0, sizeof(struct joystick_hwdata));
   2.338 -    joystick->hwdata->buffered = 1;
   2.339 -    joystick->hwdata->removed = 0;
   2.340 -    joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
   2.341 -    joystick->hwdata->guid = joystickdevice->guid;
   2.342 +    SDL_zerop(joystick->hwdata);
   2.343  
   2.344 -    if ( joystickdevice->bXInputDevice )
   2.345 -    {
   2.346 +    if (joystickdevice->bXInputDevice) {
   2.347 +        const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
   2.348 +        const Uint8 userId = joystickdevice->XInputUserId;
   2.349          XINPUT_CAPABILITIES capabilities;
   2.350 -        Uint8 userId = 0;
   2.351 -        DWORD result;
   2.352 -        JoyStick_DeviceData *joysticklist = SYS_Joystick;
   2.353 -        /* scan the opened joysticks and pick the next free xinput userid for this one */
   2.354 -        for( ; joysticklist; joysticklist = joysticklist->pNext)
   2.355 -        {
   2.356 -            if ( joysticklist->bXInputDevice && joysticklist->XInputUserId == userId )
   2.357 -                userId++;
   2.358 -        }
   2.359  
   2.360 -        if ( s_bXInputEnabled && XINPUTGETCAPABILITIES )
   2.361 -        {
   2.362 -			while ( 1 )
   2.363 -			{
   2.364 -				result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
   2.365 -				if ( result == ERROR_SUCCESS )
   2.366 -				{
   2.367 -					const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
   2.368 -					SDL_bool bIsSupported = SDL_FALSE;
   2.369 -					/* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
   2.370 -					bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD );
   2.371 +        SDL_assert(s_bXInputEnabled);
   2.372 +        SDL_assert(XINPUTGETCAPABILITIES);
   2.373 +        SDL_assert(userId >= 0);
   2.374 +        SDL_assert(userId < SDL_XINPUT_MAX_DEVICES);
   2.375  
   2.376 -					if ( !bIsSupported )
   2.377 -					{
   2.378 -						joystickdevice->bXInputDevice = SDL_FALSE;
   2.379 -					}
   2.380 -					else
   2.381 -					{
   2.382 -						/* valid */
   2.383 -						joystick->hwdata->bXInputDevice = SDL_TRUE;
   2.384 -						if ((!bIs14OrLater) || (capabilities.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
   2.385 -							joystick->hwdata->bXInputHaptic = SDL_TRUE;
   2.386 -						}
   2.387 -						SDL_memset( joystick->hwdata->XInputState, 0x0, sizeof(joystick->hwdata->XInputState) );
   2.388 -						joystickdevice->XInputUserId = userId;
   2.389 -						joystick->hwdata->userid = userId;
   2.390 -						joystick->hwdata->currentXInputSlot = 0;
   2.391 -						/* The XInput API has a hard coded button/axis mapping, so we just match it */
   2.392 -						joystick->naxes = 6;
   2.393 -						joystick->nbuttons = 15;
   2.394 -						joystick->nballs = 0;
   2.395 -						joystick->nhats = 0;
   2.396 -					}
   2.397 -					break;
   2.398 -				}
   2.399 -				else
   2.400 -				{
   2.401 -					if ( userId < XUSER_MAX_COUNT && result == ERROR_DEVICE_NOT_CONNECTED )
   2.402 -					{
   2.403 -						/* scan the opened joysticks and pick the next free xinput userid for this one */
   2.404 -						++userId;
   2.405 +        joystick->hwdata->bXInputDevice = SDL_TRUE;
   2.406  
   2.407 -						joysticklist = SYS_Joystick;
   2.408 -						for( ; joysticklist; joysticklist = joysticklist->pNext)
   2.409 -						{
   2.410 -							if ( joysticklist->bXInputDevice && joysticklist->XInputUserId == userId )
   2.411 -								userId++;
   2.412 -						}
   2.413 +        if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) {
   2.414 +            SDL_free(joystick->hwdata);
   2.415 +            joystick->hwdata = NULL;
   2.416 +            return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?");
   2.417 +        } else {
   2.418 +            /* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
   2.419 +            SDL_assert(capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD);
   2.420 +            if ((!bIs14OrLater) || (capabilities.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
   2.421 +                joystick->hwdata->bXInputHaptic = SDL_TRUE;
   2.422 +            }
   2.423 +            joystick->hwdata->userid = userId;
   2.424  
   2.425 -						if ( userId >= XUSER_MAX_COUNT )
   2.426 -						{
   2.427 -							joystickdevice->bXInputDevice = SDL_FALSE;
   2.428 -							break;
   2.429 -						}
   2.430 -					}
   2.431 -					else
   2.432 -					{
   2.433 -						joystickdevice->bXInputDevice = SDL_FALSE;
   2.434 -						break;
   2.435 -					}
   2.436 -				}
   2.437 -			}
   2.438 -        }
   2.439 -        else
   2.440 -        {
   2.441 -            joystickdevice->bXInputDevice = SDL_FALSE;
   2.442 -        }
   2.443 -    }
   2.444 +            /* The XInput API has a hard coded button/axis mapping, so we just match it */
   2.445 +            joystick->naxes = 6;
   2.446 +            joystick->nbuttons = 15;
   2.447 +            joystick->nballs = 0;
   2.448 +            joystick->nhats = 0;
   2.449 +		}
   2.450 +    } else {  /* use DirectInput, not XInput. */
   2.451 +        LPDIRECTINPUTDEVICE8 device;
   2.452 +        DIPROPDWORD dipdw;
   2.453  
   2.454 -    if ( joystickdevice->bXInputDevice == SDL_FALSE )
   2.455 -    {
   2.456 -        joystick->hwdata->bXInputDevice = SDL_FALSE;
   2.457 +        joystick->hwdata->buffered = 1;
   2.458 +        joystick->hwdata->removed = 0;
   2.459 +        joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
   2.460 +        joystick->hwdata->guid = joystickdevice->guid;
   2.461 +
   2.462 +        SDL_zero(dipdw);
   2.463 +        dipdw.diph.dwSize = sizeof(DIPROPDWORD);
   2.464 +        dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
   2.465  
   2.466          result =
   2.467              IDirectInput8_CreateDevice(dinput,
   2.468 @@ -1633,18 +1619,11 @@
   2.469          coinitialized = SDL_FALSE;
   2.470      }
   2.471  
   2.472 -    if ( s_pKnownJoystickGUIDs )
   2.473 -    {
   2.474 -        SDL_free( s_pKnownJoystickGUIDs );
   2.475 -        s_pKnownJoystickGUIDs = NULL;
   2.476 -    }
   2.477 -
   2.478      if (s_bXInputEnabled) {
   2.479          WIN_UnloadXInputDLL();
   2.480      }
   2.481  }
   2.482  
   2.483 -
   2.484  /* return the stable device guid for this device index */
   2.485  SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
   2.486  {