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