src/joystick/hidapi/SDL_hidapi_xbox360.c
changeset 12118 2b80f9635ee5
parent 12117 8cbdc9b8b055
child 12119 648377d0e573
     1.1 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c	Wed Aug 15 23:14:43 2018 -0700
     1.2 +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c	Wed Aug 15 23:14:45 2018 -0700
     1.3 @@ -34,23 +34,86 @@
     1.4  
     1.5  #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
     1.6  
     1.7 +#ifdef __WIN32__
     1.8 +#include "../../core/windows/SDL_xinput.h"
     1.9 +#endif
    1.10 +
    1.11  #define USB_PACKET_LENGTH   64
    1.12  
    1.13  
    1.14  typedef struct {
    1.15      Uint8 last_state[USB_PACKET_LENGTH];
    1.16      Uint32 rumble_expiration;
    1.17 +#ifdef __WIN32__
    1.18 +    SDL_bool xinput_enabled;
    1.19 +    Uint8 xinput_slot;
    1.20 +#endif
    1.21  } SDL_DriverXbox360_Context;
    1.22  
    1.23  
    1.24 +#ifdef __WIN32__
    1.25 +static Uint8 xinput_slots;
    1.26 +
    1.27 +static void
    1.28 +HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
    1.29 +{
    1.30 +    if (xinput_slot != XUSER_INDEX_ANY) {
    1.31 +        xinput_slots |= (0x01 << xinput_slot);
    1.32 +    }
    1.33 +}
    1.34 +
    1.35 +static void
    1.36 +HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
    1.37 +{
    1.38 +    if (xinput_slot != XUSER_INDEX_ANY) {
    1.39 +        xinput_slots &= ~(0x01 << xinput_slot);
    1.40 +    }
    1.41 +}
    1.42 +
    1.43 +static SDL_bool
    1.44 +HIDAPI_DriverXbox360_MissingXInputSlot()
    1.45 +{
    1.46 +    return xinput_slots != 0x0F;
    1.47 +}
    1.48 +
    1.49 +static Uint8
    1.50 +HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons)
    1.51 +{
    1.52 +    DWORD user_index;
    1.53 +    int match_count;
    1.54 +    Uint8 match_slot;
    1.55 +
    1.56 +    if (!XINPUTGETSTATE) {
    1.57 +        return XUSER_INDEX_ANY;
    1.58 +    }
    1.59 +
    1.60 +    match_count = 0;
    1.61 +    for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
    1.62 +        XINPUT_STATE_EX xinput_state;
    1.63 +
    1.64 +        if (XINPUTGETSTATE(user_index, &xinput_state) == ERROR_SUCCESS) {
    1.65 +            if (xinput_state.Gamepad.wButtons == wButtons) {
    1.66 +                ++match_count;
    1.67 +                match_slot = (Uint8)user_index;
    1.68 +            }
    1.69 +        }
    1.70 +    }
    1.71 +    if (match_count == 1) {
    1.72 +        return match_slot;
    1.73 +    }
    1.74 +    return XUSER_INDEX_ANY;
    1.75 +}
    1.76 +
    1.77 +#endif /* __WIN32__ */
    1.78 +
    1.79  static SDL_bool
    1.80  HIDAPI_DriverXbox360_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, Uint16 usage_page, Uint16 usage)
    1.81  {
    1.82  #if defined(__MACOSX__) || defined(__WIN32__)
    1.83 -	if (vendor_id == 0x045e && product_id == 0x028e && version == 1) {
    1.84 -		/* This is the Steam Virtual Gamepad, which isn't supported by this driver */
    1.85 -		return SDL_FALSE;
    1.86 -	}
    1.87 +    if (vendor_id == 0x045e && product_id == 0x028e && version == 1) {
    1.88 +        /* This is the Steam Virtual Gamepad, which isn't supported by this driver */
    1.89 +        return SDL_FALSE;
    1.90 +    }
    1.91      return SDL_IsJoystickXbox360(vendor_id, product_id) || SDL_IsJoystickXboxOne(vendor_id, product_id);
    1.92  #else
    1.93      return SDL_IsJoystickXbox360(vendor_id, product_id);
    1.94 @@ -68,9 +131,9 @@
    1.95      const Uint8 led_packet[] = { 0x01, 0x03, (2 + slot) };
    1.96  
    1.97      if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
    1.98 -		return SDL_FALSE;
    1.99 -	}
   1.100 -	return SDL_TRUE;
   1.101 +        return SDL_FALSE;
   1.102 +    }
   1.103 +    return SDL_TRUE;
   1.104  }
   1.105  
   1.106  static SDL_bool
   1.107 @@ -83,10 +146,17 @@
   1.108          SDL_OutOfMemory();
   1.109          return SDL_FALSE;
   1.110      }
   1.111 +#ifdef __WIN32__
   1.112 +    ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE);
   1.113 +    if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) {
   1.114 +        ctx->xinput_enabled = SDL_FALSE;
   1.115 +    }
   1.116 +    ctx->xinput_slot = XUSER_INDEX_ANY;
   1.117 +#endif
   1.118      *context = ctx;
   1.119  
   1.120      /* Set the controller LED */
   1.121 -	SetSlotLED(dev, (joystick->instance_id % 4));
   1.122 +    SetSlotLED(dev, (joystick->instance_id % 4));
   1.123  
   1.124      /* Initialize the joystick capabilities */
   1.125      joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
   1.126 @@ -100,6 +170,21 @@
   1.127  HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   1.128  {
   1.129      SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
   1.130 +#ifdef __WIN32__
   1.131 +    if (ctx->xinput_slot != XUSER_INDEX_ANY) {
   1.132 +        XINPUT_VIBRATION XVibration;
   1.133 +
   1.134 +        if (!XINPUTSETSTATE) {
   1.135 +            return SDL_Unsupported();
   1.136 +        }
   1.137 +
   1.138 +        XVibration.wLeftMotorSpeed = low_frequency_rumble;
   1.139 +        XVibration.wRightMotorSpeed = high_frequency_rumble;
   1.140 +        if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) != ERROR_SUCCESS) {
   1.141 +            return SDL_SetError("XInputSetState() failed");
   1.142 +        }
   1.143 +    }
   1.144 +#else
   1.145  #ifdef __MACOSX__
   1.146      /* On Mac OS X the 360Controller driver uses this short report,
   1.147         and we need to prefix it with a magic token so hidapi passes it through untouched
   1.148 @@ -118,6 +203,7 @@
   1.149      if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
   1.150          return SDL_SetError("Couldn't send rumble packet");
   1.151      }
   1.152 +#endif /* __WIN32__ */
   1.153  
   1.154      if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
   1.155          ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
   1.156 @@ -204,16 +290,50 @@
   1.157      axis = (int)*(Uint16*)(&data[6]) - 0x8000;
   1.158      SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
   1.159  
   1.160 -    axis = (data[9] * 257) - 32768;
   1.161 -    if (data[9] < 0x80) {
   1.162 -        axis = -axis * 2 - 32769;
   1.163 -        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
   1.164 -    } else if (data[9] > 0x80) {
   1.165 -        axis = axis * 2 - 32767;
   1.166 -        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
   1.167 +    if (ctx->xinput_slot == XUSER_INDEX_ANY && HIDAPI_DriverXbox360_MissingXInputSlot()) {
   1.168 +        WORD wButtons = 0;
   1.169 +
   1.170 +        if (data[10] & 0x01) {
   1.171 +            wButtons |= XINPUT_GAMEPAD_A;
   1.172 +        }
   1.173 +        if (data[10] & 0x02) {
   1.174 +            wButtons |= XINPUT_GAMEPAD_B;
   1.175 +        }
   1.176 +        if (data[10] & 0x04) {
   1.177 +            wButtons |= XINPUT_GAMEPAD_X;
   1.178 +        }
   1.179 +        if (data[10] & 0x08) {
   1.180 +            wButtons |= XINPUT_GAMEPAD_Y;
   1.181 +        }
   1.182 +        if (wButtons != 0) {
   1.183 +            Uint8 xinput_slot = HIDAPI_DriverXbox360_GuessXInputSlot(wButtons);
   1.184 +            if (xinput_slot != XUSER_INDEX_ANY) {
   1.185 +                HIDAPI_DriverXbox360_MarkXInputSlotUsed(xinput_slot);
   1.186 +                ctx->xinput_slot = xinput_slot;
   1.187 +            }
   1.188 +        }
   1.189 +    }
   1.190 +
   1.191 +    if (ctx->xinput_slot == XUSER_INDEX_ANY) {
   1.192 +        axis = (data[9] * 257) - 32768;
   1.193 +        if (data[9] < 0x80) {
   1.194 +            axis = -axis * 2 - 32769;
   1.195 +            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
   1.196 +        } else if (data[9] > 0x80) {
   1.197 +            axis = axis * 2 - 32767;
   1.198 +            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
   1.199 +        } else {
   1.200 +            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
   1.201 +            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
   1.202 +        }
   1.203      } else {
   1.204 -        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
   1.205 -        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
   1.206 +        XINPUT_STATE_EX xinput_state;
   1.207 +
   1.208 +        if (XINPUTGETSTATE(ctx->xinput_slot, &xinput_state) == ERROR_SUCCESS) {
   1.209 +            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
   1.210 +            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state.Gamepad.bLeftTrigger * 257) - 32768);
   1.211 +            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state.Gamepad.bRightTrigger * 257) - 32768);
   1.212 +        }
   1.213      }
   1.214  
   1.215      SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
   1.216 @@ -225,9 +345,9 @@
   1.217  {
   1.218      Sint16 axis;
   1.219  #ifdef __MACOSX__
   1.220 -	const SDL_bool invert_y_axes = SDL_FALSE;
   1.221 +    const SDL_bool invert_y_axes = SDL_FALSE;
   1.222  #else
   1.223 -	const SDL_bool invert_y_axes = SDL_TRUE;
   1.224 +    const SDL_bool invert_y_axes = SDL_TRUE;
   1.225  #endif
   1.226  
   1.227      if (ctx->last_state[2] != data[2]) {
   1.228 @@ -258,16 +378,16 @@
   1.229      axis = *(Sint16*)(&data[6]);
   1.230      SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
   1.231      axis = *(Sint16*)(&data[8]);
   1.232 -	if (invert_y_axes) {
   1.233 -		axis = ~axis;
   1.234 -	}
   1.235 +    if (invert_y_axes) {
   1.236 +        axis = ~axis;
   1.237 +    }
   1.238      SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
   1.239      axis = *(Sint16*)(&data[10]);
   1.240      SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
   1.241      axis = *(Sint16*)(&data[12]);
   1.242 -	if (invert_y_axes) {
   1.243 -		axis = ~axis;
   1.244 -	}
   1.245 +    if (invert_y_axes) {
   1.246 +        axis = ~axis;
   1.247 +    }
   1.248      SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
   1.249  
   1.250      SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
   1.251 @@ -305,12 +425,20 @@
   1.252          }
   1.253      }
   1.254  
   1.255 -	return (size >= 0);
   1.256 +    return (size >= 0);
   1.257  }
   1.258  
   1.259  static void
   1.260  HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
   1.261  {
   1.262 +#ifdef __WIN32__
   1.263 +    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
   1.264 +
   1.265 +    if (ctx->xinput_enabled) {
   1.266 +        HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
   1.267 +        WIN_UnloadXInputDLL();
   1.268 +    }
   1.269 +#endif
   1.270      SDL_free(context);
   1.271  }
   1.272