src/joystick/hidapi/SDL_hidapijoystick.c
author Sam Lantinga
Wed, 11 Dec 2019 17:46:59 -0800
changeset 13332 71116618f317
parent 13330 c88765bcf1da
child 13333 88aec26a0ad0
permissions -rw-r--r--
Shorten "Performance Designed Products" to "PDP"
     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"
    33 #include "SDL_hidapijoystick_c.h"
    34 #include "../../SDL_hints_c.h"
    35 
    36 #if defined(__WIN32__)
    37 #include "../../core/windows/SDL_windows.h"
    38 #endif
    39 
    40 #if defined(__MACOSX__)
    41 #include <CoreFoundation/CoreFoundation.h>
    42 #include <mach/mach.h>
    43 #include <IOKit/IOKitLib.h>
    44 #include <IOKit/usb/USBSpec.h>
    45 #endif
    46 
    47 #if defined(__LINUX__)
    48 #include "../../core/linux/SDL_udev.h"
    49 #ifdef SDL_USE_LIBUDEV
    50 #include <poll.h>
    51 #endif
    52 #endif
    53 
    54 struct joystick_hwdata
    55 {
    56     SDL_HIDAPI_DeviceDriver *driver;
    57     void *context;
    58 
    59     SDL_mutex *mutex;
    60     hid_device *dev;
    61 };
    62 
    63 typedef struct _SDL_HIDAPI_Device
    64 {
    65     SDL_JoystickID instance_id;
    66     char *name;
    67     char *path;
    68     Uint16 vendor_id;
    69     Uint16 product_id;
    70     Uint16 version;
    71     SDL_JoystickGUID guid;
    72     int interface_number;   /* Available on Windows and Linux */
    73     Uint16 usage_page;      /* Available on Windows and Mac OS X */
    74     Uint16 usage;           /* Available on Windows and Mac OS X */
    75     SDL_HIDAPI_DeviceDriver *driver;
    76 
    77     /* Used during scanning for device changes */
    78     SDL_bool seen;
    79 
    80     struct _SDL_HIDAPI_Device *next;
    81 } SDL_HIDAPI_Device;
    82 
    83 static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
    84 #ifdef SDL_JOYSTICK_HIDAPI_PS4
    85     &SDL_HIDAPI_DriverPS4,
    86 #endif
    87 #ifdef SDL_JOYSTICK_HIDAPI_STEAM
    88     &SDL_HIDAPI_DriverSteam,
    89 #endif
    90 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH
    91     &SDL_HIDAPI_DriverSwitch,
    92 #endif
    93 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
    94     &SDL_HIDAPI_DriverXbox360,
    95 #endif
    96 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
    97     &SDL_HIDAPI_DriverXboxOne,
    98 #endif
    99 };
   100 static int SDL_HIDAPI_numdrivers = 0;
   101 static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
   102 static int SDL_HIDAPI_numjoysticks = 0;
   103 static SDL_bool initialized = SDL_FALSE;
   104 
   105 #if defined(SDL_USE_LIBUDEV)
   106 static const SDL_UDEV_Symbols * usyms = NULL;
   107 #endif
   108 
   109 static struct
   110 {
   111     SDL_bool m_bHaveDevicesChanged;
   112     SDL_bool m_bCanGetNotifications;
   113     Uint32 m_unLastDetect;
   114 
   115 #if defined(__WIN32__)
   116     SDL_threadID m_nThreadID;
   117     WNDCLASSEXA m_wndClass;
   118     HWND m_hwndMsg;
   119     HDEVNOTIFY m_hNotify;
   120     double m_flLastWin32MessageCheck;
   121 #endif
   122 
   123 #if defined(__MACOSX__)
   124     IONotificationPortRef m_notificationPort;
   125     mach_port_t m_notificationMach;
   126 #endif
   127 
   128 #if defined(SDL_USE_LIBUDEV)
   129     struct udev *m_pUdev;
   130     struct udev_monitor *m_pUdevMonitor;
   131     int m_nUdevFd;
   132 #endif
   133 } SDL_HIDAPI_discovery;
   134 
   135 
   136 #ifdef __WIN32__
   137 struct _DEV_BROADCAST_HDR
   138 {
   139     DWORD       dbch_size;
   140     DWORD       dbch_devicetype;
   141     DWORD       dbch_reserved;
   142 };
   143 
   144 typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
   145 {
   146     DWORD       dbcc_size;
   147     DWORD       dbcc_devicetype;
   148     DWORD       dbcc_reserved;
   149     GUID        dbcc_classguid;
   150     char        dbcc_name[ 1 ];
   151 } DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
   152 
   153 typedef struct  _DEV_BROADCAST_HDR      DEV_BROADCAST_HDR;
   154 #define DBT_DEVICEARRIVAL               0x8000  /* system detected a new device */
   155 #define DBT_DEVICEREMOVECOMPLETE        0x8004  /* device was removed from the system */
   156 #define DBT_DEVTYP_DEVICEINTERFACE      0x00000005  /* device interface class */
   157 #define DBT_DEVNODES_CHANGED            0x0007
   158 #define DBT_CONFIGCHANGED               0x0018
   159 #define DBT_DEVICETYPESPECIFIC          0x8005  /* type specific event */
   160 #define DBT_DEVINSTSTARTED              0x8008  /* device installed and started */
   161 
   162 #include <initguid.h>
   163 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
   164 
   165 static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   166 {
   167     switch (message) {
   168     case WM_DEVICECHANGE:
   169         switch (wParam) {
   170         case DBT_DEVICEARRIVAL:
   171         case DBT_DEVICEREMOVECOMPLETE:
   172             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
   173                 SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
   174             }
   175             break;
   176         }
   177         return TRUE;
   178     }
   179 
   180     return DefWindowProc(hwnd, message, wParam, lParam);
   181 }
   182 #endif /* __WIN32__ */
   183 
   184 
   185 #if defined(__MACOSX__)
   186 static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator)
   187 {
   188     /* Must drain the iterator, or we won't receive new notifications */
   189     io_object_t entry;
   190     while ((entry = IOIteratorNext(portIterator)) != 0) {
   191         IOObjectRelease(entry);
   192         *(SDL_bool*)context = SDL_TRUE;
   193     }
   194 }
   195 #endif /* __MACOSX__ */
   196 
   197 static void
   198 HIDAPI_InitializeDiscovery()
   199 {
   200     SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
   201     SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_FALSE;
   202     SDL_HIDAPI_discovery.m_unLastDetect = 0;
   203 
   204 #if defined(__WIN32__)
   205     SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
   206 
   207     SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass));
   208     SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
   209     SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
   210     SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc;      /* This function is called by windows */
   211     SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX);
   212 
   213     RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass);
   214     SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
   215 
   216     {
   217         DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
   218         SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) );
   219 
   220         devBroadcast.dbcc_size = sizeof( devBroadcast );
   221         devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
   222         devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
   223 
   224         /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored,
   225          * but that seems to be necessary to get a notice after each individual usb input device actually
   226          * installs, rather than just as the composite device is seen.
   227          */
   228         SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification( SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES );
   229         SDL_HIDAPI_discovery.m_bCanGetNotifications = ( SDL_HIDAPI_discovery.m_hNotify != 0 );
   230     }
   231 #endif /* __WIN32__ */
   232 
   233 #if defined(__MACOSX__)
   234     SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
   235     if (SDL_HIDAPI_discovery.m_notificationPort) {
   236         {
   237             CFMutableDictionaryRef matchingDict = IOServiceMatching("IOUSBDevice");
   238 
   239             /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
   240             io_iterator_t portIterator = 0;
   241             io_object_t entry;
   242             if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
   243                 /* Must drain the existing iterator, or we won't receive new notifications */
   244                 while ((entry = IOIteratorNext(portIterator)) != 0) {
   245                     IOObjectRelease(entry);
   246                 }
   247             } else {
   248                 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
   249                 SDL_HIDAPI_discovery.m_notificationPort = nil;
   250             }
   251         }
   252         {
   253             CFMutableDictionaryRef matchingDict = IOServiceMatching("IOBluetoothDevice");
   254 
   255             /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
   256             io_iterator_t portIterator = 0;
   257             io_object_t entry;
   258             if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
   259                 /* Must drain the existing iterator, or we won't receive new notifications */
   260                 while ((entry = IOIteratorNext(portIterator)) != 0) {
   261                     IOObjectRelease(entry);
   262                 }
   263             } else {
   264                 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
   265                 SDL_HIDAPI_discovery.m_notificationPort = nil;
   266             }
   267         }
   268     }
   269 
   270     SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL;
   271     if (SDL_HIDAPI_discovery.m_notificationPort) {
   272         SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort);
   273     }
   274 
   275     SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL);
   276 
   277 #endif // __MACOSX__
   278 
   279 #if defined(SDL_USE_LIBUDEV)
   280     SDL_HIDAPI_discovery.m_pUdev = NULL;
   281     SDL_HIDAPI_discovery.m_pUdevMonitor = NULL;
   282     SDL_HIDAPI_discovery.m_nUdevFd = -1;
   283 
   284     usyms = SDL_UDEV_GetUdevSyms();
   285     if (usyms) {
   286         SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new();
   287     }
   288     if (SDL_HIDAPI_discovery.m_pUdev) {
   289         SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev");
   290         if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
   291             usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor);
   292             SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor);
   293             SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_TRUE;
   294         }
   295     }
   296 
   297 #endif /* SDL_USE_LIBUDEV */
   298 }
   299 
   300 static void
   301 HIDAPI_UpdateDiscovery()
   302 {
   303     if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) {
   304         const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000;  /* Update every 3 seconds */
   305         Uint32 now = SDL_GetTicks();
   306         if (!SDL_HIDAPI_discovery.m_unLastDetect || SDL_TICKS_PASSED(now, SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) {
   307             SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
   308             SDL_HIDAPI_discovery.m_unLastDetect = now;
   309         }
   310         return;
   311     }
   312 
   313 #if defined(__WIN32__)
   314 #if 0 /* just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan. */
   315     /* We'll only get messages on the same thread that created the window */
   316     if (SDL_ThreadID() == SDL_HIDAPI_discovery.m_nThreadID) {
   317         MSG msg;
   318         while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) {
   319             if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) {
   320                 TranslateMessage(&msg);
   321                 DispatchMessage(&msg);
   322             }
   323         }
   324     }
   325 #endif
   326 #endif /* __WIN32__ */
   327 
   328 #if defined(__MACOSX__)
   329     if (SDL_HIDAPI_discovery.m_notificationPort) {
   330         struct { mach_msg_header_t hdr; char payload[ 4096 ]; } msg;
   331         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) {
   332             IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort);
   333         }
   334     }
   335 #endif
   336 
   337 #if defined(SDL_USE_LIBUDEV)
   338     if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) {
   339         /* Drain all notification events.
   340          * We don't expect a lot of device notifications so just
   341          * do a new discovery on any kind or number of notifications.
   342          * This could be made more restrictive if necessary.
   343          */
   344         for (;;) {
   345             struct pollfd PollUdev;
   346             struct udev_device *pUdevDevice;
   347 
   348             PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd;
   349             PollUdev.events = POLLIN;
   350             if (poll(&PollUdev, 1, 0) != 1) {
   351                 break;
   352             }
   353 
   354             SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
   355 
   356             pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor);
   357             if (pUdevDevice) {
   358                 usyms->udev_device_unref(pUdevDevice);
   359             }
   360         }
   361     }
   362 #endif
   363 }
   364 
   365 static void
   366 HIDAPI_ShutdownDiscovery()
   367 {
   368 #if defined(__WIN32__)
   369     if (SDL_HIDAPI_discovery.m_hNotify)
   370         UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify);
   371 
   372     if (SDL_HIDAPI_discovery.m_hwndMsg) {
   373         DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg);
   374     }
   375 
   376     UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance);
   377 #endif
   378 
   379 #if defined(__MACOSX__)
   380     if (SDL_HIDAPI_discovery.m_notificationPort) {
   381         IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
   382     }
   383 #endif
   384 
   385 #if defined(SDL_USE_LIBUDEV)
   386     if (usyms) {
   387         if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
   388             usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor);
   389         }
   390         if (SDL_HIDAPI_discovery.m_pUdev) {
   391             usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev);
   392         }
   393         SDL_UDEV_ReleaseUdevSyms();
   394         usyms = NULL;
   395     }
   396 #endif
   397 }
   398 
   399 static SDL_bool
   400 HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
   401 {
   402     int i;
   403 
   404     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   405         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   406         if (driver->enabled && driver->IsSupportedDevice(vendor_id, product_id, version, -1, name)) {
   407             return SDL_TRUE;
   408         }
   409     }
   410     return SDL_FALSE;
   411 }
   412 
   413 static SDL_HIDAPI_DeviceDriver *
   414 HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
   415 {
   416     const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;
   417     const Uint16 USAGE_JOYSTICK = 0x0004;
   418     const Uint16 USAGE_GAMEPAD = 0x0005;
   419     const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
   420     int i;
   421 
   422     if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
   423         return NULL;
   424     }
   425 
   426     if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
   427         return NULL;
   428     }
   429     if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
   430         return NULL;
   431     }
   432 
   433     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   434         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   435         if (driver->enabled && driver->IsSupportedDevice(device->vendor_id, device->product_id, device->version, device->interface_number, device->name)) {
   436             return driver;
   437         }
   438     }
   439     return NULL;
   440 }
   441 
   442 static SDL_HIDAPI_Device *
   443 HIDAPI_GetJoystickByIndex(int device_index)
   444 {
   445     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
   446     while (device) {
   447         if (device->driver) {
   448             if (device_index == 0) {
   449                 break;
   450             }
   451             --device_index;
   452         }
   453         device = device->next;
   454     }
   455     return device;
   456 }
   457 
   458 static SDL_HIDAPI_Device *
   459 HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
   460 {
   461     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
   462     while (device) {
   463         if (device->vendor_id == vendor_id && device->product_id == product_id &&
   464             SDL_strcmp(device->path, path) == 0) {
   465             break;
   466         }
   467         device = device->next;
   468     }
   469     return device;
   470 }
   471 
   472 static void SDLCALL
   473 SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
   474 {
   475     int i;
   476     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
   477     SDL_bool enabled = SDL_GetStringBoolean(hint, SDL_TRUE);
   478 
   479     if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
   480         for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   481             SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   482             driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
   483         }
   484     } else {
   485         for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   486             SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   487             if (SDL_strcmp(name, driver->hint) == 0) {
   488                 driver->enabled = enabled;
   489                 break;
   490             }
   491         }
   492     }
   493 
   494     SDL_HIDAPI_numdrivers = 0;
   495     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   496         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   497         if (driver->enabled) {
   498             ++SDL_HIDAPI_numdrivers;
   499         }
   500     }
   501 
   502     /* Update device list if driver availability changes */
   503     while (device) {
   504         if (device->driver) {
   505             if (!device->driver->enabled) {
   506                 device->driver = NULL;
   507 
   508                 --SDL_HIDAPI_numjoysticks;
   509 
   510                 SDL_PrivateJoystickRemoved(device->instance_id);
   511             }
   512         } else {
   513             device->driver = HIDAPI_GetDeviceDriver(device);
   514             if (device->driver) {
   515                 device->instance_id = SDL_GetNextJoystickInstanceID();
   516 
   517                 ++SDL_HIDAPI_numjoysticks;
   518 
   519                 SDL_PrivateJoystickAdded(device->instance_id);
   520             }
   521         }
   522         device = device->next;
   523     }
   524 }
   525 
   526 static void HIDAPI_JoystickDetect(void);
   527 
   528 static int
   529 HIDAPI_JoystickInit(void)
   530 {
   531     int i;
   532 
   533     if (initialized) {
   534         return 0;
   535     }
   536 
   537     if (hid_init() < 0) {
   538         SDL_SetError("Couldn't initialize hidapi");
   539         return -1;
   540     }
   541 
   542     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   543         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   544         SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
   545     }
   546     SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
   547                         SDL_HIDAPIDriverHintChanged, NULL);
   548     HIDAPI_InitializeDiscovery();
   549     HIDAPI_JoystickDetect();
   550 
   551     initialized = SDL_TRUE;
   552 
   553     return 0;
   554 }
   555 
   556 static int
   557 HIDAPI_JoystickGetCount(void)
   558 {
   559     return SDL_HIDAPI_numjoysticks;
   560 }
   561 
   562 static void
   563 HIDAPI_AddDevice(struct hid_device_info *info)
   564 {
   565     SDL_HIDAPI_Device *device;
   566     SDL_HIDAPI_Device *curr, *last = NULL;
   567 
   568     for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
   569         continue;
   570     }
   571 
   572     device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
   573     if (!device) {
   574         return;
   575     }
   576     device->instance_id = -1;
   577     device->seen = SDL_TRUE;
   578     device->vendor_id = info->vendor_id;
   579     device->product_id = info->product_id;
   580     device->version = info->release_number;
   581     device->interface_number = info->interface_number;
   582     device->usage_page = info->usage_page;
   583     device->usage = info->usage;
   584     {
   585         /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
   586         const Uint16 vendor = device->vendor_id;
   587         const Uint16 product = device->product_id;
   588         const Uint16 version = device->version;
   589         Uint16 *guid16 = (Uint16 *)device->guid.data;
   590 
   591         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
   592         *guid16++ = 0;
   593         *guid16++ = SDL_SwapLE16(vendor);
   594         *guid16++ = 0;
   595         *guid16++ = SDL_SwapLE16(product);
   596         *guid16++ = 0;
   597         *guid16++ = SDL_SwapLE16(version);
   598         *guid16++ = 0;
   599 
   600         /* Note that this is a HIDAPI device for special handling elsewhere */
   601         device->guid.data[14] = 'h';
   602         device->guid.data[15] = 0;
   603     }
   604 
   605     /* Need the device name before getting the driver to know whether to ignore this device */
   606     if (!device->name && info->manufacturer_string && info->product_string) {
   607         char *manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
   608         char *product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
   609         if (!manufacturer_string && !product_string) {
   610             if (sizeof(wchar_t) == sizeof(Uint16)) {
   611                 manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
   612                 product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
   613             } else if (sizeof(wchar_t) == sizeof(Uint32)) {
   614                 manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
   615                 product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
   616             }
   617         }
   618         if (manufacturer_string && product_string) {
   619             size_t name_size;
   620 
   621             if (SDL_strcmp(manufacturer_string, "Performance Designed Products") == 0) {
   622                 /* Shorten this so controller names are more manageable */
   623                 SDL_memcpy(manufacturer_string, "PDP", 4);
   624             }
   625 
   626             name_size = (SDL_strlen(manufacturer_string) + 1 + SDL_strlen(product_string) + 1);
   627             device->name = (char *)SDL_malloc(name_size);
   628             if (device->name) {
   629                 if (SDL_strncasecmp(manufacturer_string, product_string, SDL_strlen(manufacturer_string)) == 0) {
   630                     SDL_strlcpy(device->name, product_string, name_size);
   631                 } else {
   632                     SDL_snprintf(device->name, name_size, "%s %s", manufacturer_string, product_string);
   633                 }
   634             }
   635         }
   636         if (manufacturer_string) {
   637             SDL_free(manufacturer_string);
   638         }
   639         if (product_string) {
   640             SDL_free(product_string);
   641         }
   642     }
   643     if (!device->name) {
   644         size_t name_size = (6 + 1 + 6 + 1);
   645         device->name = (char *)SDL_malloc(name_size);
   646         if (!device->name) {
   647             SDL_free(device);
   648             return;
   649         }
   650         SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
   651     }
   652 
   653     device->driver = HIDAPI_GetDeviceDriver(device);
   654 
   655     if (device->driver) {
   656         const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
   657         if (name) {
   658             SDL_free(device->name);
   659             device->name = SDL_strdup(name);
   660         }
   661     }
   662 
   663     device->path = SDL_strdup(info->path);
   664     if (!device->path) {
   665         SDL_free(device->name);
   666         SDL_free(device);
   667         return;
   668     }
   669 
   670 #ifdef DEBUG_HIDAPI
   671     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");
   672 #endif
   673 
   674     /* Add it to the list */
   675     if (last) {
   676         last->next = device;
   677     } else {
   678         SDL_HIDAPI_devices = device;
   679     }
   680 
   681     if (device->driver) {
   682         /* It's a joystick! */
   683         device->instance_id = SDL_GetNextJoystickInstanceID();
   684 
   685         ++SDL_HIDAPI_numjoysticks;
   686 
   687         SDL_PrivateJoystickAdded(device->instance_id);
   688     }
   689 }
   690 
   691 
   692 static void
   693 HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event)
   694 {
   695     SDL_HIDAPI_Device *curr, *last;
   696     for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
   697         if (curr == device) {
   698             if (last) {
   699                 last->next = curr->next;
   700             } else {
   701                 SDL_HIDAPI_devices = curr->next;
   702             }
   703 
   704             if (device->driver && send_event) {
   705                 /* Need to decrement the joystick count before we post the event */
   706                 --SDL_HIDAPI_numjoysticks;
   707 
   708                 SDL_PrivateJoystickRemoved(device->instance_id);
   709             }
   710 
   711             SDL_free(device->name);
   712             SDL_free(device->path);
   713             SDL_free(device);
   714             return;
   715         }
   716     }
   717 }
   718 
   719 static void
   720 HIDAPI_UpdateDeviceList(void)
   721 {
   722     SDL_HIDAPI_Device *device;
   723     struct hid_device_info *devs, *info;
   724 
   725     /* Prepare the existing device list */
   726     device = SDL_HIDAPI_devices;
   727     while (device) {
   728         device->seen = SDL_FALSE;
   729         device = device->next;
   730     }
   731 
   732     /* Enumerate the devices */
   733     if (SDL_HIDAPI_numdrivers > 0) {
   734         devs = hid_enumerate(0, 0);
   735         if (devs) {
   736             for (info = devs; info; info = info->next) {
   737                 device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
   738                 if (device) {
   739                     device->seen = SDL_TRUE;
   740                 } else {
   741                     HIDAPI_AddDevice(info);
   742                 }
   743             }
   744             hid_free_enumeration(devs);
   745         }
   746     }
   747 
   748     /* Remove any devices that weren't seen */
   749     device = SDL_HIDAPI_devices;
   750     while (device) {
   751         SDL_HIDAPI_Device *next = device->next;
   752 
   753         if (!device->seen) {
   754             HIDAPI_DelDevice(device, SDL_TRUE);
   755         }
   756         device = next;
   757     }
   758 }
   759 
   760 SDL_bool
   761 HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
   762 {
   763     SDL_HIDAPI_Device *device;
   764 
   765     /* Make sure we're initialized, as this could be called from other drivers during startup */
   766     if (HIDAPI_JoystickInit() < 0) {
   767         return SDL_FALSE;
   768     }
   769 
   770     /* Don't update the device list for devices we know aren't supported */
   771     if (!HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name)) {
   772         return SDL_FALSE;
   773     }
   774 
   775     /* Make sure the device list is completely up to date when we check for device presence */
   776     HIDAPI_UpdateDeviceList();
   777 
   778     /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
   779        or a different name than we have it listed here, etc, but if we support the device
   780        and we have something similar in our device list, mark it as present.
   781      */
   782     device = SDL_HIDAPI_devices;
   783     while (device) {
   784         if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
   785             return SDL_TRUE;
   786         }
   787         device = device->next;
   788     }
   789     return SDL_FALSE;
   790 }
   791 
   792 static void
   793 HIDAPI_JoystickDetect(void)
   794 {
   795     HIDAPI_UpdateDiscovery();
   796     if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
   797         /* FIXME: We probably need to schedule an update in a few seconds as well */
   798         HIDAPI_UpdateDeviceList();
   799         SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
   800     }
   801 }
   802 
   803 static const char *
   804 HIDAPI_JoystickGetDeviceName(int device_index)
   805 {
   806     return HIDAPI_GetJoystickByIndex(device_index)->name;
   807 }
   808 
   809 static int
   810 HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
   811 {
   812     return -1;
   813 }
   814 
   815 static SDL_JoystickGUID
   816 HIDAPI_JoystickGetDeviceGUID(int device_index)
   817 {
   818     return HIDAPI_GetJoystickByIndex(device_index)->guid;
   819 }
   820 
   821 static SDL_JoystickID
   822 HIDAPI_JoystickGetDeviceInstanceID(int device_index)
   823 {
   824     return HIDAPI_GetJoystickByIndex(device_index)->instance_id;
   825 }
   826 
   827 static int
   828 HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
   829 {
   830     SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index);
   831     struct joystick_hwdata *hwdata;
   832 
   833     hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
   834     if (!hwdata) {
   835         return SDL_OutOfMemory();
   836     }
   837 
   838     hwdata->driver = device->driver;
   839     hwdata->dev = hid_open_path(device->path, 0);
   840     if (!hwdata->dev) {
   841         SDL_free(hwdata);
   842         return SDL_SetError("Couldn't open HID device %s", device->path);
   843     }
   844     hwdata->mutex = SDL_CreateMutex();
   845 
   846     if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) {
   847         hid_close(hwdata->dev);
   848         SDL_free(hwdata);
   849         return -1;
   850     }
   851 
   852     joystick->hwdata = hwdata;
   853     return 0;
   854 }
   855 
   856 static int
   857 HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   858 {
   859     struct joystick_hwdata *hwdata = joystick->hwdata;
   860     SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
   861     int result;
   862 
   863     SDL_LockMutex(hwdata->mutex);
   864     result = driver->Rumble(joystick, hwdata->dev, hwdata->context, low_frequency_rumble, high_frequency_rumble, duration_ms);
   865     SDL_UnlockMutex(hwdata->mutex);
   866     return result;
   867 }
   868 
   869 static void
   870 HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
   871 {
   872     struct joystick_hwdata *hwdata = joystick->hwdata;
   873     SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
   874     SDL_bool succeeded;
   875 
   876     SDL_LockMutex(hwdata->mutex);
   877     succeeded = driver->Update(joystick, hwdata->dev, hwdata->context);
   878     SDL_UnlockMutex(hwdata->mutex);
   879     
   880     if (!succeeded) {
   881         SDL_HIDAPI_Device *device;
   882         for (device = SDL_HIDAPI_devices; device; device = device->next) {
   883             if (device->instance_id == joystick->instance_id) {
   884                 HIDAPI_DelDevice(device, SDL_TRUE);
   885                 break;
   886             }
   887         }
   888     }
   889 }
   890 
   891 static void
   892 HIDAPI_JoystickClose(SDL_Joystick * joystick)
   893 {
   894     struct joystick_hwdata *hwdata = joystick->hwdata;
   895     SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
   896     driver->Quit(joystick, hwdata->dev, hwdata->context);
   897 
   898     hid_close(hwdata->dev);
   899     SDL_DestroyMutex(hwdata->mutex);
   900     SDL_free(hwdata);
   901     joystick->hwdata = NULL;
   902 }
   903 
   904 static void
   905 HIDAPI_JoystickQuit(void)
   906 {
   907     int i;
   908 
   909     HIDAPI_ShutdownDiscovery();
   910 
   911     while (SDL_HIDAPI_devices) {
   912         HIDAPI_DelDevice(SDL_HIDAPI_devices, SDL_FALSE);
   913     }
   914     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   915         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   916         SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
   917     }
   918     SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
   919                         SDL_HIDAPIDriverHintChanged, NULL);
   920     SDL_HIDAPI_numjoysticks = 0;
   921 
   922     hid_exit();
   923 
   924     initialized = SDL_FALSE;
   925 }
   926 
   927 SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
   928 {
   929     HIDAPI_JoystickInit,
   930     HIDAPI_JoystickGetCount,
   931     HIDAPI_JoystickDetect,
   932     HIDAPI_JoystickGetDeviceName,
   933     HIDAPI_JoystickGetDevicePlayerIndex,
   934     HIDAPI_JoystickGetDeviceGUID,
   935     HIDAPI_JoystickGetDeviceInstanceID,
   936     HIDAPI_JoystickOpen,
   937     HIDAPI_JoystickRumble,
   938     HIDAPI_JoystickUpdate,
   939     HIDAPI_JoystickClose,
   940     HIDAPI_JoystickQuit,
   941 };
   942 
   943 #endif /* SDL_JOYSTICK_HIDAPI */
   944 
   945 /* vi: set ts=4 sw=4 expandtab: */