SDL_hidapijoystick.c (41677B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 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_atomic.h" 26 #include "SDL_endian.h" 27 #include "SDL_hints.h" 28 #include "SDL_thread.h" 29 #include "SDL_timer.h" 30 #include "SDL_joystick.h" 31 #include "../SDL_sysjoystick.h" 32 #include "SDL_hidapijoystick_c.h" 33 #include "SDL_hidapi_rumble.h" 34 #include "../../SDL_hints_c.h" 35 36 #if defined(__WIN32__) 37 #include "../../core/windows/SDL_windows.h" 38 #include "../windows/SDL_rawinputjoystick_c.h" 39 #endif 40 41 #if defined(__MACOSX__) 42 #include <CoreFoundation/CoreFoundation.h> 43 #include <mach/mach.h> 44 #include <IOKit/IOKitLib.h> 45 #include <IOKit/hid/IOHIDDevice.h> 46 #include <IOKit/usb/USBSpec.h> 47 #endif 48 49 #if defined(__LINUX__) 50 #include "../../core/linux/SDL_udev.h" 51 #ifdef SDL_USE_LIBUDEV 52 #include <poll.h> 53 #endif 54 #endif 55 56 struct joystick_hwdata 57 { 58 SDL_HIDAPI_Device *device; 59 }; 60 61 static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = { 62 #ifdef SDL_JOYSTICK_HIDAPI_PS4 63 &SDL_HIDAPI_DriverPS4, 64 #endif 65 #ifdef SDL_JOYSTICK_HIDAPI_PS5 66 &SDL_HIDAPI_DriverPS5, 67 #endif 68 #ifdef SDL_JOYSTICK_HIDAPI_STEAM 69 &SDL_HIDAPI_DriverSteam, 70 #endif 71 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH 72 &SDL_HIDAPI_DriverSwitch, 73 #endif 74 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360 75 &SDL_HIDAPI_DriverXbox360, 76 &SDL_HIDAPI_DriverXbox360W, 77 #endif 78 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE 79 &SDL_HIDAPI_DriverXboxOne, 80 #endif 81 #ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE 82 &SDL_HIDAPI_DriverGameCube, 83 #endif 84 }; 85 static int SDL_HIDAPI_numdrivers = 0; 86 static SDL_SpinLock SDL_HIDAPI_spinlock; 87 static SDL_HIDAPI_Device *SDL_HIDAPI_devices; 88 static int SDL_HIDAPI_numjoysticks = 0; 89 static SDL_bool initialized = SDL_FALSE; 90 static SDL_bool shutting_down = SDL_FALSE; 91 92 #if defined(SDL_USE_LIBUDEV) 93 static const SDL_UDEV_Symbols * usyms = NULL; 94 #endif 95 96 static struct 97 { 98 SDL_bool m_bHaveDevicesChanged; 99 SDL_bool m_bCanGetNotifications; 100 Uint32 m_unLastDetect; 101 102 #if defined(__WIN32__) 103 SDL_threadID m_nThreadID; 104 WNDCLASSEXA m_wndClass; 105 HWND m_hwndMsg; 106 HDEVNOTIFY m_hNotify; 107 double m_flLastWin32MessageCheck; 108 #endif 109 110 #if defined(__MACOSX__) 111 IONotificationPortRef m_notificationPort; 112 mach_port_t m_notificationMach; 113 #endif 114 115 #if defined(SDL_USE_LIBUDEV) 116 struct udev *m_pUdev; 117 struct udev_monitor *m_pUdevMonitor; 118 int m_nUdevFd; 119 #endif 120 } SDL_HIDAPI_discovery; 121 122 123 #ifdef __WIN32__ 124 struct _DEV_BROADCAST_HDR 125 { 126 DWORD dbch_size; 127 DWORD dbch_devicetype; 128 DWORD dbch_reserved; 129 }; 130 131 typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A 132 { 133 DWORD dbcc_size; 134 DWORD dbcc_devicetype; 135 DWORD dbcc_reserved; 136 GUID dbcc_classguid; 137 char dbcc_name[ 1 ]; 138 } DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A; 139 140 typedef struct _DEV_BROADCAST_HDR DEV_BROADCAST_HDR; 141 #define DBT_DEVICEARRIVAL 0x8000 /* system detected a new device */ 142 #define DBT_DEVICEREMOVECOMPLETE 0x8004 /* device was removed from the system */ 143 #define DBT_DEVTYP_DEVICEINTERFACE 0x00000005 /* device interface class */ 144 #define DBT_DEVNODES_CHANGED 0x0007 145 #define DBT_CONFIGCHANGED 0x0018 146 #define DBT_DEVICETYPESPECIFIC 0x8005 /* type specific event */ 147 #define DBT_DEVINSTSTARTED 0x8008 /* device installed and started */ 148 149 #include <initguid.h> 150 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED); 151 152 static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 153 { 154 switch (message) { 155 case WM_DEVICECHANGE: 156 switch (wParam) { 157 case DBT_DEVICEARRIVAL: 158 case DBT_DEVICEREMOVECOMPLETE: 159 if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { 160 SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE; 161 } 162 break; 163 } 164 return TRUE; 165 } 166 167 return DefWindowProc(hwnd, message, wParam, lParam); 168 } 169 #endif /* __WIN32__ */ 170 171 172 #if defined(__MACOSX__) 173 static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator) 174 { 175 /* Must drain the iterator, or we won't receive new notifications */ 176 io_object_t entry; 177 while ((entry = IOIteratorNext(portIterator)) != 0) { 178 IOObjectRelease(entry); 179 *(SDL_bool*)context = SDL_TRUE; 180 } 181 } 182 #endif /* __MACOSX__ */ 183 184 static void 185 HIDAPI_InitializeDiscovery() 186 { 187 SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE; 188 SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_FALSE; 189 SDL_HIDAPI_discovery.m_unLastDetect = 0; 190 191 #if defined(__WIN32__) 192 SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID(); 193 194 SDL_zero(SDL_HIDAPI_discovery.m_wndClass); 195 SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL); 196 SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION"; 197 SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc; /* This function is called by windows */ 198 SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX); 199 200 RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass); 201 SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); 202 203 { 204 DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast; 205 206 SDL_zero(devBroadcast); 207 devBroadcast.dbcc_size = sizeof( devBroadcast ); 208 devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 209 devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; 210 211 /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored, 212 * but that seems to be necessary to get a notice after each individual usb input device actually 213 * installs, rather than just as the composite device is seen. 214 */ 215 SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification( SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES ); 216 SDL_HIDAPI_discovery.m_bCanGetNotifications = ( SDL_HIDAPI_discovery.m_hNotify != 0 ); 217 } 218 #endif /* __WIN32__ */ 219 220 #if defined(__MACOSX__) 221 SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault); 222 if (SDL_HIDAPI_discovery.m_notificationPort) { 223 { 224 io_iterator_t portIterator = 0; 225 io_object_t entry; 226 IOReturn result = IOServiceAddMatchingNotification( 227 SDL_HIDAPI_discovery.m_notificationPort, 228 kIOFirstMatchNotification, 229 IOServiceMatching(kIOHIDDeviceKey), 230 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator); 231 232 if (result == 0) { 233 /* Must drain the existing iterator, or we won't receive new notifications */ 234 while ((entry = IOIteratorNext(portIterator)) != 0) { 235 IOObjectRelease(entry); 236 } 237 } else { 238 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort); 239 SDL_HIDAPI_discovery.m_notificationPort = nil; 240 } 241 } 242 { 243 io_iterator_t portIterator = 0; 244 io_object_t entry; 245 IOReturn result = IOServiceAddMatchingNotification( 246 SDL_HIDAPI_discovery.m_notificationPort, 247 kIOTerminatedNotification, 248 IOServiceMatching(kIOHIDDeviceKey), 249 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator); 250 251 if (result == 0) { 252 /* Must drain the existing iterator, or we won't receive new notifications */ 253 while ((entry = IOIteratorNext(portIterator)) != 0) { 254 IOObjectRelease(entry); 255 } 256 } else { 257 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort); 258 SDL_HIDAPI_discovery.m_notificationPort = nil; 259 } 260 } 261 } 262 263 SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL; 264 if (SDL_HIDAPI_discovery.m_notificationPort) { 265 SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort); 266 } 267 268 SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL); 269 270 #endif // __MACOSX__ 271 272 #if defined(SDL_USE_LIBUDEV) 273 SDL_HIDAPI_discovery.m_pUdev = NULL; 274 SDL_HIDAPI_discovery.m_pUdevMonitor = NULL; 275 SDL_HIDAPI_discovery.m_nUdevFd = -1; 276 277 usyms = SDL_UDEV_GetUdevSyms(); 278 if (usyms) { 279 SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new(); 280 } 281 if (SDL_HIDAPI_discovery.m_pUdev) { 282 SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev"); 283 if (SDL_HIDAPI_discovery.m_pUdevMonitor) { 284 usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor); 285 SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor); 286 SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_TRUE; 287 } 288 } 289 290 #endif /* SDL_USE_LIBUDEV */ 291 } 292 293 static void 294 HIDAPI_UpdateDiscovery() 295 { 296 if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) { 297 const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000; /* Update every 3 seconds */ 298 Uint32 now = SDL_GetTicks(); 299 if (!SDL_HIDAPI_discovery.m_unLastDetect || SDL_TICKS_PASSED(now, SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) { 300 SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE; 301 SDL_HIDAPI_discovery.m_unLastDetect = now; 302 } 303 return; 304 } 305 306 #if defined(__WIN32__) 307 #if 0 /* just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan. */ 308 /* We'll only get messages on the same thread that created the window */ 309 if (SDL_ThreadID() == SDL_HIDAPI_discovery.m_nThreadID) { 310 MSG msg; 311 while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) { 312 if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) { 313 TranslateMessage(&msg); 314 DispatchMessage(&msg); 315 } 316 } 317 } 318 #endif 319 #endif /* __WIN32__ */ 320 321 #if defined(__MACOSX__) 322 if (SDL_HIDAPI_discovery.m_notificationPort) { 323 struct { mach_msg_header_t hdr; char payload[ 4096 ]; } msg; 324 while (mach_msg(&msg.hdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg), SDL_HIDAPI_discovery.m_notificationMach, 0, MACH_PORT_NULL) == KERN_SUCCESS) { 325 IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort); 326 } 327 } 328 #endif 329 330 #if defined(SDL_USE_LIBUDEV) 331 if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) { 332 /* Drain all notification events. 333 * We don't expect a lot of device notifications so just 334 * do a new discovery on any kind or number of notifications. 335 * This could be made more restrictive if necessary. 336 */ 337 for (;;) { 338 struct pollfd PollUdev; 339 struct udev_device *pUdevDevice; 340 341 PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd; 342 PollUdev.events = POLLIN; 343 if (poll(&PollUdev, 1, 0) != 1) { 344 break; 345 } 346 347 SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE; 348 349 pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor); 350 if (pUdevDevice) { 351 usyms->udev_device_unref(pUdevDevice); 352 } 353 } 354 } 355 #endif 356 } 357 358 static void 359 HIDAPI_ShutdownDiscovery() 360 { 361 #if defined(__WIN32__) 362 if (SDL_HIDAPI_discovery.m_hNotify) 363 UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify); 364 365 if (SDL_HIDAPI_discovery.m_hwndMsg) { 366 DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg); 367 } 368 369 UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance); 370 #endif 371 372 #if defined(__MACOSX__) 373 if (SDL_HIDAPI_discovery.m_notificationPort) { 374 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort); 375 } 376 #endif 377 378 #if defined(SDL_USE_LIBUDEV) 379 if (usyms) { 380 if (SDL_HIDAPI_discovery.m_pUdevMonitor) { 381 usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor); 382 } 383 if (SDL_HIDAPI_discovery.m_pUdev) { 384 usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev); 385 } 386 SDL_UDEV_ReleaseUdevSyms(); 387 usyms = NULL; 388 } 389 #endif 390 } 391 392 void 393 HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size) 394 { 395 int i; 396 char *buffer; 397 size_t length = SDL_strlen(prefix) + 11*(USB_PACKET_LENGTH/8) + (5*USB_PACKET_LENGTH*2) + 1 + 1; 398 int start = 0, amount = size; 399 400 buffer = (char *)SDL_malloc(length); 401 SDL_snprintf(buffer, length, prefix, size); 402 for (i = start; i < start+amount; ++i) { 403 if ((i % 8) == 0) { 404 SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), "\n%.2d: ", i); 405 } 406 SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), " 0x%.2x", data[i]); 407 } 408 SDL_strlcat(buffer, "\n", length); 409 SDL_Log("%s", buffer); 410 SDL_free(buffer); 411 } 412 413 static void HIDAPI_JoystickDetect(void); 414 static void HIDAPI_JoystickClose(SDL_Joystick * joystick); 415 416 static SDL_bool 417 HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) 418 { 419 int i; 420 SDL_GameControllerType type = SDL_GetJoystickGameControllerType(name, vendor_id, product_id, -1, 0, 0, 0); 421 422 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 423 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 424 if (driver->enabled && driver->IsSupportedDevice(name, type, vendor_id, product_id, version, -1, 0, 0, 0)) { 425 return SDL_TRUE; 426 } 427 } 428 return SDL_FALSE; 429 } 430 431 static SDL_HIDAPI_DeviceDriver * 432 HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device) 433 { 434 const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001; 435 const Uint16 USAGE_JOYSTICK = 0x0004; 436 const Uint16 USAGE_GAMEPAD = 0x0005; 437 const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008; 438 int i; 439 SDL_GameControllerType type; 440 441 if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) { 442 return NULL; 443 } 444 445 if (device->vendor_id != USB_VENDOR_VALVE) { 446 if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) { 447 return NULL; 448 } 449 if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) { 450 return NULL; 451 } 452 } 453 454 type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol); 455 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 456 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 457 if (driver->enabled && driver->IsSupportedDevice(device->name, type, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) { 458 return driver; 459 } 460 } 461 return NULL; 462 } 463 464 static SDL_HIDAPI_Device * 465 HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID) 466 { 467 SDL_HIDAPI_Device *device = SDL_HIDAPI_devices; 468 while (device) { 469 if (device->driver) { 470 if (device_index < device->num_joysticks) { 471 if (pJoystickID) { 472 *pJoystickID = device->joysticks[device_index]; 473 } 474 return device; 475 } 476 device_index -= device->num_joysticks; 477 } 478 device = device->next; 479 } 480 return NULL; 481 } 482 483 static SDL_HIDAPI_Device * 484 HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id) 485 { 486 SDL_HIDAPI_Device *device = SDL_HIDAPI_devices; 487 while (device) { 488 if (device->vendor_id == vendor_id && device->product_id == product_id && 489 SDL_strcmp(device->path, path) == 0) { 490 break; 491 } 492 device = device->next; 493 } 494 return device; 495 } 496 497 static void 498 HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device) 499 { 500 if (device->driver) { 501 /* Already setup */ 502 return; 503 } 504 505 device->driver = HIDAPI_GetDeviceDriver(device); 506 if (device->driver) { 507 const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id); 508 if (name) { 509 SDL_free(device->name); 510 device->name = SDL_strdup(name); 511 } 512 } 513 514 /* Initialize the device, which may cause a connected event */ 515 if (device->driver && !device->driver->InitDevice(device)) { 516 device->driver = NULL; 517 } 518 } 519 520 static void 521 HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device) 522 { 523 if (!device->driver) { 524 /* Already cleaned up */ 525 return; 526 } 527 528 /* Disconnect any joysticks */ 529 while (device->num_joysticks) { 530 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 531 } 532 533 device->driver->FreeDevice(device); 534 device->driver = NULL; 535 } 536 537 static void SDLCALL 538 SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 539 { 540 int i; 541 SDL_HIDAPI_Device *device; 542 SDL_bool enabled = SDL_GetStringBoolean(hint, SDL_TRUE); 543 544 if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) { 545 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 546 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 547 driver->enabled = SDL_GetHintBoolean(driver->hint, enabled); 548 } 549 } else { 550 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 551 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 552 if (SDL_strcmp(name, driver->hint) == 0) { 553 driver->enabled = enabled; 554 } 555 } 556 } 557 558 SDL_HIDAPI_numdrivers = 0; 559 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 560 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 561 if (driver->enabled) { 562 ++SDL_HIDAPI_numdrivers; 563 } 564 } 565 566 /* Update device list if driver availability changes */ 567 SDL_LockJoysticks(); 568 569 for (device = SDL_HIDAPI_devices; device; device = device->next) { 570 if (device->driver && !device->driver->enabled) { 571 HIDAPI_CleanupDeviceDriver(device); 572 } 573 HIDAPI_SetupDeviceDriver(device); 574 } 575 576 SDL_UnlockJoysticks(); 577 } 578 579 static int 580 HIDAPI_JoystickInit(void) 581 { 582 int i; 583 584 if (initialized) { 585 return 0; 586 } 587 588 #if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__) 589 /* The hidapi framwork is weak-linked on Apple platforms */ 590 int HID_API_EXPORT HID_API_CALL hid_init(void) __attribute__((weak_import)); 591 592 if (hid_init == NULL) { 593 SDL_SetError("Couldn't initialize hidapi, framework not available"); 594 return -1; 595 } 596 #endif /* __MACOSX__ || __IPHONEOS__ || __TVOS__ */ 597 598 if (hid_init() < 0) { 599 SDL_SetError("Couldn't initialize hidapi"); 600 return -1; 601 } 602 603 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 604 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 605 SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL); 606 } 607 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI, 608 SDL_HIDAPIDriverHintChanged, NULL); 609 HIDAPI_InitializeDiscovery(); 610 HIDAPI_JoystickDetect(); 611 HIDAPI_UpdateDevices(); 612 613 initialized = SDL_TRUE; 614 615 return 0; 616 } 617 618 SDL_bool 619 HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID) 620 { 621 SDL_JoystickID joystickID; 622 SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks)); 623 if (!joysticks) { 624 return SDL_FALSE; 625 } 626 627 joystickID = SDL_GetNextJoystickInstanceID(); 628 device->joysticks = joysticks; 629 device->joysticks[device->num_joysticks++] = joystickID; 630 ++SDL_HIDAPI_numjoysticks; 631 632 SDL_PrivateJoystickAdded(joystickID); 633 634 if (pJoystickID) { 635 *pJoystickID = joystickID; 636 } 637 return SDL_TRUE; 638 } 639 640 void 641 HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID) 642 { 643 int i, size; 644 645 for (i = 0; i < device->num_joysticks; ++i) { 646 if (device->joysticks[i] == joystickID) { 647 SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joystickID); 648 if (joystick) { 649 HIDAPI_JoystickClose(joystick); 650 } 651 652 size = (device->num_joysticks - i - 1) * sizeof(SDL_JoystickID); 653 SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], size); 654 --device->num_joysticks; 655 656 --SDL_HIDAPI_numjoysticks; 657 if (device->num_joysticks == 0) { 658 SDL_free(device->joysticks); 659 device->joysticks = NULL; 660 } 661 662 if (!shutting_down) { 663 SDL_PrivateJoystickRemoved(joystickID); 664 } 665 return; 666 } 667 } 668 } 669 670 static int 671 HIDAPI_JoystickGetCount(void) 672 { 673 return SDL_HIDAPI_numjoysticks; 674 } 675 676 static char * 677 HIDAPI_ConvertString(const wchar_t *wide_string) 678 { 679 char *string = NULL; 680 681 if (wide_string) { 682 string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t)); 683 if (!string) { 684 if (sizeof(wchar_t) == sizeof(Uint16)) { 685 string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t)); 686 } else if (sizeof(wchar_t) == sizeof(Uint32)) { 687 string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t)); 688 } 689 } 690 } 691 return string; 692 } 693 694 static void 695 HIDAPI_AddDevice(struct hid_device_info *info) 696 { 697 SDL_HIDAPI_Device *device; 698 SDL_HIDAPI_Device *curr, *last = NULL; 699 700 for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) { 701 continue; 702 } 703 704 device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device)); 705 if (!device) { 706 return; 707 } 708 device->path = SDL_strdup(info->path); 709 if (!device->path) { 710 SDL_free(device); 711 return; 712 } 713 device->seen = SDL_TRUE; 714 device->vendor_id = info->vendor_id; 715 device->product_id = info->product_id; 716 device->version = info->release_number; 717 device->interface_number = info->interface_number; 718 device->interface_class = info->interface_class; 719 device->interface_subclass = info->interface_subclass; 720 device->interface_protocol = info->interface_protocol; 721 device->usage_page = info->usage_page; 722 device->usage = info->usage; 723 { 724 /* FIXME: Is there any way to tell whether this is a Bluetooth device? */ 725 const Uint16 vendor = device->vendor_id; 726 const Uint16 product = device->product_id; 727 const Uint16 version = device->version; 728 Uint16 *guid16 = (Uint16 *)device->guid.data; 729 730 *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB); 731 *guid16++ = 0; 732 *guid16++ = SDL_SwapLE16(vendor); 733 *guid16++ = 0; 734 *guid16++ = SDL_SwapLE16(product); 735 *guid16++ = 0; 736 *guid16++ = SDL_SwapLE16(version); 737 *guid16++ = 0; 738 739 /* Note that this is a HIDAPI device for special handling elsewhere */ 740 device->guid.data[14] = 'h'; 741 device->guid.data[15] = 0; 742 } 743 device->dev_lock = SDL_CreateMutex(); 744 745 /* Need the device name before getting the driver to know whether to ignore this device */ 746 { 747 char *manufacturer_string = HIDAPI_ConvertString(info->manufacturer_string); 748 char *product_string = HIDAPI_ConvertString(info->product_string); 749 char *serial_number = HIDAPI_ConvertString(info->serial_number); 750 751 device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string); 752 if (SDL_strncmp(device->name, "0x", 2) == 0) { 753 /* Couldn't find a controller name, try to give it one based on device type */ 754 const char *name = NULL; 755 756 switch (SDL_GetJoystickGameControllerType(NULL, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) { 757 case SDL_CONTROLLER_TYPE_XBOX360: 758 name = "Xbox 360 Controller"; 759 break; 760 case SDL_CONTROLLER_TYPE_XBOXONE: 761 name = "Xbox One Controller"; 762 break; 763 case SDL_CONTROLLER_TYPE_PS3: 764 name = "PS3 Controller"; 765 break; 766 case SDL_CONTROLLER_TYPE_PS4: 767 name = "PS4 Controller"; 768 break; 769 case SDL_CONTROLLER_TYPE_PS5: 770 name = "PS5 Controller"; 771 break; 772 case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: 773 name = "Nintendo Switch Pro Controller"; 774 break; 775 default: 776 break; 777 } 778 779 if (name) { 780 SDL_free(device->name); 781 device->name = SDL_strdup(name); 782 } 783 } 784 785 if (manufacturer_string) { 786 SDL_free(manufacturer_string); 787 } 788 if (product_string) { 789 SDL_free(product_string); 790 } 791 792 if (serial_number && *serial_number) { 793 device->serial = serial_number; 794 } else { 795 SDL_free(serial_number); 796 } 797 798 if (!device->name) { 799 SDL_free(device->serial); 800 SDL_free(device->path); 801 SDL_free(device); 802 return; 803 } 804 } 805 806 /* Add it to the list */ 807 if (last) { 808 last->next = device; 809 } else { 810 SDL_HIDAPI_devices = device; 811 } 812 813 HIDAPI_SetupDeviceDriver(device); 814 815 #ifdef DEBUG_HIDAPI 816 SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, 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)\n", device->name, device->vendor_id, device->product_id, device->version, device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED"); 817 #endif 818 } 819 820 821 static void 822 HIDAPI_DelDevice(SDL_HIDAPI_Device *device) 823 { 824 SDL_HIDAPI_Device *curr, *last; 825 826 #ifdef DEBUG_HIDAPI 827 SDL_Log("Removing HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, 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)\n", device->name, device->vendor_id, device->product_id, device->version, device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED"); 828 #endif 829 830 for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) { 831 if (curr == device) { 832 if (last) { 833 last->next = curr->next; 834 } else { 835 SDL_HIDAPI_devices = curr->next; 836 } 837 838 HIDAPI_CleanupDeviceDriver(device); 839 840 /* Make sure the rumble thread is done with this device */ 841 while (SDL_AtomicGet(&device->rumble_pending) > 0) { 842 SDL_Delay(10); 843 } 844 845 SDL_DestroyMutex(device->dev_lock); 846 SDL_free(device->serial); 847 SDL_free(device->name); 848 SDL_free(device->path); 849 SDL_free(device); 850 return; 851 } 852 } 853 } 854 855 static void 856 HIDAPI_UpdateDeviceList(void) 857 { 858 SDL_HIDAPI_Device *device; 859 struct hid_device_info *devs, *info; 860 861 SDL_LockJoysticks(); 862 863 /* Prepare the existing device list */ 864 device = SDL_HIDAPI_devices; 865 while (device) { 866 device->seen = SDL_FALSE; 867 device = device->next; 868 } 869 870 /* Enumerate the devices */ 871 if (SDL_HIDAPI_numdrivers > 0) { 872 devs = hid_enumerate(0, 0); 873 if (devs) { 874 for (info = devs; info; info = info->next) { 875 device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id); 876 if (device) { 877 device->seen = SDL_TRUE; 878 } else { 879 HIDAPI_AddDevice(info); 880 } 881 } 882 hid_free_enumeration(devs); 883 } 884 } 885 886 /* Remove any devices that weren't seen */ 887 device = SDL_HIDAPI_devices; 888 while (device) { 889 SDL_HIDAPI_Device *next = device->next; 890 891 if (!device->seen) { 892 HIDAPI_DelDevice(device); 893 } 894 device = next; 895 } 896 897 SDL_UnlockJoysticks(); 898 } 899 900 static SDL_bool 901 HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Device *device) 902 { 903 if (vendor_id == device->vendor_id && product_id == device->product_id) { 904 return SDL_TRUE; 905 } 906 907 if (vendor_id == USB_VENDOR_MICROSOFT) { 908 /* If we're looking for the wireless XBox 360 controller, also look for the dongle */ 909 if (product_id == 0x02a1 && device->product_id == 0x0719) { 910 return SDL_TRUE; 911 } 912 913 /* If we're looking for the raw input Xbox One controller, match it against any other Xbox One controller */ 914 if (product_id == USB_PRODUCT_XBOX_ONE_RAW_INPUT_CONTROLLER && 915 SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol) == SDL_CONTROLLER_TYPE_XBOXONE) { 916 return SDL_TRUE; 917 } 918 919 /* If we're looking for an XInput controller, match it against any other Xbox controller */ 920 if (product_id == USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER) { 921 SDL_GameControllerType type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol); 922 if (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) { 923 return SDL_TRUE; 924 } 925 } 926 } 927 return SDL_FALSE; 928 } 929 930 SDL_bool 931 HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) 932 { 933 SDL_HIDAPI_Device *device; 934 SDL_bool supported = SDL_FALSE; 935 SDL_bool result = SDL_FALSE; 936 937 /* Make sure we're initialized, as this could be called from other drivers during startup */ 938 if (HIDAPI_JoystickInit() < 0) { 939 return SDL_FALSE; 940 } 941 942 /* Only update the device list for devices we know might be supported. 943 If we did this for every device, it would hit the USB driver too hard and potentially 944 lock up the system. This won't catch devices that we support but can only detect using 945 USB interface details, like Xbox controllers, but hopefully the device list update is 946 responsive enough to catch those. 947 */ 948 supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name); 949 #if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE) 950 if (!supported && 951 (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) { 952 supported = SDL_TRUE; 953 } 954 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE */ 955 if (supported) { 956 if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) { 957 HIDAPI_UpdateDeviceList(); 958 SDL_AtomicUnlock(&SDL_HIDAPI_spinlock); 959 } 960 } 961 962 /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID, 963 or a different name than we have it listed here, etc, but if we support the device 964 and we have something similar in our device list, mark it as present. 965 */ 966 SDL_LockJoysticks(); 967 device = SDL_HIDAPI_devices; 968 while (device) { 969 if (device->driver && 970 HIDAPI_IsEquivalentToDevice(vendor_id, product_id, device)) { 971 result = SDL_TRUE; 972 } 973 device = device->next; 974 } 975 SDL_UnlockJoysticks(); 976 977 #ifdef DEBUG_HIDAPI 978 SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x\n", result ? "true" : "false", vendor_id, product_id); 979 #endif 980 return result; 981 } 982 983 static void 984 HIDAPI_JoystickDetect(void) 985 { 986 if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) { 987 HIDAPI_UpdateDiscovery(); 988 if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) { 989 /* FIXME: We probably need to schedule an update in a few seconds as well */ 990 HIDAPI_UpdateDeviceList(); 991 SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE; 992 } 993 SDL_AtomicUnlock(&SDL_HIDAPI_spinlock); 994 } 995 } 996 997 void 998 HIDAPI_UpdateDevices(void) 999 { 1000 SDL_HIDAPI_Device *device; 1001 1002 /* Update the devices, which may change connected joysticks and send events */ 1003 1004 /* Prepare the existing device list */ 1005 if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) { 1006 device = SDL_HIDAPI_devices; 1007 while (device) { 1008 if (device->driver) { 1009 if (SDL_TryLockMutex(device->dev_lock) == 0) { 1010 device->updating = SDL_TRUE; 1011 device->driver->UpdateDevice(device); 1012 device->updating = SDL_FALSE; 1013 SDL_UnlockMutex(device->dev_lock); 1014 } 1015 } 1016 device = device->next; 1017 } 1018 SDL_AtomicUnlock(&SDL_HIDAPI_spinlock); 1019 } 1020 } 1021 1022 static const char * 1023 HIDAPI_JoystickGetDeviceName(int device_index) 1024 { 1025 SDL_HIDAPI_Device *device; 1026 const char *name = NULL; 1027 1028 device = HIDAPI_GetDeviceByIndex(device_index, NULL); 1029 if (device) { 1030 /* FIXME: The device could be freed after this name is returned... */ 1031 name = device->name; 1032 } 1033 1034 return name; 1035 } 1036 1037 static int 1038 HIDAPI_JoystickGetDevicePlayerIndex(int device_index) 1039 { 1040 SDL_HIDAPI_Device *device; 1041 SDL_JoystickID instance_id; 1042 int player_index = -1; 1043 1044 device = HIDAPI_GetDeviceByIndex(device_index, &instance_id); 1045 if (device) { 1046 player_index = device->driver->GetDevicePlayerIndex(device, instance_id); 1047 } 1048 1049 return player_index; 1050 } 1051 1052 static void 1053 HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index) 1054 { 1055 SDL_HIDAPI_Device *device; 1056 SDL_JoystickID instance_id; 1057 1058 device = HIDAPI_GetDeviceByIndex(device_index, &instance_id); 1059 if (device) { 1060 device->driver->SetDevicePlayerIndex(device, instance_id, player_index); 1061 } 1062 } 1063 1064 static SDL_JoystickGUID 1065 HIDAPI_JoystickGetDeviceGUID(int device_index) 1066 { 1067 SDL_HIDAPI_Device *device; 1068 SDL_JoystickGUID guid; 1069 1070 device = HIDAPI_GetDeviceByIndex(device_index, NULL); 1071 if (device) { 1072 SDL_memcpy(&guid, &device->guid, sizeof(guid)); 1073 } else { 1074 SDL_zero(guid); 1075 } 1076 1077 return guid; 1078 } 1079 1080 static SDL_JoystickID 1081 HIDAPI_JoystickGetDeviceInstanceID(int device_index) 1082 { 1083 SDL_JoystickID joystickID = -1; 1084 HIDAPI_GetDeviceByIndex(device_index, &joystickID); 1085 return joystickID; 1086 } 1087 1088 static int 1089 HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index) 1090 { 1091 SDL_JoystickID joystickID; 1092 SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID); 1093 struct joystick_hwdata *hwdata; 1094 1095 hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata)); 1096 if (!hwdata) { 1097 return SDL_OutOfMemory(); 1098 } 1099 hwdata->device = device; 1100 1101 if (!device->driver->OpenJoystick(device, joystick)) { 1102 SDL_free(hwdata); 1103 return -1; 1104 } 1105 1106 if (!joystick->serial && device->serial) { 1107 joystick->serial = SDL_strdup(device->serial); 1108 } 1109 1110 joystick->hwdata = hwdata; 1111 return 0; 1112 } 1113 1114 static int 1115 HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 1116 { 1117 int result; 1118 1119 if (joystick->hwdata) { 1120 SDL_HIDAPI_Device *device = joystick->hwdata->device; 1121 1122 result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble); 1123 } else { 1124 SDL_SetError("Rumble failed, device disconnected"); 1125 result = -1; 1126 } 1127 1128 return result; 1129 } 1130 1131 static int 1132 HIDAPI_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble) 1133 { 1134 int result; 1135 1136 if (joystick->hwdata) { 1137 SDL_HIDAPI_Device *device = joystick->hwdata->device; 1138 1139 result = device->driver->RumbleJoystickTriggers(device, joystick, left_rumble, right_rumble); 1140 } else { 1141 SDL_SetError("Rumble failed, device disconnected"); 1142 result = -1; 1143 } 1144 1145 return result; 1146 } 1147 1148 static SDL_bool 1149 HIDAPI_JoystickHasLED(SDL_Joystick * joystick) 1150 { 1151 SDL_bool result = SDL_FALSE; 1152 1153 if (joystick->hwdata) { 1154 SDL_HIDAPI_Device *device = joystick->hwdata->device; 1155 1156 result = device->driver->HasJoystickLED(device, joystick); 1157 } 1158 1159 return result; 1160 } 1161 1162 static int 1163 HIDAPI_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) 1164 { 1165 int result; 1166 1167 if (joystick->hwdata) { 1168 SDL_HIDAPI_Device *device = joystick->hwdata->device; 1169 1170 result = device->driver->SetJoystickLED(device, joystick, red, green, blue); 1171 } else { 1172 SDL_SetError("SetLED failed, device disconnected"); 1173 result = -1; 1174 } 1175 1176 return result; 1177 } 1178 1179 static int 1180 HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick * joystick, SDL_bool enabled) 1181 { 1182 int result; 1183 1184 if (joystick->hwdata) { 1185 SDL_HIDAPI_Device *device = joystick->hwdata->device; 1186 1187 result = device->driver->SetJoystickSensorsEnabled(device, joystick, enabled); 1188 } else { 1189 SDL_SetError("SetSensorsEnabled failed, device disconnected"); 1190 result = -1; 1191 } 1192 1193 return result; 1194 } 1195 1196 static void 1197 HIDAPI_JoystickUpdate(SDL_Joystick * joystick) 1198 { 1199 /* This is handled in SDL_HIDAPI_UpdateDevices() */ 1200 } 1201 1202 static void 1203 HIDAPI_JoystickClose(SDL_Joystick * joystick) 1204 { 1205 if (joystick->hwdata) { 1206 SDL_HIDAPI_Device *device = joystick->hwdata->device; 1207 int i; 1208 1209 /* Wait up to 30 ms for pending rumble to complete */ 1210 if (device->updating) { 1211 /* Unlock the device so rumble can complete */ 1212 SDL_UnlockMutex(device->dev_lock); 1213 } 1214 for (i = 0; i < 3; ++i) { 1215 if (SDL_AtomicGet(&device->rumble_pending) > 0) { 1216 SDL_Delay(10); 1217 } 1218 } 1219 if (device->updating) { 1220 /* Relock the device */ 1221 SDL_LockMutex(device->dev_lock); 1222 } 1223 1224 device->driver->CloseJoystick(device, joystick); 1225 1226 SDL_free(joystick->hwdata); 1227 joystick->hwdata = NULL; 1228 } 1229 } 1230 1231 static void 1232 HIDAPI_JoystickQuit(void) 1233 { 1234 int i; 1235 1236 shutting_down = SDL_TRUE; 1237 1238 HIDAPI_ShutdownDiscovery(); 1239 1240 SDL_HIDAPI_QuitRumble(); 1241 1242 while (SDL_HIDAPI_devices) { 1243 HIDAPI_DelDevice(SDL_HIDAPI_devices); 1244 } 1245 1246 /* Make sure the drivers cleaned up properly */ 1247 SDL_assert(SDL_HIDAPI_numjoysticks == 0); 1248 1249 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { 1250 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; 1251 SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL); 1252 } 1253 SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI, 1254 SDL_HIDAPIDriverHintChanged, NULL); 1255 1256 hid_exit(); 1257 1258 shutting_down = SDL_FALSE; 1259 initialized = SDL_FALSE; 1260 } 1261 1262 static SDL_bool 1263 HIDAPI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) 1264 { 1265 return SDL_FALSE; 1266 } 1267 1268 SDL_JoystickDriver SDL_HIDAPI_JoystickDriver = 1269 { 1270 HIDAPI_JoystickInit, 1271 HIDAPI_JoystickGetCount, 1272 HIDAPI_JoystickDetect, 1273 HIDAPI_JoystickGetDeviceName, 1274 HIDAPI_JoystickGetDevicePlayerIndex, 1275 HIDAPI_JoystickSetDevicePlayerIndex, 1276 HIDAPI_JoystickGetDeviceGUID, 1277 HIDAPI_JoystickGetDeviceInstanceID, 1278 HIDAPI_JoystickOpen, 1279 HIDAPI_JoystickRumble, 1280 HIDAPI_JoystickRumbleTriggers, 1281 HIDAPI_JoystickHasLED, 1282 HIDAPI_JoystickSetLED, 1283 HIDAPI_JoystickSetSensorsEnabled, 1284 HIDAPI_JoystickUpdate, 1285 HIDAPI_JoystickClose, 1286 HIDAPI_JoystickQuit, 1287 HIDAPI_JoystickGetGamepadMapping 1288 }; 1289 1290 #endif /* SDL_JOYSTICK_HIDAPI */ 1291 1292 /* vi: set ts=4 sw=4 expandtab: */