Atlas - SDL_hidapi_xbox360.c
Home / ext / SDL2 / src / joystick / hidapi Lines: 2 | Size: 16711 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_HIDAPI 24 25#include "SDL_hints.h" 26#include "SDL_log.h" 27#include "SDL_events.h" 28#include "SDL_timer.h" 29#include "SDL_joystick.h" 30#include "SDL_gamecontroller.h" 31#include "../SDL_sysjoystick.h" 32#include "SDL_hidapijoystick_c.h" 33 34 35#ifdef SDL_JOYSTICK_HIDAPI_XBOX360 36 37#ifdef __WIN32__ 38#include "../../core/windows/SDL_xinput.h" 39#endif 40 41#define USB_PACKET_LENGTH 64 42 43 44typedef struct { 45 Uint8 last_state[USB_PACKET_LENGTH]; 46 Uint32 rumble_expiration; 47#ifdef __WIN32__ 48 SDL_bool xinput_enabled; 49 Uint8 xinput_slot; 50#endif 51} SDL_DriverXbox360_Context; 52 53 54#ifdef __WIN32__ 55static Uint8 xinput_slots; 56 57static void 58HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot) 59{ 60 if (xinput_slot != XUSER_INDEX_ANY) { 61 xinput_slots |= (0x01 << xinput_slot); 62 } 63} 64 65static void 66HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot) 67{ 68 if (xinput_slot != XUSER_INDEX_ANY) { 69 xinput_slots &= ~(0x01 << xinput_slot); 70 } 71} 72 73static SDL_bool 74HIDAPI_DriverXbox360_MissingXInputSlot() 75{ 76 return xinput_slots != 0x0F; 77} 78 79static Uint8 80HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons) 81{ 82 DWORD user_index; 83 int match_count; 84 Uint8 match_slot; 85 86 if (!XINPUTGETSTATE) { 87 return XUSER_INDEX_ANY; 88 } 89 90 match_count = 0; 91 for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) { 92 XINPUT_STATE_EX xinput_state; 93 94 if (XINPUTGETSTATE(user_index, &xinput_state) == ERROR_SUCCESS) { 95 if (xinput_state.Gamepad.wButtons == wButtons) { 96 ++match_count; 97 match_slot = (Uint8)user_index; 98 } 99 } 100 } 101 if (match_count == 1) { 102 return match_slot; 103 } 104 return XUSER_INDEX_ANY; 105} 106 107#endif /* __WIN32__ */ 108 109static SDL_bool 110HIDAPI_DriverXbox360_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, Uint16 usage_page, Uint16 usage) 111{ 112#if defined(__MACOSX__) || defined(__WIN32__) 113 if (vendor_id == 0x045e && product_id == 0x028e && version == 1) { 114 /* This is the Steam Virtual Gamepad, which isn't supported by this driver */ 115 return SDL_FALSE; 116 } 117 return SDL_IsJoystickXbox360(vendor_id, product_id) || SDL_IsJoystickXboxOne(vendor_id, product_id); 118#else 119 return SDL_IsJoystickXbox360(vendor_id, product_id); 120#endif 121} 122 123static const char * 124HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id) 125{ 126 return HIDAPI_XboxControllerName(vendor_id, product_id); 127} 128 129static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot) 130{ 131 const Uint8 led_packet[] = { 0x01, 0x03, (2 + slot) }; 132 133 if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) { 134 return SDL_FALSE; 135 } 136 return SDL_TRUE; 137} 138 139static SDL_bool 140HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context) 141{ 142 SDL_DriverXbox360_Context *ctx; 143 144 ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx)); 145 if (!ctx) { 146 SDL_OutOfMemory(); 147 return SDL_FALSE; 148 } 149#ifdef __WIN32__ 150 ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE); 151 if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) { 152 ctx->xinput_enabled = SDL_FALSE; 153 } 154 ctx->xinput_slot = XUSER_INDEX_ANY; 155#endif 156 *context = ctx; 157 158 /* Set the controller LED */ 159 SetSlotLED(dev, (joystick->instance_id % 4)); 160 161 /* Initialize the joystick capabilities */ 162 joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX; 163 joystick->naxes = SDL_CONTROLLER_AXIS_MAX; 164 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; 165 166 return SDL_TRUE; 167} 168 169static int 170HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) 171{ 172 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context; 173#ifdef __WIN32__ 174 if (ctx->xinput_slot != XUSER_INDEX_ANY) { 175 XINPUT_VIBRATION XVibration; 176 177 if (!XINPUTSETSTATE) { 178 return SDL_Unsupported(); 179 } 180 181 XVibration.wLeftMotorSpeed = low_frequency_rumble; 182 XVibration.wRightMotorSpeed = high_frequency_rumble; 183 if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) != ERROR_SUCCESS) { 184 return SDL_SetError("XInputSetState() failed"); 185 } 186 } 187#else 188#ifdef __MACOSX__ 189 /* On Mac OS X the 360Controller driver uses this short report, 190 and we need to prefix it with a magic token so hidapi passes it through untouched 191 */ 192 Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 }; 193 194 rumble_packet[6+2] = (low_frequency_rumble >> 8); 195 rumble_packet[6+3] = (high_frequency_rumble >> 8); 196#else 197 Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 198 199 rumble_packet[3] = (low_frequency_rumble >> 8); 200 rumble_packet[4] = (high_frequency_rumble >> 8); 201#endif 202 203 if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 204 return SDL_SetError("Couldn't send rumble packet"); 205 } 206#endif /* __WIN32__ */ 207 208 if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) { 209 ctx->rumble_expiration = SDL_GetTicks() + duration_ms; 210 } else { 211 ctx->rumble_expiration = 0; 212 } 213 return 0; 214} 215 216#ifdef __WIN32__ 217 /* This is the packet format for Xbox 360 and Xbox One controllers on Windows, 218 however with this interface there is no rumble support, no guide button, 219 and the left and right triggers are tied together as a single axis. 220 */ 221static void 222HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size) 223{ 224 Sint16 axis; 225 226 if (ctx->last_state[10] != data[10]) { 227 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[10] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 228 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[10] & 0x02) ? SDL_PRESSED : SDL_RELEASED); 229 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[10] & 0x04) ? SDL_PRESSED : SDL_RELEASED); 230 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[10] & 0x08) ? SDL_PRESSED : SDL_RELEASED); 231 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[10] & 0x10) ? SDL_PRESSED : SDL_RELEASED); 232 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[10] & 0x20) ? SDL_PRESSED : SDL_RELEASED); 233 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[10] & 0x40) ? SDL_PRESSED : SDL_RELEASED); 234 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[10] & 0x80) ? SDL_PRESSED : SDL_RELEASED); 235 } 236 237 if (ctx->last_state[11] != data[11]) { 238 SDL_bool dpad_up = SDL_FALSE; 239 SDL_bool dpad_down = SDL_FALSE; 240 SDL_bool dpad_left = SDL_FALSE; 241 SDL_bool dpad_right = SDL_FALSE; 242 243 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[11] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 244 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[11] & 0x02) ? SDL_PRESSED : SDL_RELEASED); 245 246 switch (data[11] & 0x3C) { 247 case 4: 248 dpad_up = SDL_TRUE; 249 break; 250 case 8: 251 dpad_up = SDL_TRUE; 252 dpad_right = SDL_TRUE; 253 break; 254 case 12: 255 dpad_right = SDL_TRUE; 256 break; 257 case 16: 258 dpad_right = SDL_TRUE; 259 dpad_down = SDL_TRUE; 260 break; 261 case 20: 262 dpad_down = SDL_TRUE; 263 break; 264 case 24: 265 dpad_left = SDL_TRUE; 266 dpad_down = SDL_TRUE; 267 break; 268 case 28: 269 dpad_left = SDL_TRUE; 270 break; 271 case 32: 272 dpad_up = SDL_TRUE; 273 dpad_left = SDL_TRUE; 274 break; 275 default: 276 break; 277 } 278 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down); 279 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up); 280 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right); 281 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left); 282 } 283 284 axis = (int)*(Uint16*)(&data[0]) - 0x8000; 285 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); 286 axis = (int)*(Uint16*)(&data[2]) - 0x8000; 287 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis); 288 axis = (int)*(Uint16*)(&data[4]) - 0x8000; 289 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); 290 axis = (int)*(Uint16*)(&data[6]) - 0x8000; 291 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); 292 293 if (ctx->xinput_slot == XUSER_INDEX_ANY && HIDAPI_DriverXbox360_MissingXInputSlot()) { 294 WORD wButtons = 0; 295 296 if (data[10] & 0x01) { 297 wButtons |= XINPUT_GAMEPAD_A; 298 } 299 if (data[10] & 0x02) { 300 wButtons |= XINPUT_GAMEPAD_B; 301 } 302 if (data[10] & 0x04) { 303 wButtons |= XINPUT_GAMEPAD_X; 304 } 305 if (data[10] & 0x08) { 306 wButtons |= XINPUT_GAMEPAD_Y; 307 } 308 if (wButtons != 0) { 309 Uint8 xinput_slot = HIDAPI_DriverXbox360_GuessXInputSlot(wButtons); 310 if (xinput_slot != XUSER_INDEX_ANY) { 311 HIDAPI_DriverXbox360_MarkXInputSlotUsed(xinput_slot); 312 ctx->xinput_slot = xinput_slot; 313 } 314 } 315 } 316 317 if (ctx->xinput_slot == XUSER_INDEX_ANY) { 318 axis = (data[9] * 257) - 32768; 319 if (data[9] < 0x80) { 320 axis = -axis * 2 - 32769; 321 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); 322 } else if (data[9] > 0x80) { 323 axis = axis * 2 - 32767; 324 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); 325 } else { 326 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16); 327 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16); 328 } 329 } else { 330 XINPUT_STATE_EX xinput_state; 331 332 if (XINPUTGETSTATE(ctx->xinput_slot, &xinput_state) == ERROR_SUCCESS) { 333 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED); 334 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state.Gamepad.bLeftTrigger * 257) - 32768); 335 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state.Gamepad.bRightTrigger * 257) - 32768); 336 } 337 } 338 339 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 340} 341#else 342 343static void 344HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size) 345{ 346 Sint16 axis; 347#ifdef __MACOSX__ 348 const SDL_bool invert_y_axes = SDL_FALSE; 349#else 350 const SDL_bool invert_y_axes = SDL_TRUE; 351#endif 352 353 if (ctx->last_state[2] != data[2]) { 354 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 355 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED); 356 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED); 357 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED); 358 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED); 359 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED); 360 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED); 361 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED); 362 } 363 364 if (ctx->last_state[3] != data[3]) { 365 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 366 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED); 367 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED); 368 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED); 369 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED); 370 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED); 371 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED); 372 } 373 374 axis = ((int)data[4] * 257) - 32768; 375 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); 376 axis = ((int)data[5] * 257) - 32768; 377 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); 378 axis = *(Sint16*)(&data[6]); 379 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); 380 axis = *(Sint16*)(&data[8]); 381 if (invert_y_axes) { 382 axis = ~axis; 383 } 384 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis); 385 axis = *(Sint16*)(&data[10]); 386 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); 387 axis = *(Sint16*)(&data[12]); 388 if (invert_y_axes) { 389 axis = ~axis; 390 } 391 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); 392 393 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 394} 395#endif /* __WIN32__ */ 396 397static SDL_bool 398HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *context) 399{ 400 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context; 401 Uint8 data[USB_PACKET_LENGTH]; 402 int size; 403 404 while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { 405#ifdef __WIN32__ 406 HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size); 407#else 408 switch (data[0]) { 409 case 0x00: 410 HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size); 411 break; 412 default: 413#ifdef DEBUG_JOYSTICK 414 SDL_Log("Unknown Xbox 360 packet: 0x%.2x\n", data[0]); 415#endif 416 break; 417 } 418#endif /* __WIN32__ */ 419 } 420 421 if (ctx->rumble_expiration) { 422 Uint32 now = SDL_GetTicks(); 423 if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) { 424 HIDAPI_DriverXbox360_Rumble(joystick, dev, context, 0, 0, 0); 425 } 426 } 427 428 return (size >= 0); 429} 430 431static void 432HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) 433{ 434#ifdef __WIN32__ 435 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context; 436 437 if (ctx->xinput_enabled) { 438 HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot); 439 WIN_UnloadXInputDLL(); 440 } 441#endif 442 SDL_free(context); 443} 444 445SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 = 446{ 447 SDL_HINT_JOYSTICK_HIDAPI_XBOX, 448 SDL_TRUE, 449 HIDAPI_DriverXbox360_IsSupportedDevice, 450 HIDAPI_DriverXbox360_GetDeviceName, 451 HIDAPI_DriverXbox360_Init, 452 HIDAPI_DriverXbox360_Rumble, 453 HIDAPI_DriverXbox360_Update, 454 HIDAPI_DriverXbox360_Quit 455}; 456 457#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */ 458 459#endif /* SDL_JOYSTICK_HIDAPI */ 460 461/* vi: set ts=4 sw=4 expandtab: */ 462[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.