SDL  2.0
SDL_hidapijoystick.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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_endian.h"
26 #include "SDL_hints.h"
27 #include "SDL_log.h"
28 #include "SDL_mutex.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 
35 #if defined(__WIN32__)
36 #include "../../core/windows/SDL_windows.h"
37 #endif
38 
39 #if defined(__MACOSX__)
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <mach/mach.h>
42 #include <IOKit/IOKitLib.h>
43 #include <IOKit/usb/USBSpec.h>
44 #endif
45 
46 #if defined(__LINUX__)
47 #include "../../core/linux/SDL_udev.h"
48 #ifdef SDL_USE_LIBUDEV
49 #include <poll.h>
50 #endif
51 #endif
52 
53 struct joystick_hwdata
54 {
56  void *context;
57 
59  hid_device *dev;
60 };
61 
62 typedef struct _SDL_HIDAPI_Device
63 {
65  char *name;
66  char *path;
67  Uint16 vendor_id;
68  Uint16 product_id;
69  Uint16 version;
71  int interface_number; /* Available on Windows and Linux */
72  Uint16 usage_page; /* Available on Windows and Mac OS X */
73  Uint16 usage; /* Available on Windows and Mac OS X */
75 
76  /* Used during scanning for device changes */
77  SDL_bool seen;
78 
79  struct _SDL_HIDAPI_Device *next;
80 } SDL_HIDAPI_Device;
81 
82 static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
83 #ifdef SDL_JOYSTICK_HIDAPI_PS4
85 #endif
86 #ifdef SDL_JOYSTICK_HIDAPI_STEAM
88 #endif
89 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH
91 #endif
92 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
94 #endif
95 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
97 #endif
98 };
99 static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
100 static int SDL_HIDAPI_numjoysticks = 0;
101 
102 #if defined(SDL_USE_LIBUDEV)
103 static const SDL_UDEV_Symbols * usyms = NULL;
104 #endif
105 
106 static struct
107 {
108  SDL_bool m_bHaveDevicesChanged;
109  SDL_bool m_bCanGetNotifications;
110  Uint32 m_unLastDetect;
111 
112 #if defined(__WIN32__)
113  SDL_threadID m_nThreadID;
114  WNDCLASSEXA m_wndClass;
115  HWND m_hwndMsg;
116  HDEVNOTIFY m_hNotify;
117  double m_flLastWin32MessageCheck;
118 #endif
119 
120 #if defined(__MACOSX__)
121  IONotificationPortRef m_notificationPort;
122  mach_port_t m_notificationMach;
123 #endif
124 
125 #if defined(SDL_USE_LIBUDEV)
126  struct udev *m_pUdev;
127  struct udev_monitor *m_pUdevMonitor;
128  int m_nUdevFd;
129 #endif
130 } SDL_HIDAPI_discovery;
131 
132 
133 #ifdef __WIN32__
134 struct _DEV_BROADCAST_HDR
135 {
136  DWORD dbch_size;
137  DWORD dbch_devicetype;
138  DWORD dbch_reserved;
139 };
140 
141 typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
142 {
143  DWORD dbcc_size;
144  DWORD dbcc_devicetype;
145  DWORD dbcc_reserved;
146  GUID dbcc_classguid;
147  char dbcc_name[ 1 ];
148 } DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
149 
150 typedef struct _DEV_BROADCAST_HDR DEV_BROADCAST_HDR;
151 #define DBT_DEVICEARRIVAL 0x8000 /* system detected a new device */
152 #define DBT_DEVICEREMOVECOMPLETE 0x8004 /* device was removed from the system */
153 #define DBT_DEVTYP_DEVICEINTERFACE 0x00000005 /* device interface class */
154 #define DBT_DEVNODES_CHANGED 0x0007
155 #define DBT_CONFIGCHANGED 0x0018
156 #define DBT_DEVICETYPESPECIFIC 0x8005 /* type specific event */
157 #define DBT_DEVINSTSTARTED 0x8008 /* device installed and started */
158 
159 #include <initguid.h>
160 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
161 
162 static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
163 {
164  switch (message) {
165  case WM_DEVICECHANGE:
166  switch (wParam) {
167  case DBT_DEVICEARRIVAL:
168  case DBT_DEVICEREMOVECOMPLETE:
169  if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
170  SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
171  }
172  break;
173  }
174  return TRUE;
175  }
176 
177  return DefWindowProc(hwnd, message, wParam, lParam);
178 }
179 #endif /* __WIN32__ */
180 
181 
182 #if defined(__MACOSX__)
183 static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator)
184 {
185  /* Must drain the iterator, or we won't receive new notifications */
186  io_object_t entry;
187  while ((entry = IOIteratorNext(portIterator)) != 0) {
188  IOObjectRelease(entry);
189  *(SDL_bool*)context = SDL_TRUE;
190  }
191 }
192 #endif /* __MACOSX__ */
193 
194 static void
195 HIDAPI_InitializeDiscovery()
196 {
197  SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
198  SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_FALSE;
199  SDL_HIDAPI_discovery.m_unLastDetect = 0;
200 
201 #if defined(__WIN32__)
202  SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
203 
204  SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass));
205  SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
206  SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
207  SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc; /* This function is called by windows */
208  SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX);
209 
210  RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass);
211  SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
212 
213  {
214  DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
215  SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) );
216 
217  devBroadcast.dbcc_size = sizeof( devBroadcast );
218  devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
219  devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
220 
221  /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored,
222  * but that seems to be necessary to get a notice after each individual usb input device actually
223  * installs, rather than just as the composite device is seen.
224  */
225  SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification( SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES );
226  SDL_HIDAPI_discovery.m_bCanGetNotifications = ( SDL_HIDAPI_discovery.m_hNotify != 0 );
227  }
228 #endif /* __WIN32__ */
229 
230 #if defined(__MACOSX__)
231  SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
232  if (SDL_HIDAPI_discovery.m_notificationPort) {
233  {
234  CFMutableDictionaryRef matchingDict = IOServiceMatching("IOUSBDevice");
235 
236  /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
237  io_iterator_t portIterator = 0;
238  io_object_t entry;
239  if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
240  /* Must drain the existing iterator, or we won't receive new notifications */
241  while ((entry = IOIteratorNext(portIterator)) != 0) {
242  IOObjectRelease(entry);
243  }
244  } else {
245  IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
246  SDL_HIDAPI_discovery.m_notificationPort = nil;
247  }
248  }
249  {
250  CFMutableDictionaryRef matchingDict = IOServiceMatching("IOBluetoothDevice");
251 
252  /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
253  io_iterator_t portIterator = 0;
254  io_object_t entry;
255  if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
256  /* Must drain the existing iterator, or we won't receive new notifications */
257  while ((entry = IOIteratorNext(portIterator)) != 0) {
258  IOObjectRelease(entry);
259  }
260  } else {
261  IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
262  SDL_HIDAPI_discovery.m_notificationPort = nil;
263  }
264  }
265  }
266 
267  SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL;
268  if (SDL_HIDAPI_discovery.m_notificationPort) {
269  SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort);
270  }
271 
272  SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL);
273 
274 #endif // __MACOSX__
275 
276 #if defined(SDL_USE_LIBUDEV)
277  SDL_HIDAPI_discovery.m_pUdev = NULL;
278  SDL_HIDAPI_discovery.m_pUdevMonitor = NULL;
279  SDL_HIDAPI_discovery.m_nUdevFd = -1;
280 
281  usyms = SDL_UDEV_GetUdevSyms();
282  if (usyms) {
283  SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new();
284  }
285  if (SDL_HIDAPI_discovery.m_pUdev) {
286  SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev");
287  if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
288  usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor);
289  SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor);
290  SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_TRUE;
291  }
292  }
293 
294 #endif /* SDL_USE_LIBUDEV */
295 }
296 
297 static void
298 HIDAPI_UpdateDiscovery()
299 {
300  if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) {
301  const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000; /* Update every 3 seconds */
302  Uint32 now = SDL_GetTicks();
303  if (!SDL_HIDAPI_discovery.m_unLastDetect || SDL_TICKS_PASSED(now, SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) {
304  SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
305  SDL_HIDAPI_discovery.m_unLastDetect = now;
306  }
307  return;
308  }
309 
310 #if defined(__WIN32__)
311 #if 0 /* just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan. */
312  /* We'll only get messages on the same thread that created the window */
313  if (SDL_ThreadID() == SDL_HIDAPI_discovery.m_nThreadID) {
314  MSG msg;
315  while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) {
316  if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) {
317  TranslateMessage(&msg);
318  DispatchMessage(&msg);
319  }
320  }
321  }
322 #endif
323 #endif /* __WIN32__ */
324 
325 #if defined(__MACOSX__)
326  if (SDL_HIDAPI_discovery.m_notificationPort) {
327  struct { mach_msg_header_t hdr; char payload[ 4096 ]; } msg;
328  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) {
329  IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort);
330  }
331  }
332 #endif
333 
334 #if defined(SDL_USE_LIBUDEV)
335  if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) {
336  /* Drain all notification events.
337  * We don't expect a lot of device notifications so just
338  * do a new discovery on any kind or number of notifications.
339  * This could be made more restrictive if necessary.
340  */
341  for (;;) {
342  struct pollfd PollUdev;
343  struct udev_device *pUdevDevice;
344 
345  PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd;
346  PollUdev.events = POLLIN;
347  if (poll(&PollUdev, 1, 0) != 1) {
348  break;
349  }
350 
351  SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
352 
353  pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor);
354  if (pUdevDevice) {
355  usyms->udev_device_unref(pUdevDevice);
356  }
357  }
358  }
359 #endif
360 }
361 
362 static void
363 HIDAPI_ShutdownDiscovery()
364 {
365 #if defined(__WIN32__)
366  if (SDL_HIDAPI_discovery.m_hNotify)
367  UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify);
368 
369  if (SDL_HIDAPI_discovery.m_hwndMsg) {
370  DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg);
371  }
372 
373  UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance);
374 #endif
375 
376 #if defined(__MACOSX__)
377  if (SDL_HIDAPI_discovery.m_notificationPort) {
378  IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
379  }
380 #endif
381 
382 #if defined(SDL_USE_LIBUDEV)
383  if (usyms) {
384  if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
385  usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor);
386  }
387  if (SDL_HIDAPI_discovery.m_pUdev) {
388  usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev);
389  }
390  SDL_UDEV_ReleaseUdevSyms();
391  usyms = NULL;
392  }
393 #endif
394 }
395 
396 
397 const char *
398 HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id)
399 {
400  static struct
401  {
402  Uint32 vidpid;
403  const char *name;
404  } names[] = {
405  { MAKE_VIDPID(0x0079, 0x18d4), "GPD Win 2 X-Box Controller" },
406  { MAKE_VIDPID(0x044f, 0xb326), "Thrustmaster Gamepad GP XID" },
407  { MAKE_VIDPID(0x045e, 0x028e), "Microsoft X-Box 360 pad" },
408  { MAKE_VIDPID(0x045e, 0x028f), "Microsoft X-Box 360 pad v2" },
409  { MAKE_VIDPID(0x045e, 0x0291), "Xbox 360 Wireless Receiver (XBOX)" },
410  { MAKE_VIDPID(0x045e, 0x02d1), "Microsoft X-Box One pad" },
411  { MAKE_VIDPID(0x045e, 0x02dd), "Microsoft X-Box One pad (Firmware 2015)" },
412  { MAKE_VIDPID(0x045e, 0x02e3), "Microsoft X-Box One Elite pad" },
413  { MAKE_VIDPID(0x045e, 0x02ea), "Microsoft X-Box One S pad" },
414  { MAKE_VIDPID(0x045e, 0x02ff), "Microsoft X-Box One pad" },
415  { MAKE_VIDPID(0x045e, 0x0719), "Xbox 360 Wireless Receiver" },
416  { MAKE_VIDPID(0x046d, 0xc21d), "Logitech Gamepad F310" },
417  { MAKE_VIDPID(0x046d, 0xc21e), "Logitech Gamepad F510" },
418  { MAKE_VIDPID(0x046d, 0xc21f), "Logitech Gamepad F710" },
419  { MAKE_VIDPID(0x046d, 0xc242), "Logitech Chillstream Controller" },
420  { MAKE_VIDPID(0x046d, 0xcaa3), "Logitech DriveFx Racing Wheel" },
421  { MAKE_VIDPID(0x056e, 0x2004), "Elecom JC-U3613M" },
422  { MAKE_VIDPID(0x06a3, 0xf51a), "Saitek P3600" },
423  { MAKE_VIDPID(0x0738, 0x4716), "Mad Catz Wired Xbox 360 Controller" },
424  { MAKE_VIDPID(0x0738, 0x4718), "Mad Catz Street Fighter IV FightStick SE" },
425  { MAKE_VIDPID(0x0738, 0x4726), "Mad Catz Xbox 360 Controller" },
426  { MAKE_VIDPID(0x0738, 0x4728), "Mad Catz Street Fighter IV FightPad" },
427  { MAKE_VIDPID(0x0738, 0x4736), "Mad Catz MicroCon Gamepad" },
428  { MAKE_VIDPID(0x0738, 0x4738), "Mad Catz Wired Xbox 360 Controller (SFIV)" },
429  { MAKE_VIDPID(0x0738, 0x4740), "Mad Catz Beat Pad" },
430  { MAKE_VIDPID(0x0738, 0x4758), "Mad Catz Arcade Game Stick" },
431  { MAKE_VIDPID(0x0738, 0x4a01), "Mad Catz FightStick TE 2" },
432  { MAKE_VIDPID(0x0738, 0x9871), "Mad Catz Portable Drum" },
433  { MAKE_VIDPID(0x0738, 0xb726), "Mad Catz Xbox controller - MW2" },
434  { MAKE_VIDPID(0x0738, 0xb738), "Mad Catz MVC2TE Stick 2" },
435  { MAKE_VIDPID(0x0738, 0xbeef), "Mad Catz JOYTECH NEO SE Advanced GamePad" },
436  { MAKE_VIDPID(0x0738, 0xcb02), "Saitek Cyborg Rumble Pad - PC/Xbox 360" },
437  { MAKE_VIDPID(0x0738, 0xcb03), "Saitek P3200 Rumble Pad - PC/Xbox 360" },
438  { MAKE_VIDPID(0x0738, 0xcb29), "Saitek Aviator Stick AV8R02" },
439  { MAKE_VIDPID(0x0738, 0xf738), "Super SFIV FightStick TE S" },
440  { MAKE_VIDPID(0x07ff, 0xffff), "Mad Catz GamePad" },
441  { MAKE_VIDPID(0x0e6f, 0x0105), "HSM3 Xbox360 dancepad" },
442  { MAKE_VIDPID(0x0e6f, 0x0113), "Afterglow AX.1 Gamepad for Xbox 360" },
443  { MAKE_VIDPID(0x0e6f, 0x011f), "Rock Candy Gamepad Wired Controller" },
444  { MAKE_VIDPID(0x0e6f, 0x0131), "PDP EA Sports Controller" },
445  { MAKE_VIDPID(0x0e6f, 0x0133), "Xbox 360 Wired Controller" },
446  { MAKE_VIDPID(0x0e6f, 0x0139), "Afterglow Prismatic Wired Controller" },
447  { MAKE_VIDPID(0x0e6f, 0x013a), "PDP Xbox One Controller" },
448  { MAKE_VIDPID(0x0e6f, 0x0146), "Rock Candy Wired Controller for Xbox One" },
449  { MAKE_VIDPID(0x0e6f, 0x0147), "PDP Marvel Xbox One Controller" },
450  { MAKE_VIDPID(0x0e6f, 0x015c), "PDP Xbox One Arcade Stick" },
451  { MAKE_VIDPID(0x0e6f, 0x0161), "PDP Xbox One Controller" },
452  { MAKE_VIDPID(0x0e6f, 0x0162), "PDP Xbox One Controller" },
453  { MAKE_VIDPID(0x0e6f, 0x0163), "PDP Xbox One Controller" },
454  { MAKE_VIDPID(0x0e6f, 0x0164), "PDP Battlefield One" },
455  { MAKE_VIDPID(0x0e6f, 0x0165), "PDP Titanfall 2" },
456  { MAKE_VIDPID(0x0e6f, 0x0201), "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller" },
457  { MAKE_VIDPID(0x0e6f, 0x0213), "Afterglow Gamepad for Xbox 360" },
458  { MAKE_VIDPID(0x0e6f, 0x021f), "Rock Candy Gamepad for Xbox 360" },
459  { MAKE_VIDPID(0x0e6f, 0x0246), "Rock Candy Gamepad for Xbox One 2015" },
460  { MAKE_VIDPID(0x0e6f, 0x02a4), "PDP Wired Controller for Xbox One - Stealth Series" },
461  { MAKE_VIDPID(0x0e6f, 0x02ab), "PDP Controller for Xbox One" },
462  { MAKE_VIDPID(0x0e6f, 0x0301), "Logic3 Controller" },
463  { MAKE_VIDPID(0x0e6f, 0x0346), "Rock Candy Gamepad for Xbox One 2016" },
464  { MAKE_VIDPID(0x0e6f, 0x0401), "Logic3 Controller" },
465  { MAKE_VIDPID(0x0e6f, 0x0413), "Afterglow AX.1 Gamepad for Xbox 360" },
466  { MAKE_VIDPID(0x0e6f, 0x0501), "PDP Xbox 360 Controller" },
467  { MAKE_VIDPID(0x0e6f, 0xf900), "PDP Afterglow AX.1" },
468  { MAKE_VIDPID(0x0f0d, 0x000a), "Hori Co. DOA4 FightStick" },
469  { MAKE_VIDPID(0x0f0d, 0x000c), "Hori PadEX Turbo" },
470  { MAKE_VIDPID(0x0f0d, 0x000d), "Hori Fighting Stick EX2" },
471  { MAKE_VIDPID(0x0f0d, 0x0016), "Hori Real Arcade Pro.EX" },
472  { MAKE_VIDPID(0x0f0d, 0x001b), "Hori Real Arcade Pro VX" },
473  { MAKE_VIDPID(0x0f0d, 0x0063), "Hori Real Arcade Pro Hayabusa (USA) Xbox One" },
474  { MAKE_VIDPID(0x0f0d, 0x0067), "HORIPAD ONE" },
475  { MAKE_VIDPID(0x0f0d, 0x0078), "Hori Real Arcade Pro V Kai Xbox One" },
476  { MAKE_VIDPID(0x11c9, 0x55f0), "Nacon GC-100XF" },
477  { MAKE_VIDPID(0x12ab, 0x0004), "Honey Bee Xbox360 dancepad" },
478  { MAKE_VIDPID(0x12ab, 0x0301), "PDP AFTERGLOW AX.1" },
479  { MAKE_VIDPID(0x12ab, 0x0303), "Mortal Kombat Klassic FightStick" },
480  { MAKE_VIDPID(0x1430, 0x4748), "RedOctane Guitar Hero X-plorer" },
481  { MAKE_VIDPID(0x1430, 0xf801), "RedOctane Controller" },
482  { MAKE_VIDPID(0x146b, 0x0601), "BigBen Interactive XBOX 360 Controller" },
483  { MAKE_VIDPID(0x1532, 0x0037), "Razer Sabertooth" },
484  { MAKE_VIDPID(0x1532, 0x0a00), "Razer Atrox Arcade Stick" },
485  { MAKE_VIDPID(0x1532, 0x0a03), "Razer Wildcat" },
486  { MAKE_VIDPID(0x15e4, 0x3f00), "Power A Mini Pro Elite" },
487  { MAKE_VIDPID(0x15e4, 0x3f0a), "Xbox Airflo wired controller" },
488  { MAKE_VIDPID(0x15e4, 0x3f10), "Batarang Xbox 360 controller" },
489  { MAKE_VIDPID(0x162e, 0xbeef), "Joytech Neo-Se Take2" },
490  { MAKE_VIDPID(0x1689, 0xfd00), "Razer Onza Tournament Edition" },
491  { MAKE_VIDPID(0x1689, 0xfd01), "Razer Onza Classic Edition" },
492  { MAKE_VIDPID(0x1689, 0xfe00), "Razer Sabertooth" },
493  { MAKE_VIDPID(0x1bad, 0x0002), "Harmonix Rock Band Guitar" },
494  { MAKE_VIDPID(0x1bad, 0x0003), "Harmonix Rock Band Drumkit" },
495  { MAKE_VIDPID(0x1bad, 0x0130), "Ion Drum Rocker" },
496  { MAKE_VIDPID(0x1bad, 0xf016), "Mad Catz Xbox 360 Controller" },
497  { MAKE_VIDPID(0x1bad, 0xf018), "Mad Catz Street Fighter IV SE Fighting Stick" },
498  { MAKE_VIDPID(0x1bad, 0xf019), "Mad Catz Brawlstick for Xbox 360" },
499  { MAKE_VIDPID(0x1bad, 0xf021), "Mad Cats Ghost Recon FS GamePad" },
500  { MAKE_VIDPID(0x1bad, 0xf023), "MLG Pro Circuit Controller (Xbox)" },
501  { MAKE_VIDPID(0x1bad, 0xf025), "Mad Catz Call Of Duty" },
502  { MAKE_VIDPID(0x1bad, 0xf027), "Mad Catz FPS Pro" },
503  { MAKE_VIDPID(0x1bad, 0xf028), "Street Fighter IV FightPad" },
504  { MAKE_VIDPID(0x1bad, 0xf02e), "Mad Catz Fightpad" },
505  { MAKE_VIDPID(0x1bad, 0xf030), "Mad Catz Xbox 360 MC2 MicroCon Racing Wheel" },
506  { MAKE_VIDPID(0x1bad, 0xf036), "Mad Catz MicroCon GamePad Pro" },
507  { MAKE_VIDPID(0x1bad, 0xf038), "Street Fighter IV FightStick TE" },
508  { MAKE_VIDPID(0x1bad, 0xf039), "Mad Catz MvC2 TE" },
509  { MAKE_VIDPID(0x1bad, 0xf03a), "Mad Catz SFxT Fightstick Pro" },
510  { MAKE_VIDPID(0x1bad, 0xf03d), "Street Fighter IV Arcade Stick TE - Chun Li" },
511  { MAKE_VIDPID(0x1bad, 0xf03e), "Mad Catz MLG FightStick TE" },
512  { MAKE_VIDPID(0x1bad, 0xf03f), "Mad Catz FightStick SoulCaliber" },
513  { MAKE_VIDPID(0x1bad, 0xf042), "Mad Catz FightStick TES+" },
514  { MAKE_VIDPID(0x1bad, 0xf080), "Mad Catz FightStick TE2" },
515  { MAKE_VIDPID(0x1bad, 0xf501), "HoriPad EX2 Turbo" },
516  { MAKE_VIDPID(0x1bad, 0xf502), "Hori Real Arcade Pro.VX SA" },
517  { MAKE_VIDPID(0x1bad, 0xf503), "Hori Fighting Stick VX" },
518  { MAKE_VIDPID(0x1bad, 0xf504), "Hori Real Arcade Pro. EX" },
519  { MAKE_VIDPID(0x1bad, 0xf505), "Hori Fighting Stick EX2B" },
520  { MAKE_VIDPID(0x1bad, 0xf506), "Hori Real Arcade Pro.EX Premium VLX" },
521  { MAKE_VIDPID(0x1bad, 0xf900), "Harmonix Xbox 360 Controller" },
522  { MAKE_VIDPID(0x1bad, 0xf901), "Gamestop Xbox 360 Controller" },
523  { MAKE_VIDPID(0x1bad, 0xf903), "Tron Xbox 360 controller" },
524  { MAKE_VIDPID(0x1bad, 0xf904), "PDP Versus Fighting Pad" },
525  { MAKE_VIDPID(0x1bad, 0xf906), "MortalKombat FightStick" },
526  { MAKE_VIDPID(0x1bad, 0xfa01), "MadCatz GamePad" },
527  { MAKE_VIDPID(0x1bad, 0xfd00), "Razer Onza TE" },
528  { MAKE_VIDPID(0x1bad, 0xfd01), "Razer Onza" },
529  { MAKE_VIDPID(0x24c6, 0x5000), "Razer Atrox Arcade Stick" },
530  { MAKE_VIDPID(0x24c6, 0x5300), "PowerA MINI PROEX Controller" },
531  { MAKE_VIDPID(0x24c6, 0x5303), "Xbox Airflo wired controller" },
532  { MAKE_VIDPID(0x24c6, 0x530a), "Xbox 360 Pro EX Controller" },
533  { MAKE_VIDPID(0x24c6, 0x531a), "PowerA Pro Ex" },
534  { MAKE_VIDPID(0x24c6, 0x5397), "FUS1ON Tournament Controller" },
535  { MAKE_VIDPID(0x24c6, 0x541a), "PowerA Xbox One Mini Wired Controller" },
536  { MAKE_VIDPID(0x24c6, 0x542a), "Xbox ONE spectra" },
537  { MAKE_VIDPID(0x24c6, 0x543a), "PowerA Xbox One wired controller" },
538  { MAKE_VIDPID(0x24c6, 0x5500), "Hori XBOX 360 EX 2 with Turbo" },
539  { MAKE_VIDPID(0x24c6, 0x5501), "Hori Real Arcade Pro VX-SA" },
540  { MAKE_VIDPID(0x24c6, 0x5502), "Hori Fighting Stick VX Alt" },
541  { MAKE_VIDPID(0x24c6, 0x5503), "Hori Fighting Edge" },
542  { MAKE_VIDPID(0x24c6, 0x5506), "Hori SOULCALIBUR V Stick" },
543  { MAKE_VIDPID(0x24c6, 0x550d), "Hori GEM Xbox controller" },
544  { MAKE_VIDPID(0x24c6, 0x550e), "Hori Real Arcade Pro V Kai 360" },
545  { MAKE_VIDPID(0x24c6, 0x551a), "PowerA FUSION Pro Controller" },
546  { MAKE_VIDPID(0x24c6, 0x561a), "PowerA FUSION Controller" },
547  { MAKE_VIDPID(0x24c6, 0x5b00), "ThrustMaster Ferrari 458 Racing Wheel" },
548  { MAKE_VIDPID(0x24c6, 0x5b02), "Thrustmaster, Inc. GPX Controller" },
549  { MAKE_VIDPID(0x24c6, 0x5b03), "Thrustmaster Ferrari 458 Racing Wheel" },
550  { MAKE_VIDPID(0x24c6, 0x5d04), "Razer Sabertooth" },
551  { MAKE_VIDPID(0x24c6, 0xfafe), "Rock Candy Gamepad for Xbox 360" },
552  };
553  int i;
554  Uint32 vidpid = MAKE_VIDPID(vendor_id, product_id);
555 
556  for (i = 0; i < SDL_arraysize(names); ++i) {
557  if (vidpid == names[i].vidpid) {
558  return names[i].name;
559  }
560  }
561  return NULL;
562 }
563 
564 static SDL_bool
565 HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version)
566 {
567  int i;
568 
569  for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
570  SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
571  if (driver->enabled && driver->IsSupportedDevice(vendor_id, product_id, version, -1)) {
572  return SDL_TRUE;
573  }
574  }
575  return SDL_FALSE;
576 }
577 
579 HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
580 {
581  const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;
582  const Uint16 USAGE_JOYSTICK = 0x0004;
583  const Uint16 USAGE_GAMEPAD = 0x0005;
584  const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
585  int i;
586 
587  if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
588  return NULL;
589  }
590 
591  if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
592  return NULL;
593  }
594  if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
595  return NULL;
596  }
597 
598  for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
599  SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
600  if (driver->enabled && driver->IsSupportedDevice(device->vendor_id, device->product_id, device->version, device->interface_number)) {
601  return driver;
602  }
603  }
604  return NULL;
605 }
606 
607 static SDL_HIDAPI_Device *
608 HIDAPI_GetJoystickByIndex(int device_index)
609 {
610  SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
611  while (device) {
612  if (device->driver) {
613  if (device_index == 0) {
614  break;
615  }
616  --device_index;
617  }
618  device = device->next;
619  }
620  return device;
621 }
622 
623 static SDL_HIDAPI_Device *
624 HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
625 {
626  SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
627  while (device) {
628  if (device->vendor_id == vendor_id && device->product_id == product_id &&
629  SDL_strcmp(device->path, path) == 0) {
630  break;
631  }
632  device = device->next;
633  }
634  return device;
635 }
636 
637 static void SDLCALL
638 SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
639 {
640  int i;
641  SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
642  SDL_bool enabled = (!hint || !*hint || ((*hint != '0') && (SDL_strcasecmp(hint, "false") != 0)));
643 
644  if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
645  for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
646  SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
647  driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
648  }
649  } else {
650  for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
651  SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
652  if (SDL_strcmp(name, driver->hint) == 0) {
653  driver->enabled = enabled;
654  break;
655  }
656  }
657  }
658 
659  /* Update device list if driver availability changes */
660  while (device) {
661  if (device->driver) {
662  if (!device->driver->enabled) {
663  device->driver = NULL;
664 
665  --SDL_HIDAPI_numjoysticks;
666 
667  SDL_PrivateJoystickRemoved(device->instance_id);
668  }
669  } else {
670  device->driver = HIDAPI_GetDeviceDriver(device);
671  if (device->driver) {
672  device->instance_id = SDL_GetNextJoystickInstanceID();
673 
674  ++SDL_HIDAPI_numjoysticks;
675 
676  SDL_PrivateJoystickAdded(device->instance_id);
677  }
678  }
679  device = device->next;
680  }
681 }
682 
683 static void HIDAPI_JoystickDetect(void);
684 
685 static int
686 HIDAPI_JoystickInit(void)
687 {
688  int i;
689 
690  if (hid_init() < 0) {
691  SDL_SetError("Couldn't initialize hidapi");
692  return -1;
693  }
694 
695  for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
696  SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
697  SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
698  }
700  SDL_HIDAPIDriverHintChanged, NULL);
701  HIDAPI_InitializeDiscovery();
702  HIDAPI_JoystickDetect();
703  return 0;
704 }
705 
706 static int
707 HIDAPI_JoystickGetCount(void)
708 {
709  return SDL_HIDAPI_numjoysticks;
710 }
711 
712 static void
713 HIDAPI_AddDevice(struct hid_device_info *info)
714 {
715  SDL_HIDAPI_Device *device;
716  SDL_HIDAPI_Device *curr, *last = NULL;
717 
718  for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
719  continue;
720  }
721 
722  device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
723  if (!device) {
724  return;
725  }
726  device->instance_id = -1;
727  device->seen = SDL_TRUE;
728  device->vendor_id = info->vendor_id;
729  device->product_id = info->product_id;
730  device->version = info->release_number;
731  device->interface_number = info->interface_number;
732  device->usage_page = info->usage_page;
733  device->usage = info->usage;
734  {
735  /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
736  const Uint16 vendor = device->vendor_id;
737  const Uint16 product = device->product_id;
738  const Uint16 version = device->version;
739  Uint16 *guid16 = (Uint16 *)device->guid.data;
740 
741  *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
742  *guid16++ = 0;
743  *guid16++ = SDL_SwapLE16(vendor);
744  *guid16++ = 0;
745  *guid16++ = SDL_SwapLE16(product);
746  *guid16++ = 0;
747  *guid16++ = SDL_SwapLE16(version);
748  *guid16++ = 0;
749 
750  /* Note that this is a HIDAPI device for special handling elsewhere */
751  device->guid.data[14] = 'h';
752  device->guid.data[15] = 0;
753  }
754 
755  /* Need the device name before getting the driver to know whether to ignore this device */
756  if (!device->name && info->manufacturer_string && info->product_string) {
757  char *manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
758  char *product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
759  if (!manufacturer_string && !product_string) {
760  if (sizeof(wchar_t) == sizeof(Uint16)) {
761  manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
762  product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
763  } else if (sizeof(wchar_t) == sizeof(Uint32)) {
764  manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
765  product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
766  }
767  }
768  if (manufacturer_string && product_string) {
769  size_t name_size = (SDL_strlen(manufacturer_string) + 1 + SDL_strlen(product_string) + 1);
770  device->name = (char *)SDL_malloc(name_size);
771  if (device->name) {
772  SDL_snprintf(device->name, name_size, "%s %s", manufacturer_string, product_string);
773  }
774  }
775  if (manufacturer_string) {
776  SDL_free(manufacturer_string);
777  }
778  if (product_string) {
779  SDL_free(product_string);
780  }
781  }
782  if (!device->name) {
783  size_t name_size = (6 + 1 + 6 + 1);
784  device->name = (char *)SDL_malloc(name_size);
785  if (!device->name) {
786  SDL_free(device);
787  return;
788  }
789  SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
790  }
791 
792  device->driver = HIDAPI_GetDeviceDriver(device);
793 
794  if (device->driver) {
795  const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
796  if (name) {
797  SDL_free(device->name);
798  device->name = SDL_strdup(name);
799  }
800  }
801 
802  device->path = SDL_strdup(info->path);
803  if (!device->path) {
804  SDL_free(device->name);
805  SDL_free(device);
806  return;
807  }
808 
809 #ifdef DEBUG_HIDAPI
810  SDL_Log("Adding HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, usage page 0x%.4x, usage 0x%.4x, driver = %s\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->usage_page, device->usage, device->driver ? device->driver->hint : "NONE");
811 #endif
812 
813  /* Add it to the list */
814  if (last) {
815  last->next = device;
816  } else {
817  SDL_HIDAPI_devices = device;
818  }
819 
820  if (device->driver) {
821  /* It's a joystick! */
822  device->instance_id = SDL_GetNextJoystickInstanceID();
823 
824  ++SDL_HIDAPI_numjoysticks;
825 
826  SDL_PrivateJoystickAdded(device->instance_id);
827  }
828 }
829 
830 
831 static void
832 HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event)
833 {
834  SDL_HIDAPI_Device *curr, *last;
835  for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
836  if (curr == device) {
837  if (last) {
838  last->next = curr->next;
839  } else {
840  SDL_HIDAPI_devices = curr->next;
841  }
842 
843  if (device->driver && send_event) {
844  /* Need to decrement the joystick count before we post the event */
845  --SDL_HIDAPI_numjoysticks;
846 
847  SDL_PrivateJoystickRemoved(device->instance_id);
848  }
849 
850  SDL_free(device->name);
851  SDL_free(device->path);
852  SDL_free(device);
853  return;
854  }
855  }
856 }
857 
858 static void
859 HIDAPI_UpdateDeviceList(void)
860 {
861  SDL_HIDAPI_Device *device;
862  struct hid_device_info *devs, *info;
863 
864  /* Prepare the existing device list */
865  device = SDL_HIDAPI_devices;
866  while (device) {
867  device->seen = SDL_FALSE;
868  device = device->next;
869  }
870 
871  /* Enumerate the devices */
872  devs = hid_enumerate(0, 0);
873  if (devs) {
874  for (info = devs; info; info = info->next) {
875  device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
876  if (device) {
877  device->seen = SDL_TRUE;
878  } else {
879  HIDAPI_AddDevice(info);
880  }
881  }
882  hid_free_enumeration(devs);
883  }
884 
885  /* Remove any devices that weren't seen */
886  device = SDL_HIDAPI_devices;
887  while (device) {
888  SDL_HIDAPI_Device *next = device->next;
889 
890  if (!device->seen) {
891  HIDAPI_DelDevice(device, SDL_TRUE);
892  }
893  device = next;
894  }
895 }
896 
897 SDL_bool
898 HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
899 {
900  SDL_HIDAPI_Device *device;
901 
902  /* Don't update the device list for devices we know aren't supported */
903  if (!HIDAPI_IsDeviceSupported(vendor_id, product_id, version)) {
904  return SDL_FALSE;
905  }
906 
907  /* Make sure the device list is completely up to date when we check for device presence */
908  HIDAPI_UpdateDeviceList();
909 
910  device = SDL_HIDAPI_devices;
911  while (device) {
912  if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
913  return SDL_TRUE;
914  }
915  device = device->next;
916  }
917  return SDL_FALSE;
918 }
919 
920 static void
921 HIDAPI_JoystickDetect(void)
922 {
923  HIDAPI_UpdateDiscovery();
924  if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
925  /* FIXME: We probably need to schedule an update in a few seconds as well */
926  HIDAPI_UpdateDeviceList();
927  SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
928  }
929 }
930 
931 static const char *
932 HIDAPI_JoystickGetDeviceName(int device_index)
933 {
934  return HIDAPI_GetJoystickByIndex(device_index)->name;
935 }
936 
937 static int
938 HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
939 {
940  return -1;
941 }
942 
943 static SDL_JoystickGUID
944 HIDAPI_JoystickGetDeviceGUID(int device_index)
945 {
946  return HIDAPI_GetJoystickByIndex(device_index)->guid;
947 }
948 
949 static SDL_JoystickID
950 HIDAPI_JoystickGetDeviceInstanceID(int device_index)
951 {
952  return HIDAPI_GetJoystickByIndex(device_index)->instance_id;
953 }
954 
955 static int
956 HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
957 {
958  SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index);
959  struct joystick_hwdata *hwdata;
960 
961  hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
962  if (!hwdata) {
963  return SDL_OutOfMemory();
964  }
965 
966  hwdata->driver = device->driver;
967  hwdata->dev = hid_open_path(device->path, 0);
968  if (!hwdata->dev) {
969  SDL_free(hwdata);
970  return SDL_SetError("Couldn't open HID device %s", device->path);
971  }
972  hwdata->mutex = SDL_CreateMutex();
973 
974  if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) {
975  hid_close(hwdata->dev);
976  SDL_free(hwdata);
977  return -1;
978  }
979 
980  joystick->hwdata = hwdata;
981  return 0;
982 }
983 
984 static int
985 HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
986 {
987  struct joystick_hwdata *hwdata = joystick->hwdata;
988  SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
989  int result;
990 
991  SDL_LockMutex(hwdata->mutex);
992  result = driver->Rumble(joystick, hwdata->dev, hwdata->context, low_frequency_rumble, high_frequency_rumble, duration_ms);
993  SDL_UnlockMutex(hwdata->mutex);
994  return result;
995 }
996 
997 static void
998 HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
999 {
1000  struct joystick_hwdata *hwdata = joystick->hwdata;
1001  SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
1002  SDL_bool succeeded;
1003 
1004  SDL_LockMutex(hwdata->mutex);
1005  succeeded = driver->Update(joystick, hwdata->dev, hwdata->context);
1006  SDL_UnlockMutex(hwdata->mutex);
1007 
1008  if (!succeeded) {
1009  SDL_HIDAPI_Device *device;
1010  for (device = SDL_HIDAPI_devices; device; device = device->next) {
1011  if (device->instance_id == joystick->instance_id) {
1012  HIDAPI_DelDevice(device, SDL_TRUE);
1013  break;
1014  }
1015  }
1016  }
1017 }
1018 
1019 static void
1020 HIDAPI_JoystickClose(SDL_Joystick * joystick)
1021 {
1022  struct joystick_hwdata *hwdata = joystick->hwdata;
1023  SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
1024  driver->Quit(joystick, hwdata->dev, hwdata->context);
1025 
1026  hid_close(hwdata->dev);
1027  SDL_DestroyMutex(hwdata->mutex);
1028  SDL_free(hwdata);
1029  joystick->hwdata = NULL;
1030 }
1031 
1032 static void
1033 HIDAPI_JoystickQuit(void)
1034 {
1035  int i;
1036 
1037  HIDAPI_ShutdownDiscovery();
1038 
1039  while (SDL_HIDAPI_devices) {
1040  HIDAPI_DelDevice(SDL_HIDAPI_devices, SDL_FALSE);
1041  }
1042  for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
1043  SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
1044  SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
1045  }
1047  SDL_HIDAPIDriverHintChanged, NULL);
1048  SDL_HIDAPI_numjoysticks = 0;
1049 
1050  hid_exit();
1051 }
1052 
1054 {
1055  HIDAPI_JoystickInit,
1056  HIDAPI_JoystickGetCount,
1057  HIDAPI_JoystickDetect,
1058  HIDAPI_JoystickGetDeviceName,
1059  HIDAPI_JoystickGetDevicePlayerIndex,
1060  HIDAPI_JoystickGetDeviceGUID,
1061  HIDAPI_JoystickGetDeviceInstanceID,
1062  HIDAPI_JoystickOpen,
1063  HIDAPI_JoystickRumble,
1064  HIDAPI_JoystickUpdate,
1065  HIDAPI_JoystickClose,
1066  HIDAPI_JoystickQuit,
1067 };
1068 
1069 #endif /* SDL_JOYSTICK_HIDAPI */
1070 
1071 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_ThreadID
#define SDL_LockMutex
int interface_number
Definition: hidapi.h:79
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4
#define MAKE_VIDPID(VID, PID)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:800
GLuint64EXT * result
SDL_JoystickGUID guid
GLuint GLsizei const GLchar * message
GLuint GLuint * names
#define SDL_HARDWARE_BUS_USB
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
Free an enumeration Linked List.
Definition: hid.cpp:979
SDL_Joystick * joystick
#define SDL_CreateMutex
static screen_context_t context
Definition: video.c:25
SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
uint16_t Uint16
Definition: SDL_stdinc.h:191
struct joystick_hwdata * next
SDL_bool(* Update)(SDL_Joystick *joystick, hid_device *dev, void *context)
static SDL_mutex * mutex
Definition: testlock.c:23
wchar_t * manufacturer_string
Definition: hidapi.h:66
#define SDL_strcasecmp
char * path
Definition: hidapi.h:55
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch
GLuint const GLchar * name
#define SDL_GetHintBoolean
static SDL_AudioDeviceID device
Definition: loopwave.c:37
int(* Rumble)(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
struct hid_device_info * next
Definition: hidapi.h:82
#define SDL_Log
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
Close a HID device.
Definition: hid.cpp:1090
unsigned short product_id
Definition: hidapi.h:59
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
const char * HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id)
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
#define SDL_wcslen
#define SDL_HINT_JOYSTICK_HIDAPI
A variable controlling whether the HIDAPI joystick drivers should be used.
Definition: SDL_hints.h:487
#define SDL_free
#define TRUE
Definition: edid-parse.c:33
int hid_exit(void)
Finalize the HIDAPI library.
Definition: hid.cpp:1154
unsigned short vendor_id
Definition: hidapi.h:57
void(* Quit)(SDL_Joystick *joystick, hid_device *dev, void *context)
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne
wchar_t * product_string
Definition: hidapi.h:68
unsigned short usage
Definition: hidapi.h:74
SDL_JoystickDriver SDL_HIDAPI_JoystickDriver
GLenum GLenum GLsizei const GLuint GLboolean enabled
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:751
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:161
#define SDL_SetError
SDL_bool(* IsSupportedDevice)(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
int hid_init(void)
Initialize the HIDAPI library.
Definition: hid.cpp:956
#define SDL_DestroyMutex
unsigned short usage_page
Definition: hidapi.h:71
#define SDL_calloc
SDL_JoystickID SDL_GetNextJoystickInstanceID()
Definition: SDL_joystick.c:163
#define SDL_strlen
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam
#define SDL_strdup
uint32_t Uint32
Definition: SDL_stdinc.h:203
#define SDL_AddHintCallback
#define SDL_iconv_string
#define SDL_DelHintCallback
GLuint GLfloat x0
#define SDL_snprintf
HID_API_EXPORT hid_device *HID_API_CALL hid_open_path(const char *path, int bExclusive)
Open a HID device by its path name.
Definition: hid.cpp:995
#define SDL_UnlockMutex
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
#define SDL_malloc
GLsizei const GLchar *const * path
#define SDL_strcmp
#define SDL_SwapLE16(X)
Definition: SDL_endian.h:232
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
Definition: SDL_timer.h:56
SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
#define SDLCALL
Definition: SDL_internal.h:45
struct hid_device_info HID_API_EXPORT *HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
Enumerate the HID Devices.
Definition: hid.cpp:961
#define SDL_memset
unsigned short release_number
Definition: hidapi.h:64
unsigned long SDL_threadID
Definition: SDL_thread.h:49