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