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