Atlas - SDL_hidapijoystick.c

Home / ext / SDL / src / joystick / hidapi Lines: 3 | Size: 56000 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 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_sysjoystick.h" 26#include "SDL_hidapijoystick_c.h" 27#include "SDL_hidapi_rumble.h" 28#include "../../SDL_hints_c.h" 29 30#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 31#include "../windows/SDL_rawinputjoystick_c.h" 32#endif 33 34 35struct joystick_hwdata 36{ 37 SDL_HIDAPI_Device *device; 38}; 39 40static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = { 41#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE 42 &SDL_HIDAPI_DriverGameCube, 43#endif 44#ifdef SDL_JOYSTICK_HIDAPI_LUNA 45 &SDL_HIDAPI_DriverLuna, 46#endif 47#ifdef SDL_JOYSTICK_HIDAPI_SHIELD 48 &SDL_HIDAPI_DriverShield, 49#endif 50#ifdef SDL_JOYSTICK_HIDAPI_PS3 51 &SDL_HIDAPI_DriverPS3, 52 &SDL_HIDAPI_DriverPS3ThirdParty, 53 &SDL_HIDAPI_DriverPS3SonySixaxis, 54#endif 55#ifdef SDL_JOYSTICK_HIDAPI_PS4 56 &SDL_HIDAPI_DriverPS4, 57#endif 58#ifdef SDL_JOYSTICK_HIDAPI_PS5 59 &SDL_HIDAPI_DriverPS5, 60#endif 61#ifdef SDL_JOYSTICK_HIDAPI_STADIA 62 &SDL_HIDAPI_DriverStadia, 63#endif 64#ifdef SDL_JOYSTICK_HIDAPI_STEAM 65 &SDL_HIDAPI_DriverSteam, 66#endif 67#ifdef SDL_JOYSTICK_HIDAPI_STEAM_HORI 68 &SDL_HIDAPI_DriverSteamHori, 69#endif 70#ifdef SDL_JOYSTICK_HIDAPI_STEAMDECK 71 &SDL_HIDAPI_DriverSteamDeck, 72#endif 73#ifdef SDL_JOYSTICK_HIDAPI_STEAMDECK 74 &SDL_HIDAPI_DriverSteamTriton, 75#endif 76#ifdef SDL_JOYSTICK_HIDAPI_SWITCH 77 &SDL_HIDAPI_DriverNintendoClassic, 78 &SDL_HIDAPI_DriverJoyCons, 79 &SDL_HIDAPI_DriverSwitch, 80#endif 81#ifdef SDL_JOYSTICK_HIDAPI_SWITCH2 82 &SDL_HIDAPI_DriverSwitch2, 83#endif 84#ifdef SDL_JOYSTICK_HIDAPI_WII 85 &SDL_HIDAPI_DriverWii, 86#endif 87#ifdef SDL_JOYSTICK_HIDAPI_XBOX360 88 &SDL_HIDAPI_DriverXbox360, 89 &SDL_HIDAPI_DriverXbox360W, 90#endif 91#ifdef SDL_JOYSTICK_HIDAPI_GIP 92 &SDL_HIDAPI_DriverGIP, 93#endif 94#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE 95 &SDL_HIDAPI_DriverXboxOne, 96#endif 97#ifdef SDL_JOYSTICK_HIDAPI_LG4FF 98 &SDL_HIDAPI_DriverLg4ff, 99#endif 100#ifdef SDL_JOYSTICK_HIDAPI_8BITDO 101 &SDL_HIDAPI_Driver8BitDo, 102#endif 103#ifdef SDL_JOYSTICK_HIDAPI_FLYDIGI 104 &SDL_HIDAPI_DriverFlydigi, 105#endif 106#ifdef SDL_JOYSTICK_HIDAPI_SINPUT 107 &SDL_HIDAPI_DriverSInput, 108#endif 109#ifdef SDL_JOYSTICK_HIDAPI_GAMESIR 110 &SDL_HIDAPI_DriverGameSir, 111#endif 112#ifdef SDL_JOYSTICK_HIDAPI_ZUIKI 113 &SDL_HIDAPI_DriverZUIKI, 114#endif 115}; 116static int SDL_HIDAPI_numdrivers = 0; 117static SDL_AtomicInt SDL_HIDAPI_updating_devices; 118static bool SDL_HIDAPI_hints_changed = false; 119static Uint32 SDL_HIDAPI_change_count = 0; 120static SDL_HIDAPI_Device *SDL_HIDAPI_devices SDL_GUARDED_BY(SDL_joystick_lock); 121static int SDL_HIDAPI_numjoysticks = 0; 122static bool SDL_HIDAPI_combine_joycons = true; 123static bool initialized = false; 124static bool shutting_down = false; 125 126static char *HIDAPI_ConvertString(const wchar_t *wide_string) 127{ 128 char *string = NULL; 129 130 if (wide_string) { 131 string = SDL_iconv_string("UTF-8", "WCHAR_T", (char *)wide_string, (SDL_wcslen(wide_string) + 1) * sizeof(wchar_t)); 132 if (!string) { 133 switch (sizeof(wchar_t)) { 134 case 2: 135 string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char *)wide_string, (SDL_wcslen(wide_string) + 1) * sizeof(wchar_t)); 136 break; 137 case 4: 138 string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char *)wide_string, (SDL_wcslen(wide_string) + 1) * sizeof(wchar_t)); 139 break; 140 } 141 } 142 } 143 return string; 144} 145 146void HIDAPI_DumpPacket(const char *prefix, const Uint8 *data, int size) 147{ 148 int i; 149 char *buffer; 150 size_t length = SDL_strlen(prefix) + 11 * (size / 8) + (5 * size * 2) + 1 + 1; 151 int start = 0, amount = size; 152 size_t current_len; 153 154 buffer = (char *)SDL_malloc(length); 155 current_len = SDL_snprintf(buffer, length, prefix, size); 156 for (i = start; i < start + amount; ++i) { 157 if ((i % 8) == 0) { 158 current_len += SDL_snprintf(&buffer[current_len], length - current_len, "\n%.2d: ", i); 159 } 160 current_len += SDL_snprintf(&buffer[current_len], length - current_len, " 0x%.2x", data[i]); 161 } 162 SDL_strlcat(buffer, "\n", length); 163 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "%s", buffer); 164 SDL_free(buffer); 165} 166 167bool HIDAPI_SupportsPlaystationDetection(Uint16 vendor, Uint16 product) 168{ 169 /* If we already know the controller is a different type, don't try to detect it. 170 * This fixes a hang with the HORIPAD for Nintendo Switch (0x0f0d/0x00c1) 171 */ 172 if (SDL_GetGamepadTypeFromVIDPID(vendor, product, NULL, false) != SDL_GAMEPAD_TYPE_STANDARD) { 173 return false; 174 } 175 176 switch (vendor) { 177 case USB_VENDOR_CRKD: 178 return true; 179 case USB_VENDOR_DRAGONRISE: 180 return true; 181 case USB_VENDOR_HORI: 182 return true; 183 case USB_VENDOR_LOGITECH: 184 /* Most Logitech devices are not PlayStation controllers, and some of them 185 * lock up or reset when we send them the Sony third-party query feature 186 * report, so don't include that vendor here. Instead add devices as 187 * appropriate to controller_list.h 188 */ 189 return false; 190 case USB_VENDOR_MADCATZ: 191 if (product == USB_PRODUCT_MADCATZ_SAITEK_SIDE_PANEL_CONTROL_DECK) { 192 // This is not a Playstation compatible device 193 return false; 194 } 195 return true; 196 case USB_VENDOR_MAYFLASH: 197 return true; 198 case USB_VENDOR_NACON: 199 case USB_VENDOR_NACON_ALT: 200 return true; 201 case USB_VENDOR_PDP: 202 return true; 203 case USB_VENDOR_POWERA: 204 return true; 205 case USB_VENDOR_POWERA_ALT: 206 return true; 207 case USB_VENDOR_QANBA: 208 return true; 209 case USB_VENDOR_RAZER: 210 /* Most Razer devices are not PlayStation controllers, and some of them 211 * lock up or reset when we send them the Sony third-party query feature 212 * report, so don't include that vendor here. Instead add devices as 213 * appropriate to controller_list.h 214 * 215 * Reference: https://github.com/libsdl-org/SDL/issues/6733 216 * https://github.com/libsdl-org/SDL/issues/6799 217 */ 218 return false; 219 case USB_VENDOR_SHANWAN: 220 return true; 221 case USB_VENDOR_SHANWAN_ALT: 222 return true; 223 case USB_VENDOR_THRUSTMASTER: 224 /* Most of these are wheels, don't have the full set of effects, and 225 * at least in the case of the T248 and T300 RS, the hid-tmff2 driver 226 * puts them in a non-standard report mode and they can't be read. 227 * 228 * If these should use the HIDAPI driver, add them to controller_list.h 229 */ 230 return false; 231 case USB_VENDOR_ZEROPLUS: 232 return true; 233 case 0x7545 /* SZ-MYPOWER */: 234 return true; 235 default: 236 return false; 237 } 238} 239 240float HIDAPI_RemapVal(float val, float val_min, float val_max, float output_min, float output_max) 241{ 242 return output_min + (output_max - output_min) * (val - val_min) / (val_max - val_min); 243} 244 245static void HIDAPI_UpdateDeviceList(void); 246static void HIDAPI_JoystickClose(SDL_Joystick *joystick); 247 248static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, Uint16 vendor, Uint16 product, int interface_number, int interface_class, int interface_subclass, int interface_protocol) 249{ 250 static const int LIBUSB_CLASS_VENDOR_SPEC = 0xFF; 251 static const int XB360_IFACE_SUBCLASS = 93; 252 static const int XB360_IFACE_PROTOCOL = 1; // Wired 253 static const int XB360W_IFACE_PROTOCOL = 129; // Wireless 254 static const int XBONE_IFACE_SUBCLASS = 71; 255 static const int XBONE_IFACE_PROTOCOL = 208; 256 257 SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD; 258 259 // This code should match the checks in libusb/hid.c and HIDDeviceManager.java 260 if (interface_class == LIBUSB_CLASS_VENDOR_SPEC && 261 interface_subclass == XB360_IFACE_SUBCLASS && 262 (interface_protocol == XB360_IFACE_PROTOCOL || 263 interface_protocol == XB360W_IFACE_PROTOCOL)) { 264 265 static const int SUPPORTED_VENDORS[] = { 266 0x0079, // GPD Win 2 267 0x044f, // Thrustmaster 268 0x045e, // Microsoft 269 0x046d, // Logitech 270 0x056e, // Elecom 271 0x06a3, // Saitek 272 0x0738, // Mad Catz 273 0x07ff, // Mad Catz 274 0x0e6f, // PDP 275 0x0f0d, // Hori 276 0x1038, // SteelSeries 277 0x11c9, // Nacon 278 0x12ab, // Unknown 279 0x1430, // RedOctane 280 0x146b, // BigBen 281 0x1532, // Razer 282 0x15e4, // Numark 283 0x162e, // Joytech 284 0x1689, // Razer Onza 285 0x1949, // Lab126, Inc. 286 0x1bad, // Harmonix 287 0x20d6, // PowerA 288 0x24c6, // PowerA 289 0x2c22, // Qanba 290 0x2dc8, // 8BitDo 291 0x37d7, // Flydigi 292 0x9886, // ASTRO Gaming 293 }; 294 295 int i; 296 for (i = 0; i < SDL_arraysize(SUPPORTED_VENDORS); ++i) { 297 if (vendor == SUPPORTED_VENDORS[i]) { 298 type = SDL_GAMEPAD_TYPE_XBOX360; 299 break; 300 } 301 } 302 } 303 304 if (interface_number == 0 && 305 interface_class == LIBUSB_CLASS_VENDOR_SPEC && 306 interface_subclass == XBONE_IFACE_SUBCLASS && 307 interface_protocol == XBONE_IFACE_PROTOCOL) { 308 309 static const int SUPPORTED_VENDORS[] = { 310 0x03f0, // HP 311 0x044f, // Thrustmaster 312 0x045e, // Microsoft 313 0x0738, // Mad Catz 314 0x0b05, // ASUS 315 0x0e6f, // PDP 316 0x0f0d, // Hori 317 0x10f5, // Turtle Beach 318 0x1532, // Razer 319 0x20d6, // PowerA 320 0x24c6, // PowerA 321 0x294b, // Snakebyte 322 0x2dc8, // 8BitDo 323 0x2e24, // Hyperkin 324 0x2e95, // SCUF 325 0x3285, // Nacon 326 0x3537, // GameSir 327 0x366c, // ByoWave 328 }; 329 330 int i; 331 for (i = 0; i < SDL_arraysize(SUPPORTED_VENDORS); ++i) { 332 if (vendor == SUPPORTED_VENDORS[i]) { 333 type = SDL_GAMEPAD_TYPE_XBOXONE; 334 break; 335 } 336 } 337 } 338 339 if (type == SDL_GAMEPAD_TYPE_STANDARD) { 340 type = SDL_GetGamepadTypeFromVIDPID(vendor, product, name, false); 341 } 342 return type; 343} 344 345static bool HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) 346{ 347 int i; 348 SDL_GamepadType type = SDL_GetJoystickGameControllerProtocol(name, vendor_id, product_id, -1, 0, 0, 0); 349 350 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 351 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 352 if (driver->enabled && driver->IsSupportedDevice(NULL, name, type, vendor_id, product_id, version, -1, 0, 0, 0)) { 353 return true; 354 } 355 } 356 return false; 357} 358 359static SDL_HIDAPI_DeviceDriver *HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device) 360{ 361 const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001; 362 const Uint16 USAGE_JOYSTICK = 0x0004; 363 const Uint16 USAGE_GAMEPAD = 0x0005; 364 const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008; 365 int i; 366 367 if (device->num_children > 0) { 368 return &SDL_HIDAPI_DriverCombined; 369 } 370 371 if (SDL_ShouldIgnoreJoystick(device->vendor_id, device->product_id, device->version, device->name)) { 372 return NULL; 373 } 374 375 if (device->vendor_id != USB_VENDOR_VALVE && device->vendor_id != USB_VENDOR_FLYDIGI_V1 && device->vendor_id != USB_VENDOR_FLYDIGI_V2) { 376 if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) { 377 return NULL; 378 } 379 if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) { 380 return NULL; 381 } 382 } 383 384 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 385 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 386 if (driver->enabled && driver->IsSupportedDevice(device, device->name, device->type, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) { 387 return driver; 388 } 389 } 390 return NULL; 391} 392 393static SDL_HIDAPI_Device *HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID) 394{ 395 SDL_HIDAPI_Device *device; 396 397 SDL_AssertJoysticksLocked(); 398 399 for (device = SDL_HIDAPI_devices; device; device = device->next) { 400 if (device->parent || device->broken) { 401 continue; 402 } 403 if (device->driver) { 404 if (device_index < device->num_joysticks) { 405 if (pJoystickID) { 406 *pJoystickID = device->joysticks[device_index]; 407 } 408 return device; 409 } 410 device_index -= device->num_joysticks; 411 } 412 } 413 return NULL; 414} 415 416static SDL_HIDAPI_Device *HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id) 417{ 418 SDL_HIDAPI_Device *device; 419 420 SDL_AssertJoysticksLocked(); 421 422 for (device = SDL_HIDAPI_devices; device; device = device->next) { 423 if (device->vendor_id == vendor_id && device->product_id == product_id && 424 SDL_strcmp(device->path, path) == 0) { 425 break; 426 } 427 } 428 return device; 429} 430 431static void HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device) 432{ 433 if (!device->driver) { 434 return; // Already cleaned up 435 } 436 437 // Disconnect any joysticks 438 while (device->num_joysticks && device->joysticks) { 439 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 440 } 441 442 device->driver->FreeDevice(device); 443 device->driver = NULL; 444 445 if (device->dev) { 446 SDL_hid_close(device->dev); 447 device->dev = NULL; 448 } 449 450 if (device->context) { 451 SDL_free(device->context); 452 device->context = NULL; 453 } 454} 455 456static void HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device, bool *removed) SDL_NO_THREAD_SAFETY_ANALYSIS // We unlock the joystick lock to be able to open the HID device on Android 457{ 458 *removed = false; 459 460 if (device->driver) { 461 bool enabled; 462 463 if (device->vendor_id == USB_VENDOR_NINTENDO && 464 (device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR || 465 device->product_id == USB_PRODUCT_NINTENDO_SWITCH2_JOYCON_PAIR)) { 466 enabled = SDL_HIDAPI_combine_joycons; 467 } else { 468 enabled = device->driver->enabled; 469 } 470 if (device->children) { 471 int i; 472 473 for (i = 0; i < device->num_children; ++i) { 474 SDL_HIDAPI_Device *child = device->children[i]; 475 if (!child->driver || !child->driver->enabled) { 476 enabled = false; 477 break; 478 } 479 } 480 } 481 if (!enabled) { 482 HIDAPI_CleanupDeviceDriver(device); 483 } 484 return; // Already setup 485 } 486 487 if (HIDAPI_GetDeviceDriver(device)) { 488 // We might have a device driver for this device, try opening it and see 489 if (device->num_children == 0) { 490 SDL_hid_device *dev; 491 492 // Wait a little bit for the device to initialize 493 SDL_Delay(10); 494 495 dev = SDL_hid_open_path(device->path); 496 497 if (dev == NULL) { 498 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, 499 "HIDAPI_SetupDeviceDriver() couldn't open %s: %s", 500 device->path, SDL_GetError()); 501 return; 502 } 503 SDL_hid_set_nonblocking(dev, 1); 504 505 device->dev = dev; 506 } 507 508 device->driver = HIDAPI_GetDeviceDriver(device); 509 510 // Initialize the device, which may cause a connected event 511 if (device->driver && !device->driver->InitDevice(device)) { 512 HIDAPI_CleanupDeviceDriver(device); 513 } 514 515 if (!device->driver && device->dev) { 516 // No driver claimed this device, go ahead and close it 517 SDL_hid_close(device->dev); 518 device->dev = NULL; 519 } 520 } 521} 522 523static void SDL_HIDAPI_UpdateDrivers(void) 524{ 525 int i; 526 SDL_HIDAPI_Device *device; 527 bool removed; 528 529 SDL_AssertJoysticksLocked(); 530 531 SDL_HIDAPI_numdrivers = 0; 532 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 533 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 534 driver->enabled = driver->IsEnabled(); 535 if (driver->enabled && driver != &SDL_HIDAPI_DriverCombined) { 536 ++SDL_HIDAPI_numdrivers; 537 } 538 } 539 540 removed = false; 541 do { 542 for (device = SDL_HIDAPI_devices; device; device = device->next) { 543 HIDAPI_SetupDeviceDriver(device, &removed); 544 if (removed) { 545 break; 546 } 547 } 548 } while (removed); 549} 550 551static void SDLCALL SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 552{ 553 if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS) == 0) { 554 SDL_HIDAPI_combine_joycons = SDL_GetStringBoolean(hint, true); 555 } 556 SDL_HIDAPI_hints_changed = true; 557 SDL_HIDAPI_change_count = 0; 558} 559 560static bool HIDAPI_JoystickInit(void) 561{ 562 int i; 563 564 if (initialized) { 565 return true; 566 } 567 568 if (SDL_hid_init() < 0) { 569 return SDL_SetError("Couldn't initialize hidapi"); 570 } 571 572 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 573 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 574 driver->RegisterHints(SDL_HIDAPIDriverHintChanged, driver); 575 } 576 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, 577 SDL_HIDAPIDriverHintChanged, NULL); 578 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI, 579 SDL_HIDAPIDriverHintChanged, NULL); 580 581 SDL_HIDAPI_change_count = SDL_hid_device_change_count(); 582 HIDAPI_UpdateDeviceList(); 583 HIDAPI_UpdateDevices(); 584 585 initialized = true; 586 587 return true; 588} 589 590static bool HIDAPI_AddJoystickInstanceToDevice(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID) 591{ 592 SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1) * sizeof(*device->joysticks)); 593 if (!joysticks) { 594 return false; 595 } 596 597 device->joysticks = joysticks; 598 device->joysticks[device->num_joysticks++] = joystickID; 599 return true; 600} 601 602static bool HIDAPI_DelJoystickInstanceFromDevice(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID) 603{ 604 int i, size; 605 606 for (i = 0; i < device->num_joysticks; ++i) { 607 if (device->joysticks[i] == joystickID) { 608 size = (device->num_joysticks - i - 1) * sizeof(SDL_JoystickID); 609 SDL_memmove(&device->joysticks[i], &device->joysticks[i + 1], size); 610 --device->num_joysticks; 611 if (device->num_joysticks == 0) { 612 SDL_free(device->joysticks); 613 device->joysticks = NULL; 614 } 615 return true; 616 } 617 } 618 return false; 619} 620 621static bool HIDAPI_JoystickInstanceIsUnique(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID) 622{ 623 if (device->parent && device->num_joysticks == 1 && device->parent->num_joysticks == 1 && 624 device->joysticks[0] == device->parent->joysticks[0]) { 625 return false; 626 } 627 return true; 628} 629 630void HIDAPI_SetDeviceName(SDL_HIDAPI_Device *device, const char *name) 631{ 632 if (name && *name && SDL_strcmp(name, device->name) != 0) { 633 SDL_free(device->name); 634 device->name = SDL_strdup(name); 635 SDL_SetJoystickGUIDCRC(&device->guid, SDL_crc16(0, name, SDL_strlen(name))); 636 } 637} 638 639void HIDAPI_SetDeviceProduct(SDL_HIDAPI_Device *device, Uint16 vendor_id, Uint16 product_id) 640{ 641 // Don't set the device product ID directly, or we'll constantly re-enumerate this device 642 device->guid = SDL_CreateJoystickGUID(device->guid.data[0], vendor_id, product_id, device->version, device->manufacturer_string, device->product_string, 'h', 0); 643} 644 645static void HIDAPI_UpdateJoystickSerial(SDL_HIDAPI_Device *device) 646{ 647 int i; 648 649 SDL_AssertJoysticksLocked(); 650 651 for (i = 0; i < device->num_joysticks; ++i) { 652 SDL_Joystick *joystick = SDL_GetJoystickFromID(device->joysticks[i]); 653 if (joystick && device->serial) { 654 SDL_free(joystick->serial); 655 joystick->serial = SDL_strdup(device->serial); 656 } 657 } 658} 659 660static bool HIDAPI_SerialIsEmpty(SDL_HIDAPI_Device *device) 661{ 662 bool all_zeroes = true; 663 664 if (device->serial) { 665 const char *serial = device->serial; 666 for (serial = device->serial; *serial; ++serial) { 667 if (*serial != '0') { 668 all_zeroes = false; 669 break; 670 } 671 } 672 } 673 return all_zeroes; 674} 675 676void HIDAPI_SetDeviceSerial(SDL_HIDAPI_Device *device, const char *serial) 677{ 678 if (serial && *serial && (!device->serial || SDL_strcmp(serial, device->serial) != 0)) { 679 SDL_free(device->serial); 680 device->serial = SDL_strdup(serial); 681 HIDAPI_UpdateJoystickSerial(device); 682 } 683} 684 685static int wcstrcmp(const wchar_t *str1, const char *str2) 686{ 687 int result; 688 689 while (1) { 690 result = (*str1 - *str2); 691 if (result != 0 || *str1 == 0) { 692 break; 693 } 694 ++str1; 695 ++str2; 696 } 697 return result; 698} 699 700static void HIDAPI_SetDeviceSerialW(SDL_HIDAPI_Device *device, const wchar_t *serial) 701{ 702 if (serial && *serial && (!device->serial || wcstrcmp(serial, device->serial) != 0)) { 703 SDL_free(device->serial); 704 device->serial = HIDAPI_ConvertString(serial); 705 HIDAPI_UpdateJoystickSerial(device); 706 } 707} 708 709bool HIDAPI_HasConnectedUSBDevice(const char *serial) 710{ 711 SDL_HIDAPI_Device *device; 712 713 SDL_AssertJoysticksLocked(); 714 715 if (!serial) { 716 return false; 717 } 718 719 for (device = SDL_HIDAPI_devices; device; device = device->next) { 720 if (!device->driver || device->broken) { 721 continue; 722 } 723 724 if (device->is_bluetooth) { 725 continue; 726 } 727 728 if (device->serial && SDL_strcmp(serial, device->serial) == 0) { 729 return true; 730 } 731 } 732 return false; 733} 734 735void HIDAPI_DisconnectBluetoothDevice(const char *serial) 736{ 737 SDL_HIDAPI_Device *device; 738 739 SDL_AssertJoysticksLocked(); 740 741 if (!serial) { 742 return; 743 } 744 745 for (device = SDL_HIDAPI_devices; device; device = device->next) { 746 if (!device->driver || device->broken) { 747 continue; 748 } 749 750 if (!device->is_bluetooth) { 751 continue; 752 } 753 754 if (device->serial && SDL_strcmp(serial, device->serial) == 0) { 755 while (device->num_joysticks && device->joysticks) { 756 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 757 } 758 } 759 } 760} 761 762bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID) 763{ 764 int i, j; 765 SDL_JoystickID joystickID; 766 767 SDL_AssertJoysticksLocked(); 768 769 for (i = 0; i < device->num_children; ++i) { 770 SDL_HIDAPI_Device *child = device->children[i]; 771 for (j = child->num_joysticks; j--;) { 772 HIDAPI_JoystickDisconnected(child, child->joysticks[j]); 773 } 774 } 775 776 joystickID = SDL_GetNextObjectID(); 777 HIDAPI_AddJoystickInstanceToDevice(device, joystickID); 778 779 for (i = 0; i < device->num_children; ++i) { 780 SDL_HIDAPI_Device *child = device->children[i]; 781 HIDAPI_AddJoystickInstanceToDevice(child, joystickID); 782 } 783 784 ++SDL_HIDAPI_numjoysticks; 785 786 if (pJoystickID) { 787 *pJoystickID = joystickID; 788 } 789 790 SDL_PrivateJoystickAdded(joystickID); 791 792 return true; 793} 794 795void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID) 796{ 797 int i, j; 798 799 SDL_LockJoysticks(); 800 801 if (!HIDAPI_JoystickInstanceIsUnique(device, joystickID)) { 802 // Disconnecting a child always disconnects the parent 803 device = device->parent; 804 } 805 806 for (i = 0; i < device->num_joysticks; ++i) { 807 if (device->joysticks[i] == joystickID) { 808 SDL_Joystick *joystick = SDL_GetJoystickFromID(joystickID); 809 if (joystick) { 810 HIDAPI_JoystickClose(joystick); 811 } 812 813 HIDAPI_DelJoystickInstanceFromDevice(device, joystickID); 814 815 for (j = 0; j < device->num_children; ++j) { 816 SDL_HIDAPI_Device *child = device->children[j]; 817 HIDAPI_DelJoystickInstanceFromDevice(child, joystickID); 818 } 819 820 --SDL_HIDAPI_numjoysticks; 821 822 if (!shutting_down) { 823 SDL_PrivateJoystickRemoved(joystickID); 824 } 825 } 826 } 827 828 // Rescan the device list in case device state has changed 829 SDL_HIDAPI_change_count = 0; 830 831 SDL_UnlockJoysticks(); 832} 833 834static void HIDAPI_UpdateJoystickProperties(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 835{ 836 SDL_PropertiesID props = SDL_GetJoystickProperties(joystick); 837 Uint32 caps = device->driver->GetJoystickCapabilities(device, joystick); 838 839 if (caps & SDL_JOYSTICK_CAP_MONO_LED) { 840 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_MONO_LED_BOOLEAN, true); 841 } else { 842 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_MONO_LED_BOOLEAN, false); 843 } 844 if (caps & SDL_JOYSTICK_CAP_RGB_LED) { 845 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true); 846 } else { 847 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, false); 848 } 849 if (caps & SDL_JOYSTICK_CAP_PLAYER_LED) { 850 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_PLAYER_LED_BOOLEAN, true); 851 } else { 852 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_PLAYER_LED_BOOLEAN, false); 853 } 854 if (caps & SDL_JOYSTICK_CAP_RUMBLE) { 855 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true); 856 } else { 857 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, false); 858 } 859 if (caps & SDL_JOYSTICK_CAP_TRIGGER_RUMBLE) { 860 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true); 861 } else { 862 SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, false); 863 } 864} 865 866void HIDAPI_UpdateDeviceProperties(SDL_HIDAPI_Device *device) 867{ 868 int i; 869 870 SDL_LockJoysticks(); 871 872 for (i = 0; i < device->num_joysticks; ++i) { 873 SDL_Joystick *joystick = SDL_GetJoystickFromID(device->joysticks[i]); 874 if (joystick) { 875 HIDAPI_UpdateJoystickProperties(device, joystick); 876 } 877 } 878 879 SDL_UnlockJoysticks(); 880} 881 882static int HIDAPI_JoystickGetCount(void) 883{ 884 return SDL_HIDAPI_numjoysticks; 885} 886 887static SDL_HIDAPI_Device *HIDAPI_AddDevice(const struct SDL_hid_device_info *info, int num_children, SDL_HIDAPI_Device **children) 888{ 889 SDL_HIDAPI_Device *device; 890 SDL_HIDAPI_Device *curr, *last = NULL; 891 bool removed; 892 Uint16 bus; 893 894 SDL_AssertJoysticksLocked(); 895 896 for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) { 897 } 898 899 device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device)); 900 if (!device) { 901 return NULL; 902 } 903 SDL_SetObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK, true); 904 if (info->path) { 905 device->path = SDL_strdup(info->path); 906 } 907 device->seen = true; 908 device->vendor_id = info->vendor_id; 909 device->product_id = info->product_id; 910 device->version = info->release_number; 911 device->interface_number = info->interface_number; 912 device->interface_class = info->interface_class; 913 device->interface_subclass = info->interface_subclass; 914 device->interface_protocol = info->interface_protocol; 915 device->usage_page = info->usage_page; 916 device->usage = info->usage; 917 device->is_bluetooth = (info->bus_type == SDL_HID_API_BUS_BLUETOOTH); 918 919 // Need the device name before getting the driver to know whether to ignore this device 920 { 921 char *serial_number = HIDAPI_ConvertString(info->serial_number); 922 923 device->manufacturer_string = HIDAPI_ConvertString(info->manufacturer_string); 924 device->product_string = HIDAPI_ConvertString(info->product_string); 925 device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, device->manufacturer_string, device->product_string); 926 927 if (serial_number && *serial_number) { 928 device->serial = serial_number; 929 } else { 930 SDL_free(serial_number); 931 } 932 933 if (!device->name) { 934 SDL_free(device->manufacturer_string); 935 SDL_free(device->product_string); 936 SDL_free(device->serial); 937 SDL_free(device->path); 938 SDL_free(device); 939 return NULL; 940 } 941 } 942 943 if (info->bus_type == SDL_HID_API_BUS_BLUETOOTH) { 944 bus = SDL_HARDWARE_BUS_BLUETOOTH; 945 } else { 946 bus = SDL_HARDWARE_BUS_USB; 947 } 948 device->guid = SDL_CreateJoystickGUID(bus, device->vendor_id, device->product_id, device->version, device->manufacturer_string, device->product_string, 'h', 0); 949 device->joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD; 950 device->type = SDL_GetJoystickGameControllerProtocol(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol); 951 device->steam_virtual_gamepad_slot = -1; 952 953 if (num_children > 0) { 954 int i; 955 956 device->num_children = num_children; 957 device->children = children; 958 for (i = 0; i < num_children; ++i) { 959 children[i]->parent = device; 960 } 961 } 962 963 // Add it to the list 964 if (last) { 965 last->next = device; 966 } else { 967 SDL_HIDAPI_devices = device; 968 } 969 970 removed = false; 971 HIDAPI_SetupDeviceDriver(device, &removed); 972 if (removed) { 973 return NULL; 974 } 975 976 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, bluetooth %d, version %d, serial %s, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)", device->name, device->vendor_id, device->product_id, device->is_bluetooth, device->version, 977 device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, 978 device->path, device->driver ? device->driver->name : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED"); 979 980 return device; 981} 982 983static void HIDAPI_DelDevice(SDL_HIDAPI_Device *device) 984{ 985 SDL_HIDAPI_Device *curr, *last; 986 int i; 987 988 SDL_AssertJoysticksLocked(); 989 990 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Removing HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, bluetooth %d, version %d, serial %s, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)", device->name, device->vendor_id, device->product_id, device->is_bluetooth, device->version, 991 device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, 992 device->path, device->driver ? device->driver->name : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED"); 993 994 for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) { 995 if (curr == device) { 996 if (last) { 997 last->next = curr->next; 998 } else { 999 SDL_HIDAPI_devices = curr->next; 1000 } 1001 1002 HIDAPI_CleanupDeviceDriver(device); 1003 1004 // Make sure the rumble thread is done with this device 1005 while (SDL_GetAtomicInt(&device->rumble_pending) > 0) { 1006 SDL_Delay(10); 1007 } 1008 1009 for (i = 0; i < device->num_children; ++i) { 1010 device->children[i]->parent = NULL; 1011 } 1012 1013 SDL_SetObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK, false); 1014 SDL_free(device->manufacturer_string); 1015 SDL_free(device->product_string); 1016 SDL_free(device->serial); 1017 SDL_free(device->name); 1018 SDL_free(device->path); 1019 SDL_free(device->children); 1020 SDL_free(device); 1021 return; 1022 } 1023 } 1024} 1025 1026static bool HIDAPI_CreateCombinedJoyCons(void) 1027{ 1028 SDL_HIDAPI_Device *device, *combined; 1029 SDL_HIDAPI_Device *joycons[2] = { NULL, NULL }; 1030 1031 SDL_AssertJoysticksLocked(); 1032 1033 if (!SDL_HIDAPI_combine_joycons) { 1034 return false; 1035 } 1036 1037 for (device = SDL_HIDAPI_devices; device; device = device->next) { 1038 Uint16 vendor, product; 1039 1040 if (!device->driver) { 1041 // Unsupported device 1042 continue; 1043 } 1044 if (device->parent) { 1045 // This device is already part of a combined device 1046 continue; 1047 } 1048 if (device->broken) { 1049 // This device can't be used 1050 continue; 1051 } 1052 1053 SDL_GetJoystickGUIDInfo(device->guid, &vendor, &product, NULL, NULL); 1054 1055 if (!joycons[0] && 1056 (SDL_IsJoystickNintendoSwitchJoyConLeft(vendor, product) || 1057 (SDL_IsJoystickNintendoSwitchJoyConGrip(vendor, product) && 1058 SDL_strstr(device->name, "(L)") != NULL))) { 1059 joycons[0] = device; 1060 } 1061 if (!joycons[1] && 1062 (SDL_IsJoystickNintendoSwitchJoyConRight(vendor, product) || 1063 (SDL_IsJoystickNintendoSwitchJoyConGrip(vendor, product) && 1064 SDL_strstr(device->name, "(R)") != NULL))) { 1065 joycons[1] = device; 1066 } 1067 if (joycons[0] && joycons[1]) { 1068 SDL_hid_device_info info; 1069 SDL_HIDAPI_Device **children = (SDL_HIDAPI_Device **)SDL_malloc(2 * sizeof(SDL_HIDAPI_Device *)); 1070 if (!children) { 1071 return false; 1072 } 1073 children[0] = joycons[0]; 1074 children[1] = joycons[1]; 1075 1076 SDL_zero(info); 1077 info.path = "nintendo_joycons_combined"; 1078 info.vendor_id = USB_VENDOR_NINTENDO; 1079 if (joycons[0]->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) { 1080 info.product_id = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR; 1081 } else { 1082 info.product_id = USB_PRODUCT_NINTENDO_SWITCH2_JOYCON_PAIR; 1083 } 1084 info.interface_number = -1; 1085 info.usage_page = USB_USAGEPAGE_GENERIC_DESKTOP; 1086 info.usage = USB_USAGE_GENERIC_GAMEPAD; 1087 info.manufacturer_string = L"Nintendo"; 1088 info.product_string = L"Switch Joy-Con (L/R)"; 1089 if (children[0]->is_bluetooth || children[1]->is_bluetooth) { 1090 info.bus_type = SDL_HID_API_BUS_BLUETOOTH; 1091 } else { 1092 info.bus_type = SDL_HID_API_BUS_USB; 1093 } 1094 1095 combined = HIDAPI_AddDevice(&info, 2, children); 1096 if (combined && combined->driver) { 1097 return true; 1098 } else { 1099 if (combined) { 1100 HIDAPI_DelDevice(combined); 1101 } else { 1102 SDL_free(children); 1103 } 1104 return false; 1105 } 1106 } 1107 } 1108 return false; 1109} 1110 1111static void HIDAPI_UpdateDeviceList(void) 1112{ 1113 SDL_HIDAPI_Device *device; 1114 struct SDL_hid_device_info *devs, *info; 1115 1116 SDL_LockJoysticks(); 1117 1118 if (SDL_HIDAPI_hints_changed) { 1119 SDL_HIDAPI_UpdateDrivers(); 1120 SDL_HIDAPI_hints_changed = false; 1121 } 1122 1123 // Prepare the existing device list 1124 for (device = SDL_HIDAPI_devices; device; device = device->next) { 1125 if (device->children) { 1126 continue; 1127 } 1128 device->seen = false; 1129 } 1130 1131 // Enumerate the devices 1132 if (SDL_HIDAPI_numdrivers > 0) { 1133 devs = SDL_hid_enumerate(0, 0); 1134 if (devs) { 1135 for (info = devs; info; info = info->next) { 1136 device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id); 1137 if (device) { 1138 device->seen = true; 1139 1140 // Check to see if the serial number is available now 1141 if(HIDAPI_SerialIsEmpty(device)) { 1142 HIDAPI_SetDeviceSerialW(device, info->serial_number); 1143 } 1144 } else { 1145 HIDAPI_AddDevice(info, 0, NULL); 1146 } 1147 } 1148 SDL_hid_free_enumeration(devs); 1149 } 1150 } 1151 1152 // Remove any devices that weren't seen or have been disconnected due to read errors 1153check_removed: 1154 device = SDL_HIDAPI_devices; 1155 while (device) { 1156 SDL_HIDAPI_Device *next = device->next; 1157 1158 if (!device->seen || 1159 ((device->driver || device->children) && device->num_joysticks == 0 && !device->dev)) { 1160 if (device->parent) { 1161 // When a child device goes away, so does the parent 1162 int i; 1163 device = device->parent; 1164 for (i = 0; i < device->num_children; ++i) { 1165 HIDAPI_DelDevice(device->children[i]); 1166 } 1167 HIDAPI_DelDevice(device); 1168 1169 // Update the device list again to pick up any children left 1170 SDL_HIDAPI_change_count = 0; 1171 1172 // We deleted more than one device here, restart the loop 1173 goto check_removed; 1174 } else { 1175 HIDAPI_DelDevice(device); 1176 device = NULL; 1177 1178 // Update the device list again in case this device comes back 1179 SDL_HIDAPI_change_count = 0; 1180 } 1181 } 1182 if (device && device->broken && device->parent) { 1183 HIDAPI_DelDevice(device->parent); 1184 1185 // We deleted a different device here, restart the loop 1186 goto check_removed; 1187 } 1188 device = next; 1189 } 1190 1191 // See if we can create any combined Joy-Con controllers 1192 while (HIDAPI_CreateCombinedJoyCons()) { 1193 } 1194 1195 SDL_UnlockJoysticks(); 1196} 1197 1198static bool HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Device *device) 1199{ 1200 if (vendor_id == device->vendor_id && product_id == device->product_id) { 1201 return true; 1202 } 1203 1204 if (vendor_id == USB_VENDOR_MICROSOFT) { 1205 // If we're looking for the wireless XBox 360 controller, also look for the dongle 1206 if (product_id == USB_PRODUCT_XBOX360_XUSB_CONTROLLER && device->product_id == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER) { 1207 return true; 1208 } 1209 1210 // If we're looking for the raw input Xbox One controller, match it against any other Xbox One controller 1211 if (product_id == USB_PRODUCT_XBOX_ONE_XBOXGIP_CONTROLLER && 1212 device->type == SDL_GAMEPAD_TYPE_XBOXONE) { 1213 return true; 1214 } 1215 1216 // If we're looking for an XInput controller, match it against any other Xbox controller 1217 if (product_id == USB_PRODUCT_XBOX360_XUSB_CONTROLLER) { 1218 if (device->type == SDL_GAMEPAD_TYPE_XBOX360 || device->type == SDL_GAMEPAD_TYPE_XBOXONE) { 1219 return true; 1220 } 1221 } 1222 } 1223 1224 if (vendor_id == USB_VENDOR_NVIDIA) { 1225 // If we're looking for the NVIDIA SHIELD controller Xbox interface, match it against any NVIDIA SHIELD controller 1226 if (product_id == 0xb400 && 1227 SDL_IsJoystickNVIDIASHIELDController(vendor_id, product_id)) { 1228 return true; 1229 } 1230 } 1231 return false; 1232} 1233 1234static bool HIDAPI_StartUpdatingDevices(void) 1235{ 1236 return SDL_CompareAndSwapAtomicInt(&SDL_HIDAPI_updating_devices, false, true); 1237} 1238 1239static void HIDAPI_FinishUpdatingDevices(void) 1240{ 1241 SDL_SetAtomicInt(&SDL_HIDAPI_updating_devices, false); 1242} 1243 1244bool HIDAPI_IsDeviceTypePresent(SDL_GamepadType type) 1245{ 1246 SDL_HIDAPI_Device *device; 1247 bool result = false; 1248 1249 // Make sure we're initialized, as this could be called from other drivers during startup 1250 if (!HIDAPI_JoystickInit()) { 1251 return false; 1252 } 1253 1254 if (HIDAPI_StartUpdatingDevices()) { 1255 HIDAPI_UpdateDeviceList(); 1256 HIDAPI_FinishUpdatingDevices(); 1257 } 1258 1259 SDL_LockJoysticks(); 1260 for (device = SDL_HIDAPI_devices; device; device = device->next) { 1261 if (device->driver && device->type == type) { 1262 result = true; 1263 break; 1264 } 1265 } 1266 SDL_UnlockJoysticks(); 1267 1268#ifdef DEBUG_HIDAPI 1269 SDL_Log("HIDAPI_IsDeviceTypePresent() returning %s for %d", result ? "true" : "false", type); 1270#endif 1271 return result; 1272} 1273 1274bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) 1275{ 1276 SDL_HIDAPI_Device *device; 1277 bool supported = false; 1278 bool result = false; 1279 1280 // Make sure we're initialized, as this could be called from other drivers during startup 1281 if (!HIDAPI_JoystickInit()) { 1282 return false; 1283 } 1284 1285 /* Only update the device list for devices we know might be supported. 1286 If we did this for every device, it would hit the USB driver too hard and potentially 1287 lock up the system. This won't catch devices that we support but can only detect using 1288 USB interface details, like Xbox controllers, but hopefully the device list update is 1289 responsive enough to catch those. 1290 */ 1291 supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name); 1292#if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE) 1293 if (!supported && 1294 (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) { 1295 supported = true; 1296 } 1297#endif // SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE 1298 if (supported) { 1299 if (HIDAPI_StartUpdatingDevices()) { 1300 HIDAPI_UpdateDeviceList(); 1301 HIDAPI_FinishUpdatingDevices(); 1302 } 1303 } 1304 1305 /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID, 1306 or a different name than we have it listed here, etc, but if we support the device 1307 and we have something similar in our device list, mark it as present. 1308 */ 1309 SDL_LockJoysticks(); 1310 for (device = SDL_HIDAPI_devices; device; device = device->next) { 1311 // The HIDAPI functionality will be available when the FlyDigi Space Station app has 1312 // enabled third party controller mapping, so the driver needs to be active to watch 1313 // for that change. Since this is dynamic and we don't have a way to re-trigger device 1314 // changes when that happens, we'll pretend the driver isn't available so the XInput 1315 // interface will always show up (but won't have any input when the controller is in 1316 // enhanced mode) 1317 if (device->vendor_id == USB_VENDOR_FLYDIGI_V2) { 1318 continue; 1319 } 1320 1321 if (device->driver && 1322 HIDAPI_IsEquivalentToDevice(vendor_id, product_id, device)) { 1323 result = true; 1324 break; 1325 } 1326 } 1327 SDL_UnlockJoysticks(); 1328 1329#ifdef DEBUG_HIDAPI 1330 SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x", result ? "true" : "false", vendor_id, product_id); 1331#endif 1332 return result; 1333} 1334 1335char *HIDAPI_GetDeviceProductName(Uint16 vendor_id, Uint16 product_id) 1336{ 1337 SDL_HIDAPI_Device *device; 1338 char *name = NULL; 1339 1340 SDL_LockJoysticks(); 1341 for (device = SDL_HIDAPI_devices; device; device = device->next) { 1342 if (vendor_id == device->vendor_id && product_id == device->product_id) { 1343 if (device->product_string) { 1344 name = SDL_strdup(device->product_string); 1345 } 1346 break; 1347 } 1348 } 1349 SDL_UnlockJoysticks(); 1350 1351 return name; 1352} 1353 1354char *HIDAPI_GetDeviceManufacturerName(Uint16 vendor_id, Uint16 product_id) 1355{ 1356 SDL_HIDAPI_Device *device; 1357 char *name = NULL; 1358 1359 SDL_LockJoysticks(); 1360 for (device = SDL_HIDAPI_devices; device; device = device->next) { 1361 if (vendor_id == device->vendor_id && product_id == device->product_id) { 1362 if (device->manufacturer_string) { 1363 name = SDL_strdup(device->manufacturer_string); 1364 } 1365 break; 1366 } 1367 } 1368 SDL_UnlockJoysticks(); 1369 1370 return name; 1371} 1372 1373SDL_JoystickType HIDAPI_GetJoystickTypeFromGUID(SDL_GUID guid) 1374{ 1375 SDL_HIDAPI_Device *device; 1376 SDL_JoystickType type = SDL_JOYSTICK_TYPE_UNKNOWN; 1377 1378 SDL_LockJoysticks(); 1379 for (device = SDL_HIDAPI_devices; device; device = device->next) { 1380 if (SDL_memcmp(&guid, &device->guid, sizeof(guid)) == 0) { 1381 type = device->joystick_type; 1382 break; 1383 } 1384 } 1385 SDL_UnlockJoysticks(); 1386 1387 return type; 1388} 1389 1390SDL_GamepadType HIDAPI_GetGamepadTypeFromGUID(SDL_GUID guid) 1391{ 1392 SDL_HIDAPI_Device *device; 1393 SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD; 1394 1395 SDL_LockJoysticks(); 1396 for (device = SDL_HIDAPI_devices; device; device = device->next) { 1397 if (SDL_memcmp(&guid, &device->guid, sizeof(guid)) == 0) { 1398 type = device->type; 1399 break; 1400 } 1401 } 1402 SDL_UnlockJoysticks(); 1403 1404 return type; 1405} 1406 1407static void HIDAPI_JoystickDetect(void) 1408{ 1409 if (HIDAPI_StartUpdatingDevices()) { 1410 Uint32 count = SDL_hid_device_change_count(); 1411 if (SDL_HIDAPI_change_count != count) { 1412 SDL_HIDAPI_change_count = count; 1413 HIDAPI_UpdateDeviceList(); 1414 } 1415 HIDAPI_FinishUpdatingDevices(); 1416 } 1417} 1418 1419void HIDAPI_UpdateDevices(void) 1420{ 1421 SDL_HIDAPI_Device *device; 1422 1423 SDL_AssertJoysticksLocked(); 1424 1425 // Update the devices, which may change connected joysticks and send events 1426 1427 // Prepare the existing device list 1428 if (HIDAPI_StartUpdatingDevices()) { 1429 for (device = SDL_HIDAPI_devices; device; device = device->next) { 1430 if (device->parent) { 1431 continue; 1432 } 1433 if (device->driver) { 1434 device->driver->UpdateDevice(device); 1435 } 1436 } 1437 HIDAPI_FinishUpdatingDevices(); 1438 } 1439} 1440 1441static const char *HIDAPI_JoystickGetDeviceName(int device_index) 1442{ 1443 SDL_HIDAPI_Device *device; 1444 const char *name = NULL; 1445 1446 device = HIDAPI_GetDeviceByIndex(device_index, NULL); 1447 if (device) { 1448 // FIXME: The device could be freed after this name is returned... 1449 name = device->name; 1450 } 1451 1452 return name; 1453} 1454 1455static const char *HIDAPI_JoystickGetDevicePath(int device_index) 1456{ 1457 SDL_HIDAPI_Device *device; 1458 const char *path = NULL; 1459 1460 device = HIDAPI_GetDeviceByIndex(device_index, NULL); 1461 if (device) { 1462 // FIXME: The device could be freed after this path is returned... 1463 path = device->path; 1464 } 1465 1466 return path; 1467} 1468 1469static int HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) 1470{ 1471 SDL_HIDAPI_Device *device; 1472 1473 device = HIDAPI_GetDeviceByIndex(device_index, NULL); 1474 if (device) { 1475 return device->steam_virtual_gamepad_slot; 1476 } 1477 return -1; 1478} 1479 1480static int HIDAPI_JoystickGetDevicePlayerIndex(int device_index) 1481{ 1482 SDL_HIDAPI_Device *device; 1483 SDL_JoystickID instance_id; 1484 int player_index = -1; 1485 1486 device = HIDAPI_GetDeviceByIndex(device_index, &instance_id); 1487 if (device) { 1488 player_index = device->driver->GetDevicePlayerIndex(device, instance_id); 1489 } 1490 1491 return player_index; 1492} 1493 1494static void HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index) 1495{ 1496 SDL_HIDAPI_Device *device; 1497 SDL_JoystickID instance_id; 1498 1499 device = HIDAPI_GetDeviceByIndex(device_index, &instance_id); 1500 if (device) { 1501 device->driver->SetDevicePlayerIndex(device, instance_id, player_index); 1502 } 1503} 1504 1505static SDL_GUID HIDAPI_JoystickGetDeviceGUID(int device_index) 1506{ 1507 SDL_HIDAPI_Device *device; 1508 SDL_GUID guid; 1509 1510 device = HIDAPI_GetDeviceByIndex(device_index, NULL); 1511 if (device) { 1512 SDL_memcpy(&guid, &device->guid, sizeof(guid)); 1513 } else { 1514 SDL_zero(guid); 1515 } 1516 1517 return guid; 1518} 1519 1520static SDL_JoystickID HIDAPI_JoystickGetDeviceInstanceID(int device_index) 1521{ 1522 SDL_JoystickID joystickID = 0; 1523 HIDAPI_GetDeviceByIndex(device_index, &joystickID); 1524 return joystickID; 1525} 1526 1527static bool HIDAPI_JoystickOpen(SDL_Joystick *joystick, int device_index) 1528{ 1529 SDL_JoystickID joystickID = 0; 1530 SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID); 1531 struct joystick_hwdata *hwdata; 1532 1533 SDL_AssertJoysticksLocked(); 1534 1535 if (!device || !device->driver || device->broken) { 1536 // This should never happen - validated before being called 1537 return SDL_SetError("Couldn't find HIDAPI device at index %d", device_index); 1538 } 1539 1540 hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata)); 1541 if (!hwdata) { 1542 return false; 1543 } 1544 hwdata->device = device; 1545 1546 // Process any pending reports before opening the device 1547 device->driver->UpdateDevice(device); 1548 1549 // UpdateDevice() may have called HIDAPI_JoystickDisconnected() if the device went away 1550 if (device->num_joysticks == 0) { 1551 SDL_free(hwdata); 1552 return SDL_SetError("HIDAPI device disconnected while opening"); 1553 } 1554 1555 // Set the default connection state, can be overridden below 1556 if (device->is_bluetooth) { 1557 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS; 1558 } else { 1559 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED; 1560 } 1561 1562 if (!device->driver->OpenJoystick(device, joystick)) { 1563 // The open failed, mark this device as disconnected and update devices 1564 HIDAPI_JoystickDisconnected(device, joystickID); 1565 SDL_free(hwdata); 1566 return false; 1567 } 1568 1569 HIDAPI_UpdateJoystickProperties(device, joystick); 1570 1571 if (device->serial) { 1572 joystick->serial = SDL_strdup(device->serial); 1573 } 1574 1575 joystick->hwdata = hwdata; 1576 return true; 1577} 1578 1579static bool HIDAPI_GetJoystickDevice(SDL_Joystick *joystick, SDL_HIDAPI_Device **device) 1580{ 1581 SDL_AssertJoysticksLocked(); 1582 1583 if (joystick && joystick->hwdata) { 1584 *device = joystick->hwdata->device; 1585 if (SDL_ObjectValid(*device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK) && (*device)->driver != NULL) { 1586 return true; 1587 } 1588 } 1589 return false; 1590} 1591 1592static bool HIDAPI_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 1593{ 1594 bool result; 1595 SDL_HIDAPI_Device *device = NULL; 1596 1597 if (HIDAPI_GetJoystickDevice(joystick, &device)) { 1598 result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble); 1599 } else { 1600 result = SDL_SetError("Rumble failed, device disconnected"); 1601 } 1602 1603 return result; 1604} 1605 1606static bool HIDAPI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 1607{ 1608 bool result; 1609 SDL_HIDAPI_Device *device = NULL; 1610 1611 if (HIDAPI_GetJoystickDevice(joystick, &device)) { 1612 result = device->driver->RumbleJoystickTriggers(device, joystick, left_rumble, right_rumble); 1613 } else { 1614 result = SDL_SetError("Rumble failed, device disconnected"); 1615 } 1616 1617 return result; 1618} 1619 1620static bool HIDAPI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 1621{ 1622 bool result; 1623 SDL_HIDAPI_Device *device = NULL; 1624 1625 if (HIDAPI_GetJoystickDevice(joystick, &device)) { 1626 result = device->driver->SetJoystickLED(device, joystick, red, green, blue); 1627 } else { 1628 result = SDL_SetError("SetLED failed, device disconnected"); 1629 } 1630 1631 return result; 1632} 1633 1634static bool HIDAPI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) 1635{ 1636 bool result; 1637 SDL_HIDAPI_Device *device = NULL; 1638 1639 if (HIDAPI_GetJoystickDevice(joystick, &device)) { 1640 result = device->driver->SendJoystickEffect(device, joystick, data, size); 1641 } else { 1642 result = SDL_SetError("SendEffect failed, device disconnected"); 1643 } 1644 1645 return result; 1646} 1647 1648static bool HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled) 1649{ 1650 bool result; 1651 SDL_HIDAPI_Device *device = NULL; 1652 1653 if (HIDAPI_GetJoystickDevice(joystick, &device)) { 1654 result = device->driver->SetJoystickSensorsEnabled(device, joystick, enabled); 1655 } else { 1656 result = SDL_SetError("SetSensorsEnabled failed, device disconnected"); 1657 } 1658 1659 return result; 1660} 1661 1662static void HIDAPI_JoystickUpdate(SDL_Joystick *joystick) 1663{ 1664 // This is handled in SDL_HIDAPI_UpdateDevices() 1665} 1666 1667static void HIDAPI_JoystickClose(SDL_Joystick *joystick) SDL_NO_THREAD_SAFETY_ANALYSIS // We unlock the device lock so rumble can complete 1668{ 1669 SDL_AssertJoysticksLocked(); 1670 1671 if (joystick->hwdata) { 1672 SDL_HIDAPI_Device *device = joystick->hwdata->device; 1673 1674 // Wait up to 30 ms for pending rumble to complete 1675 for (int i = 0; i < 3; ++i) { 1676 if (SDL_GetAtomicInt(&device->rumble_pending) > 0) { 1677 SDL_Delay(10); 1678 } 1679 } 1680 1681 device->driver->CloseJoystick(device, joystick); 1682 1683 SDL_free(joystick->hwdata); 1684 joystick->hwdata = NULL; 1685 } 1686} 1687 1688static void HIDAPI_JoystickQuit(void) 1689{ 1690 int i; 1691 1692 SDL_AssertJoysticksLocked(); 1693 1694 shutting_down = true; 1695 1696 SDL_HIDAPI_QuitRumble(); 1697 1698 while (SDL_HIDAPI_devices) { 1699 SDL_HIDAPI_Device *device = SDL_HIDAPI_devices; 1700 if (device->parent) { 1701 // When a child device goes away, so does the parent 1702 device = device->parent; 1703 for (i = 0; i < device->num_children; ++i) { 1704 HIDAPI_DelDevice(device->children[i]); 1705 } 1706 HIDAPI_DelDevice(device); 1707 } else { 1708 HIDAPI_DelDevice(device); 1709 } 1710 } 1711 1712 // Make sure the drivers cleaned up properly 1713 SDL_assert(SDL_HIDAPI_numjoysticks == 0); 1714 1715 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 1716 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 1717 driver->UnregisterHints(SDL_HIDAPIDriverHintChanged, driver); 1718 } 1719 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, 1720 SDL_HIDAPIDriverHintChanged, NULL); 1721 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI, 1722 SDL_HIDAPIDriverHintChanged, NULL); 1723 1724 SDL_hid_exit(); 1725 1726 SDL_HIDAPI_change_count = 0; 1727 shutting_down = false; 1728 initialized = false; 1729} 1730 1731static bool HIDAPI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) 1732{ 1733 return false; 1734} 1735 1736SDL_JoystickDriver SDL_HIDAPI_JoystickDriver = { 1737 HIDAPI_JoystickInit, 1738 HIDAPI_JoystickGetCount, 1739 HIDAPI_JoystickDetect, 1740 HIDAPI_IsDevicePresent, 1741 HIDAPI_JoystickGetDeviceName, 1742 HIDAPI_JoystickGetDevicePath, 1743 HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot, 1744 HIDAPI_JoystickGetDevicePlayerIndex, 1745 HIDAPI_JoystickSetDevicePlayerIndex, 1746 HIDAPI_JoystickGetDeviceGUID, 1747 HIDAPI_JoystickGetDeviceInstanceID, 1748 HIDAPI_JoystickOpen, 1749 HIDAPI_JoystickRumble, 1750 HIDAPI_JoystickRumbleTriggers, 1751 HIDAPI_JoystickSetLED, 1752 HIDAPI_JoystickSendEffect, 1753 HIDAPI_JoystickSetSensorsEnabled, 1754 HIDAPI_JoystickUpdate, 1755 HIDAPI_JoystickClose, 1756 HIDAPI_JoystickQuit, 1757 HIDAPI_JoystickGetGamepadMapping 1758}; 1759 1760#endif // SDL_JOYSTICK_HIDAPI 1761
[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.