21 #include "../../SDL_internal.h"
23 #ifdef SDL_JOYSTICK_HIDAPI
33 #include "../SDL_sysjoystick.h"
36 #include "../../SDL_hints_c.h"
38 #if defined(__WIN32__)
39 #include "../../core/windows/SDL_windows.h"
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>
50 #if defined(__LINUX__)
51 #include "../../core/linux/SDL_udev.h"
52 #ifdef SDL_USE_LIBUDEV
62 static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
63 #ifdef SDL_JOYSTICK_HIDAPI_PS4
66 #ifdef SDL_JOYSTICK_HIDAPI_STEAM
69 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH
72 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
76 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
79 #ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
83 static int SDL_HIDAPI_numdrivers = 0;
85 static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
86 static int SDL_HIDAPI_numjoysticks = 0;
90 #if defined(SDL_USE_LIBUDEV)
91 static const SDL_UDEV_Symbols * usyms =
NULL;
100 #if defined(__WIN32__)
102 WNDCLASSEXA m_wndClass;
104 HDEVNOTIFY m_hNotify;
105 double m_flLastWin32MessageCheck;
108 #if defined(__MACOSX__)
109 IONotificationPortRef m_notificationPort;
110 mach_port_t m_notificationMach;
113 #if defined(SDL_USE_LIBUDEV)
114 struct udev *m_pUdev;
115 struct udev_monitor *m_pUdevMonitor;
118 } SDL_HIDAPI_discovery;
122 struct _DEV_BROADCAST_HDR
125 DWORD dbch_devicetype;
129 typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
132 DWORD dbcc_devicetype;
136 } DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
138 typedef struct _DEV_BROADCAST_HDR DEV_BROADCAST_HDR;
139 #define DBT_DEVICEARRIVAL 0x8000
140 #define DBT_DEVICEREMOVECOMPLETE 0x8004
141 #define DBT_DEVTYP_DEVICEINTERFACE 0x00000005
142 #define DBT_DEVNODES_CHANGED 0x0007
143 #define DBT_CONFIGCHANGED 0x0018
144 #define DBT_DEVICETYPESPECIFIC 0x8005
145 #define DBT_DEVINSTSTARTED 0x8008
147 #include <initguid.h>
148 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
150 static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT
message, WPARAM wParam, LPARAM lParam)
153 case WM_DEVICECHANGE:
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;
165 return DefWindowProc(hwnd,
message, wParam, lParam);
170 #if defined(__MACOSX__)
171 static void CallbackIOServiceFunc(
void *
context, io_iterator_t portIterator)
175 while ((entry = IOIteratorNext(portIterator)) != 0) {
176 IOObjectRelease(entry);
183 HIDAPI_InitializeDiscovery()
185 SDL_HIDAPI_discovery.m_bHaveDevicesChanged =
SDL_TRUE;
186 SDL_HIDAPI_discovery.m_bCanGetNotifications =
SDL_FALSE;
187 SDL_HIDAPI_discovery.m_unLastDetect = 0;
189 #if defined(__WIN32__)
192 SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0
x0,
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;
196 SDL_HIDAPI_discovery.m_wndClass.cbSize =
sizeof(WNDCLASSEX);
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);
202 DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
203 SDL_memset( &devBroadcast, 0
x0,
sizeof( devBroadcast ) );
205 devBroadcast.dbcc_size =
sizeof( devBroadcast );
206 devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
207 devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
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 );
218 #if defined(__MACOSX__)
219 SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
220 if (SDL_HIDAPI_discovery.m_notificationPort) {
222 io_iterator_t portIterator = 0;
224 IOReturn
result = IOServiceAddMatchingNotification(
225 SDL_HIDAPI_discovery.m_notificationPort,
226 kIOFirstMatchNotification,
227 IOServiceMatching(kIOHIDDeviceKey),
228 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
232 while ((entry = IOIteratorNext(portIterator)) != 0) {
233 IOObjectRelease(entry);
236 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
237 SDL_HIDAPI_discovery.m_notificationPort = nil;
241 io_iterator_t portIterator = 0;
243 IOReturn
result = IOServiceAddMatchingNotification(
244 SDL_HIDAPI_discovery.m_notificationPort,
245 kIOTerminatedNotification,
246 IOServiceMatching(kIOHIDDeviceKey),
247 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
251 while ((entry = IOIteratorNext(portIterator)) != 0) {
252 IOObjectRelease(entry);
255 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
256 SDL_HIDAPI_discovery.m_notificationPort = nil;
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);
266 SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL);
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;
275 usyms = SDL_UDEV_GetUdevSyms();
277 SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new();
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;
292 HIDAPI_UpdateDiscovery()
294 if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) {
295 const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000;
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;
304 #if defined(__WIN32__)
307 if (
SDL_ThreadID() == SDL_HIDAPI_discovery.m_nThreadID) {
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);
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);
328 #if defined(SDL_USE_LIBUDEV)
329 if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) {
336 struct pollfd PollUdev;
337 struct udev_device *pUdevDevice;
339 PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd;
340 PollUdev.events = POLLIN;
341 if (poll(&PollUdev, 1, 0) != 1) {
345 SDL_HIDAPI_discovery.m_bHaveDevicesChanged =
SDL_TRUE;
347 pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor);
349 usyms->udev_device_unref(pUdevDevice);
357 HIDAPI_ShutdownDiscovery()
359 #if defined(__WIN32__)
360 if (SDL_HIDAPI_discovery.m_hNotify)
361 UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify);
363 if (SDL_HIDAPI_discovery.m_hwndMsg) {
364 DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg);
367 UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance);
370 #if defined(__MACOSX__)
371 if (SDL_HIDAPI_discovery.m_notificationPort) {
372 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
376 #if defined(SDL_USE_LIBUDEV)
378 if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
379 usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor);
381 if (SDL_HIDAPI_discovery.m_pUdev) {
382 usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev);
384 SDL_UDEV_ReleaseUdevSyms();
390 static void HIDAPI_JoystickDetect(
void);
391 static void HIDAPI_JoystickClose(SDL_Joystick * joystick);
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)) {
408 static SDL_HIDAPI_DeviceDriver *
409 HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *
device)
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;
422 if (
device->usage_page &&
device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
425 if (
device->usage &&
device->usage != USAGE_JOYSTICK &&
device->usage != USAGE_GAMEPAD &&
device->usage != USAGE_MULTIAXISCONTROLLER) {
431 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[
i];
439 static SDL_HIDAPI_Device *
440 HIDAPI_GetDeviceByIndex(
int device_index,
SDL_JoystickID *pJoystickID)
442 SDL_HIDAPI_Device *
device = SDL_HIDAPI_devices;
445 if (device_index < device->num_joysticks) {
447 *pJoystickID =
device->joysticks[device_index];
451 device_index -=
device->num_joysticks;
458 static SDL_HIDAPI_Device *
459 HIDAPI_GetJoystickByInfo(
const char *
path,
Uint16 vendor_id,
Uint16 product_id)
461 SDL_HIDAPI_Device *
device = SDL_HIDAPI_devices;
463 if (
device->vendor_id == vendor_id &&
device->product_id == product_id &&
473 HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *
device)
496 HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *
device)
504 while (
device->num_joysticks) {
513 SDL_HIDAPIDriverHintChanged(
void *userdata,
const char *
name,
const char *oldValue,
const char *hint)
516 SDL_HIDAPI_Device *
device;
521 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[
i];
526 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[
i];
533 SDL_HIDAPI_numdrivers = 0;
535 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[
i];
536 if (driver->enabled) {
537 ++SDL_HIDAPI_numdrivers;
546 HIDAPI_CleanupDeviceDriver(
device);
548 HIDAPI_SetupDeviceDriver(
device);
555 HIDAPI_JoystickInit(
void)
563 #if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__)
568 SDL_SetError(
"Couldn't initialize hidapi, framework not available");
579 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[
i];
583 SDL_HIDAPIDriverHintChanged,
NULL);
584 HIDAPI_InitializeDiscovery();
585 HIDAPI_JoystickDetect();
603 device->joysticks = joysticks;
604 device->joysticks[
device->num_joysticks++] = joystickID;
605 ++SDL_HIDAPI_numjoysticks;
610 *pJoystickID = joystickID;
620 for (
i = 0;
i <
device->num_joysticks; ++
i) {
621 if (
device->joysticks[
i] == joystickID) {
624 HIDAPI_JoystickClose(joystick);
629 --SDL_HIDAPI_numjoysticks;
630 if (
device->num_joysticks == 0) {
635 if (!shutting_down) {
644 HIDAPI_JoystickGetCount(
void)
646 return SDL_HIDAPI_numjoysticks;
652 SDL_HIDAPI_Device *
device;
653 SDL_HIDAPI_Device *curr, *last =
NULL;
655 for (curr = SDL_HIDAPI_devices, last =
NULL; curr; last = curr, curr = curr->next) {
695 device->guid.data[14] =
'h';
696 device->guid.data[15] = 0;
708 const char *manufacturer_remapped;
711 if (!manufacturer_string && !product_string) {
712 if (
sizeof(
wchar_t) ==
sizeof(
Uint16)) {
715 }
else if (
sizeof(
wchar_t) ==
sizeof(
Uint32)) {
722 if (manufacturer_remapped != manufacturer_string) {
724 manufacturer_string =
SDL_strdup(manufacturer_remapped);
727 if (manufacturer_string && product_string) {
738 if (manufacturer_string) {
741 if (product_string) {
746 size_t name_size = (6 + 1 + 6 + 1);
760 SDL_HIDAPI_devices =
device;
763 HIDAPI_SetupDeviceDriver(
device);
766 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");
772 HIDAPI_DelDevice(SDL_HIDAPI_Device *
device)
774 SDL_HIDAPI_Device *curr, *last;
775 for (curr = SDL_HIDAPI_devices, last =
NULL; curr; last = curr, curr = curr->next) {
778 last->next = curr->next;
780 SDL_HIDAPI_devices = curr->next;
783 HIDAPI_CleanupDeviceDriver(
device);
795 HIDAPI_UpdateDeviceList(
void)
797 SDL_HIDAPI_Device *
device;
803 device = SDL_HIDAPI_devices;
810 if (SDL_HIDAPI_numdrivers > 0) {
813 for (info = devs; info; info = info->
next) {
818 HIDAPI_AddDevice(info);
826 device = SDL_HIDAPI_devices;
842 SDL_HIDAPI_Device *
device;
847 if (HIDAPI_JoystickInit() < 0) {
858 #if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)
866 HIDAPI_UpdateDeviceList();
876 device = SDL_HIDAPI_devices;
897 HIDAPI_JoystickDetect(
void)
900 HIDAPI_UpdateDiscovery();
901 if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
903 HIDAPI_UpdateDeviceList();
904 SDL_HIDAPI_discovery.m_bHaveDevicesChanged =
SDL_FALSE;
913 SDL_HIDAPI_Device *
device;
919 device = SDL_HIDAPI_devices;
934 HIDAPI_JoystickGetDeviceName(
int device_index)
936 SDL_HIDAPI_Device *
device;
939 device = HIDAPI_GetDeviceByIndex(device_index,
NULL);
949 HIDAPI_JoystickGetDevicePlayerIndex(
int device_index)
951 SDL_HIDAPI_Device *
device;
953 int player_index = -1;
955 device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
957 player_index =
device->driver->GetDevicePlayerIndex(
device, instance_id);
964 HIDAPI_JoystickSetDevicePlayerIndex(
int device_index,
int player_index)
966 SDL_HIDAPI_Device *
device;
969 device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
971 device->driver->SetDevicePlayerIndex(
device, instance_id, player_index);
976 HIDAPI_JoystickGetDeviceGUID(
int device_index)
978 SDL_HIDAPI_Device *
device;
981 device = HIDAPI_GetDeviceByIndex(device_index,
NULL);
992 HIDAPI_JoystickGetDeviceInstanceID(
int device_index)
995 HIDAPI_GetDeviceByIndex(device_index, &joystickID);
1000 HIDAPI_JoystickOpen(SDL_Joystick * joystick,
int device_index)
1003 SDL_HIDAPI_Device *
device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
1022 HIDAPI_JoystickRumble(SDL_Joystick *
joystick,
Uint16 low_frequency_rumble,
Uint16 high_frequency_rumble)
1039 HIDAPI_JoystickUpdate(SDL_Joystick *
joystick)
1045 HIDAPI_JoystickClose(SDL_Joystick *
joystick)
1063 HIDAPI_JoystickQuit(
void)
1069 HIDAPI_ShutdownDiscovery();
1071 while (SDL_HIDAPI_devices) {
1072 HIDAPI_DelDevice(SDL_HIDAPI_devices);
1075 SDL_HIDAPI_QuitRumble();
1078 SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[
i];
1082 SDL_HIDAPIDriverHintChanged,
NULL);
1095 HIDAPI_JoystickInit,
1096 HIDAPI_JoystickGetCount,
1097 HIDAPI_JoystickDetect,
1098 HIDAPI_JoystickGetDeviceName,
1099 HIDAPI_JoystickGetDevicePlayerIndex,
1100 HIDAPI_JoystickSetDevicePlayerIndex,
1101 HIDAPI_JoystickGetDeviceGUID,
1102 HIDAPI_JoystickGetDeviceInstanceID,
1103 HIDAPI_JoystickOpen,
1104 HIDAPI_JoystickRumble,
1105 HIDAPI_JoystickUpdate,
1106 HIDAPI_JoystickClose,
1107 HIDAPI_JoystickQuit,