src/joystick/hidapi/SDL_hidapijoystick.c
author Ethan Lee <flibitijibibo@flibitijibibo.com>
Tue, 12 Mar 2019 20:27:54 -0400
changeset 12641 64597a7e8771
parent 12503 806492103856
child 12787 0411f841b035
permissions -rw-r--r--
hidapi: Add support for Wii U/Switch USB GameCube controller adapter.

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