1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #ifdef SDL_JOYSTICK_HIDAPI
24
25 #include "SDL_atomic.h"
26 #include "SDL_endian.h"
27 #include "SDL_hints.h"
28 #include "SDL_timer.h"
29 #include "SDL_joystick.h"
30 #include "../SDL_sysjoystick.h"
31 #include "SDL_hidapijoystick_c.h"
32 #include "SDL_hidapi_rumble.h"
33 #include "../../SDL_hints_c.h"
34
35 #if defined(__WIN32__)
36 #include "../windows/SDL_rawinputjoystick_c.h"
37 #endif
38
39
40 struct joystick_hwdata
41 {
42 SDL_HIDAPI_Device *device;
43 };
44
45 static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
46 #ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
47 &SDL_HIDAPI_DriverGameCube,
48 #endif
49 #ifdef SDL_JOYSTICK_HIDAPI_LUNA
50 &SDL_HIDAPI_DriverLuna,
51 #endif
52 #ifdef SDL_JOYSTICK_HIDAPI_PS4
53 &SDL_HIDAPI_DriverPS4,
54 #endif
55 #ifdef SDL_JOYSTICK_HIDAPI_PS5
56 &SDL_HIDAPI_DriverPS5,
57 #endif
58 #ifdef SDL_JOYSTICK_HIDAPI_STADIA
59 &SDL_HIDAPI_DriverStadia,
60 #endif
61 #ifdef SDL_JOYSTICK_HIDAPI_STEAM
62 &SDL_HIDAPI_DriverSteam,
63 #endif
64 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH
65 &SDL_HIDAPI_DriverSwitch,
66 #endif
67 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
68 &SDL_HIDAPI_DriverXbox360,
69 &SDL_HIDAPI_DriverXbox360W,
70 #endif
71 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
72 &SDL_HIDAPI_DriverXboxOne,
73 #endif
74 };
75 static int SDL_HIDAPI_numdrivers = 0;
76 static SDL_SpinLock SDL_HIDAPI_spinlock;
77 static Uint32 SDL_HIDAPI_change_count = 0;
78 static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
79 static int SDL_HIDAPI_numjoysticks = 0;
80 static SDL_bool initialized = SDL_FALSE;
81 static SDL_bool shutting_down = SDL_FALSE;
82
83 void
HIDAPI_DumpPacket(const char * prefix,Uint8 * data,int size)84 HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size)
85 {
86 int i;
87 char *buffer;
88 size_t length = SDL_strlen(prefix) + 11*(USB_PACKET_LENGTH/8) + (5*USB_PACKET_LENGTH*2) + 1 + 1;
89 int start = 0, amount = size;
90
91 buffer = (char *)SDL_malloc(length);
92 SDL_snprintf(buffer, length, prefix, size);
93 for (i = start; i < start+amount; ++i) {
94 if ((i % 8) == 0) {
95 SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), "\n%.2d: ", i);
96 }
97 SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), " 0x%.2x", data[i]);
98 }
99 SDL_strlcat(buffer, "\n", length);
100 SDL_Log("%s", buffer);
101 SDL_free(buffer);
102 }
103
104 float
HIDAPI_RemapVal(float val,float val_min,float val_max,float output_min,float output_max)105 HIDAPI_RemapVal(float val, float val_min, float val_max, float output_min, float output_max)
106 {
107 return output_min + (output_max - output_min) * (val - val_min) / (val_max - val_min);
108 }
109
110 static void HIDAPI_JoystickDetect(void);
111 static void HIDAPI_JoystickClose(SDL_Joystick *joystick);
112
113 static SDL_bool
HIDAPI_IsDeviceSupported(Uint16 vendor_id,Uint16 product_id,Uint16 version,const char * name)114 HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
115 {
116 int i;
117 SDL_GameControllerType type = SDL_GetJoystickGameControllerType(name, vendor_id, product_id, -1, 0, 0, 0);
118
119 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
120 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
121 if (driver->enabled && driver->IsSupportedDevice(name, type, vendor_id, product_id, version, -1, 0, 0, 0)) {
122 return SDL_TRUE;
123 }
124 }
125 return SDL_FALSE;
126 }
127
128 static SDL_HIDAPI_DeviceDriver *
HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device * device)129 HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
130 {
131 const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;
132 const Uint16 USAGE_JOYSTICK = 0x0004;
133 const Uint16 USAGE_GAMEPAD = 0x0005;
134 const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
135 int i;
136 SDL_GameControllerType type;
137 SDL_JoystickGUID check_guid;
138
139 /* Make sure we have a generic GUID here, otherwise if we pass a HIDAPI
140 guid, this call will create a game controller mapping for the device.
141 */
142 check_guid = device->guid;
143 check_guid.data[14] = 0;
144 if (SDL_ShouldIgnoreJoystick(device->name, check_guid)) {
145 return NULL;
146 }
147
148 if (device->vendor_id != USB_VENDOR_VALVE) {
149 if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
150 return NULL;
151 }
152 if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
153 return NULL;
154 }
155 }
156
157 type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
158 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
159 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
160 if (driver->enabled && driver->IsSupportedDevice(device->name, type, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
161 return driver;
162 }
163 }
164 return NULL;
165 }
166
167 static SDL_HIDAPI_Device *
HIDAPI_GetDeviceByIndex(int device_index,SDL_JoystickID * pJoystickID)168 HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID)
169 {
170 SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
171 while (device) {
172 if (device->driver) {
173 if (device_index < device->num_joysticks) {
174 if (pJoystickID) {
175 *pJoystickID = device->joysticks[device_index];
176 }
177 return device;
178 }
179 device_index -= device->num_joysticks;
180 }
181 device = device->next;
182 }
183 return NULL;
184 }
185
186 static SDL_HIDAPI_Device *
HIDAPI_GetJoystickByInfo(const char * path,Uint16 vendor_id,Uint16 product_id)187 HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
188 {
189 SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
190 while (device) {
191 if (device->vendor_id == vendor_id && device->product_id == product_id &&
192 SDL_strcmp(device->path, path) == 0) {
193 break;
194 }
195 device = device->next;
196 }
197 return device;
198 }
199
200 static void
HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device * device)201 HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device)
202 {
203 if (device->driver) {
204 return; /* Already setup */
205 }
206
207 device->driver = HIDAPI_GetDeviceDriver(device);
208 if (device->driver) {
209 const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
210 if (name) {
211 SDL_free(device->name);
212 device->name = SDL_strdup(name);
213 }
214 }
215
216 /* Initialize the device, which may cause a connected event */
217 if (device->driver && !device->driver->InitDevice(device)) {
218 device->driver = NULL;
219 }
220 }
221
222 static void
HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device * device)223 HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
224 {
225 if (!device->driver) {
226 return; /* Already cleaned up */
227 }
228
229 /* Disconnect any joysticks */
230 while (device->num_joysticks) {
231 HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
232 }
233
234 device->driver->FreeDevice(device);
235 device->driver = NULL;
236 }
237
238 static void SDLCALL
SDL_HIDAPIDriverHintChanged(void * userdata,const char * name,const char * oldValue,const char * hint)239 SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
240 {
241 int i;
242 SDL_HIDAPI_Device *device;
243 SDL_bool enabled = SDL_GetStringBoolean(hint, SDL_TRUE);
244
245 if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
246 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
247 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
248 driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
249 }
250 } else {
251 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
252 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
253 if (SDL_strcmp(name, driver->hint) == 0) {
254 driver->enabled = enabled;
255 }
256 }
257 }
258
259 SDL_HIDAPI_numdrivers = 0;
260 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
261 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
262 if (driver->enabled) {
263 ++SDL_HIDAPI_numdrivers;
264 }
265 }
266
267 /* Update device list if driver availability changes */
268 SDL_LockJoysticks();
269
270 for (device = SDL_HIDAPI_devices; device; device = device->next) {
271 if (device->driver && !device->driver->enabled) {
272 HIDAPI_CleanupDeviceDriver(device);
273 }
274 HIDAPI_SetupDeviceDriver(device);
275 }
276
277 SDL_UnlockJoysticks();
278 }
279
280 static int
HIDAPI_JoystickInit(void)281 HIDAPI_JoystickInit(void)
282 {
283 int i;
284
285 if (initialized) {
286 return 0;
287 }
288
289 #if defined(SDL_USE_LIBUDEV)
290 if (linux_enumeration_method == ENUMERATION_UNSET) {
291 if (SDL_getenv("SDL_HIDAPI_JOYSTICK_DISABLE_UDEV") != NULL) {
292 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
293 "udev disabled by SDL_HIDAPI_JOYSTICK_DISABLE_UDEV");
294 linux_enumeration_method = ENUMERATION_FALLBACK;
295 } else if (access("/.flatpak-info", F_OK) == 0
296 || access("/run/host/container-manager", F_OK) == 0) {
297 /* Explicitly check `/.flatpak-info` because, for old versions of
298 * Flatpak, this was the only available way to tell if we were in
299 * a Flatpak container. */
300 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
301 "Container detected, disabling HIDAPI udev integration");
302 linux_enumeration_method = ENUMERATION_FALLBACK;
303 } else {
304 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
305 "Using udev for HIDAPI joystick device discovery");
306 linux_enumeration_method = ENUMERATION_LIBUDEV;
307 }
308 }
309 #endif
310
311 if (SDL_hid_init() < 0) {
312 SDL_SetError("Couldn't initialize hidapi");
313 return -1;
314 }
315
316 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
317 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
318 SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
319 }
320 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
321 SDL_HIDAPIDriverHintChanged, NULL);
322 HIDAPI_JoystickDetect();
323 HIDAPI_UpdateDevices();
324
325 initialized = SDL_TRUE;
326
327 return 0;
328 }
329
330 SDL_bool
HIDAPI_JoystickConnected(SDL_HIDAPI_Device * device,SDL_JoystickID * pJoystickID)331 HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
332 {
333 SDL_JoystickID joystickID;
334 SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks));
335 if (!joysticks) {
336 return SDL_FALSE;
337 }
338
339 joystickID = SDL_GetNextJoystickInstanceID();
340 device->joysticks = joysticks;
341 device->joysticks[device->num_joysticks++] = joystickID;
342 ++SDL_HIDAPI_numjoysticks;
343
344 SDL_PrivateJoystickAdded(joystickID);
345
346 if (pJoystickID) {
347 *pJoystickID = joystickID;
348 }
349 return SDL_TRUE;
350 }
351
352 void
HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device * device,SDL_JoystickID joystickID)353 HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
354 {
355 int i, size;
356
357 for (i = 0; i < device->num_joysticks; ++i) {
358 if (device->joysticks[i] == joystickID) {
359 SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joystickID);
360 if (joystick) {
361 HIDAPI_JoystickClose(joystick);
362 }
363
364 size = (device->num_joysticks - i - 1) * sizeof(SDL_JoystickID);
365 SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], size);
366 --device->num_joysticks;
367
368 --SDL_HIDAPI_numjoysticks;
369 if (device->num_joysticks == 0) {
370 SDL_free(device->joysticks);
371 device->joysticks = NULL;
372 }
373
374 if (!shutting_down) {
375 SDL_PrivateJoystickRemoved(joystickID);
376 }
377 return;
378 }
379 }
380 }
381
382 static int
HIDAPI_JoystickGetCount(void)383 HIDAPI_JoystickGetCount(void)
384 {
385 return SDL_HIDAPI_numjoysticks;
386 }
387
388 static char *
HIDAPI_ConvertString(const wchar_t * wide_string)389 HIDAPI_ConvertString(const wchar_t *wide_string)
390 {
391 char *string = NULL;
392
393 if (wide_string) {
394 string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
395 if (!string) {
396 switch (sizeof(wchar_t)) {
397 case 2:
398 string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
399 break;
400 case 4:
401 string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
402 break;
403 }
404 }
405 }
406 return string;
407 }
408
409 static void
HIDAPI_AddDevice(struct SDL_hid_device_info * info)410 HIDAPI_AddDevice(struct SDL_hid_device_info *info)
411 {
412 SDL_HIDAPI_Device *device;
413 SDL_HIDAPI_Device *curr, *last = NULL;
414
415 for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
416 continue;
417 }
418
419 device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
420 if (!device) {
421 return;
422 }
423 device->path = SDL_strdup(info->path);
424 if (!device->path) {
425 SDL_free(device);
426 return;
427 }
428 device->seen = SDL_TRUE;
429 device->vendor_id = info->vendor_id;
430 device->product_id = info->product_id;
431 device->version = info->release_number;
432 device->interface_number = info->interface_number;
433 device->interface_class = info->interface_class;
434 device->interface_subclass = info->interface_subclass;
435 device->interface_protocol = info->interface_protocol;
436 device->usage_page = info->usage_page;
437 device->usage = info->usage;
438 {
439 /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
440 const Uint16 vendor = device->vendor_id;
441 const Uint16 product = device->product_id;
442 const Uint16 version = device->version;
443 Uint16 *guid16 = (Uint16 *)device->guid.data;
444
445 *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
446 *guid16++ = 0;
447 *guid16++ = SDL_SwapLE16(vendor);
448 *guid16++ = 0;
449 *guid16++ = SDL_SwapLE16(product);
450 *guid16++ = 0;
451 *guid16++ = SDL_SwapLE16(version);
452 *guid16++ = 0;
453
454 /* Note that this is a HIDAPI device for special handling elsewhere */
455 device->guid.data[14] = 'h';
456 device->guid.data[15] = 0;
457 }
458 device->dev_lock = SDL_CreateMutex();
459
460 /* Need the device name before getting the driver to know whether to ignore this device */
461 {
462 char *manufacturer_string = HIDAPI_ConvertString(info->manufacturer_string);
463 char *product_string = HIDAPI_ConvertString(info->product_string);
464 char *serial_number = HIDAPI_ConvertString(info->serial_number);
465
466 device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string);
467 if (SDL_strncmp(device->name, "0x", 2) == 0) {
468 /* Couldn't find a controller name, try to give it one based on device type */
469 const char *name = NULL;
470
471 switch (SDL_GetJoystickGameControllerType(NULL, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
472 case SDL_CONTROLLER_TYPE_XBOX360:
473 name = "Xbox 360 Controller";
474 break;
475 case SDL_CONTROLLER_TYPE_XBOXONE:
476 name = "Xbox One Controller";
477 break;
478 case SDL_CONTROLLER_TYPE_PS3:
479 name = "PS3 Controller";
480 break;
481 case SDL_CONTROLLER_TYPE_PS4:
482 name = "PS4 Controller";
483 break;
484 case SDL_CONTROLLER_TYPE_PS5:
485 name = "PS5 Controller";
486 break;
487 case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
488 name = "Nintendo Switch Pro Controller";
489 break;
490 default:
491 break;
492 }
493
494 if (name) {
495 SDL_free(device->name);
496 device->name = SDL_strdup(name);
497 }
498 }
499
500 if (manufacturer_string) {
501 SDL_free(manufacturer_string);
502 }
503 if (product_string) {
504 SDL_free(product_string);
505 }
506
507 if (serial_number && *serial_number) {
508 device->serial = serial_number;
509 } else {
510 SDL_free(serial_number);
511 }
512
513 if (!device->name) {
514 SDL_free(device->serial);
515 SDL_free(device->path);
516 SDL_free(device);
517 return;
518 }
519 }
520
521 /* Add it to the list */
522 if (last) {
523 last->next = device;
524 } else {
525 SDL_HIDAPI_devices = device;
526 }
527
528 HIDAPI_SetupDeviceDriver(device);
529
530 #ifdef DEBUG_HIDAPI
531 SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, serial %s, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)\n", device->name, device->vendor_id, device->product_id, device->version, device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
532 #endif
533 }
534
535
536 static void
HIDAPI_DelDevice(SDL_HIDAPI_Device * device)537 HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
538 {
539 SDL_HIDAPI_Device *curr, *last;
540
541 #ifdef DEBUG_HIDAPI
542 SDL_Log("Removing HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, serial %s, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)\n", device->name, device->vendor_id, device->product_id, device->version, device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
543 #endif
544
545 for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
546 if (curr == device) {
547 if (last) {
548 last->next = curr->next;
549 } else {
550 SDL_HIDAPI_devices = curr->next;
551 }
552
553 HIDAPI_CleanupDeviceDriver(device);
554
555 /* Make sure the rumble thread is done with this device */
556 while (SDL_AtomicGet(&device->rumble_pending) > 0) {
557 SDL_Delay(10);
558 }
559
560 SDL_DestroyMutex(device->dev_lock);
561 SDL_free(device->serial);
562 SDL_free(device->name);
563 SDL_free(device->path);
564 SDL_free(device);
565 return;
566 }
567 }
568 }
569
570 static void
HIDAPI_UpdateDeviceList(void)571 HIDAPI_UpdateDeviceList(void)
572 {
573 SDL_HIDAPI_Device *device;
574 struct SDL_hid_device_info *devs, *info;
575
576 SDL_LockJoysticks();
577
578 /* Prepare the existing device list */
579 device = SDL_HIDAPI_devices;
580 while (device) {
581 device->seen = SDL_FALSE;
582 device = device->next;
583 }
584
585 /* Enumerate the devices */
586 if (SDL_HIDAPI_numdrivers > 0) {
587 devs = SDL_hid_enumerate(0, 0);
588 if (devs) {
589 for (info = devs; info; info = info->next) {
590 device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
591 if (device) {
592 device->seen = SDL_TRUE;
593 } else {
594 HIDAPI_AddDevice(info);
595 }
596 }
597 SDL_hid_free_enumeration(devs);
598 }
599 }
600
601 /* Remove any devices that weren't seen or have been disconnected due to read errors */
602 device = SDL_HIDAPI_devices;
603 while (device) {
604 SDL_HIDAPI_Device *next = device->next;
605
606 if (!device->seen ||
607 (device->driver && device->num_joysticks == 0 && !device->dev)) {
608 HIDAPI_DelDevice(device);
609 }
610 device = next;
611 }
612
613 SDL_UnlockJoysticks();
614 }
615
616 static SDL_bool
HIDAPI_IsEquivalentToDevice(Uint16 vendor_id,Uint16 product_id,SDL_HIDAPI_Device * device)617 HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Device *device)
618 {
619 if (vendor_id == device->vendor_id && product_id == device->product_id) {
620 return SDL_TRUE;
621 }
622
623 if (vendor_id == USB_VENDOR_MICROSOFT) {
624 /* If we're looking for the wireless XBox 360 controller, also look for the dongle */
625 if (product_id == USB_PRODUCT_XBOX360_XUSB_CONTROLLER && device->product_id == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER) {
626 return SDL_TRUE;
627 }
628
629 /* If we're looking for the raw input Xbox One controller, match it against any other Xbox One controller */
630 if (product_id == USB_PRODUCT_XBOX_ONE_XBOXGIP_CONTROLLER &&
631 SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol) == SDL_CONTROLLER_TYPE_XBOXONE) {
632 return SDL_TRUE;
633 }
634
635 /* If we're looking for an XInput controller, match it against any other Xbox controller */
636 if (product_id == USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER) {
637 SDL_GameControllerType type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
638 if (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) {
639 return SDL_TRUE;
640 }
641 }
642 }
643 return SDL_FALSE;
644 }
645
646 SDL_bool
HIDAPI_IsDeviceTypePresent(SDL_GameControllerType type)647 HIDAPI_IsDeviceTypePresent(SDL_GameControllerType type)
648 {
649 SDL_HIDAPI_Device *device;
650 SDL_bool result = SDL_FALSE;
651
652 /* Make sure we're initialized, as this could be called from other drivers during startup */
653 if (HIDAPI_JoystickInit() < 0) {
654 return SDL_FALSE;
655 }
656
657 if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
658 HIDAPI_UpdateDeviceList();
659 SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
660 }
661
662 SDL_LockJoysticks();
663 device = SDL_HIDAPI_devices;
664 while (device) {
665 if (device->driver &&
666 SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol) == type) {
667 result = SDL_TRUE;
668 break;
669 }
670 device = device->next;
671 }
672 SDL_UnlockJoysticks();
673
674 #ifdef DEBUG_HIDAPI
675 SDL_Log("HIDAPI_IsDeviceTypePresent() returning %s for %d\n", result ? "true" : "false", type);
676 #endif
677 return result;
678 }
679
680 SDL_bool
HIDAPI_IsDevicePresent(Uint16 vendor_id,Uint16 product_id,Uint16 version,const char * name)681 HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
682 {
683 SDL_HIDAPI_Device *device;
684 SDL_bool supported = SDL_FALSE;
685 SDL_bool result = SDL_FALSE;
686
687 /* Make sure we're initialized, as this could be called from other drivers during startup */
688 if (HIDAPI_JoystickInit() < 0) {
689 return SDL_FALSE;
690 }
691
692 /* Only update the device list for devices we know might be supported.
693 If we did this for every device, it would hit the USB driver too hard and potentially
694 lock up the system. This won't catch devices that we support but can only detect using
695 USB interface details, like Xbox controllers, but hopefully the device list update is
696 responsive enough to catch those.
697 */
698 supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name);
699 #if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)
700 if (!supported &&
701 (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) {
702 supported = SDL_TRUE;
703 }
704 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE */
705 if (supported) {
706 if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
707 HIDAPI_UpdateDeviceList();
708 SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
709 }
710 }
711
712 /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
713 or a different name than we have it listed here, etc, but if we support the device
714 and we have something similar in our device list, mark it as present.
715 */
716 SDL_LockJoysticks();
717 device = SDL_HIDAPI_devices;
718 while (device) {
719 if (device->driver &&
720 HIDAPI_IsEquivalentToDevice(vendor_id, product_id, device)) {
721 result = SDL_TRUE;
722 break;
723 }
724 device = device->next;
725 }
726 SDL_UnlockJoysticks();
727
728 #ifdef DEBUG_HIDAPI
729 SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x\n", result ? "true" : "false", vendor_id, product_id);
730 #endif
731 return result;
732 }
733
734 static void
HIDAPI_JoystickDetect(void)735 HIDAPI_JoystickDetect(void)
736 {
737 if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
738 Uint32 count = SDL_hid_device_change_count();
739 if (SDL_HIDAPI_change_count != count) {
740 HIDAPI_UpdateDeviceList();
741 SDL_HIDAPI_change_count = count;
742 }
743 SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
744 }
745 }
746
747 void
HIDAPI_UpdateDevices(void)748 HIDAPI_UpdateDevices(void)
749 {
750 SDL_HIDAPI_Device *device;
751
752 /* Update the devices, which may change connected joysticks and send events */
753
754 /* Prepare the existing device list */
755 if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
756 device = SDL_HIDAPI_devices;
757 while (device) {
758 if (device->driver) {
759 if (SDL_TryLockMutex(device->dev_lock) == 0) {
760 device->updating = SDL_TRUE;
761 device->driver->UpdateDevice(device);
762 device->updating = SDL_FALSE;
763 SDL_UnlockMutex(device->dev_lock);
764 }
765 }
766 device = device->next;
767 }
768 SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
769 }
770 }
771
772 static const char *
HIDAPI_JoystickGetDeviceName(int device_index)773 HIDAPI_JoystickGetDeviceName(int device_index)
774 {
775 SDL_HIDAPI_Device *device;
776 const char *name = NULL;
777
778 device = HIDAPI_GetDeviceByIndex(device_index, NULL);
779 if (device) {
780 /* FIXME: The device could be freed after this name is returned... */
781 name = device->name;
782 }
783
784 return name;
785 }
786
787 static int
HIDAPI_JoystickGetDevicePlayerIndex(int device_index)788 HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
789 {
790 SDL_HIDAPI_Device *device;
791 SDL_JoystickID instance_id;
792 int player_index = -1;
793
794 device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
795 if (device) {
796 player_index = device->driver->GetDevicePlayerIndex(device, instance_id);
797 }
798
799 return player_index;
800 }
801
802 static void
HIDAPI_JoystickSetDevicePlayerIndex(int device_index,int player_index)803 HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
804 {
805 SDL_HIDAPI_Device *device;
806 SDL_JoystickID instance_id;
807
808 device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
809 if (device) {
810 device->driver->SetDevicePlayerIndex(device, instance_id, player_index);
811 }
812 }
813
814 static SDL_JoystickGUID
HIDAPI_JoystickGetDeviceGUID(int device_index)815 HIDAPI_JoystickGetDeviceGUID(int device_index)
816 {
817 SDL_HIDAPI_Device *device;
818 SDL_JoystickGUID guid;
819
820 device = HIDAPI_GetDeviceByIndex(device_index, NULL);
821 if (device) {
822 SDL_memcpy(&guid, &device->guid, sizeof(guid));
823 } else {
824 SDL_zero(guid);
825 }
826
827 return guid;
828 }
829
830 static SDL_JoystickID
HIDAPI_JoystickGetDeviceInstanceID(int device_index)831 HIDAPI_JoystickGetDeviceInstanceID(int device_index)
832 {
833 SDL_JoystickID joystickID = -1;
834 HIDAPI_GetDeviceByIndex(device_index, &joystickID);
835 return joystickID;
836 }
837
838 static int
HIDAPI_JoystickOpen(SDL_Joystick * joystick,int device_index)839 HIDAPI_JoystickOpen(SDL_Joystick *joystick, int device_index)
840 {
841 SDL_JoystickID joystickID;
842 SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
843 struct joystick_hwdata *hwdata;
844
845 hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
846 if (!hwdata) {
847 return SDL_OutOfMemory();
848 }
849 hwdata->device = device;
850
851 if (!device->driver->OpenJoystick(device, joystick)) {
852 SDL_free(hwdata);
853 return -1;
854 }
855
856 if (!joystick->serial && device->serial) {
857 joystick->serial = SDL_strdup(device->serial);
858 }
859
860 joystick->hwdata = hwdata;
861 return 0;
862 }
863
864 static int
HIDAPI_JoystickRumble(SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)865 HIDAPI_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
866 {
867 int result;
868
869 if (joystick->hwdata) {
870 SDL_HIDAPI_Device *device = joystick->hwdata->device;
871
872 result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble);
873 } else {
874 SDL_SetError("Rumble failed, device disconnected");
875 result = -1;
876 }
877
878 return result;
879 }
880
881 static int
HIDAPI_JoystickRumbleTriggers(SDL_Joystick * joystick,Uint16 left_rumble,Uint16 right_rumble)882 HIDAPI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
883 {
884 int result;
885
886 if (joystick->hwdata) {
887 SDL_HIDAPI_Device *device = joystick->hwdata->device;
888
889 result = device->driver->RumbleJoystickTriggers(device, joystick, left_rumble, right_rumble);
890 } else {
891 SDL_SetError("Rumble failed, device disconnected");
892 result = -1;
893 }
894
895 return result;
896 }
897
898 static Uint32
HIDAPI_JoystickGetCapabilities(SDL_Joystick * joystick)899 HIDAPI_JoystickGetCapabilities(SDL_Joystick *joystick)
900 {
901 Uint32 result = 0;
902
903 if (joystick->hwdata) {
904 SDL_HIDAPI_Device *device = joystick->hwdata->device;
905
906 result = device->driver->GetJoystickCapabilities(device, joystick);
907 }
908
909 return result;
910 }
911
912 static int
HIDAPI_JoystickSetLED(SDL_Joystick * joystick,Uint8 red,Uint8 green,Uint8 blue)913 HIDAPI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
914 {
915 int result;
916
917 if (joystick->hwdata) {
918 SDL_HIDAPI_Device *device = joystick->hwdata->device;
919
920 result = device->driver->SetJoystickLED(device, joystick, red, green, blue);
921 } else {
922 SDL_SetError("SetLED failed, device disconnected");
923 result = -1;
924 }
925
926 return result;
927 }
928
929 static int
HIDAPI_JoystickSendEffect(SDL_Joystick * joystick,const void * data,int size)930 HIDAPI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
931 {
932 int result;
933
934 if (joystick->hwdata) {
935 SDL_HIDAPI_Device *device = joystick->hwdata->device;
936
937 result = device->driver->SendJoystickEffect(device, joystick, data, size);
938 } else {
939 SDL_SetError("SendEffect failed, device disconnected");
940 result = -1;
941 }
942
943 return result;
944 }
945
946 static int
HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick * joystick,SDL_bool enabled)947 HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
948 {
949 int result;
950
951 if (joystick->hwdata) {
952 SDL_HIDAPI_Device *device = joystick->hwdata->device;
953
954 result = device->driver->SetJoystickSensorsEnabled(device, joystick, enabled);
955 } else {
956 SDL_SetError("SetSensorsEnabled failed, device disconnected");
957 result = -1;
958 }
959
960 return result;
961 }
962
963 static void
HIDAPI_JoystickUpdate(SDL_Joystick * joystick)964 HIDAPI_JoystickUpdate(SDL_Joystick *joystick)
965 {
966 /* This is handled in SDL_HIDAPI_UpdateDevices() */
967 }
968
969 static void
HIDAPI_JoystickClose(SDL_Joystick * joystick)970 HIDAPI_JoystickClose(SDL_Joystick *joystick)
971 {
972 if (joystick->hwdata) {
973 SDL_HIDAPI_Device *device = joystick->hwdata->device;
974 int i;
975
976 /* Wait up to 30 ms for pending rumble to complete */
977 if (device->updating) {
978 /* Unlock the device so rumble can complete */
979 SDL_UnlockMutex(device->dev_lock);
980 }
981 for (i = 0; i < 3; ++i) {
982 if (SDL_AtomicGet(&device->rumble_pending) > 0) {
983 SDL_Delay(10);
984 }
985 }
986 if (device->updating) {
987 /* Relock the device */
988 SDL_LockMutex(device->dev_lock);
989 }
990
991 device->driver->CloseJoystick(device, joystick);
992
993 SDL_free(joystick->hwdata);
994 joystick->hwdata = NULL;
995 }
996 }
997
998 static void
HIDAPI_JoystickQuit(void)999 HIDAPI_JoystickQuit(void)
1000 {
1001 int i;
1002
1003 shutting_down = SDL_TRUE;
1004
1005 SDL_HIDAPI_QuitRumble();
1006
1007 while (SDL_HIDAPI_devices) {
1008 HIDAPI_DelDevice(SDL_HIDAPI_devices);
1009 }
1010
1011 /* Make sure the drivers cleaned up properly */
1012 SDL_assert(SDL_HIDAPI_numjoysticks == 0);
1013
1014 for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
1015 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
1016 SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
1017 }
1018 SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
1019 SDL_HIDAPIDriverHintChanged, NULL);
1020
1021 SDL_hid_exit();
1022
1023 shutting_down = SDL_FALSE;
1024 initialized = SDL_FALSE;
1025 }
1026
1027 static SDL_bool
HIDAPI_JoystickGetGamepadMapping(int device_index,SDL_GamepadMapping * out)1028 HIDAPI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
1029 {
1030 return SDL_FALSE;
1031 }
1032
1033 SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
1034 {
1035 HIDAPI_JoystickInit,
1036 HIDAPI_JoystickGetCount,
1037 HIDAPI_JoystickDetect,
1038 HIDAPI_JoystickGetDeviceName,
1039 HIDAPI_JoystickGetDevicePlayerIndex,
1040 HIDAPI_JoystickSetDevicePlayerIndex,
1041 HIDAPI_JoystickGetDeviceGUID,
1042 HIDAPI_JoystickGetDeviceInstanceID,
1043 HIDAPI_JoystickOpen,
1044 HIDAPI_JoystickRumble,
1045 HIDAPI_JoystickRumbleTriggers,
1046 HIDAPI_JoystickGetCapabilities,
1047 HIDAPI_JoystickSetLED,
1048 HIDAPI_JoystickSendEffect,
1049 HIDAPI_JoystickSetSensorsEnabled,
1050 HIDAPI_JoystickUpdate,
1051 HIDAPI_JoystickClose,
1052 HIDAPI_JoystickQuit,
1053 HIDAPI_JoystickGetGamepadMapping
1054 };
1055
1056 #endif /* SDL_JOYSTICK_HIDAPI */
1057
1058 /* vi: set ts=4 sw=4 expandtab: */
1059