Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/joystick/hidapi/SDL_hidapijoystick.c
9906 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4
5
This software is provided 'as-is', without any express or implied
6
warranty. In no event will the authors be held liable for any damages
7
arising from the use of this software.
8
9
Permission is granted to anyone to use this software for any purpose,
10
including commercial applications, and to alter it and redistribute it
11
freely, subject to the following restrictions:
12
13
1. The origin of this software must not be misrepresented; you must not
14
claim that you wrote the original software. If you use this software
15
in a product, an acknowledgment in the product documentation would be
16
appreciated but is not required.
17
2. Altered source versions must be plainly marked as such, and must not be
18
misrepresented as being the original software.
19
3. This notice may not be removed or altered from any source distribution.
20
*/
21
#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
35
struct joystick_hwdata
36
{
37
SDL_HIDAPI_Device *device;
38
};
39
40
static 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
};
89
static int SDL_HIDAPI_numdrivers = 0;
90
static SDL_AtomicInt SDL_HIDAPI_updating_devices;
91
static bool SDL_HIDAPI_hints_changed = false;
92
static Uint32 SDL_HIDAPI_change_count = 0;
93
static SDL_HIDAPI_Device *SDL_HIDAPI_devices SDL_GUARDED_BY(SDL_joystick_lock);
94
static int SDL_HIDAPI_numjoysticks = 0;
95
static bool SDL_HIDAPI_combine_joycons = true;
96
static bool initialized = false;
97
static bool shutting_down = false;
98
99
static 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
119
void 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
140
bool 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
211
float 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
216
static void HIDAPI_UpdateDeviceList(void);
217
static void HIDAPI_JoystickClose(SDL_Joystick *joystick);
218
219
static 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
311
static 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
325
static 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
359
static 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
382
static 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
397
static 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
426
static 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
491
static 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
519
static 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
528
static 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
558
static 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
570
static 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
589
static 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
598
void 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
607
void 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
613
static 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
628
static 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
644
void 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
653
static 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
668
static 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
677
bool 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
703
void 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
730
bool 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
if (pJoystickID) {
755
*pJoystickID = joystickID;
756
}
757
758
SDL_PrivateJoystickAdded(joystickID);
759
760
return true;
761
}
762
763
void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
764
{
765
int i, j;
766
767
SDL_LockJoysticks();
768
769
if (!HIDAPI_JoystickInstanceIsUnique(device, joystickID)) {
770
// Disconnecting a child always disconnects the parent
771
device = device->parent;
772
}
773
774
for (i = 0; i < device->num_joysticks; ++i) {
775
if (device->joysticks[i] == joystickID) {
776
SDL_Joystick *joystick = SDL_GetJoystickFromID(joystickID);
777
if (joystick) {
778
HIDAPI_JoystickClose(joystick);
779
}
780
781
HIDAPI_DelJoystickInstanceFromDevice(device, joystickID);
782
783
for (j = 0; j < device->num_children; ++j) {
784
SDL_HIDAPI_Device *child = device->children[j];
785
HIDAPI_DelJoystickInstanceFromDevice(child, joystickID);
786
}
787
788
--SDL_HIDAPI_numjoysticks;
789
790
if (!shutting_down) {
791
SDL_PrivateJoystickRemoved(joystickID);
792
}
793
}
794
}
795
796
// Rescan the device list in case device state has changed
797
SDL_HIDAPI_change_count = 0;
798
799
SDL_UnlockJoysticks();
800
}
801
802
static void HIDAPI_UpdateJoystickProperties(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
803
{
804
SDL_PropertiesID props = SDL_GetJoystickProperties(joystick);
805
Uint32 caps = device->driver->GetJoystickCapabilities(device, joystick);
806
807
if (caps & SDL_JOYSTICK_CAP_MONO_LED) {
808
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_MONO_LED_BOOLEAN, true);
809
} else {
810
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_MONO_LED_BOOLEAN, false);
811
}
812
if (caps & SDL_JOYSTICK_CAP_RGB_LED) {
813
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true);
814
} else {
815
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, false);
816
}
817
if (caps & SDL_JOYSTICK_CAP_PLAYER_LED) {
818
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_PLAYER_LED_BOOLEAN, true);
819
} else {
820
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_PLAYER_LED_BOOLEAN, false);
821
}
822
if (caps & SDL_JOYSTICK_CAP_RUMBLE) {
823
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);
824
} else {
825
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, false);
826
}
827
if (caps & SDL_JOYSTICK_CAP_TRIGGER_RUMBLE) {
828
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true);
829
} else {
830
SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, false);
831
}
832
}
833
834
void HIDAPI_UpdateDeviceProperties(SDL_HIDAPI_Device *device)
835
{
836
int i;
837
838
SDL_LockJoysticks();
839
840
for (i = 0; i < device->num_joysticks; ++i) {
841
SDL_Joystick *joystick = SDL_GetJoystickFromID(device->joysticks[i]);
842
if (joystick) {
843
HIDAPI_UpdateJoystickProperties(device, joystick);
844
}
845
}
846
847
SDL_UnlockJoysticks();
848
}
849
850
static int HIDAPI_JoystickGetCount(void)
851
{
852
return SDL_HIDAPI_numjoysticks;
853
}
854
855
static SDL_HIDAPI_Device *HIDAPI_AddDevice(const struct SDL_hid_device_info *info, int num_children, SDL_HIDAPI_Device **children)
856
{
857
SDL_HIDAPI_Device *device;
858
SDL_HIDAPI_Device *curr, *last = NULL;
859
bool removed;
860
Uint16 bus;
861
862
SDL_AssertJoysticksLocked();
863
864
for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
865
}
866
867
device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
868
if (!device) {
869
return NULL;
870
}
871
SDL_SetObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK, true);
872
if (info->path) {
873
device->path = SDL_strdup(info->path);
874
}
875
device->seen = true;
876
device->vendor_id = info->vendor_id;
877
device->product_id = info->product_id;
878
device->version = info->release_number;
879
device->interface_number = info->interface_number;
880
device->interface_class = info->interface_class;
881
device->interface_subclass = info->interface_subclass;
882
device->interface_protocol = info->interface_protocol;
883
device->usage_page = info->usage_page;
884
device->usage = info->usage;
885
device->is_bluetooth = (info->bus_type == SDL_HID_API_BUS_BLUETOOTH);
886
device->dev_lock = SDL_CreateMutex();
887
888
// Need the device name before getting the driver to know whether to ignore this device
889
{
890
char *serial_number = HIDAPI_ConvertString(info->serial_number);
891
892
device->manufacturer_string = HIDAPI_ConvertString(info->manufacturer_string);
893
device->product_string = HIDAPI_ConvertString(info->product_string);
894
device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, device->manufacturer_string, device->product_string);
895
896
if (serial_number && *serial_number) {
897
device->serial = serial_number;
898
} else {
899
SDL_free(serial_number);
900
}
901
902
if (!device->name) {
903
SDL_free(device->manufacturer_string);
904
SDL_free(device->product_string);
905
SDL_free(device->serial);
906
SDL_free(device->path);
907
SDL_free(device);
908
return NULL;
909
}
910
}
911
912
if (info->bus_type == SDL_HID_API_BUS_BLUETOOTH) {
913
bus = SDL_HARDWARE_BUS_BLUETOOTH;
914
} else {
915
bus = SDL_HARDWARE_BUS_USB;
916
}
917
device->guid = SDL_CreateJoystickGUID(bus, device->vendor_id, device->product_id, device->version, device->manufacturer_string, device->product_string, 'h', 0);
918
device->joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD;
919
device->type = SDL_GetJoystickGameControllerProtocol(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
920
device->steam_virtual_gamepad_slot = -1;
921
922
if (num_children > 0) {
923
int i;
924
925
device->num_children = num_children;
926
device->children = children;
927
for (i = 0; i < num_children; ++i) {
928
children[i]->parent = device;
929
}
930
}
931
932
// Add it to the list
933
if (last) {
934
last->next = device;
935
} else {
936
SDL_HIDAPI_devices = device;
937
}
938
939
removed = false;
940
HIDAPI_SetupDeviceDriver(device, &removed);
941
if (removed) {
942
return NULL;
943
}
944
945
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,
946
device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage,
947
device->path, device->driver ? device->driver->name : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
948
949
return device;
950
}
951
952
static void HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
953
{
954
SDL_HIDAPI_Device *curr, *last;
955
int i;
956
957
SDL_AssertJoysticksLocked();
958
959
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,
960
device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage,
961
device->path, device->driver ? device->driver->name : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
962
963
for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
964
if (curr == device) {
965
if (last) {
966
last->next = curr->next;
967
} else {
968
SDL_HIDAPI_devices = curr->next;
969
}
970
971
HIDAPI_CleanupDeviceDriver(device);
972
973
// Make sure the rumble thread is done with this device
974
while (SDL_GetAtomicInt(&device->rumble_pending) > 0) {
975
SDL_Delay(10);
976
}
977
978
for (i = 0; i < device->num_children; ++i) {
979
device->children[i]->parent = NULL;
980
}
981
982
SDL_SetObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK, false);
983
SDL_DestroyMutex(device->dev_lock);
984
SDL_free(device->manufacturer_string);
985
SDL_free(device->product_string);
986
SDL_free(device->serial);
987
SDL_free(device->name);
988
SDL_free(device->path);
989
SDL_free(device->children);
990
SDL_free(device);
991
return;
992
}
993
}
994
}
995
996
static bool HIDAPI_CreateCombinedJoyCons(void)
997
{
998
SDL_HIDAPI_Device *device, *combined;
999
SDL_HIDAPI_Device *joycons[2] = { NULL, NULL };
1000
1001
SDL_AssertJoysticksLocked();
1002
1003
if (!SDL_HIDAPI_combine_joycons) {
1004
return false;
1005
}
1006
1007
for (device = SDL_HIDAPI_devices; device; device = device->next) {
1008
Uint16 vendor, product;
1009
1010
if (!device->driver) {
1011
// Unsupported device
1012
continue;
1013
}
1014
if (device->parent) {
1015
// This device is already part of a combined device
1016
continue;
1017
}
1018
if (device->broken) {
1019
// This device can't be used
1020
continue;
1021
}
1022
1023
SDL_GetJoystickGUIDInfo(device->guid, &vendor, &product, NULL, NULL);
1024
1025
if (!joycons[0] &&
1026
(SDL_IsJoystickNintendoSwitchJoyConLeft(vendor, product) ||
1027
(SDL_IsJoystickNintendoSwitchJoyConGrip(vendor, product) &&
1028
SDL_strstr(device->name, "(L)") != NULL))) {
1029
joycons[0] = device;
1030
}
1031
if (!joycons[1] &&
1032
(SDL_IsJoystickNintendoSwitchJoyConRight(vendor, product) ||
1033
(SDL_IsJoystickNintendoSwitchJoyConGrip(vendor, product) &&
1034
SDL_strstr(device->name, "(R)") != NULL))) {
1035
joycons[1] = device;
1036
}
1037
if (joycons[0] && joycons[1]) {
1038
SDL_hid_device_info info;
1039
SDL_HIDAPI_Device **children = (SDL_HIDAPI_Device **)SDL_malloc(2 * sizeof(SDL_HIDAPI_Device *));
1040
if (!children) {
1041
return false;
1042
}
1043
children[0] = joycons[0];
1044
children[1] = joycons[1];
1045
1046
SDL_zero(info);
1047
info.path = "nintendo_joycons_combined";
1048
info.vendor_id = USB_VENDOR_NINTENDO;
1049
info.product_id = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;
1050
info.interface_number = -1;
1051
info.usage_page = USB_USAGEPAGE_GENERIC_DESKTOP;
1052
info.usage = USB_USAGE_GENERIC_GAMEPAD;
1053
info.manufacturer_string = L"Nintendo";
1054
info.product_string = L"Switch Joy-Con (L/R)";
1055
if (children[0]->is_bluetooth || children[1]->is_bluetooth) {
1056
info.bus_type = SDL_HID_API_BUS_BLUETOOTH;
1057
} else {
1058
info.bus_type = SDL_HID_API_BUS_USB;
1059
}
1060
1061
combined = HIDAPI_AddDevice(&info, 2, children);
1062
if (combined && combined->driver) {
1063
return true;
1064
} else {
1065
if (combined) {
1066
HIDAPI_DelDevice(combined);
1067
} else {
1068
SDL_free(children);
1069
}
1070
return false;
1071
}
1072
}
1073
}
1074
return false;
1075
}
1076
1077
static void HIDAPI_UpdateDeviceList(void)
1078
{
1079
SDL_HIDAPI_Device *device;
1080
struct SDL_hid_device_info *devs, *info;
1081
1082
SDL_LockJoysticks();
1083
1084
if (SDL_HIDAPI_hints_changed) {
1085
SDL_HIDAPI_UpdateDrivers();
1086
SDL_HIDAPI_hints_changed = false;
1087
}
1088
1089
// Prepare the existing device list
1090
for (device = SDL_HIDAPI_devices; device; device = device->next) {
1091
if (device->children) {
1092
continue;
1093
}
1094
device->seen = false;
1095
}
1096
1097
// Enumerate the devices
1098
if (SDL_HIDAPI_numdrivers > 0) {
1099
devs = SDL_hid_enumerate(0, 0);
1100
if (devs) {
1101
for (info = devs; info; info = info->next) {
1102
device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
1103
if (device) {
1104
device->seen = true;
1105
1106
// Check to see if the serial number is available now
1107
if(HIDAPI_SerialIsEmpty(device)) {
1108
HIDAPI_SetDeviceSerialW(device, info->serial_number);
1109
}
1110
} else {
1111
HIDAPI_AddDevice(info, 0, NULL);
1112
}
1113
}
1114
SDL_hid_free_enumeration(devs);
1115
}
1116
}
1117
1118
// Remove any devices that weren't seen or have been disconnected due to read errors
1119
check_removed:
1120
device = SDL_HIDAPI_devices;
1121
while (device) {
1122
SDL_HIDAPI_Device *next = device->next;
1123
1124
if (!device->seen ||
1125
((device->driver || device->children) && device->num_joysticks == 0 && !device->dev)) {
1126
if (device->parent) {
1127
// When a child device goes away, so does the parent
1128
int i;
1129
device = device->parent;
1130
for (i = 0; i < device->num_children; ++i) {
1131
HIDAPI_DelDevice(device->children[i]);
1132
}
1133
HIDAPI_DelDevice(device);
1134
1135
// Update the device list again to pick up any children left
1136
SDL_HIDAPI_change_count = 0;
1137
1138
// We deleted more than one device here, restart the loop
1139
goto check_removed;
1140
} else {
1141
HIDAPI_DelDevice(device);
1142
device = NULL;
1143
1144
// Update the device list again in case this device comes back
1145
SDL_HIDAPI_change_count = 0;
1146
}
1147
}
1148
if (device && device->broken && device->parent) {
1149
HIDAPI_DelDevice(device->parent);
1150
1151
// We deleted a different device here, restart the loop
1152
goto check_removed;
1153
}
1154
device = next;
1155
}
1156
1157
// See if we can create any combined Joy-Con controllers
1158
while (HIDAPI_CreateCombinedJoyCons()) {
1159
}
1160
1161
SDL_UnlockJoysticks();
1162
}
1163
1164
static bool HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Device *device)
1165
{
1166
if (vendor_id == device->vendor_id && product_id == device->product_id) {
1167
return true;
1168
}
1169
1170
if (vendor_id == USB_VENDOR_MICROSOFT) {
1171
// If we're looking for the wireless XBox 360 controller, also look for the dongle
1172
if (product_id == USB_PRODUCT_XBOX360_XUSB_CONTROLLER && device->product_id == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER) {
1173
return true;
1174
}
1175
1176
// If we're looking for the raw input Xbox One controller, match it against any other Xbox One controller
1177
if (product_id == USB_PRODUCT_XBOX_ONE_XBOXGIP_CONTROLLER &&
1178
device->type == SDL_GAMEPAD_TYPE_XBOXONE) {
1179
return true;
1180
}
1181
1182
// If we're looking for an XInput controller, match it against any other Xbox controller
1183
if (product_id == USB_PRODUCT_XBOX360_XUSB_CONTROLLER) {
1184
if (device->type == SDL_GAMEPAD_TYPE_XBOX360 || device->type == SDL_GAMEPAD_TYPE_XBOXONE) {
1185
return true;
1186
}
1187
}
1188
}
1189
1190
if (vendor_id == USB_VENDOR_NVIDIA) {
1191
// If we're looking for the NVIDIA SHIELD controller Xbox interface, match it against any NVIDIA SHIELD controller
1192
if (product_id == 0xb400 &&
1193
SDL_IsJoystickNVIDIASHIELDController(vendor_id, product_id)) {
1194
return true;
1195
}
1196
}
1197
return false;
1198
}
1199
1200
static bool HIDAPI_StartUpdatingDevices(void)
1201
{
1202
return SDL_CompareAndSwapAtomicInt(&SDL_HIDAPI_updating_devices, false, true);
1203
}
1204
1205
static void HIDAPI_FinishUpdatingDevices(void)
1206
{
1207
SDL_SetAtomicInt(&SDL_HIDAPI_updating_devices, false);
1208
}
1209
1210
bool HIDAPI_IsDeviceTypePresent(SDL_GamepadType type)
1211
{
1212
SDL_HIDAPI_Device *device;
1213
bool result = false;
1214
1215
// Make sure we're initialized, as this could be called from other drivers during startup
1216
if (!HIDAPI_JoystickInit()) {
1217
return false;
1218
}
1219
1220
if (HIDAPI_StartUpdatingDevices()) {
1221
HIDAPI_UpdateDeviceList();
1222
HIDAPI_FinishUpdatingDevices();
1223
}
1224
1225
SDL_LockJoysticks();
1226
for (device = SDL_HIDAPI_devices; device; device = device->next) {
1227
if (device->driver && device->type == type) {
1228
result = true;
1229
break;
1230
}
1231
}
1232
SDL_UnlockJoysticks();
1233
1234
#ifdef DEBUG_HIDAPI
1235
SDL_Log("HIDAPI_IsDeviceTypePresent() returning %s for %d", result ? "true" : "false", type);
1236
#endif
1237
return result;
1238
}
1239
1240
bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
1241
{
1242
SDL_HIDAPI_Device *device;
1243
bool supported = false;
1244
bool result = false;
1245
1246
// Make sure we're initialized, as this could be called from other drivers during startup
1247
if (!HIDAPI_JoystickInit()) {
1248
return false;
1249
}
1250
1251
/* Only update the device list for devices we know might be supported.
1252
If we did this for every device, it would hit the USB driver too hard and potentially
1253
lock up the system. This won't catch devices that we support but can only detect using
1254
USB interface details, like Xbox controllers, but hopefully the device list update is
1255
responsive enough to catch those.
1256
*/
1257
supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name);
1258
#if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)
1259
if (!supported &&
1260
(SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) {
1261
supported = true;
1262
}
1263
#endif // SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE
1264
if (supported) {
1265
if (HIDAPI_StartUpdatingDevices()) {
1266
HIDAPI_UpdateDeviceList();
1267
HIDAPI_FinishUpdatingDevices();
1268
}
1269
}
1270
1271
/* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
1272
or a different name than we have it listed here, etc, but if we support the device
1273
and we have something similar in our device list, mark it as present.
1274
*/
1275
SDL_LockJoysticks();
1276
for (device = SDL_HIDAPI_devices; device; device = device->next) {
1277
if (device->driver &&
1278
HIDAPI_IsEquivalentToDevice(vendor_id, product_id, device)) {
1279
result = true;
1280
break;
1281
}
1282
}
1283
SDL_UnlockJoysticks();
1284
1285
#ifdef DEBUG_HIDAPI
1286
SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x", result ? "true" : "false", vendor_id, product_id);
1287
#endif
1288
return result;
1289
}
1290
1291
char *HIDAPI_GetDeviceProductName(Uint16 vendor_id, Uint16 product_id)
1292
{
1293
SDL_HIDAPI_Device *device;
1294
char *name = NULL;
1295
1296
SDL_LockJoysticks();
1297
for (device = SDL_HIDAPI_devices; device; device = device->next) {
1298
if (vendor_id == device->vendor_id && product_id == device->product_id) {
1299
if (device->product_string) {
1300
name = SDL_strdup(device->product_string);
1301
}
1302
break;
1303
}
1304
}
1305
SDL_UnlockJoysticks();
1306
1307
return name;
1308
}
1309
1310
char *HIDAPI_GetDeviceManufacturerName(Uint16 vendor_id, Uint16 product_id)
1311
{
1312
SDL_HIDAPI_Device *device;
1313
char *name = NULL;
1314
1315
SDL_LockJoysticks();
1316
for (device = SDL_HIDAPI_devices; device; device = device->next) {
1317
if (vendor_id == device->vendor_id && product_id == device->product_id) {
1318
if (device->manufacturer_string) {
1319
name = SDL_strdup(device->manufacturer_string);
1320
}
1321
break;
1322
}
1323
}
1324
SDL_UnlockJoysticks();
1325
1326
return name;
1327
}
1328
1329
SDL_JoystickType HIDAPI_GetJoystickTypeFromGUID(SDL_GUID guid)
1330
{
1331
SDL_HIDAPI_Device *device;
1332
SDL_JoystickType type = SDL_JOYSTICK_TYPE_UNKNOWN;
1333
1334
SDL_LockJoysticks();
1335
for (device = SDL_HIDAPI_devices; device; device = device->next) {
1336
if (SDL_memcmp(&guid, &device->guid, sizeof(guid)) == 0) {
1337
type = device->joystick_type;
1338
break;
1339
}
1340
}
1341
SDL_UnlockJoysticks();
1342
1343
return type;
1344
}
1345
1346
SDL_GamepadType HIDAPI_GetGamepadTypeFromGUID(SDL_GUID guid)
1347
{
1348
SDL_HIDAPI_Device *device;
1349
SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD;
1350
1351
SDL_LockJoysticks();
1352
for (device = SDL_HIDAPI_devices; device; device = device->next) {
1353
if (SDL_memcmp(&guid, &device->guid, sizeof(guid)) == 0) {
1354
type = device->type;
1355
break;
1356
}
1357
}
1358
SDL_UnlockJoysticks();
1359
1360
return type;
1361
}
1362
1363
static void HIDAPI_JoystickDetect(void)
1364
{
1365
if (HIDAPI_StartUpdatingDevices()) {
1366
Uint32 count = SDL_hid_device_change_count();
1367
if (SDL_HIDAPI_change_count != count) {
1368
SDL_HIDAPI_change_count = count;
1369
HIDAPI_UpdateDeviceList();
1370
}
1371
HIDAPI_FinishUpdatingDevices();
1372
}
1373
}
1374
1375
void HIDAPI_UpdateDevices(void)
1376
{
1377
SDL_HIDAPI_Device *device;
1378
1379
SDL_AssertJoysticksLocked();
1380
1381
// Update the devices, which may change connected joysticks and send events
1382
1383
// Prepare the existing device list
1384
if (HIDAPI_StartUpdatingDevices()) {
1385
for (device = SDL_HIDAPI_devices; device; device = device->next) {
1386
if (device->parent) {
1387
continue;
1388
}
1389
if (device->driver) {
1390
if (SDL_TryLockMutex(device->dev_lock)) {
1391
device->updating = true;
1392
device->driver->UpdateDevice(device);
1393
device->updating = false;
1394
SDL_UnlockMutex(device->dev_lock);
1395
}
1396
}
1397
}
1398
HIDAPI_FinishUpdatingDevices();
1399
}
1400
}
1401
1402
static const char *HIDAPI_JoystickGetDeviceName(int device_index)
1403
{
1404
SDL_HIDAPI_Device *device;
1405
const char *name = NULL;
1406
1407
device = HIDAPI_GetDeviceByIndex(device_index, NULL);
1408
if (device) {
1409
// FIXME: The device could be freed after this name is returned...
1410
name = device->name;
1411
}
1412
1413
return name;
1414
}
1415
1416
static const char *HIDAPI_JoystickGetDevicePath(int device_index)
1417
{
1418
SDL_HIDAPI_Device *device;
1419
const char *path = NULL;
1420
1421
device = HIDAPI_GetDeviceByIndex(device_index, NULL);
1422
if (device) {
1423
// FIXME: The device could be freed after this path is returned...
1424
path = device->path;
1425
}
1426
1427
return path;
1428
}
1429
1430
static int HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
1431
{
1432
SDL_HIDAPI_Device *device;
1433
1434
device = HIDAPI_GetDeviceByIndex(device_index, NULL);
1435
if (device) {
1436
return device->steam_virtual_gamepad_slot;
1437
}
1438
return -1;
1439
}
1440
1441
static int HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
1442
{
1443
SDL_HIDAPI_Device *device;
1444
SDL_JoystickID instance_id;
1445
int player_index = -1;
1446
1447
device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
1448
if (device) {
1449
player_index = device->driver->GetDevicePlayerIndex(device, instance_id);
1450
}
1451
1452
return player_index;
1453
}
1454
1455
static void HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
1456
{
1457
SDL_HIDAPI_Device *device;
1458
SDL_JoystickID instance_id;
1459
1460
device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
1461
if (device) {
1462
device->driver->SetDevicePlayerIndex(device, instance_id, player_index);
1463
}
1464
}
1465
1466
static SDL_GUID HIDAPI_JoystickGetDeviceGUID(int device_index)
1467
{
1468
SDL_HIDAPI_Device *device;
1469
SDL_GUID guid;
1470
1471
device = HIDAPI_GetDeviceByIndex(device_index, NULL);
1472
if (device) {
1473
SDL_memcpy(&guid, &device->guid, sizeof(guid));
1474
} else {
1475
SDL_zero(guid);
1476
}
1477
1478
return guid;
1479
}
1480
1481
static SDL_JoystickID HIDAPI_JoystickGetDeviceInstanceID(int device_index)
1482
{
1483
SDL_JoystickID joystickID = 0;
1484
HIDAPI_GetDeviceByIndex(device_index, &joystickID);
1485
return joystickID;
1486
}
1487
1488
static bool HIDAPI_JoystickOpen(SDL_Joystick *joystick, int device_index)
1489
{
1490
SDL_JoystickID joystickID = 0;
1491
SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
1492
struct joystick_hwdata *hwdata;
1493
1494
SDL_AssertJoysticksLocked();
1495
1496
if (!device || !device->driver || device->broken) {
1497
// This should never happen - validated before being called
1498
return SDL_SetError("Couldn't find HIDAPI device at index %d", device_index);
1499
}
1500
1501
hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
1502
if (!hwdata) {
1503
return false;
1504
}
1505
hwdata->device = device;
1506
1507
// Process any pending reports before opening the device
1508
SDL_LockMutex(device->dev_lock);
1509
device->updating = true;
1510
device->driver->UpdateDevice(device);
1511
device->updating = false;
1512
SDL_UnlockMutex(device->dev_lock);
1513
1514
// UpdateDevice() may have called HIDAPI_JoystickDisconnected() if the device went away
1515
if (device->num_joysticks == 0) {
1516
SDL_free(hwdata);
1517
return SDL_SetError("HIDAPI device disconnected while opening");
1518
}
1519
1520
// Set the default connection state, can be overridden below
1521
if (device->is_bluetooth) {
1522
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
1523
} else {
1524
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;
1525
}
1526
1527
if (!device->driver->OpenJoystick(device, joystick)) {
1528
// The open failed, mark this device as disconnected and update devices
1529
HIDAPI_JoystickDisconnected(device, joystickID);
1530
SDL_free(hwdata);
1531
return false;
1532
}
1533
1534
HIDAPI_UpdateJoystickProperties(device, joystick);
1535
1536
if (device->serial) {
1537
joystick->serial = SDL_strdup(device->serial);
1538
}
1539
1540
joystick->hwdata = hwdata;
1541
return true;
1542
}
1543
1544
static bool HIDAPI_GetJoystickDevice(SDL_Joystick *joystick, SDL_HIDAPI_Device **device)
1545
{
1546
SDL_AssertJoysticksLocked();
1547
1548
if (joystick && joystick->hwdata) {
1549
*device = joystick->hwdata->device;
1550
if (SDL_ObjectValid(*device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK) && (*device)->driver != NULL) {
1551
return true;
1552
}
1553
}
1554
return false;
1555
}
1556
1557
static bool HIDAPI_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1558
{
1559
bool result;
1560
SDL_HIDAPI_Device *device = NULL;
1561
1562
if (HIDAPI_GetJoystickDevice(joystick, &device)) {
1563
result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble);
1564
} else {
1565
result = SDL_SetError("Rumble failed, device disconnected");
1566
}
1567
1568
return result;
1569
}
1570
1571
static bool HIDAPI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
1572
{
1573
bool result;
1574
SDL_HIDAPI_Device *device = NULL;
1575
1576
if (HIDAPI_GetJoystickDevice(joystick, &device)) {
1577
result = device->driver->RumbleJoystickTriggers(device, joystick, left_rumble, right_rumble);
1578
} else {
1579
result = SDL_SetError("Rumble failed, device disconnected");
1580
}
1581
1582
return result;
1583
}
1584
1585
static bool HIDAPI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
1586
{
1587
bool result;
1588
SDL_HIDAPI_Device *device = NULL;
1589
1590
if (HIDAPI_GetJoystickDevice(joystick, &device)) {
1591
result = device->driver->SetJoystickLED(device, joystick, red, green, blue);
1592
} else {
1593
result = SDL_SetError("SetLED failed, device disconnected");
1594
}
1595
1596
return result;
1597
}
1598
1599
static bool HIDAPI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
1600
{
1601
bool result;
1602
SDL_HIDAPI_Device *device = NULL;
1603
1604
if (HIDAPI_GetJoystickDevice(joystick, &device)) {
1605
result = device->driver->SendJoystickEffect(device, joystick, data, size);
1606
} else {
1607
result = SDL_SetError("SendEffect failed, device disconnected");
1608
}
1609
1610
return result;
1611
}
1612
1613
static bool HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)
1614
{
1615
bool result;
1616
SDL_HIDAPI_Device *device = NULL;
1617
1618
if (HIDAPI_GetJoystickDevice(joystick, &device)) {
1619
result = device->driver->SetJoystickSensorsEnabled(device, joystick, enabled);
1620
} else {
1621
result = SDL_SetError("SetSensorsEnabled failed, device disconnected");
1622
}
1623
1624
return result;
1625
}
1626
1627
static void HIDAPI_JoystickUpdate(SDL_Joystick *joystick)
1628
{
1629
// This is handled in SDL_HIDAPI_UpdateDevices()
1630
}
1631
1632
static void HIDAPI_JoystickClose(SDL_Joystick *joystick) SDL_NO_THREAD_SAFETY_ANALYSIS // We unlock the device lock so rumble can complete
1633
{
1634
SDL_AssertJoysticksLocked();
1635
1636
if (joystick->hwdata) {
1637
SDL_HIDAPI_Device *device = joystick->hwdata->device;
1638
int i;
1639
1640
// Wait up to 30 ms for pending rumble to complete
1641
if (device->updating) {
1642
// Unlock the device so rumble can complete
1643
SDL_UnlockMutex(device->dev_lock);
1644
}
1645
for (i = 0; i < 3; ++i) {
1646
if (SDL_GetAtomicInt(&device->rumble_pending) > 0) {
1647
SDL_Delay(10);
1648
}
1649
}
1650
if (device->updating) {
1651
// Relock the device
1652
SDL_LockMutex(device->dev_lock);
1653
}
1654
1655
device->driver->CloseJoystick(device, joystick);
1656
1657
SDL_free(joystick->hwdata);
1658
joystick->hwdata = NULL;
1659
}
1660
}
1661
1662
static void HIDAPI_JoystickQuit(void)
1663
{
1664
int i;
1665
1666
SDL_AssertJoysticksLocked();
1667
1668
shutting_down = true;
1669
1670
SDL_HIDAPI_QuitRumble();
1671
1672
while (SDL_HIDAPI_devices) {
1673
SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
1674
if (device->parent) {
1675
// When a child device goes away, so does the parent
1676
device = device->parent;
1677
for (i = 0; i < device->num_children; ++i) {
1678
HIDAPI_DelDevice(device->children[i]);
1679
}
1680
HIDAPI_DelDevice(device);
1681
} else {
1682
HIDAPI_DelDevice(device);
1683
}
1684
}
1685
1686
// Make sure the drivers cleaned up properly
1687
SDL_assert(SDL_HIDAPI_numjoysticks == 0);
1688
1689
for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
1690
SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
1691
driver->UnregisterHints(SDL_HIDAPIDriverHintChanged, driver);
1692
}
1693
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS,
1694
SDL_HIDAPIDriverHintChanged, NULL);
1695
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
1696
SDL_HIDAPIDriverHintChanged, NULL);
1697
1698
SDL_hid_exit();
1699
1700
SDL_HIDAPI_change_count = 0;
1701
shutting_down = false;
1702
initialized = false;
1703
}
1704
1705
static bool HIDAPI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
1706
{
1707
return false;
1708
}
1709
1710
SDL_JoystickDriver SDL_HIDAPI_JoystickDriver = {
1711
HIDAPI_JoystickInit,
1712
HIDAPI_JoystickGetCount,
1713
HIDAPI_JoystickDetect,
1714
HIDAPI_IsDevicePresent,
1715
HIDAPI_JoystickGetDeviceName,
1716
HIDAPI_JoystickGetDevicePath,
1717
HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot,
1718
HIDAPI_JoystickGetDevicePlayerIndex,
1719
HIDAPI_JoystickSetDevicePlayerIndex,
1720
HIDAPI_JoystickGetDeviceGUID,
1721
HIDAPI_JoystickGetDeviceInstanceID,
1722
HIDAPI_JoystickOpen,
1723
HIDAPI_JoystickRumble,
1724
HIDAPI_JoystickRumbleTriggers,
1725
HIDAPI_JoystickSetLED,
1726
HIDAPI_JoystickSendEffect,
1727
HIDAPI_JoystickSetSensorsEnabled,
1728
HIDAPI_JoystickUpdate,
1729
HIDAPI_JoystickClose,
1730
HIDAPI_JoystickQuit,
1731
HIDAPI_JoystickGetGamepadMapping
1732
};
1733
1734
#endif // SDL_JOYSTICK_HIDAPI
1735
1736