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