Atlas - SDL_hidapi.c
Home / ext / SDL / src / hidapi Lines: 1 | Size: 55936 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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 22/* Original hybrid wrapper for Linux by Valve Software. Their original notes: 23 * 24 * The libusb version doesn't support Bluetooth, but not all Linux 25 * distributions allow access to /dev/hidraw* 26 * 27 * This merges the two, at a small performance cost, until distributions 28 * have granted access to /dev/hidraw* 29 */ 30 31#include "SDL_internal.h" 32 33#include "SDL_hidapi_c.h" 34#include "../joystick/usb_ids.h" 35#include "../SDL_hints_c.h" 36 37// Initial type declarations 38#define HID_API_NO_EXPORT_DEFINE // do not export hidapi procedures 39#include "hidapi/hidapi.h" 40 41#ifndef SDL_HIDAPI_DISABLED 42 43#ifdef SDL_LIBUSB_DYNAMIC 44SDL_ELF_NOTE_DLOPEN( 45 "hidabi-libusb", 46 "Support for joysticks through libusb", 47 SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, 48 SDL_LIBUSB_DYNAMIC 49) 50#endif 51 52#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 53#include "../core/windows/SDL_windows.h" 54#endif 55 56#ifdef SDL_PLATFORM_MACOS 57#include <CoreFoundation/CoreFoundation.h> 58#include <mach/mach.h> 59#include <IOKit/IOKitLib.h> 60#include <IOKit/hid/IOHIDDevice.h> 61#include <IOKit/usb/USBSpec.h> 62#include <AvailabilityMacros.h> 63// Things named "Master" were renamed to "Main" in macOS 12.0's SDK. 64#if MAC_OS_X_VERSION_MIN_REQUIRED < 120000 65#define kIOMainPortDefault kIOMasterPortDefault 66#endif 67#endif 68 69#include "../core/linux/SDL_udev.h" 70#ifdef SDL_USE_LIBUDEV 71#include <poll.h> 72#endif 73 74#ifdef HAVE_INOTIFY 75#include <string.h> // strerror 76#include <errno.h> // errno 77#include <fcntl.h> 78#include <limits.h> // For the definition of NAME_MAX 79#include <sys/inotify.h> 80#endif 81 82#if defined(SDL_USE_LIBUDEV) || defined(HAVE_INOTIFY) 83#include <unistd.h> 84#endif 85 86#ifdef SDL_USE_LIBUDEV 87typedef enum 88{ 89 ENUMERATION_UNSET, 90 ENUMERATION_LIBUDEV, 91 ENUMERATION_FALLBACK 92} LinuxEnumerationMethod; 93 94static LinuxEnumerationMethod linux_enumeration_method = ENUMERATION_UNSET; 95#endif 96 97#ifdef HAVE_INOTIFY 98static int inotify_fd = -1; 99#endif 100 101#ifdef SDL_USE_LIBUDEV 102static const SDL_UDEV_Symbols *usyms = NULL; 103#endif 104 105static struct 106{ 107 bool m_bInitialized; 108 Uint32 m_unDeviceChangeCounter; 109 bool m_bCanGetNotifications; 110 Uint64 m_unLastDetect; 111 112#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 113 SDL_ThreadID m_nThreadID; 114 WNDCLASSEXA m_wndClass; 115 HWND m_hwndMsg; 116 HDEVNOTIFY m_hNotify; 117 double m_flLastWin32MessageCheck; 118#endif 119 120#ifdef SDL_PLATFORM_MACOS 121 IONotificationPortRef m_notificationPort; 122 mach_port_t m_notificationMach; 123#endif 124 125#ifdef SDL_USE_LIBUDEV 126 struct udev *m_pUdev; 127 struct udev_monitor *m_pUdevMonitor; 128 int m_nUdevFd; 129#endif 130} SDL_HIDAPI_discovery; 131 132#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 133struct _DEV_BROADCAST_HDR 134{ 135 DWORD dbch_size; 136 DWORD dbch_devicetype; 137 DWORD dbch_reserved; 138}; 139 140typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A 141{ 142 DWORD dbcc_size; 143 DWORD dbcc_devicetype; 144 DWORD dbcc_reserved; 145 GUID dbcc_classguid; 146 char dbcc_name[1]; 147} DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A; 148 149typedef struct _DEV_BROADCAST_HDR DEV_BROADCAST_HDR; 150#define DBT_DEVICEARRIVAL 0x8000 // system detected a new device 151#define DBT_DEVICEREMOVECOMPLETE 0x8004 // device was removed from the system 152#define DBT_DEVTYP_DEVICEINTERFACE 0x00000005 // device interface class 153#define DBT_DEVNODES_CHANGED 0x0007 154#define DBT_CONFIGCHANGED 0x0018 155#define DBT_DEVICETYPESPECIFIC 0x8005 // type specific event 156#define DBT_DEVINSTSTARTED 0x8008 // device installed and started 157 158#include <initguid.h> 159DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED); 160 161static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 162{ 163 switch (message) { 164 case WM_DEVICECHANGE: 165 switch (wParam) { 166 case DBT_DEVICEARRIVAL: 167 case DBT_DEVICEREMOVECOMPLETE: 168 if (((DEV_BROADCAST_HDR *)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { 169 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter; 170 } 171 break; 172 } 173 return TRUE; 174 } 175 176 return DefWindowProc(hwnd, message, wParam, lParam); 177} 178#endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 179 180#ifdef SDL_PLATFORM_MACOS 181static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator) 182{ 183 // Must drain the iterator, or we won't receive new notifications 184 io_object_t entry; 185 while ((entry = IOIteratorNext(portIterator)) != 0) { 186 IOObjectRelease(entry); 187 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter; 188 } 189} 190#endif // SDL_PLATFORM_MACOS 191 192#ifdef HAVE_INOTIFY 193#ifdef HAVE_INOTIFY_INIT1 194static int SDL_inotify_init1(void) 195{ 196 return inotify_init1(IN_NONBLOCK | IN_CLOEXEC); 197} 198#else 199static int SDL_inotify_init1(void) 200{ 201 int fd = inotify_init(); 202 if (fd < 0) { 203 return -1; 204 } 205 fcntl(fd, F_SETFL, O_NONBLOCK); 206 fcntl(fd, F_SETFD, FD_CLOEXEC); 207 return fd; 208} 209#endif 210 211static int StrHasPrefix(const char *string, const char *prefix) 212{ 213 return SDL_strncmp(string, prefix, SDL_strlen(prefix)) == 0; 214} 215 216static int StrIsInteger(const char *string) 217{ 218 const char *p; 219 220 if (*string == '\0') { 221 return 0; 222 } 223 224 for (p = string; *p != '\0'; p++) { 225 if (*p < '0' || *p > '9') { 226 return 0; 227 } 228 } 229 230 return 1; 231} 232#endif // HAVE_INOTIFY 233 234static void HIDAPI_InitializeDiscovery(void) 235{ 236 SDL_HIDAPI_discovery.m_bInitialized = true; 237 SDL_HIDAPI_discovery.m_unDeviceChangeCounter = 1; 238 SDL_HIDAPI_discovery.m_bCanGetNotifications = false; 239 SDL_HIDAPI_discovery.m_unLastDetect = 0; 240 241#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 242 SDL_HIDAPI_discovery.m_nThreadID = SDL_GetCurrentThreadID(); 243 244 SDL_zero(SDL_HIDAPI_discovery.m_wndClass); 245 SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL); 246 SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION"; 247 SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc; // This function is called by windows 248 SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX); 249 250 RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass); 251 SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); 252 253 { 254 DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast; 255 256 SDL_zero(devBroadcast); 257 devBroadcast.dbcc_size = sizeof(devBroadcast); 258 devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 259 devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; 260 261 /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored, 262 * but that seems to be necessary to get a notice after each individual usb input device actually 263 * installs, rather than just as the composite device is seen. 264 */ 265 SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification(SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); 266 SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_hNotify != 0); 267 } 268#endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 269 270#ifdef SDL_PLATFORM_MACOS 271 SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMainPortDefault); 272 if (SDL_HIDAPI_discovery.m_notificationPort) { 273 { 274 io_iterator_t portIterator = 0; 275 io_object_t entry; 276 IOReturn result = IOServiceAddMatchingNotification( 277 SDL_HIDAPI_discovery.m_notificationPort, 278 kIOFirstMatchNotification, 279 IOServiceMatching(kIOHIDDeviceKey), 280 CallbackIOServiceFunc, NULL, &portIterator); 281 282 if (result == 0) { 283 // Must drain the existing iterator, or we won't receive new notifications 284 while ((entry = IOIteratorNext(portIterator)) != 0) { 285 IOObjectRelease(entry); 286 } 287 } else { 288 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort); 289 SDL_HIDAPI_discovery.m_notificationPort = nil; 290 } 291 } 292 { 293 io_iterator_t portIterator = 0; 294 io_object_t entry; 295 IOReturn result = IOServiceAddMatchingNotification( 296 SDL_HIDAPI_discovery.m_notificationPort, 297 kIOTerminatedNotification, 298 IOServiceMatching(kIOHIDDeviceKey), 299 CallbackIOServiceFunc, NULL, &portIterator); 300 301 if (result == 0) { 302 // Must drain the existing iterator, or we won't receive new notifications 303 while ((entry = IOIteratorNext(portIterator)) != 0) { 304 IOObjectRelease(entry); 305 } 306 } else { 307 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort); 308 SDL_HIDAPI_discovery.m_notificationPort = nil; 309 } 310 } 311 } 312 313 SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL; 314 if (SDL_HIDAPI_discovery.m_notificationPort) { 315 SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort); 316 } 317 318 SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL); 319 320#endif // SDL_PLATFORM_MACOS 321 322#ifdef SDL_USE_LIBUDEV 323 if (linux_enumeration_method == ENUMERATION_LIBUDEV) { 324 SDL_HIDAPI_discovery.m_pUdev = NULL; 325 SDL_HIDAPI_discovery.m_pUdevMonitor = NULL; 326 SDL_HIDAPI_discovery.m_nUdevFd = -1; 327 328 usyms = SDL_UDEV_GetUdevSyms(); 329 if (usyms != NULL) { 330 SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new(); 331 if (SDL_HIDAPI_discovery.m_pUdev != NULL) { 332 SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev"); 333 if (SDL_HIDAPI_discovery.m_pUdevMonitor != NULL) { 334 usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor); 335 SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor); 336 SDL_HIDAPI_discovery.m_bCanGetNotifications = true; 337 } 338 } 339 } 340 } else 341#endif // SDL_USE_LIBUDEV 342 { 343#ifdef HAVE_INOTIFY 344 inotify_fd = SDL_inotify_init1(); 345 346 if (inotify_fd < 0) { 347 SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, 348 "Unable to initialize inotify, falling back to polling: %s", 349 strerror(errno)); 350 return; 351 } 352 353 /* We need to watch for attribute changes in addition to 354 * creation, because when a device is first created, it has 355 * permissions that we can't read. When udev chmods it to 356 * something that we maybe *can* read, we'll get an 357 * IN_ATTRIB event to tell us. */ 358 if (inotify_add_watch(inotify_fd, "/dev", 359 IN_CREATE | IN_DELETE | IN_MOVE | IN_ATTRIB) < 0) { 360 close(inotify_fd); 361 inotify_fd = -1; 362 SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, 363 "Unable to add inotify watch, falling back to polling: %s", 364 strerror(errno)); 365 return; 366 } 367 368 SDL_HIDAPI_discovery.m_bCanGetNotifications = true; 369#endif // HAVE_INOTIFY 370 } 371} 372 373static void HIDAPI_UpdateDiscovery(void) 374{ 375 if (!SDL_HIDAPI_discovery.m_bInitialized) { 376 HIDAPI_InitializeDiscovery(); 377 } 378 379 if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) { 380 const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000; // Update every 3 seconds 381 Uint64 now = SDL_GetTicks(); 382 if (!SDL_HIDAPI_discovery.m_unLastDetect || now >= (SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) { 383 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter; 384 SDL_HIDAPI_discovery.m_unLastDetect = now; 385 } 386 return; 387 } 388 389#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 390#if 0 // just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan. 391 // We'll only get messages on the same thread that created the window 392 if (SDL_GetCurrentThreadID() == SDL_HIDAPI_discovery.m_nThreadID) { 393 MSG msg; 394 while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) { 395 if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) { 396 TranslateMessage(&msg); 397 DispatchMessage(&msg); 398 } 399 } 400 } 401#endif 402#endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 403 404#ifdef SDL_PLATFORM_MACOS 405 if (SDL_HIDAPI_discovery.m_notificationPort) { 406 struct 407 { 408 mach_msg_header_t hdr; 409 char payload[4096]; 410 } msg; 411 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) { 412 IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort); 413 } 414 } 415#endif 416 417#ifdef SDL_USE_LIBUDEV 418 if (linux_enumeration_method == ENUMERATION_LIBUDEV) { 419 if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) { 420 /* Drain all notification events. 421 * We don't expect a lot of device notifications so just 422 * do a new discovery on any kind or number of notifications. 423 * This could be made more restrictive if necessary. 424 */ 425 for (;;) { 426 struct pollfd PollUdev; 427 struct udev_device *pUdevDevice; 428 429 PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd; 430 PollUdev.events = POLLIN; 431 if (poll(&PollUdev, 1, 0) != 1) { 432 break; 433 } 434 435 pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor); 436 if (pUdevDevice) { 437 const char *action = NULL; 438 action = usyms->udev_device_get_action(pUdevDevice); 439 if (action == NULL || SDL_strcmp(action, "add") == 0 || SDL_strcmp(action, "remove") == 0) { 440 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter; 441 } 442 usyms->udev_device_unref(pUdevDevice); 443 } 444 } 445 } 446 } else 447#endif // SDL_USE_LIBUDEV 448 { 449#ifdef HAVE_INOTIFY 450 if (inotify_fd >= 0) { 451 union 452 { 453 struct inotify_event event; 454 char storage[4096]; 455 char enough_for_inotify[sizeof(struct inotify_event) + NAME_MAX + 1]; 456 } buf; 457 ssize_t bytes; 458 size_t remain = 0; 459 size_t len; 460 461 bytes = read(inotify_fd, &buf, sizeof(buf)); 462 463 if (bytes > 0) { 464 remain = (size_t)bytes; 465 } 466 467 while (remain > 0) { 468 if (buf.event.len > 0) { 469 if (StrHasPrefix(buf.event.name, "hidraw") && 470 StrIsInteger(buf.event.name + SDL_strlen("hidraw"))) { 471 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter; 472 /* We found an hidraw change. We still continue to 473 * drain the inotify fd to avoid leaving old 474 * notifications in the queue. */ 475 } 476 } 477 478 len = sizeof(struct inotify_event) + buf.event.len; 479 remain -= len; 480 481 if (remain != 0) { 482 SDL_memmove(&buf.storage[0], &buf.storage[len], remain); 483 } 484 } 485 } 486#endif // HAVE_INOTIFY 487 } 488} 489 490static void HIDAPI_ShutdownDiscovery(void) 491{ 492 if (!SDL_HIDAPI_discovery.m_bInitialized) { 493 return; 494 } 495 496#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 497 if (SDL_HIDAPI_discovery.m_hNotify) { 498 UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify); 499 } 500 501 if (SDL_HIDAPI_discovery.m_hwndMsg) { 502 DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg); 503 } 504 505 UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance); 506#endif 507 508#ifdef SDL_PLATFORM_MACOS 509 if (SDL_HIDAPI_discovery.m_notificationPort) { 510 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort); 511 } 512#endif 513 514#ifdef SDL_USE_LIBUDEV 515 if (linux_enumeration_method == ENUMERATION_LIBUDEV) { 516 if (usyms) { 517 if (SDL_HIDAPI_discovery.m_pUdevMonitor) { 518 usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor); 519 } 520 if (SDL_HIDAPI_discovery.m_pUdev) { 521 usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev); 522 } 523 SDL_UDEV_ReleaseUdevSyms(); 524 usyms = NULL; 525 } 526 } else 527#endif // SDL_USE_LIBUDEV 528 { 529#ifdef HAVE_INOTIFY 530 if (inotify_fd >= 0) { 531 close(inotify_fd); 532 inotify_fd = -1; 533 } 534#endif 535 } 536 537 SDL_HIDAPI_discovery.m_bInitialized = false; 538} 539 540// Platform HIDAPI Implementation 541 542#define HIDAPI_USING_SDL_RUNTIME 543#define HIDAPI_IGNORE_DEVICE(BUS, VID, PID, USAGE_PAGE, USAGE) \ 544 SDL_HIDAPI_ShouldIgnoreDevice(BUS, VID, PID, USAGE_PAGE, USAGE) 545 546struct PLATFORM_hid_device_; 547typedef struct PLATFORM_hid_device_ PLATFORM_hid_device; 548 549#define api_version PLATFORM_api_version 550#define create_device_info_for_device PLATFORM_create_device_info_for_device 551#define free_hid_device PLATFORM_free_hid_device 552#define hid_close PLATFORM_hid_close 553#define hid_device PLATFORM_hid_device 554#define hid_device_ PLATFORM_hid_device_ 555#define hid_enumerate PLATFORM_hid_enumerate 556#define hid_error PLATFORM_hid_error 557#define hid_exit PLATFORM_hid_exit 558#define hid_free_enumeration PLATFORM_hid_free_enumeration 559#define hid_get_device_info PLATFORM_hid_get_device_info 560#define hid_get_feature_report PLATFORM_hid_get_feature_report 561#define hid_get_indexed_string PLATFORM_hid_get_indexed_string 562#define hid_get_input_report PLATFORM_hid_get_input_report 563#define hid_get_manufacturer_string PLATFORM_hid_get_manufacturer_string 564#define hid_get_product_string PLATFORM_hid_get_product_string 565#define hid_get_report_descriptor PLATFORM_hid_get_report_descriptor 566#define hid_get_serial_number_string PLATFORM_hid_get_serial_number_string 567#define hid_init PLATFORM_hid_init 568#define hid_open_path PLATFORM_hid_open_path 569#define hid_open PLATFORM_hid_open 570#define hid_read PLATFORM_hid_read 571#define hid_read_timeout PLATFORM_hid_read_timeout 572#define hid_send_feature_report PLATFORM_hid_send_feature_report 573#define hid_set_nonblocking PLATFORM_hid_set_nonblocking 574#define hid_version PLATFORM_hid_version 575#define hid_version_str PLATFORM_hid_version_str 576#define hid_write PLATFORM_hid_write 577#define input_report PLATFORM_input_report 578#define make_path PLATFORM_make_path 579#define new_hid_device PLATFORM_new_hid_device 580#define read_thread PLATFORM_read_thread 581#define return_data PLATFORM_return_data 582 583#ifdef SDL_PLATFORM_LINUX 584#include "SDL_hidapi_linux.h" 585#elif defined(SDL_PLATFORM_NETBSD) 586#include "SDL_hidapi_netbsd.h" 587#elif defined(SDL_PLATFORM_MACOS) 588#include "SDL_hidapi_mac.h" 589#elif defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 590#include "SDL_hidapi_windows.h" 591#elif defined(SDL_PLATFORM_ANDROID) 592#include "SDL_hidapi_android.h" 593#elif defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS) 594#include "SDL_hidapi_ios.h" 595#endif 596 597#undef api_version 598#undef create_device_info_for_device 599#undef free_hid_device 600#undef hid_close 601#undef hid_device 602#undef hid_device_ 603#undef hid_enumerate 604#undef hid_error 605#undef hid_exit 606#undef hid_free_enumeration 607#undef hid_get_device_info 608#undef hid_get_feature_report 609#undef hid_get_indexed_string 610#undef hid_get_input_report 611#undef hid_get_manufacturer_string 612#undef hid_get_product_string 613#undef hid_get_report_descriptor 614#undef hid_get_serial_number_string 615#undef hid_init 616#undef hid_open 617#undef hid_open_path 618#undef hid_read 619#undef hid_read_timeout 620#undef hid_send_feature_report 621#undef hid_set_nonblocking 622#undef hid_version 623#undef hid_version_str 624#undef hid_write 625#undef input_report 626#undef make_path 627#undef new_hid_device 628#undef read_thread 629#undef return_data 630 631#ifdef SDL_JOYSTICK_HIDAPI_STEAMXBOX 632#define HAVE_DRIVER_BACKEND 1 633#endif 634 635#ifdef HAVE_DRIVER_BACKEND 636 637// DRIVER HIDAPI Implementation 638 639struct DRIVER_hid_device_; 640typedef struct DRIVER_hid_device_ DRIVER_hid_device; 641 642#define hid_close DRIVER_hid_close 643#define hid_device DRIVER_hid_device 644#define hid_device_ DRIVER_hid_device_ 645#define hid_enumerate DRIVER_hid_enumerate 646#define hid_error DRIVER_hid_error 647#define hid_exit DRIVER_hid_exit 648#define hid_free_enumeration DRIVER_hid_free_enumeration 649#define hid_get_device_info DRIVER_hid_get_device_info 650#define hid_get_feature_report DRIVER_hid_get_feature_report 651#define hid_get_indexed_string DRIVER_hid_get_indexed_string 652#define hid_get_input_report DRIVER_hid_get_input_report 653#define hid_get_manufacturer_string DRIVER_hid_get_manufacturer_string 654#define hid_get_product_string DRIVER_hid_get_product_string 655#define hid_get_report_descriptor DRIVER_hid_get_report_descriptor 656#define hid_get_serial_number_string DRIVER_hid_get_serial_number_string 657#define hid_init DRIVER_hid_init 658#define hid_open DRIVER_hid_open 659#define hid_open_path DRIVER_hid_open_path 660#define hid_read DRIVER_hid_read 661#define hid_read_timeout DRIVER_hid_read_timeout 662#define hid_send_feature_report DRIVER_hid_send_feature_report 663#define hid_set_nonblocking DRIVER_hid_set_nonblocking 664#define hid_write DRIVER_hid_write 665 666#ifdef SDL_JOYSTICK_HIDAPI_STEAMXBOX 667#include "SDL_hidapi_steamxbox.h" 668#else 669#error Need a driver hid.c for this platform! 670#endif 671 672#undef hid_close 673#undef hid_device 674#undef hid_device_ 675#undef hid_enumerate 676#undef hid_error 677#undef hid_exit 678#undef hid_free_enumeration 679#undef hid_get_device_info 680#undef hid_get_feature_report 681#undef hid_get_indexed_string 682#undef hid_get_input_report 683#undef hid_get_manufacturer_string 684#undef hid_get_product_string 685#undef hid_get_report_descriptor 686#undef hid_get_serial_number_string 687#undef hid_init 688#undef hid_open 689#undef hid_open_path 690#undef hid_read 691#undef hid_read_timeout 692#undef hid_send_feature_report 693#undef hid_set_nonblocking 694#undef hid_write 695 696#endif // HAVE_DRIVER_BACKEND 697 698#ifdef HAVE_LIBUSB 699// libusb HIDAPI Implementation 700 701#include "../misc/SDL_libusb.h" 702 703static SDL_LibUSBContext *libusb_ctx; 704 705#define libusb_init libusb_ctx->init 706#define libusb_exit libusb_ctx->exit 707#define libusb_get_device_list libusb_ctx->get_device_list 708#define libusb_free_device_list libusb_ctx->free_device_list 709#define libusb_get_device_descriptor libusb_ctx->get_device_descriptor 710#define libusb_get_active_config_descriptor libusb_ctx->get_active_config_descriptor 711#define libusb_get_config_descriptor libusb_ctx->get_config_descriptor 712#define libusb_free_config_descriptor libusb_ctx->free_config_descriptor 713#define libusb_get_bus_number libusb_ctx->get_bus_number 714#define libusb_get_port_numbers libusb_ctx->get_port_numbers 715#define libusb_get_device_address libusb_ctx->get_device_address 716#define libusb_open libusb_ctx->open 717#define libusb_close libusb_ctx->close 718#define libusb_get_device libusb_ctx->get_device 719#define libusb_claim_interface libusb_ctx->claim_interface 720#define libusb_release_interface libusb_ctx->release_interface 721#define libusb_kernel_driver_active libusb_ctx->kernel_driver_active 722#define libusb_detach_kernel_driver libusb_ctx->detach_kernel_driver 723#define libusb_attach_kernel_driver libusb_ctx->attach_kernel_driver 724#define libusb_set_interface_alt_setting libusb_ctx->set_interface_alt_setting 725#define libusb_alloc_transfer libusb_ctx->alloc_transfer 726#define libusb_submit_transfer libusb_ctx->submit_transfer 727#define libusb_cancel_transfer libusb_ctx->cancel_transfer 728#define libusb_free_transfer libusb_ctx->free_transfer 729#define libusb_control_transfer libusb_ctx->control_transfer 730#define libusb_interrupt_transfer libusb_ctx->interrupt_transfer 731#define libusb_bulk_transfer libusb_ctx->bulk_transfer 732#define libusb_handle_events libusb_ctx->handle_events 733#define libusb_handle_events_completed libusb_ctx->handle_events_completed 734#define libusb_error_name libusb_ctx->error_name 735 736struct LIBUSB_hid_device_; 737typedef struct LIBUSB_hid_device_ LIBUSB_hid_device; 738 739#define free_hid_device LIBUSB_free_hid_device 740#define hid_close LIBUSB_hid_close 741#define hid_device LIBUSB_hid_device 742#define hid_device_ LIBUSB_hid_device_ 743#define hid_enumerate LIBUSB_hid_enumerate 744#define hid_error LIBUSB_hid_error 745#define hid_exit LIBUSB_hid_exit 746#define hid_free_enumeration LIBUSB_hid_free_enumeration 747#define hid_get_device_info LIBUSB_hid_get_device_info 748#define hid_get_feature_report LIBUSB_hid_get_feature_report 749#define hid_get_indexed_string LIBUSB_hid_get_indexed_string 750#define hid_get_input_report LIBUSB_hid_get_input_report 751#define hid_get_manufacturer_string LIBUSB_hid_get_manufacturer_string 752#define hid_get_product_string LIBUSB_hid_get_product_string 753#define hid_get_report_descriptor LIBUSB_hid_get_report_descriptor 754#define hid_get_serial_number_string LIBUSB_hid_get_serial_number_string 755#define hid_init LIBUSB_hid_init 756#define hid_open LIBUSB_hid_open 757#define hid_open_path LIBUSB_hid_open_path 758#define hid_read LIBUSB_hid_read 759#define hid_read_timeout LIBUSB_hid_read_timeout 760#define hid_send_feature_report LIBUSB_hid_send_feature_report 761#define hid_set_nonblocking LIBUSB_hid_set_nonblocking 762#define hid_write LIBUSB_hid_write 763#define hid_version LIBUSB_hid_version 764#define hid_version_str LIBUSB_hid_version_str 765#define input_report LIBUSB_input_report 766#define make_path LIBUSB_make_path 767#define new_hid_device LIBUSB_new_hid_device 768#define read_thread LIBUSB_read_thread 769#define return_data LIBUSB_return_data 770 771#include "SDL_hidapi_libusb.h" 772 773#undef libusb_init 774#undef libusb_exit 775#undef libusb_get_device_list 776#undef libusb_free_device_list 777#undef libusb_get_device_descriptor 778#undef libusb_get_active_config_descriptor 779#undef libusb_get_config_descriptor 780#undef libusb_free_config_descriptor 781#undef libusb_get_bus_number 782#undef libusb_get_port_numbers 783#undef libusb_get_device_address 784#undef libusb_open 785#undef libusb_close 786#undef libusb_get_device 787#undef libusb_claim_interface 788#undef libusb_release_interface 789#undef libusb_kernel_driver_active 790#undef libusb_detach_kernel_driver 791#undef libusb_attach_kernel_driver 792#undef libusb_set_interface_alt_setting 793#undef libusb_alloc_transfer 794#undef libusb_submit_transfer 795#undef libusb_cancel_transfer 796#undef libusb_free_transfer 797#undef libusb_control_transfer 798#undef libusb_interrupt_transfer 799#undef libusb_bulk_transfer 800#undef libusb_handle_events 801#undef libusb_handle_events_completed 802#undef libusb_error_name 803 804#undef free_hid_device 805#undef hid_close 806#undef hid_device 807#undef hid_device_ 808#undef hid_enumerate 809#undef hid_error 810#undef hid_exit 811#undef hid_free_enumeration 812#undef hid_get_device_info 813#undef hid_get_feature_report 814#undef hid_get_indexed_string 815#undef hid_get_input_report 816#undef hid_get_manufacturer_string 817#undef hid_get_product_string 818#undef hid_get_report_descriptor 819#undef hid_get_serial_number_string 820#undef hid_init 821#undef hid_open 822#undef hid_open_path 823#undef hid_read 824#undef hid_read_timeout 825#undef hid_send_feature_report 826#undef hid_set_nonblocking 827#undef hid_write 828#undef input_report 829#undef make_path 830#undef new_hid_device 831#undef read_thread 832#undef return_data 833 834/* If the platform has any backend other than libusb, try to avoid using 835 * libusb as the main backend for devices, since it detaches drivers and 836 * therefore makes devices inaccessible to the rest of the OS. 837 * 838 * We do this by whitelisting devices we know to be accessible _exclusively_ 839 * via libusb; these are typically devices that look like HIDs but have a 840 * quirk that requires direct access to the hardware. 841 */ 842static const struct { 843 Uint16 vendor; 844 Uint16 product; 845} SDL_libusb_whitelist[] = { 846 { USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER }, 847 { USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER }, 848 { USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH2_JOYCON_LEFT }, 849 { USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH2_JOYCON_RIGHT }, 850 { USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH2_PRO }, 851}; 852 853static bool IsInWhitelist(Uint16 vendor, Uint16 product) 854{ 855 int i; 856 for (i = 0; i < SDL_arraysize(SDL_libusb_whitelist); i += 1) { 857 if (vendor == SDL_libusb_whitelist[i].vendor && 858 product == SDL_libusb_whitelist[i].product) { 859 return true; 860 } 861 } 862 return false; 863} 864 865#endif // HAVE_LIBUSB 866 867#endif // !SDL_HIDAPI_DISABLED 868 869#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) 870// We have another way to get HID devices, so use the whitelist to get devices where libusb is preferred 871#define SDL_HINT_HIDAPI_LIBUSB_WHITELIST_DEFAULT true 872#else 873// libusb is the only way to get HID devices, so don't use the whitelist, get them all 874#define SDL_HINT_HIDAPI_LIBUSB_WHITELIST_DEFAULT false 875#endif // HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND 876 877static bool use_libusb_whitelist = SDL_HINT_HIDAPI_LIBUSB_WHITELIST_DEFAULT; 878 879// Shared HIDAPI Implementation 880 881struct hidapi_backend 882{ 883 int (*hid_write)(void *device, const unsigned char *data, size_t length); 884 int (*hid_read_timeout)(void *device, unsigned char *data, size_t length, int milliseconds); 885 int (*hid_read)(void *device, unsigned char *data, size_t length); 886 int (*hid_set_nonblocking)(void *device, int nonblock); 887 int (*hid_send_feature_report)(void *device, const unsigned char *data, size_t length); 888 int (*hid_get_feature_report)(void *device, unsigned char *data, size_t length); 889 int (*hid_get_input_report)(void *device, unsigned char *data, size_t length); 890 void (*hid_close)(void *device); 891 int (*hid_get_manufacturer_string)(void *device, wchar_t *string, size_t maxlen); 892 int (*hid_get_product_string)(void *device, wchar_t *string, size_t maxlen); 893 int (*hid_get_serial_number_string)(void *device, wchar_t *string, size_t maxlen); 894 int (*hid_get_indexed_string)(void *device, int string_index, wchar_t *string, size_t maxlen); 895 struct hid_device_info *(*hid_get_device_info)(void *device); 896 int (*hid_get_report_descriptor)(void *device, unsigned char *buf, size_t buf_size); 897 const wchar_t *(*hid_error)(void *device); 898}; 899 900#ifdef HAVE_PLATFORM_BACKEND 901static const struct hidapi_backend PLATFORM_Backend = { 902 (void *)PLATFORM_hid_write, 903 (void *)PLATFORM_hid_read_timeout, 904 (void *)PLATFORM_hid_read, 905 (void *)PLATFORM_hid_set_nonblocking, 906 (void *)PLATFORM_hid_send_feature_report, 907 (void *)PLATFORM_hid_get_feature_report, 908 (void *)PLATFORM_hid_get_input_report, 909 (void *)PLATFORM_hid_close, 910 (void *)PLATFORM_hid_get_manufacturer_string, 911 (void *)PLATFORM_hid_get_product_string, 912 (void *)PLATFORM_hid_get_serial_number_string, 913 (void *)PLATFORM_hid_get_indexed_string, 914 (void *)PLATFORM_hid_get_device_info, 915 (void *)PLATFORM_hid_get_report_descriptor, 916 (void *)PLATFORM_hid_error 917}; 918#endif // HAVE_PLATFORM_BACKEND 919 920#ifdef HAVE_DRIVER_BACKEND 921static const struct hidapi_backend DRIVER_Backend = { 922 (void *)DRIVER_hid_write, 923 (void *)DRIVER_hid_read_timeout, 924 (void *)DRIVER_hid_read, 925 (void *)DRIVER_hid_set_nonblocking, 926 (void *)DRIVER_hid_send_feature_report, 927 (void *)DRIVER_hid_get_feature_report, 928 (void *)DRIVER_hid_get_input_report, 929 (void *)DRIVER_hid_close, 930 (void *)DRIVER_hid_get_manufacturer_string, 931 (void *)DRIVER_hid_get_product_string, 932 (void *)DRIVER_hid_get_serial_number_string, 933 (void *)DRIVER_hid_get_indexed_string, 934 (void *)DRIVER_hid_get_device_info, 935 (void *)DRIVER_hid_get_report_descriptor, 936 (void *)DRIVER_hid_error 937}; 938#endif // HAVE_DRIVER_BACKEND 939 940#ifdef HAVE_LIBUSB 941static const struct hidapi_backend LIBUSB_Backend = { 942 (void *)LIBUSB_hid_write, 943 (void *)LIBUSB_hid_read_timeout, 944 (void *)LIBUSB_hid_read, 945 (void *)LIBUSB_hid_set_nonblocking, 946 (void *)LIBUSB_hid_send_feature_report, 947 (void *)LIBUSB_hid_get_feature_report, 948 (void *)LIBUSB_hid_get_input_report, 949 (void *)LIBUSB_hid_close, 950 (void *)LIBUSB_hid_get_manufacturer_string, 951 (void *)LIBUSB_hid_get_product_string, 952 (void *)LIBUSB_hid_get_serial_number_string, 953 (void *)LIBUSB_hid_get_indexed_string, 954 (void *)LIBUSB_hid_get_device_info, 955 (void *)LIBUSB_hid_get_report_descriptor, 956 (void *)LIBUSB_hid_error 957}; 958#endif // HAVE_LIBUSB 959 960struct SDL_hid_device 961{ 962 void *device; 963 const struct hidapi_backend *backend; 964 SDL_hid_device_info info; 965 SDL_PropertiesID props; 966}; 967 968#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB) 969 970static SDL_hid_device *CreateHIDDeviceWrapper(void *device, const struct hidapi_backend *backend) 971{ 972 SDL_hid_device *wrapper = (SDL_hid_device *)SDL_calloc(1, sizeof(*wrapper)); 973 SDL_SetObjectValid(wrapper, SDL_OBJECT_TYPE_HIDAPI_DEVICE, true); 974 wrapper->device = device; 975 wrapper->backend = backend; 976 SDL_zero(wrapper->info); 977 return wrapper; 978} 979 980#endif // HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB 981 982static void DeleteHIDDeviceWrapper(SDL_hid_device *wrapper) 983{ 984 SDL_SetObjectValid(wrapper, SDL_OBJECT_TYPE_HIDAPI_DEVICE, false); 985 SDL_free(wrapper->info.path); 986 SDL_free(wrapper->info.serial_number); 987 SDL_free(wrapper->info.manufacturer_string); 988 SDL_free(wrapper->info.product_string); 989 SDL_free(wrapper); 990} 991 992#define CHECK_DEVICE_MAGIC(device, result) \ 993 if (!SDL_ObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_DEVICE)) { \ 994 SDL_SetError("Invalid device"); \ 995 return result; \ 996 } 997 998#define COPY_IF_EXISTS(var) \ 999 if (pSrc->var != NULL) { \ 1000 pDst->var = SDL_strdup(pSrc->var); \ 1001 } else { \ 1002 pDst->var = NULL; \ 1003 } 1004#define WCOPY_IF_EXISTS(var) \ 1005 if (pSrc->var != NULL) { \ 1006 pDst->var = SDL_wcsdup(pSrc->var); \ 1007 } else { \ 1008 pDst->var = NULL; \ 1009 } 1010 1011static void CopyHIDDeviceInfo(struct hid_device_info *pSrc, struct SDL_hid_device_info *pDst) 1012{ 1013 COPY_IF_EXISTS(path) 1014 pDst->vendor_id = pSrc->vendor_id; 1015 pDst->product_id = pSrc->product_id; 1016 WCOPY_IF_EXISTS(serial_number) 1017 pDst->release_number = pSrc->release_number; 1018 WCOPY_IF_EXISTS(manufacturer_string) 1019 WCOPY_IF_EXISTS(product_string) 1020 pDst->usage_page = pSrc->usage_page; 1021 pDst->usage = pSrc->usage; 1022 pDst->interface_number = pSrc->interface_number; 1023 pDst->interface_class = pSrc->interface_class; 1024 pDst->interface_subclass = pSrc->interface_subclass; 1025 pDst->interface_protocol = pSrc->interface_protocol; 1026 pDst->bus_type = (SDL_hid_bus_type)pSrc->bus_type; 1027 pDst->next = NULL; 1028} 1029 1030#undef COPY_IF_EXISTS 1031#undef WCOPY_IF_EXISTS 1032 1033static int SDL_hidapi_refcount = 0; 1034static bool SDL_hidapi_only_controllers; 1035static char *SDL_hidapi_ignored_devices = NULL; 1036 1037static void SDLCALL OnlyControllersChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 1038{ 1039 SDL_hidapi_only_controllers = SDL_GetStringBoolean(hint, true); 1040} 1041 1042static void SDLCALL IgnoredDevicesChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 1043{ 1044 SDL_free(SDL_hidapi_ignored_devices); 1045 if (hint && *hint) { 1046 SDL_hidapi_ignored_devices = SDL_strdup(hint); 1047 } else { 1048 SDL_hidapi_ignored_devices = NULL; 1049 } 1050} 1051 1052bool SDL_HIDAPI_ShouldIgnoreDevice(int bus, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage) 1053{ 1054 // See if there are any devices we should skip in enumeration 1055 if (SDL_hidapi_only_controllers && usage_page) { 1056 if (vendor_id == USB_VENDOR_VALVE) { 1057 // Ignore the mouse/keyboard interface on Steam Controllers 1058 if ( 1059#ifdef SDL_PLATFORM_WIN32 1060 // Check the usage page and usage on both USB and Bluetooth 1061#else 1062 // Only check the usage page and usage on USB 1063 bus == HID_API_BUS_USB && 1064#endif 1065 usage_page == USB_USAGEPAGE_GENERIC_DESKTOP && 1066 (usage == USB_USAGE_GENERIC_KEYBOARD || usage == USB_USAGE_GENERIC_MOUSE)) { 1067 return true; 1068 } 1069 } else if (vendor_id == USB_VENDOR_FLYDIGI_V1 && product_id == USB_PRODUCT_FLYDIGI_V1_GAMEPAD) { 1070 if (usage_page == USB_USAGEPAGE_VENDOR_FLYDIGI) { 1071 return false; 1072 } 1073 return true; 1074 } else if (vendor_id == USB_VENDOR_FLYDIGI_V2 && 1075 (product_id == USB_PRODUCT_FLYDIGI_V2_APEX || product_id == USB_PRODUCT_FLYDIGI_V2_VADER)) { 1076 if (usage_page == USB_USAGEPAGE_VENDOR_FLYDIGI) { 1077 return false; 1078 } 1079 return true; 1080 } else if (usage_page == USB_USAGEPAGE_GENERIC_DESKTOP && 1081 (usage == USB_USAGE_GENERIC_JOYSTICK || usage == USB_USAGE_GENERIC_GAMEPAD || usage == USB_USAGE_GENERIC_MULTIAXISCONTROLLER)) { 1082 // This is a controller 1083 } else { 1084 return true; 1085 } 1086 } 1087 if (SDL_hidapi_ignored_devices) { 1088 char vendor_match[16], product_match[16]; 1089 SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", vendor_id); 1090 SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", vendor_id, product_id); 1091 if (SDL_strcasestr(SDL_hidapi_ignored_devices, vendor_match) || 1092 SDL_strcasestr(SDL_hidapi_ignored_devices, product_match)) { 1093 return true; 1094 } 1095 } 1096 return false; 1097} 1098 1099int SDL_hid_init(void) 1100{ 1101 int attempts = 0, success = 0; 1102 1103 if (SDL_hidapi_refcount > 0) { 1104 ++SDL_hidapi_refcount; 1105 return 0; 1106 } 1107 1108 SDL_AddHintCallback(SDL_HINT_HIDAPI_ENUMERATE_ONLY_CONTROLLERS, OnlyControllersChanged, NULL); 1109 SDL_AddHintCallback(SDL_HINT_HIDAPI_IGNORE_DEVICES, IgnoredDevicesChanged, NULL); 1110 1111#ifdef SDL_USE_LIBUDEV 1112 if (!SDL_GetHintBoolean(SDL_HINT_HIDAPI_UDEV, true)) { 1113 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, 1114 "udev disabled by SDL_HINT_HIDAPI_UDEV"); 1115 linux_enumeration_method = ENUMERATION_FALLBACK; 1116 } else if (SDL_GetSandbox() != SDL_SANDBOX_NONE) { 1117 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, 1118 "Container detected, disabling HIDAPI udev integration"); 1119 linux_enumeration_method = ENUMERATION_FALLBACK; 1120 } else { 1121 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, 1122 "Using udev for HIDAPI joystick device discovery"); 1123 linux_enumeration_method = ENUMERATION_LIBUDEV; 1124 } 1125#endif 1126 1127 use_libusb_whitelist = SDL_GetHintBoolean(SDL_HINT_HIDAPI_LIBUSB_WHITELIST, 1128 SDL_HINT_HIDAPI_LIBUSB_WHITELIST_DEFAULT); 1129#ifdef HAVE_LIBUSB 1130 if (!SDL_GetHintBoolean(SDL_HINT_HIDAPI_LIBUSB, true)) { 1131 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, 1132 "libusb disabled with SDL_HINT_HIDAPI_LIBUSB"); 1133 } else { 1134 ++attempts; 1135 if (!SDL_InitLibUSB(&libusb_ctx)) { 1136 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Couldn't load libusb"); 1137 } else if (LIBUSB_hid_init() < 0) { 1138 SDL_QuitLibUSB(); 1139 libusb_ctx = NULL; 1140 } else { 1141 ++success; 1142 } 1143 } 1144#endif // HAVE_LIBUSB 1145 1146#ifdef HAVE_PLATFORM_BACKEND 1147 ++attempts; 1148#ifdef SDL_PLATFORM_LINUX 1149 udev_ctx = SDL_UDEV_GetUdevSyms(); 1150#endif // __LINUX __ 1151 if (udev_ctx && PLATFORM_hid_init() == 0) { 1152 ++success; 1153 } 1154#endif // HAVE_PLATFORM_BACKEND 1155 1156 if (attempts > 0 && success == 0) { 1157 return -1; 1158 } 1159 1160#if defined(SDL_PLATFORM_MACOS) && !defined(SDL_HIDAPI_DISABLED) 1161 hid_darwin_set_open_exclusive(0); 1162#endif 1163 1164 ++SDL_hidapi_refcount; 1165 return 0; 1166} 1167 1168int SDL_hid_exit(void) 1169{ 1170 int result = 0; 1171 1172 if (SDL_hidapi_refcount == 0) { 1173 return 0; 1174 } 1175 --SDL_hidapi_refcount; 1176 if (SDL_hidapi_refcount > 0) { 1177 return 0; 1178 } 1179 SDL_hidapi_refcount = 0; 1180 1181#ifndef SDL_HIDAPI_DISABLED 1182 HIDAPI_ShutdownDiscovery(); 1183#endif 1184 1185#ifdef HAVE_PLATFORM_BACKEND 1186 if (udev_ctx) { 1187 result |= PLATFORM_hid_exit(); 1188 } 1189#ifdef SDL_PLATFORM_LINUX 1190 SDL_UDEV_ReleaseUdevSyms(); 1191#endif // __LINUX __ 1192#endif // HAVE_PLATFORM_BACKEND 1193 1194#ifdef HAVE_LIBUSB 1195 if (libusb_ctx) { 1196 result |= LIBUSB_hid_exit(); 1197 SDL_QuitLibUSB(); 1198 libusb_ctx = NULL; 1199 } 1200#endif // HAVE_LIBUSB 1201 1202 SDL_RemoveHintCallback(SDL_HINT_HIDAPI_ENUMERATE_ONLY_CONTROLLERS, OnlyControllersChanged, NULL); 1203 SDL_RemoveHintCallback(SDL_HINT_HIDAPI_IGNORE_DEVICES, IgnoredDevicesChanged, NULL); 1204 1205 if (SDL_hidapi_ignored_devices) { 1206 SDL_free(SDL_hidapi_ignored_devices); 1207 SDL_hidapi_ignored_devices = NULL; 1208 } 1209 1210 return result; 1211} 1212 1213Uint32 SDL_hid_device_change_count(void) 1214{ 1215 Uint32 counter = 0; 1216 1217#ifndef SDL_HIDAPI_DISABLED 1218 if (SDL_hidapi_refcount == 0 && SDL_hid_init() < 0) { 1219 return 0; 1220 } 1221 1222 HIDAPI_UpdateDiscovery(); 1223 1224 if (SDL_HIDAPI_discovery.m_unDeviceChangeCounter == 0) { 1225 // Counter wrapped! 1226 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter; 1227 } 1228 counter = SDL_HIDAPI_discovery.m_unDeviceChangeCounter; 1229 1230#endif // !SDL_HIDAPI_DISABLED 1231 1232 return counter; 1233} 1234 1235static void AddDeviceToEnumeration(const char *driver_name, struct hid_device_info *dev, struct SDL_hid_device_info **devs, struct SDL_hid_device_info **last) 1236{ 1237 struct SDL_hid_device_info *new_dev; 1238 1239#ifdef DEBUG_HIDAPI 1240 SDL_Log("Adding %s device to enumeration: %ls %ls 0x%.4hx/0x%.4hx/%d", 1241 driver_name, dev->manufacturer_string, dev->product_string, dev->vendor_id, dev->product_id, dev->interface_number); 1242#else 1243 (void)driver_name; 1244#endif 1245 1246 new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info)); 1247 if (new_dev == NULL) { 1248 // Don't bother returning an error, get as many devices as possible 1249 return; 1250 } 1251 CopyHIDDeviceInfo(dev, new_dev); 1252 1253 if ((*last) != NULL) { 1254 (*last)->next = new_dev; 1255 } else { 1256 *devs = new_dev; 1257 } 1258 *last = new_dev; 1259} 1260 1261#if defined(HAVE_LIBUSB) || defined(HAVE_PLATFORM_BACKEND) 1262static void RemoveDeviceFromEnumeration(const char *driver_name, struct hid_device_info *dev, struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *)) 1263{ 1264 struct hid_device_info *last = NULL, *curr, *next; 1265 1266 for (curr = *devs; curr; curr = next) { 1267 next = curr->next; 1268 1269 if (dev->vendor_id == curr->vendor_id && 1270 dev->product_id == curr->product_id && 1271 (dev->interface_number < 0 || curr->interface_number < 0 || dev->interface_number == curr->interface_number)) { 1272#ifdef DEBUG_HIDAPI 1273 SDL_Log("Skipping %s device: %ls %ls 0x%.4hx/0x%.4hx/%d", 1274 driver_name, curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number); 1275#else 1276 (void)driver_name; 1277#endif 1278 if (last) { 1279 last->next = next; 1280 } else { 1281 *devs = next; 1282 } 1283 1284 curr->next = NULL; 1285 free_device_info(curr); 1286 continue; 1287 } 1288 last = curr; 1289 } 1290} 1291#endif // HAVE_LIBUSB || HAVE_PLATFORM_BACKEND 1292 1293#ifdef HAVE_LIBUSB 1294static void RemoveNonWhitelistedDevicesFromEnumeration(struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *)) 1295{ 1296 struct hid_device_info *last = NULL, *curr, *next; 1297 1298 for (curr = *devs; curr; curr = next) { 1299 next = curr->next; 1300 1301 if (!IsInWhitelist(curr->vendor_id, curr->product_id)) { 1302#ifdef DEBUG_HIDAPI 1303 SDL_Log("Device was not in libusb whitelist, skipping: %ls %ls 0x%.4hx/0x%.4hx/%d", 1304 curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number); 1305#endif 1306 if (last) { 1307 last->next = next; 1308 } else { 1309 *devs = next; 1310 } 1311 1312 curr->next = NULL; 1313 free_device_info(curr); 1314 continue; 1315 } 1316 last = curr; 1317 } 1318} 1319#endif // HAVE_LIBUSB 1320 1321struct SDL_hid_device_info *SDL_hid_enumerate(unsigned short vendor_id, unsigned short product_id) 1322{ 1323 struct hid_device_info *driver_devs = NULL; 1324 struct hid_device_info *usb_devs = NULL; 1325 struct hid_device_info *raw_devs = NULL; 1326 struct hid_device_info *dev; 1327 struct SDL_hid_device_info *devs = NULL, *last = NULL; 1328 1329 if (SDL_hidapi_refcount == 0 && SDL_hid_init() < 0) { 1330 return NULL; 1331 } 1332 1333 // Collect the available devices 1334#ifdef HAVE_DRIVER_BACKEND 1335 driver_devs = DRIVER_hid_enumerate(vendor_id, product_id); 1336#endif 1337 1338#ifdef HAVE_LIBUSB 1339 if (libusb_ctx) { 1340 usb_devs = LIBUSB_hid_enumerate(vendor_id, product_id); 1341 1342 if (use_libusb_whitelist) { 1343 RemoveNonWhitelistedDevicesFromEnumeration(&usb_devs, LIBUSB_hid_free_enumeration); 1344 } 1345 } 1346#endif // HAVE_LIBUSB 1347 1348#ifdef HAVE_PLATFORM_BACKEND 1349 if (udev_ctx) { 1350 raw_devs = PLATFORM_hid_enumerate(vendor_id, product_id); 1351 } 1352#endif 1353 1354 // Highest priority are custom driver devices 1355 for (dev = driver_devs; dev; dev = dev->next) { 1356 AddDeviceToEnumeration("driver", dev, &devs, &last); 1357#ifdef HAVE_LIBUSB 1358 RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration); 1359#endif 1360#ifdef HAVE_PLATFORM_BACKEND 1361 RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration); 1362#endif 1363 } 1364 1365 // If whitelist is in effect, libusb has priority, otherwise raw devices do 1366 if (use_libusb_whitelist) { 1367 for (dev = usb_devs; dev; dev = dev->next) { 1368 AddDeviceToEnumeration("libusb", dev, &devs, &last); 1369#ifdef HAVE_PLATFORM_BACKEND 1370 RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration); 1371#endif 1372 } 1373 for (dev = raw_devs; dev; dev = dev->next) { 1374 AddDeviceToEnumeration("platform", dev, &devs, &last); 1375 } 1376 } else { 1377 for (dev = raw_devs; dev; dev = dev->next) { 1378 AddDeviceToEnumeration("raw", dev, &devs, &last); 1379#ifdef HAVE_LIBUSB 1380 RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration); 1381#endif 1382 } 1383 for (dev = usb_devs; dev; dev = dev->next) { 1384 AddDeviceToEnumeration("libusb", dev, &devs, &last); 1385 } 1386 } 1387 1388#ifdef HAVE_DRIVER_BACKEND 1389 DRIVER_hid_free_enumeration(driver_devs); 1390#endif 1391#ifdef HAVE_LIBUSB 1392 LIBUSB_hid_free_enumeration(usb_devs); 1393#endif 1394#ifdef HAVE_PLATFORM_BACKEND 1395 PLATFORM_hid_free_enumeration(raw_devs); 1396#endif 1397 1398 return devs; 1399} 1400 1401void SDL_hid_free_enumeration(struct SDL_hid_device_info *devs) 1402{ 1403 while (devs) { 1404 struct SDL_hid_device_info *next = devs->next; 1405 SDL_free(devs->path); 1406 SDL_free(devs->serial_number); 1407 SDL_free(devs->manufacturer_string); 1408 SDL_free(devs->product_string); 1409 SDL_free(devs); 1410 devs = next; 1411 } 1412} 1413 1414SDL_hid_device *SDL_hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 1415{ 1416#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB) 1417 void *pDevice = NULL; 1418 1419 if (SDL_hidapi_refcount == 0 && SDL_hid_init() < 0) { 1420 return NULL; 1421 } 1422 1423#ifdef HAVE_PLATFORM_BACKEND 1424 if (udev_ctx) { 1425 pDevice = PLATFORM_hid_open(vendor_id, product_id, serial_number); 1426 if (pDevice != NULL) { 1427 return CreateHIDDeviceWrapper(pDevice, &PLATFORM_Backend); 1428 } 1429 } 1430#endif // HAVE_PLATFORM_BACKEND 1431 1432#ifdef HAVE_DRIVER_BACKEND 1433 pDevice = DRIVER_hid_open(vendor_id, product_id, serial_number); 1434 if (pDevice != NULL) { 1435 return CreateHIDDeviceWrapper(pDevice, &DRIVER_Backend); 1436 } 1437#endif // HAVE_DRIVER_BACKEND 1438 1439#ifdef HAVE_LIBUSB 1440 if (libusb_ctx) { 1441 pDevice = LIBUSB_hid_open(vendor_id, product_id, serial_number); 1442 if (pDevice != NULL) { 1443 SDL_hid_device *dev = CreateHIDDeviceWrapper(pDevice, &LIBUSB_Backend); 1444 SDL_SetPointerProperty(SDL_hid_get_properties(dev), SDL_PROP_HIDAPI_LIBUSB_DEVICE_HANDLE_POINTER, ((LIBUSB_hid_device *)pDevice)->device_handle); 1445 return dev; 1446 } 1447 } 1448#endif // HAVE_LIBUSB 1449 1450#endif // HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB 1451 1452 return NULL; 1453} 1454 1455SDL_hid_device *SDL_hid_open_path(const char *path) 1456{ 1457#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB) 1458 void *pDevice = NULL; 1459 1460 if (SDL_hidapi_refcount == 0 && SDL_hid_init() < 0) { 1461 return NULL; 1462 } 1463 1464#ifdef HAVE_PLATFORM_BACKEND 1465 if (udev_ctx) { 1466 pDevice = PLATFORM_hid_open_path(path); 1467 if (pDevice != NULL) { 1468 return CreateHIDDeviceWrapper(pDevice, &PLATFORM_Backend); 1469 } 1470 } 1471#endif // HAVE_PLATFORM_BACKEND 1472 1473#ifdef HAVE_DRIVER_BACKEND 1474 pDevice = DRIVER_hid_open_path(path); 1475 if (pDevice != NULL) { 1476 return CreateHIDDeviceWrapper(pDevice, &DRIVER_Backend); 1477 } 1478#endif // HAVE_DRIVER_BACKEND 1479 1480#ifdef HAVE_LIBUSB 1481 if (libusb_ctx) { 1482 pDevice = LIBUSB_hid_open_path(path); 1483 if (pDevice != NULL) { 1484 SDL_hid_device *dev = CreateHIDDeviceWrapper(pDevice, &LIBUSB_Backend); 1485 SDL_SetPointerProperty(SDL_hid_get_properties(dev), SDL_PROP_HIDAPI_LIBUSB_DEVICE_HANDLE_POINTER, ((LIBUSB_hid_device *)pDevice)->device_handle); 1486 return dev; 1487 } 1488 } 1489#endif // HAVE_LIBUSB 1490 1491#endif // HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB 1492 1493 return NULL; 1494} 1495 1496SDL_PropertiesID SDL_hid_get_properties(SDL_hid_device *device) 1497{ 1498 CHECK_DEVICE_MAGIC(device, 0); 1499 1500 if (!device->props) { 1501 device->props = SDL_CreateProperties(); 1502 } 1503 return device->props; 1504} 1505 1506int SDL_hid_write(SDL_hid_device *device, const unsigned char *data, size_t length) 1507{ 1508 CHECK_DEVICE_MAGIC(device, -1); 1509 1510 return device->backend->hid_write(device->device, data, length); 1511} 1512 1513int SDL_hid_read_timeout(SDL_hid_device *device, unsigned char *data, size_t length, int milliseconds) 1514{ 1515 CHECK_DEVICE_MAGIC(device, -1); 1516 1517 return device->backend->hid_read_timeout(device->device, data, length, milliseconds); 1518} 1519 1520int SDL_hid_read(SDL_hid_device *device, unsigned char *data, size_t length) 1521{ 1522 CHECK_DEVICE_MAGIC(device, -1); 1523 1524 return device->backend->hid_read(device->device, data, length); 1525} 1526 1527int SDL_hid_set_nonblocking(SDL_hid_device *device, int nonblock) 1528{ 1529 CHECK_DEVICE_MAGIC(device, -1); 1530 1531 return device->backend->hid_set_nonblocking(device->device, nonblock); 1532} 1533 1534int SDL_hid_send_feature_report(SDL_hid_device *device, const unsigned char *data, size_t length) 1535{ 1536 CHECK_DEVICE_MAGIC(device, -1); 1537 1538 return device->backend->hid_send_feature_report(device->device, data, length); 1539} 1540 1541int SDL_hid_get_feature_report(SDL_hid_device *device, unsigned char *data, size_t length) 1542{ 1543 CHECK_DEVICE_MAGIC(device, -1); 1544 1545 return device->backend->hid_get_feature_report(device->device, data, length); 1546} 1547 1548int SDL_hid_get_input_report(SDL_hid_device *device, unsigned char *data, size_t length) 1549{ 1550 CHECK_DEVICE_MAGIC(device, -1); 1551 1552 return device->backend->hid_get_input_report(device->device, data, length); 1553} 1554 1555int SDL_hid_close(SDL_hid_device *device) 1556{ 1557 CHECK_DEVICE_MAGIC(device, -1); 1558 1559 device->backend->hid_close(device->device); 1560 SDL_DestroyProperties(device->props); 1561 DeleteHIDDeviceWrapper(device); 1562 return 0; 1563} 1564 1565int SDL_hid_get_manufacturer_string(SDL_hid_device *device, wchar_t *string, size_t maxlen) 1566{ 1567 CHECK_DEVICE_MAGIC(device, -1); 1568 1569 return device->backend->hid_get_manufacturer_string(device->device, string, maxlen); 1570} 1571 1572int SDL_hid_get_product_string(SDL_hid_device *device, wchar_t *string, size_t maxlen) 1573{ 1574 CHECK_DEVICE_MAGIC(device, -1); 1575 1576 return device->backend->hid_get_product_string(device->device, string, maxlen); 1577} 1578 1579int SDL_hid_get_serial_number_string(SDL_hid_device *device, wchar_t *string, size_t maxlen) 1580{ 1581 CHECK_DEVICE_MAGIC(device, -1); 1582 1583 return device->backend->hid_get_serial_number_string(device->device, string, maxlen); 1584} 1585 1586int SDL_hid_get_indexed_string(SDL_hid_device *device, int string_index, wchar_t *string, size_t maxlen) 1587{ 1588 CHECK_DEVICE_MAGIC(device, -1); 1589 1590 return device->backend->hid_get_indexed_string(device->device, string_index, string, maxlen); 1591} 1592 1593SDL_hid_device_info *SDL_hid_get_device_info(SDL_hid_device *device) 1594{ 1595 struct hid_device_info *info; 1596 1597 CHECK_DEVICE_MAGIC(device, NULL); 1598 1599 info = device->backend->hid_get_device_info(device->device); 1600 if (info) { 1601 CopyHIDDeviceInfo(info, &device->info); 1602 return &device->info; 1603 } else { 1604 return NULL; 1605 } 1606} 1607 1608int SDL_hid_get_report_descriptor(SDL_hid_device *device, unsigned char *buf, size_t buf_size) 1609{ 1610 CHECK_DEVICE_MAGIC(device, -1); 1611 1612 return device->backend->hid_get_report_descriptor(device->device, buf, buf_size); 1613} 1614 1615void SDL_hid_ble_scan(bool active) 1616{ 1617#if !defined(SDL_HIDAPI_DISABLED) && (defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)) 1618 extern void hid_ble_scan(int bStart); 1619 hid_ble_scan(active); 1620#endif 1621} 1622[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.