1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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_assert.h"
26 #include "SDL_atomic.h"
27 #include "SDL_endian.h"
28 #include "SDL_hints.h"
29 #include "SDL_thread.h"
30 #include "SDL_timer.h"
31 #include "SDL_joystick.h"
32 #include "../SDL_sysjoystick.h"
33 #include "SDL_hidapijoystick_c.h"
34 #include "SDL_hidapi_rumble.h"
35 #include "../../SDL_hints_c.h"
36 
37 #if defined(__WIN32__)
38 #include "../../core/windows/SDL_windows.h"
39 #include "../windows/SDL_rawinputjoystick_c.h"
40 #endif
41 
42 #if defined(__MACOSX__)
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <mach/mach.h>
45 #include <IOKit/IOKitLib.h>
46 #include <IOKit/hid/IOHIDDevice.h>
47 #include <IOKit/usb/USBSpec.h>
48 #endif
49 
50 #if defined(__LINUX__)
51 #include "../../core/linux/SDL_udev.h"
52 #ifdef SDL_USE_LIBUDEV
53 #include <poll.h>
54 #endif
55 #endif
56 
57 struct joystick_hwdata
58 {
59     SDL_HIDAPI_Device *device;
60 };
61 
62 static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
63 #ifdef SDL_JOYSTICK_HIDAPI_PS4
64     &SDL_HIDAPI_DriverPS4,
65 #endif
66 #ifdef SDL_JOYSTICK_HIDAPI_STEAM
67     &SDL_HIDAPI_DriverSteam,
68 #endif
69 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH
70     &SDL_HIDAPI_DriverSwitch,
71 #endif
72 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
73     &SDL_HIDAPI_DriverXbox360,
74     &SDL_HIDAPI_DriverXbox360W,
75 #endif
76 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
77     &SDL_HIDAPI_DriverXboxOne,
78 #endif
79 #ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
80     &SDL_HIDAPI_DriverGameCube,
81 #endif
82 };
83 static int SDL_HIDAPI_numdrivers = 0;
84 static SDL_SpinLock SDL_HIDAPI_spinlock;
85 static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
86 static int SDL_HIDAPI_numjoysticks = 0;
87 static SDL_bool initialized = SDL_FALSE;
88 static SDL_bool shutting_down = SDL_FALSE;
89 
90 #if defined(SDL_USE_LIBUDEV)
91 static const SDL_UDEV_Symbols * usyms = NULL;
92 #endif
93 
94 static struct
95 {
96     SDL_bool m_bHaveDevicesChanged;
97     SDL_bool m_bCanGetNotifications;
98     Uint32 m_unLastDetect;
99 
100 #if defined(__WIN32__)
101     SDL_threadID m_nThreadID;
102     WNDCLASSEXA m_wndClass;
103     HWND m_hwndMsg;
104     HDEVNOTIFY m_hNotify;
105     double m_flLastWin32MessageCheck;
106 #endif
107 
108 #if defined(__MACOSX__)
109     IONotificationPortRef m_notificationPort;
110     mach_port_t m_notificationMach;
111 #endif
112 
113 #if defined(SDL_USE_LIBUDEV)
114     struct udev *m_pUdev;
115     struct udev_monitor *m_pUdevMonitor;
116     int m_nUdevFd;
117 #endif
118 } SDL_HIDAPI_discovery;
119 
120 
121 #ifdef __WIN32__
122 struct _DEV_BROADCAST_HDR
123 {
124     DWORD       dbch_size;
125     DWORD       dbch_devicetype;
126     DWORD       dbch_reserved;
127 };
128 
129 typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
130 {
131     DWORD       dbcc_size;
132     DWORD       dbcc_devicetype;
133     DWORD       dbcc_reserved;
134     GUID        dbcc_classguid;
135     char        dbcc_name[ 1 ];
136 } DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
137 
138 typedef struct  _DEV_BROADCAST_HDR      DEV_BROADCAST_HDR;
139 #define DBT_DEVICEARRIVAL               0x8000  /* system detected a new device */
140 #define DBT_DEVICEREMOVECOMPLETE        0x8004  /* device was removed from the system */
141 #define DBT_DEVTYP_DEVICEINTERFACE      0x00000005  /* device interface class */
142 #define DBT_DEVNODES_CHANGED            0x0007
143 #define DBT_CONFIGCHANGED               0x0018
144 #define DBT_DEVICETYPESPECIFIC          0x8005  /* type specific event */
145 #define DBT_DEVINSTSTARTED              0x8008  /* device installed and started */
146 
147 #include <initguid.h>
148 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
149 
ControllerWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)150 static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
151 {
152     switch (message) {
153     case WM_DEVICECHANGE:
154         switch (wParam) {
155         case DBT_DEVICEARRIVAL:
156         case DBT_DEVICEREMOVECOMPLETE:
157             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
158                 SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
159             }
160             break;
161         }
162         return TRUE;
163     }
164 
165     return DefWindowProc(hwnd, message, wParam, lParam);
166 }
167 #endif /* __WIN32__ */
168 
169 
170 #if defined(__MACOSX__)
CallbackIOServiceFunc(void * context,io_iterator_t portIterator)171 static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator)
172 {
173     /* Must drain the iterator, or we won't receive new notifications */
174     io_object_t entry;
175     while ((entry = IOIteratorNext(portIterator)) != 0) {
176         IOObjectRelease(entry);
177         *(SDL_bool*)context = SDL_TRUE;
178     }
179 }
180 #endif /* __MACOSX__ */
181 
182 static void
HIDAPI_InitializeDiscovery()183 HIDAPI_InitializeDiscovery()
184 {
185     SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
186     SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_FALSE;
187     SDL_HIDAPI_discovery.m_unLastDetect = 0;
188 
189 #if defined(__WIN32__)
190     SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
191 
192     SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass));
193     SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
194     SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
195     SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc;      /* This function is called by windows */
196     SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX);
197 
198     RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass);
199     SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
200 
201     {
202         DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
203         SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) );
204 
205         devBroadcast.dbcc_size = sizeof( devBroadcast );
206         devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
207         devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
208 
209         /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored,
210          * but that seems to be necessary to get a notice after each individual usb input device actually
211          * installs, rather than just as the composite device is seen.
212          */
213         SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification( SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES );
214         SDL_HIDAPI_discovery.m_bCanGetNotifications = ( SDL_HIDAPI_discovery.m_hNotify != 0 );
215     }
216 #endif /* __WIN32__ */
217 
218 #if defined(__MACOSX__)
219     SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
220     if (SDL_HIDAPI_discovery.m_notificationPort) {
221         {
222             io_iterator_t portIterator = 0;
223             io_object_t entry;
224             IOReturn result = IOServiceAddMatchingNotification(
225                 SDL_HIDAPI_discovery.m_notificationPort,
226                 kIOFirstMatchNotification,
227                 IOServiceMatching(kIOHIDDeviceKey),
228                 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
229 
230             if (result == 0) {
231                 /* Must drain the existing iterator, or we won't receive new notifications */
232                 while ((entry = IOIteratorNext(portIterator)) != 0) {
233                     IOObjectRelease(entry);
234                 }
235             } else {
236                 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
237                 SDL_HIDAPI_discovery.m_notificationPort = nil;
238             }
239         }
240         {
241             io_iterator_t portIterator = 0;
242             io_object_t entry;
243             IOReturn result = IOServiceAddMatchingNotification(
244                 SDL_HIDAPI_discovery.m_notificationPort,
245                 kIOTerminatedNotification,
246                 IOServiceMatching(kIOHIDDeviceKey),
247                 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
248 
249             if (result == 0) {
250                 /* Must drain the existing iterator, or we won't receive new notifications */
251                 while ((entry = IOIteratorNext(portIterator)) != 0) {
252                     IOObjectRelease(entry);
253                 }
254             } else {
255                 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
256                 SDL_HIDAPI_discovery.m_notificationPort = nil;
257             }
258         }
259     }
260 
261     SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL;
262     if (SDL_HIDAPI_discovery.m_notificationPort) {
263         SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort);
264     }
265 
266     SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL);
267 
268 #endif // __MACOSX__
269 
270 #if defined(SDL_USE_LIBUDEV)
271     SDL_HIDAPI_discovery.m_pUdev = NULL;
272     SDL_HIDAPI_discovery.m_pUdevMonitor = NULL;
273     SDL_HIDAPI_discovery.m_nUdevFd = -1;
274 
275     usyms = SDL_UDEV_GetUdevSyms();
276     if (usyms) {
277         SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new();
278     }
279     if (SDL_HIDAPI_discovery.m_pUdev) {
280         SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev");
281         if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
282             usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor);
283             SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor);
284             SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_TRUE;
285         }
286     }
287 
288 #endif /* SDL_USE_LIBUDEV */
289 }
290 
291 static void
HIDAPI_UpdateDiscovery()292 HIDAPI_UpdateDiscovery()
293 {
294     if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) {
295         const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000;  /* Update every 3 seconds */
296         Uint32 now = SDL_GetTicks();
297         if (!SDL_HIDAPI_discovery.m_unLastDetect || SDL_TICKS_PASSED(now, SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) {
298             SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
299             SDL_HIDAPI_discovery.m_unLastDetect = now;
300         }
301         return;
302     }
303 
304 #if defined(__WIN32__)
305 #if 0 /* just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan. */
306     /* We'll only get messages on the same thread that created the window */
307     if (SDL_ThreadID() == SDL_HIDAPI_discovery.m_nThreadID) {
308         MSG msg;
309         while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) {
310             if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) {
311                 TranslateMessage(&msg);
312                 DispatchMessage(&msg);
313             }
314         }
315     }
316 #endif
317 #endif /* __WIN32__ */
318 
319 #if defined(__MACOSX__)
320     if (SDL_HIDAPI_discovery.m_notificationPort) {
321         struct { mach_msg_header_t hdr; char payload[ 4096 ]; } msg;
322         while (mach_msg(&msg.hdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg), SDL_HIDAPI_discovery.m_notificationMach, 0, MACH_PORT_NULL) == KERN_SUCCESS) {
323             IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort);
324         }
325     }
326 #endif
327 
328 #if defined(SDL_USE_LIBUDEV)
329     if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) {
330         /* Drain all notification events.
331          * We don't expect a lot of device notifications so just
332          * do a new discovery on any kind or number of notifications.
333          * This could be made more restrictive if necessary.
334          */
335         for (;;) {
336             struct pollfd PollUdev;
337             struct udev_device *pUdevDevice;
338 
339             PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd;
340             PollUdev.events = POLLIN;
341             if (poll(&PollUdev, 1, 0) != 1) {
342                 break;
343             }
344 
345             SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
346 
347             pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor);
348             if (pUdevDevice) {
349                 usyms->udev_device_unref(pUdevDevice);
350             }
351         }
352     }
353 #endif
354 }
355 
356 static void
HIDAPI_ShutdownDiscovery()357 HIDAPI_ShutdownDiscovery()
358 {
359 #if defined(__WIN32__)
360     if (SDL_HIDAPI_discovery.m_hNotify)
361         UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify);
362 
363     if (SDL_HIDAPI_discovery.m_hwndMsg) {
364         DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg);
365     }
366 
367     UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance);
368 #endif
369 
370 #if defined(__MACOSX__)
371     if (SDL_HIDAPI_discovery.m_notificationPort) {
372         IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
373     }
374 #endif
375 
376 #if defined(SDL_USE_LIBUDEV)
377     if (usyms) {
378         if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
379             usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor);
380         }
381         if (SDL_HIDAPI_discovery.m_pUdev) {
382             usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev);
383         }
384         SDL_UDEV_ReleaseUdevSyms();
385         usyms = NULL;
386     }
387 #endif
388 }
389 
390 static void HIDAPI_JoystickDetect(void);
391 static void HIDAPI_JoystickClose(SDL_Joystick * joystick);
392 
393 static SDL_bool
HIDAPI_IsDeviceSupported(Uint16 vendor_id,Uint16 product_id,Uint16 version,const char * name)394 HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
395 {
396     int i;
397     SDL_GameControllerType type = SDL_GetJoystickGameControllerType(name, vendor_id, product_id, -1, 0, 0, 0);
398 
399     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
400         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
401         if (driver->enabled && driver->IsSupportedDevice(name, type, vendor_id, product_id, version, -1, 0, 0, 0)) {
402             return SDL_TRUE;
403         }
404     }
405     return SDL_FALSE;
406 }
407 
408 static SDL_HIDAPI_DeviceDriver *
HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device * device)409 HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
410 {
411     const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;
412     const Uint16 USAGE_JOYSTICK = 0x0004;
413     const Uint16 USAGE_GAMEPAD = 0x0005;
414     const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
415     int i;
416     SDL_GameControllerType type;
417 
418     if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
419         return NULL;
420     }
421 
422 #ifdef SDL_JOYSTICK_RAWINPUT
423     if (RAWINPUT_IsDevicePresent(device->vendor_id, device->product_id, device->version)) {
424         /* The RAWINPUT driver is taking care of this device */
425         return NULL;
426     }
427 #endif
428 
429 	if (device->vendor_id != USB_VENDOR_VALVE) {
430         if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
431             return NULL;
432         }
433         if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
434             return NULL;
435         }
436     }
437 
438     type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
439     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
440         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
441         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)) {
442             return driver;
443         }
444     }
445     return NULL;
446 }
447 
448 static SDL_HIDAPI_Device *
HIDAPI_GetDeviceByIndex(int device_index,SDL_JoystickID * pJoystickID)449 HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID)
450 {
451     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
452     while (device) {
453         if (device->driver) {
454             if (device_index < device->num_joysticks) {
455                 if (pJoystickID) {
456                     *pJoystickID = device->joysticks[device_index];
457                 }
458                 return device;
459             }
460             device_index -= device->num_joysticks;
461         }
462         device = device->next;
463     }
464     return NULL;
465 }
466 
467 static SDL_HIDAPI_Device *
HIDAPI_GetJoystickByInfo(const char * path,Uint16 vendor_id,Uint16 product_id)468 HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
469 {
470     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
471     while (device) {
472         if (device->vendor_id == vendor_id && device->product_id == product_id &&
473             SDL_strcmp(device->path, path) == 0) {
474             break;
475         }
476         device = device->next;
477     }
478     return device;
479 }
480 
481 static void
HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device * device)482 HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device)
483 {
484     if (device->driver) {
485         /* Already setup */
486         return;
487     }
488 
489     device->driver = HIDAPI_GetDeviceDriver(device);
490     if (device->driver) {
491         const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
492         if (name) {
493             SDL_free(device->name);
494             device->name = SDL_strdup(name);
495         }
496     }
497 
498     /* Initialize the device, which may cause a connected event */
499     if (device->driver && !device->driver->InitDevice(device)) {
500         device->driver = NULL;
501     }
502 }
503 
504 static void
HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device * device)505 HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
506 {
507     if (!device->driver) {
508         /* Already cleaned up */
509         return;
510     }
511 
512     /* Disconnect any joysticks */
513     while (device->num_joysticks) {
514         HIDAPI_JoystickDisconnected(device, device->joysticks[0], SDL_FALSE);
515     }
516 
517     device->driver->FreeDevice(device);
518     device->driver = NULL;
519 }
520 
521 static void SDLCALL
SDL_HIDAPIDriverHintChanged(void * userdata,const char * name,const char * oldValue,const char * hint)522 SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
523 {
524     int i;
525     SDL_HIDAPI_Device *device;
526     SDL_bool enabled = SDL_GetStringBoolean(hint, SDL_TRUE);
527 
528     if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
529         for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
530             SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
531             driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
532         }
533     } else {
534         for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
535             SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
536             if (SDL_strcmp(name, driver->hint) == 0) {
537                 driver->enabled = enabled;
538             }
539         }
540     }
541 
542     SDL_HIDAPI_numdrivers = 0;
543     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
544         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
545         if (driver->enabled) {
546             ++SDL_HIDAPI_numdrivers;
547         }
548     }
549 
550     /* Update device list if driver availability changes */
551     SDL_LockJoysticks();
552 
553     for (device = SDL_HIDAPI_devices; device; device = device->next) {
554         if (device->driver && !device->driver->enabled) {
555             HIDAPI_CleanupDeviceDriver(device);
556         }
557         HIDAPI_SetupDeviceDriver(device);
558     }
559 
560     SDL_UnlockJoysticks();
561 }
562 
563 static int
HIDAPI_JoystickInit(void)564 HIDAPI_JoystickInit(void)
565 {
566     int i;
567 
568     if (initialized) {
569         return 0;
570     }
571 
572 #if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__)
573 	/* The hidapi framwork is weak-linked on Apple platforms */
574     int HID_API_EXPORT HID_API_CALL hid_init(void) __attribute__((weak_import));
575 
576     if (hid_init == NULL) {
577         SDL_SetError("Couldn't initialize hidapi, framework not available");
578         return -1;
579     }
580 #endif /* __MACOSX__ || __IPHONEOS__ || __TVOS__ */
581 
582     if (hid_init() < 0) {
583         SDL_SetError("Couldn't initialize hidapi");
584         return -1;
585     }
586 
587 #ifdef __WINDOWS__
588     /* On Windows, turns out HIDAPI for Xbox controllers doesn't allow background input, so off by default */
589     SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0", SDL_HINT_DEFAULT);
590 #endif
591 
592     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
593         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
594         SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
595     }
596     SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
597                         SDL_HIDAPIDriverHintChanged, NULL);
598     HIDAPI_InitializeDiscovery();
599     HIDAPI_JoystickDetect();
600     HIDAPI_UpdateDevices();
601 
602     initialized = SDL_TRUE;
603 
604     return 0;
605 }
606 
607 SDL_bool
HIDAPI_JoystickConnected(SDL_HIDAPI_Device * device,SDL_JoystickID * pJoystickID,SDL_bool is_external)608 HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID, SDL_bool is_external)
609 {
610     SDL_JoystickID joystickID;
611     SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks));
612     if (!joysticks) {
613         return SDL_FALSE;
614     }
615 
616     joystickID = SDL_GetNextJoystickInstanceID();
617     device->joysticks = joysticks;
618     device->joysticks[device->num_joysticks++] = joystickID;
619     if (!is_external) {
620         ++SDL_HIDAPI_numjoysticks;
621     }
622 
623     SDL_PrivateJoystickAdded(joystickID);
624 
625     if (pJoystickID) {
626         *pJoystickID = joystickID;
627     }
628     return SDL_TRUE;
629 }
630 
631 void
HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device * device,SDL_JoystickID joystickID,SDL_bool is_external)632 HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID, SDL_bool is_external)
633 {
634     int i, size;
635 
636     for (i = 0; i < device->num_joysticks; ++i) {
637         if (device->joysticks[i] == joystickID) {
638             SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joystickID);
639             if (joystick && !is_external) {
640                 HIDAPI_JoystickClose(joystick);
641             }
642 
643             size = (device->num_joysticks - i - 1) * sizeof(SDL_JoystickID);
644             SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], size);
645             --device->num_joysticks;
646 
647             if (!is_external) {
648                 --SDL_HIDAPI_numjoysticks;
649             }
650             if (device->num_joysticks == 0) {
651                 SDL_free(device->joysticks);
652                 device->joysticks = NULL;
653             }
654 
655             if (!shutting_down) {
656                 SDL_PrivateJoystickRemoved(joystickID);
657             }
658             return;
659         }
660     }
661 }
662 
663 static int
HIDAPI_JoystickGetCount(void)664 HIDAPI_JoystickGetCount(void)
665 {
666     return SDL_HIDAPI_numjoysticks;
667 }
668 
669 static void
HIDAPI_AddDevice(struct hid_device_info * info)670 HIDAPI_AddDevice(struct hid_device_info *info)
671 {
672     SDL_HIDAPI_Device *device;
673     SDL_HIDAPI_Device *curr, *last = NULL;
674 
675     for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
676         continue;
677     }
678 
679     device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
680     if (!device) {
681         return;
682     }
683     device->path = SDL_strdup(info->path);
684     if (!device->path) {
685         SDL_free(device);
686         return;
687     }
688     device->seen = SDL_TRUE;
689     device->vendor_id = info->vendor_id;
690     device->product_id = info->product_id;
691     device->version = info->release_number;
692     device->interface_number = info->interface_number;
693     device->interface_class = info->interface_class;
694     device->interface_subclass = info->interface_subclass;
695     device->interface_protocol = info->interface_protocol;
696     device->usage_page = info->usage_page;
697     device->usage = info->usage;
698     {
699         /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
700         const Uint16 vendor = device->vendor_id;
701         const Uint16 product = device->product_id;
702         const Uint16 version = device->version;
703         Uint16 *guid16 = (Uint16 *)device->guid.data;
704 
705         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
706         *guid16++ = 0;
707         *guid16++ = SDL_SwapLE16(vendor);
708         *guid16++ = 0;
709         *guid16++ = SDL_SwapLE16(product);
710         *guid16++ = 0;
711         *guid16++ = SDL_SwapLE16(version);
712         *guid16++ = 0;
713 
714         /* Note that this is a HIDAPI device for special handling elsewhere */
715         device->guid.data[14] = 'h';
716         device->guid.data[15] = 0;
717     }
718     device->dev_lock = SDL_CreateMutex();
719 
720     /* Need the device name before getting the driver to know whether to ignore this device */
721     {
722         char *manufacturer_string = NULL;
723         char *product_string = NULL;
724 
725         if (info->manufacturer_string) {
726             manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
727             if (!manufacturer_string) {
728                 if (sizeof(wchar_t) == sizeof(Uint16)) {
729                     manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
730                 } else if (sizeof(wchar_t) == sizeof(Uint32)) {
731                     manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
732                 }
733             }
734         }
735         if (info->product_string) {
736             product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
737             if (!product_string) {
738                 if (sizeof(wchar_t) == sizeof(Uint16)) {
739                     product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
740                 } else if (sizeof(wchar_t) == sizeof(Uint32)) {
741                     product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
742                 }
743             }
744         }
745 
746         device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string);
747 
748         if (manufacturer_string) {
749             SDL_free(manufacturer_string);
750         }
751         if (product_string) {
752             SDL_free(product_string);
753         }
754 
755         if (!device->name) {
756             SDL_free(device->path);
757             SDL_free(device);
758             return;
759         }
760     }
761 
762     /* Add it to the list */
763     if (last) {
764         last->next = device;
765     } else {
766         SDL_HIDAPI_devices = device;
767     }
768 
769     HIDAPI_SetupDeviceDriver(device);
770 
771 #ifdef DEBUG_HIDAPI
772     SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, 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->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");
773 #endif
774 }
775 
776 
777 static void
HIDAPI_DelDevice(SDL_HIDAPI_Device * device)778 HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
779 {
780     SDL_HIDAPI_Device *curr, *last;
781     for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
782         if (curr == device) {
783             if (last) {
784                 last->next = curr->next;
785             } else {
786                 SDL_HIDAPI_devices = curr->next;
787             }
788 
789             HIDAPI_CleanupDeviceDriver(device);
790 
791             SDL_DestroyMutex(device->dev_lock);
792             SDL_free(device->name);
793             SDL_free(device->path);
794             SDL_free(device);
795             return;
796         }
797     }
798 }
799 
800 static void
HIDAPI_UpdateDeviceList(void)801 HIDAPI_UpdateDeviceList(void)
802 {
803     SDL_HIDAPI_Device *device;
804     struct hid_device_info *devs, *info;
805 
806     SDL_LockJoysticks();
807 
808     /* Prepare the existing device list */
809     device = SDL_HIDAPI_devices;
810     while (device) {
811         device->seen = SDL_FALSE;
812         device = device->next;
813     }
814 
815     /* Enumerate the devices */
816     if (SDL_HIDAPI_numdrivers > 0) {
817         devs = hid_enumerate(0, 0);
818         if (devs) {
819             for (info = devs; info; info = info->next) {
820                 device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
821                 if (device) {
822                     device->seen = SDL_TRUE;
823                 } else {
824                     HIDAPI_AddDevice(info);
825                 }
826             }
827             hid_free_enumeration(devs);
828         }
829     }
830 
831     /* Remove any devices that weren't seen */
832     device = SDL_HIDAPI_devices;
833     while (device) {
834         SDL_HIDAPI_Device *next = device->next;
835 
836         if (!device->seen) {
837             HIDAPI_DelDevice(device);
838         }
839         device = next;
840     }
841 
842     SDL_UnlockJoysticks();
843 }
844 
845 SDL_bool
HIDAPI_IsDevicePresent(Uint16 vendor_id,Uint16 product_id,Uint16 version,const char * name)846 HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
847 {
848     SDL_HIDAPI_Device *device;
849     SDL_bool supported = SDL_FALSE;
850     SDL_bool result = SDL_FALSE;
851 
852     /* Make sure we're initialized, as this could be called from other drivers during startup */
853     if (HIDAPI_JoystickInit() < 0) {
854         return SDL_FALSE;
855     }
856 
857     /* Only update the device list for devices we know might be supported.
858        If we did this for every device, it would hit the USB driver too hard and potentially
859        lock up the system. This won't catch devices that we support but can only detect using
860        USB interface details, like Xbox controllers, but hopefully the device list update is
861        responsive enough to catch those.
862      */
863     supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name);
864 #if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)
865     if (!supported &&
866         (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) {
867         supported = SDL_TRUE;
868     }
869 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE */
870     if (supported) {
871         if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
872             HIDAPI_UpdateDeviceList();
873             SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
874         }
875     }
876 
877     /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
878        or a different name than we have it listed here, etc, but if we support the device
879        and we have something similar in our device list, mark it as present.
880      */
881     SDL_LockJoysticks();
882     device = SDL_HIDAPI_devices;
883     while (device) {
884         if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
885             result = SDL_TRUE;
886         }
887         device = device->next;
888     }
889     SDL_UnlockJoysticks();
890 
891     /* If we're looking for the wireless XBox 360 controller, also look for the dongle */
892     if (!result && vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x02a1) {
893         return HIDAPI_IsDevicePresent(USB_VENDOR_MICROSOFT, 0x0719, version, name);
894     }
895 
896 #ifdef DEBUG_HIDAPI
897     SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x\n", result ? "true" : "false", vendor_id, product_id);
898 #endif
899     return result;
900 }
901 
902 static void
HIDAPI_JoystickDetect(void)903 HIDAPI_JoystickDetect(void)
904 {
905     int i;
906     if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
907         HIDAPI_UpdateDiscovery();
908         if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
909             /* FIXME: We probably need to schedule an update in a few seconds as well */
910             HIDAPI_UpdateDeviceList();
911             SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
912         }
913         SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
914     }
915     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
916         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
917         if (driver->enabled && driver->PostUpdate) {
918             driver->PostUpdate();
919         }
920     }
921 }
922 
923 void
HIDAPI_UpdateDevices(void)924 HIDAPI_UpdateDevices(void)
925 {
926     SDL_HIDAPI_Device *device;
927 
928     /* Update the devices, which may change connected joysticks and send events */
929 
930     /* Prepare the existing device list */
931     if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
932         device = SDL_HIDAPI_devices;
933         while (device) {
934             if (device->driver) {
935                 if (SDL_TryLockMutex(device->dev_lock) == 0) {
936                     device->driver->UpdateDevice(device);
937                     SDL_UnlockMutex(device->dev_lock);
938                 }
939             }
940             device = device->next;
941         }
942         SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
943     }
944 }
945 
946 static const char *
HIDAPI_JoystickGetDeviceName(int device_index)947 HIDAPI_JoystickGetDeviceName(int device_index)
948 {
949     SDL_HIDAPI_Device *device;
950     const char *name = NULL;
951 
952     device = HIDAPI_GetDeviceByIndex(device_index, NULL);
953     if (device) {
954         /* FIXME: The device could be freed after this name is returned... */
955         name = device->name;
956     }
957 
958     return name;
959 }
960 
961 static int
HIDAPI_JoystickGetDevicePlayerIndex(int device_index)962 HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
963 {
964     SDL_HIDAPI_Device *device;
965     SDL_JoystickID instance_id;
966     int player_index = -1;
967 
968     device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
969     if (device) {
970         player_index = device->driver->GetDevicePlayerIndex(device, instance_id);
971     }
972 
973     return player_index;
974 }
975 
976 static void
HIDAPI_JoystickSetDevicePlayerIndex(int device_index,int player_index)977 HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
978 {
979     SDL_HIDAPI_Device *device;
980     SDL_JoystickID instance_id;
981 
982     device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
983     if (device) {
984         device->driver->SetDevicePlayerIndex(device, instance_id, player_index);
985     }
986 }
987 
988 static SDL_JoystickGUID
HIDAPI_JoystickGetDeviceGUID(int device_index)989 HIDAPI_JoystickGetDeviceGUID(int device_index)
990 {
991     SDL_HIDAPI_Device *device;
992     SDL_JoystickGUID guid;
993 
994     device = HIDAPI_GetDeviceByIndex(device_index, NULL);
995     if (device) {
996         SDL_memcpy(&guid, &device->guid, sizeof(guid));
997     } else {
998         SDL_zero(guid);
999     }
1000 
1001     return guid;
1002 }
1003 
1004 static SDL_JoystickID
HIDAPI_JoystickGetDeviceInstanceID(int device_index)1005 HIDAPI_JoystickGetDeviceInstanceID(int device_index)
1006 {
1007     SDL_JoystickID joystickID = -1;
1008     HIDAPI_GetDeviceByIndex(device_index, &joystickID);
1009     return joystickID;
1010 }
1011 
1012 static int
HIDAPI_JoystickOpen(SDL_Joystick * joystick,int device_index)1013 HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
1014 {
1015     SDL_JoystickID joystickID;
1016     SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
1017     struct joystick_hwdata *hwdata;
1018 
1019     hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
1020     if (!hwdata) {
1021         return SDL_OutOfMemory();
1022     }
1023     hwdata->device = device;
1024 
1025     if (!device->driver->OpenJoystick(device, joystick)) {
1026         SDL_free(hwdata);
1027         return -1;
1028     }
1029 
1030     joystick->hwdata = hwdata;
1031     return 0;
1032 }
1033 
1034 static int
HIDAPI_JoystickRumble(SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)1035 HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1036 {
1037     int result;
1038 
1039     if (joystick->hwdata) {
1040         SDL_HIDAPI_Device *device = joystick->hwdata->device;
1041 
1042         result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble);
1043     } else {
1044         SDL_SetError("Rumble failed, device disconnected");
1045         result = -1;
1046     }
1047 
1048     return result;
1049 }
1050 
1051 static void
HIDAPI_JoystickUpdate(SDL_Joystick * joystick)1052 HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
1053 {
1054     /* This is handled in SDL_HIDAPI_UpdateDevices() */
1055 }
1056 
1057 static void
HIDAPI_JoystickClose(SDL_Joystick * joystick)1058 HIDAPI_JoystickClose(SDL_Joystick * joystick)
1059 {
1060     if (joystick->hwdata) {
1061         SDL_HIDAPI_Device *device = joystick->hwdata->device;
1062 
1063         /* Wait for pending rumble to complete */
1064         while (SDL_AtomicGet(&device->rumble_pending) > 0) {
1065             SDL_Delay(10);
1066         }
1067 
1068         device->driver->CloseJoystick(device, joystick);
1069 
1070         SDL_free(joystick->hwdata);
1071         joystick->hwdata = NULL;
1072     }
1073 }
1074 
1075 static void
HIDAPI_JoystickQuit(void)1076 HIDAPI_JoystickQuit(void)
1077 {
1078     int i;
1079 
1080     shutting_down = SDL_TRUE;
1081 
1082     HIDAPI_ShutdownDiscovery();
1083 
1084     while (SDL_HIDAPI_devices) {
1085         HIDAPI_DelDevice(SDL_HIDAPI_devices);
1086     }
1087 
1088     SDL_HIDAPI_QuitRumble();
1089 
1090     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
1091         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
1092         SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
1093     }
1094     SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
1095                         SDL_HIDAPIDriverHintChanged, NULL);
1096 
1097     hid_exit();
1098 
1099     /* Make sure the drivers cleaned up properly */
1100     SDL_assert(SDL_HIDAPI_numjoysticks == 0);
1101 
1102     shutting_down = SDL_FALSE;
1103     initialized = SDL_FALSE;
1104 }
1105 
1106 static SDL_bool
HIDAPI_JoystickGetGamepadMapping(int device_index,SDL_GamepadMapping * out)1107 HIDAPI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
1108 {
1109     return SDL_FALSE;
1110 }
1111 
1112 SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
1113 {
1114     HIDAPI_JoystickInit,
1115     HIDAPI_JoystickGetCount,
1116     HIDAPI_JoystickDetect,
1117     HIDAPI_JoystickGetDeviceName,
1118     HIDAPI_JoystickGetDevicePlayerIndex,
1119     HIDAPI_JoystickSetDevicePlayerIndex,
1120     HIDAPI_JoystickGetDeviceGUID,
1121     HIDAPI_JoystickGetDeviceInstanceID,
1122     HIDAPI_JoystickOpen,
1123     HIDAPI_JoystickRumble,
1124     HIDAPI_JoystickUpdate,
1125     HIDAPI_JoystickClose,
1126     HIDAPI_JoystickQuit,
1127     HIDAPI_JoystickGetGamepadMapping
1128 };
1129 
1130 #endif /* SDL_JOYSTICK_HIDAPI */
1131 
1132 /* vi: set ts=4 sw=4 expandtab: */
1133