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