SDL 2.0
SDL_hidapijoystick.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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"
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
53struct joystick_hwdata
54{
56 void *context;
57
59 hid_device *dev;
60};
61
62typedef struct _SDL_HIDAPI_Device
63{
64 SDL_JoystickID instance_id;
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
82static 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};
99static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
100static int SDL_HIDAPI_numjoysticks = 0;
101
102#if defined(SDL_USE_LIBUDEV)
103static const SDL_UDEV_Symbols * usyms = NULL;
104#endif
105
106static 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__
134struct _DEV_BROADCAST_HDR
135{
136 DWORD dbch_size;
137 DWORD dbch_devicetype;
138 DWORD dbch_reserved;
139};
140
141typedef 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
150typedef 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>
160DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
161
162static 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__)
183static 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);
190 }
191}
192#endif /* __MACOSX__ */
193
194static void
195HIDAPI_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
297static void
298HIDAPI_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
362static void
363HIDAPI_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
397const char *
398HIDAPI_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
564static SDL_bool
565HIDAPI_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
579HIDAPI_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
607static SDL_HIDAPI_Device *
608HIDAPI_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
623static SDL_HIDAPI_Device *
624HIDAPI_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
637static void SDLCALL
638SDL_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
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
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
683static void HIDAPI_JoystickDetect(void);
684
685static int
686HIDAPI_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
706static int
707HIDAPI_JoystickGetCount(void)
708{
709 return SDL_HIDAPI_numjoysticks;
710}
711
712static void
713HIDAPI_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
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) {
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);
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
831static void
832HIDAPI_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
848 }
849
850 SDL_free(device->name);
851 SDL_free(device->path);
853 return;
854 }
855 }
856}
857
858static void
859HIDAPI_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 }
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
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
920static void
921HIDAPI_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
931static const char *
932HIDAPI_JoystickGetDeviceName(int device_index)
933{
934 return HIDAPI_GetJoystickByIndex(device_index)->name;
935}
936
937static int
938HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
939{
940 return -1;
941}
942
943static SDL_JoystickGUID
944HIDAPI_JoystickGetDeviceGUID(int device_index)
945{
946 return HIDAPI_GetJoystickByIndex(device_index)->guid;
947}
948
949static SDL_JoystickID
950HIDAPI_JoystickGetDeviceInstanceID(int device_index)
951{
952 return HIDAPI_GetJoystickByIndex(device_index)->instance_id;
953}
954
955static int
956HIDAPI_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
984static int
985HIDAPI_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
997static void
998HIDAPI_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
1019static void
1020HIDAPI_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
1032static void
1033HIDAPI_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_SetError
#define SDL_memset
#define SDL_DelHintCallback
#define SDL_wcslen
#define SDL_LockMutex
#define SDL_malloc
#define SDL_strlen
#define SDL_ThreadID
#define SDL_strcasecmp
#define SDL_CreateMutex
#define SDL_free
#define SDL_strdup
#define SDL_strcmp
#define SDL_iconv_string
#define SDL_GetHintBoolean
#define SDL_DestroyMutex
#define SDL_AddHintCallback
#define SDL_Log
#define SDL_snprintf
#define SDL_calloc
#define SDL_UnlockMutex
#define SDL_SwapLE16(X)
Definition: SDL_endian.h:232
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
const char * HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id)
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch
SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
#define SDL_HINT_JOYSTICK_HIDAPI
A variable controlling whether the HIDAPI joystick drivers should be used.
Definition: SDL_hints.h:507
#define SDLCALL
Definition: SDL_internal.h:49
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:805
SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:755
SDL_JoystickID SDL_GetNextJoystickInstanceID()
Definition: SDL_joystick.c:163
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLuint64EXT * result
GLuint GLuint * names
GLuint GLsizei const GLchar * message
GLuint const GLchar * name
GLsizei const GLchar *const * path
GLsizeiptr const void GLenum usage
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
uint32_t Uint32
Definition: SDL_stdinc.h:203
uint16_t Uint16
Definition: SDL_stdinc.h:191
#define SDL_HARDWARE_BUS_USB
SDL_JoystickDriver SDL_HIDAPI_JoystickDriver
#define MAKE_VIDPID(VID, PID)
unsigned long SDL_threadID
Definition: SDL_thread.h:49
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
Definition: SDL_timer.h:56
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
#define NULL
Definition: begin_code.h:167
#define TRUE
Definition: edid-parse.c:33
int HID_API_EXPORT HID_API_CALL hid_init(void)
Initialize the HIDAPI library.
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
Close a HID device.
struct hid_device_ hid_device
Definition: hidapi.h:50
struct hid_device_info HID_API_EXPORT *HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
Enumerate the HID Devices.
HID_API_EXPORT hid_device *HID_API_CALL hid_open_path(const char *path, int bExclusive)
Open a HID device by its path name.
int HID_API_EXPORT HID_API_CALL hid_exit(void)
Finalize the HIDAPI library.
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
Free an enumeration Linked List.
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)
SDL_bool(* IsSupportedDevice)(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
void(* Quit)(SDL_Joystick *joystick, hid_device *dev, void *context)
SDL_bool(* Update)(SDL_Joystick *joystick, hid_device *dev, void *context)
unsigned short product_id
Definition: hidapi.h:59
struct hid_device_info * next
Definition: hidapi.h:82
unsigned short usage
Definition: hidapi.h:74
wchar_t * manufacturer_string
Definition: hidapi.h:66
unsigned short vendor_id
Definition: hidapi.h:57
char * path
Definition: hidapi.h:55
unsigned short release_number
Definition: hidapi.h:64
int interface_number
Definition: hidapi.h:79
unsigned short usage_page
Definition: hidapi.h:71
wchar_t * product_string
Definition: hidapi.h:68
SDL_Joystick * joystick
static SDL_mutex * mutex
Definition: testlock.c:23
static screen_context_t context
Definition: video.c:25