Fix SDL xinput code to work at all when xinput has devices at high indexes but no device connected at lower index, for instance 0->disconnected, 1->wireles, 2->wired. Previously the SDL code assumed the indexes were always used up in order which is not true at all and lead to a bunch of failure cases where controllers would go unrecognized.
authorSam Lantinga <slouken@libsdl.org>
Wed, 21 Aug 2013 10:31:44 -0700
changeset 7684dee82553d409
parent 7683 f7c061286490
child 7685 89f669598b87
Fix SDL xinput code to work at all when xinput has devices at high indexes but no device connected at lower index, for instance 0->disconnected, 1->wireles, 2->wired. Previously the SDL code assumed the indexes were always used up in order which is not true at all and lead to a bunch of failure cases where controllers would go unrecognized.

This entire function is kind of a mess and more complicated than needed, but I don't want to refactor it too heavily tonight. May look at improving how the indexes are assigned more significanly later. The way it handles not finding a valid "gamepad" type device is also super broken, it leaves in place the xinput bindings but opens the controller with dinput and ends up with completely wrong mappings, not solving that now, but fixing the bug where we'd very frequently not find a controller due to gaps in assigned player numbers should mostly avoid it.
src/joystick/windows/SDL_dxjoystick.c
     1.1 --- a/src/joystick/windows/SDL_dxjoystick.c	Wed Aug 21 10:07:48 2013 -0700
     1.2 +++ b/src/joystick/windows/SDL_dxjoystick.c	Wed Aug 21 10:31:44 2013 -0700
     1.3 @@ -1014,40 +1014,66 @@
     1.4  
     1.5          if ( s_bXInputEnabled && XINPUTGETCAPABILITIES )
     1.6          {
     1.7 -            result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
     1.8 -            if ( result == ERROR_SUCCESS )
     1.9 -            {
    1.10 -                const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
    1.11 -                SDL_bool bIsSupported = SDL_FALSE;
    1.12 -                /* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
    1.13 -                bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD );
    1.14 +			while ( 1 )
    1.15 +			{
    1.16 +				result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
    1.17 +				if ( result == ERROR_SUCCESS )
    1.18 +				{
    1.19 +					const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
    1.20 +					SDL_bool bIsSupported = SDL_FALSE;
    1.21 +					/* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
    1.22 +					bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD );
    1.23  
    1.24 -                if ( !bIsSupported )
    1.25 -                {
    1.26 -                    joystickdevice->bXInputDevice = SDL_FALSE;
    1.27 -                }
    1.28 -                else
    1.29 -                {
    1.30 -                    /* valid */
    1.31 -                    joystick->hwdata->bXInputDevice = SDL_TRUE;
    1.32 -                    if ((!bIs14OrLater) || (capabilities.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
    1.33 -                        joystick->hwdata->bXInputHaptic = SDL_TRUE;
    1.34 -                    }
    1.35 -                    SDL_memset( joystick->hwdata->XInputState, 0x0, sizeof(joystick->hwdata->XInputState) );
    1.36 -                    joystickdevice->XInputUserId = userId;
    1.37 -                    joystick->hwdata->userid = userId;
    1.38 -                    joystick->hwdata->currentXInputSlot = 0;
    1.39 -                    /* The XInput API has a hard coded button/axis mapping, so we just match it */
    1.40 -                    joystick->naxes = 6;
    1.41 -                    joystick->nbuttons = 15;
    1.42 -                    joystick->nballs = 0;
    1.43 -                    joystick->nhats = 0;
    1.44 -                }
    1.45 -            }
    1.46 -            else
    1.47 -            {
    1.48 -                joystickdevice->bXInputDevice = SDL_FALSE;
    1.49 -            }
    1.50 +					if ( !bIsSupported )
    1.51 +					{
    1.52 +						joystickdevice->bXInputDevice = SDL_FALSE;
    1.53 +					}
    1.54 +					else
    1.55 +					{
    1.56 +						/* valid */
    1.57 +						joystick->hwdata->bXInputDevice = SDL_TRUE;
    1.58 +						if ((!bIs14OrLater) || (capabilities.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
    1.59 +							joystick->hwdata->bXInputHaptic = SDL_TRUE;
    1.60 +						}
    1.61 +						SDL_memset( joystick->hwdata->XInputState, 0x0, sizeof(joystick->hwdata->XInputState) );
    1.62 +						joystickdevice->XInputUserId = userId;
    1.63 +						joystick->hwdata->userid = userId;
    1.64 +						joystick->hwdata->currentXInputSlot = 0;
    1.65 +						/* The XInput API has a hard coded button/axis mapping, so we just match it */
    1.66 +						joystick->naxes = 6;
    1.67 +						joystick->nbuttons = 15;
    1.68 +						joystick->nballs = 0;
    1.69 +						joystick->nhats = 0;
    1.70 +					}
    1.71 +					break;
    1.72 +				}
    1.73 +				else
    1.74 +				{
    1.75 +					if ( userId < XUSER_MAX_COUNT && result == ERROR_DEVICE_NOT_CONNECTED )
    1.76 +					{
    1.77 +						/* scan the opened joysticks and pick the next free xinput userid for this one */
    1.78 +						++userId;
    1.79 +
    1.80 +						joysticklist = SYS_Joystick;
    1.81 +						for( ; joysticklist; joysticklist = joysticklist->pNext)
    1.82 +						{
    1.83 +							if ( joysticklist->bXInputDevice && joysticklist->XInputUserId == userId )
    1.84 +								userId++;
    1.85 +						}
    1.86 +
    1.87 +						if ( userId >= XUSER_MAX_COUNT )
    1.88 +						{
    1.89 +							joystickdevice->bXInputDevice = SDL_FALSE;
    1.90 +							break;
    1.91 +						}
    1.92 +					}
    1.93 +					else
    1.94 +					{
    1.95 +						joystickdevice->bXInputDevice = SDL_FALSE;
    1.96 +						break;
    1.97 +					}
    1.98 +				}
    1.99 +			}
   1.100          }
   1.101          else
   1.102          {