src/joystick/hidapi/SDL_hidapijoystick.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 20 Mar 2020 21:05:07 -0700
changeset 13662 d32d92e782ab
parent 13633 340324c76848
child 13696 ea20a7434b98
permissions -rw-r--r--
Allow Valve devices in driver check, we know they're well behaved controllers
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 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_assert.h"
    26 #include "SDL_atomic.h"
    27 #include "SDL_endian.h"
    28 #include "SDL_hints.h"
    29 #include "SDL_log.h"
    30 #include "SDL_thread.h"
    31 #include "SDL_timer.h"
    32 #include "SDL_joystick.h"
    33 #include "../SDL_sysjoystick.h"
    34 #include "SDL_hidapijoystick_c.h"
    35 #include "SDL_hidapi_rumble.h"
    36 #include "../../SDL_hints_c.h"
    37 
    38 #if defined(__WIN32__)
    39 #include "../../core/windows/SDL_windows.h"
    40 #include "../windows/SDL_rawinputjoystick_c.h"
    41 #endif
    42 
    43 #if defined(__MACOSX__)
    44 #include <CoreFoundation/CoreFoundation.h>
    45 #include <mach/mach.h>
    46 #include <IOKit/IOKitLib.h>
    47 #include <IOKit/hid/IOHIDDevice.h>
    48 #include <IOKit/usb/USBSpec.h>
    49 #endif
    50 
    51 #if defined(__LINUX__)
    52 #include "../../core/linux/SDL_udev.h"
    53 #ifdef SDL_USE_LIBUDEV
    54 #include <poll.h>
    55 #endif
    56 #endif
    57 
    58 struct joystick_hwdata
    59 {
    60     SDL_HIDAPI_Device *device;
    61 };
    62 
    63 static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
    64 #ifdef SDL_JOYSTICK_HIDAPI_PS4
    65     &SDL_HIDAPI_DriverPS4,
    66 #endif
    67 #ifdef SDL_JOYSTICK_HIDAPI_STEAM
    68     &SDL_HIDAPI_DriverSteam,
    69 #endif
    70 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH
    71     &SDL_HIDAPI_DriverSwitch,
    72 #endif
    73 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
    74     &SDL_HIDAPI_DriverXbox360,
    75     &SDL_HIDAPI_DriverXbox360W,
    76 #endif
    77 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
    78     &SDL_HIDAPI_DriverXboxOne,
    79 #endif
    80 #ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
    81     &SDL_HIDAPI_DriverGameCube,
    82 #endif
    83 };
    84 static int SDL_HIDAPI_numdrivers = 0;
    85 static SDL_SpinLock SDL_HIDAPI_spinlock;
    86 static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
    87 static int SDL_HIDAPI_numjoysticks = 0;
    88 static SDL_bool initialized = SDL_FALSE;
    89 static SDL_bool shutting_down = SDL_FALSE;
    90 
    91 #if defined(SDL_USE_LIBUDEV)
    92 static const SDL_UDEV_Symbols * usyms = NULL;
    93 #endif
    94 
    95 static struct
    96 {
    97     SDL_bool m_bHaveDevicesChanged;
    98     SDL_bool m_bCanGetNotifications;
    99     Uint32 m_unLastDetect;
   100 
   101 #if defined(__WIN32__)
   102     SDL_threadID m_nThreadID;
   103     WNDCLASSEXA m_wndClass;
   104     HWND m_hwndMsg;
   105     HDEVNOTIFY m_hNotify;
   106     double m_flLastWin32MessageCheck;
   107 #endif
   108 
   109 #if defined(__MACOSX__)
   110     IONotificationPortRef m_notificationPort;
   111     mach_port_t m_notificationMach;
   112 #endif
   113 
   114 #if defined(SDL_USE_LIBUDEV)
   115     struct udev *m_pUdev;
   116     struct udev_monitor *m_pUdevMonitor;
   117     int m_nUdevFd;
   118 #endif
   119 } SDL_HIDAPI_discovery;
   120 
   121 
   122 #ifdef __WIN32__
   123 struct _DEV_BROADCAST_HDR
   124 {
   125     DWORD       dbch_size;
   126     DWORD       dbch_devicetype;
   127     DWORD       dbch_reserved;
   128 };
   129 
   130 typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
   131 {
   132     DWORD       dbcc_size;
   133     DWORD       dbcc_devicetype;
   134     DWORD       dbcc_reserved;
   135     GUID        dbcc_classguid;
   136     char        dbcc_name[ 1 ];
   137 } DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
   138 
   139 typedef struct  _DEV_BROADCAST_HDR      DEV_BROADCAST_HDR;
   140 #define DBT_DEVICEARRIVAL               0x8000  /* system detected a new device */
   141 #define DBT_DEVICEREMOVECOMPLETE        0x8004  /* device was removed from the system */
   142 #define DBT_DEVTYP_DEVICEINTERFACE      0x00000005  /* device interface class */
   143 #define DBT_DEVNODES_CHANGED            0x0007
   144 #define DBT_CONFIGCHANGED               0x0018
   145 #define DBT_DEVICETYPESPECIFIC          0x8005  /* type specific event */
   146 #define DBT_DEVINSTSTARTED              0x8008  /* device installed and started */
   147 
   148 #include <initguid.h>
   149 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
   150 
   151 static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   152 {
   153     switch (message) {
   154     case WM_DEVICECHANGE:
   155         switch (wParam) {
   156         case DBT_DEVICEARRIVAL:
   157         case DBT_DEVICEREMOVECOMPLETE:
   158             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
   159                 SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
   160             }
   161             break;
   162         }
   163         return TRUE;
   164     }
   165 
   166     return DefWindowProc(hwnd, message, wParam, lParam);
   167 }
   168 #endif /* __WIN32__ */
   169 
   170 
   171 #if defined(__MACOSX__)
   172 static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator)
   173 {
   174     /* Must drain the iterator, or we won't receive new notifications */
   175     io_object_t entry;
   176     while ((entry = IOIteratorNext(portIterator)) != 0) {
   177         IOObjectRelease(entry);
   178         *(SDL_bool*)context = SDL_TRUE;
   179     }
   180 }
   181 #endif /* __MACOSX__ */
   182 
   183 static void
   184 HIDAPI_InitializeDiscovery()
   185 {
   186     SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
   187     SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_FALSE;
   188     SDL_HIDAPI_discovery.m_unLastDetect = 0;
   189 
   190 #if defined(__WIN32__)
   191     SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
   192 
   193     SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass));
   194     SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
   195     SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
   196     SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc;      /* This function is called by windows */
   197     SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX);
   198 
   199     RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass);
   200     SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
   201 
   202     {
   203         DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
   204         SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) );
   205 
   206         devBroadcast.dbcc_size = sizeof( devBroadcast );
   207         devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
   208         devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
   209 
   210         /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored,
   211          * but that seems to be necessary to get a notice after each individual usb input device actually
   212          * installs, rather than just as the composite device is seen.
   213          */
   214         SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification( SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES );
   215         SDL_HIDAPI_discovery.m_bCanGetNotifications = ( SDL_HIDAPI_discovery.m_hNotify != 0 );
   216     }
   217 #endif /* __WIN32__ */
   218 
   219 #if defined(__MACOSX__)
   220     SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
   221     if (SDL_HIDAPI_discovery.m_notificationPort) {
   222         {
   223             io_iterator_t portIterator = 0;
   224             io_object_t entry;
   225             IOReturn result = IOServiceAddMatchingNotification(
   226                 SDL_HIDAPI_discovery.m_notificationPort,
   227                 kIOFirstMatchNotification,
   228                 IOServiceMatching(kIOHIDDeviceKey),
   229                 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
   230 
   231             if (result == 0) {
   232                 /* Must drain the existing iterator, or we won't receive new notifications */
   233                 while ((entry = IOIteratorNext(portIterator)) != 0) {
   234                     IOObjectRelease(entry);
   235                 }
   236             } else {
   237                 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
   238                 SDL_HIDAPI_discovery.m_notificationPort = nil;
   239             }
   240         }
   241         {
   242             io_iterator_t portIterator = 0;
   243             io_object_t entry;
   244             IOReturn result = IOServiceAddMatchingNotification(
   245                 SDL_HIDAPI_discovery.m_notificationPort,
   246                 kIOTerminatedNotification,
   247                 IOServiceMatching(kIOHIDDeviceKey),
   248                 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
   249 
   250             if (result == 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 HIDAPI_JoystickDetect(void);
   392 static void HIDAPI_JoystickClose(SDL_Joystick * joystick);
   393 
   394 static SDL_bool
   395 HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
   396 {
   397     int i;
   398     SDL_GameControllerType type = SDL_GetJoystickGameControllerType(name, vendor_id, product_id, -1, 0, 0, 0);
   399 
   400     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   401         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   402         if (driver->enabled && driver->IsSupportedDevice(name, type, vendor_id, product_id, version, -1, 0, 0, 0)) {
   403             return SDL_TRUE;
   404         }
   405     }
   406     return SDL_FALSE;
   407 }
   408 
   409 static SDL_HIDAPI_DeviceDriver *
   410 HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
   411 {
   412     const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;
   413     const Uint16 USAGE_JOYSTICK = 0x0004;
   414     const Uint16 USAGE_GAMEPAD = 0x0005;
   415     const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
   416     int i;
   417     SDL_GameControllerType type;
   418 
   419     if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
   420         return NULL;
   421     }
   422 
   423 #ifdef SDL_JOYSTICK_RAWINPUT
   424     if (RAWINPUT_IsDevicePresent(device->vendor_id, device->product_id, device->version)) {
   425         /* The RAWINPUT driver is taking care of this device */
   426         return NULL;
   427     }
   428 #endif
   429 
   430 	if (device->vendor_id != USB_VENDOR_VALVE) {
   431         if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
   432             return NULL;
   433         }
   434         if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
   435             return NULL;
   436         }
   437     }
   438 
   439     type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
   440     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   441         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   442         if (driver->enabled && driver->IsSupportedDevice(device->name, type, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
   443             return driver;
   444         }
   445     }
   446     return NULL;
   447 }
   448 
   449 static SDL_HIDAPI_Device *
   450 HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID)
   451 {
   452     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
   453     while (device) {
   454         if (device->driver) {
   455             if (device_index < device->num_joysticks) {
   456                 if (pJoystickID) {
   457                     *pJoystickID = device->joysticks[device_index];
   458                 }
   459                 return device;
   460             }
   461             device_index -= device->num_joysticks;
   462         }
   463         device = device->next;
   464     }
   465     return NULL;
   466 }
   467 
   468 static SDL_HIDAPI_Device *
   469 HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
   470 {
   471     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
   472     while (device) {
   473         if (device->vendor_id == vendor_id && device->product_id == product_id &&
   474             SDL_strcmp(device->path, path) == 0) {
   475             break;
   476         }
   477         device = device->next;
   478     }
   479     return device;
   480 }
   481 
   482 static void
   483 HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device)
   484 {
   485     if (device->driver) {
   486         /* Already setup */
   487         return;
   488     }
   489 
   490     device->driver = HIDAPI_GetDeviceDriver(device);
   491     if (device->driver) {
   492         const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
   493         if (name) {
   494             SDL_free(device->name);
   495             device->name = SDL_strdup(name);
   496         }
   497     }
   498 
   499     /* Initialize the device, which may cause a connected event */
   500     if (device->driver && !device->driver->InitDevice(device)) {
   501         device->driver = NULL;
   502     }
   503 }
   504 
   505 static void
   506 HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
   507 {
   508     if (!device->driver) {
   509         /* Already cleaned up */
   510         return;
   511     }
   512 
   513     /* Disconnect any joysticks */
   514     while (device->num_joysticks) {
   515         HIDAPI_JoystickDisconnected(device, device->joysticks[0], SDL_FALSE);
   516     }
   517 
   518     device->driver->FreeDevice(device);
   519     device->driver = NULL;
   520 }
   521 
   522 static void SDLCALL
   523 SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
   524 {
   525     int i;
   526     SDL_HIDAPI_Device *device;
   527     SDL_bool enabled = SDL_GetStringBoolean(hint, SDL_TRUE);
   528 
   529     if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
   530         for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   531             SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   532             driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
   533         }
   534     } else {
   535         for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   536             SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   537             if (SDL_strcmp(name, driver->hint) == 0) {
   538                 driver->enabled = enabled;
   539             }
   540         }
   541     }
   542 
   543     SDL_HIDAPI_numdrivers = 0;
   544     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   545         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   546         if (driver->enabled) {
   547             ++SDL_HIDAPI_numdrivers;
   548         }
   549     }
   550 
   551     /* Update device list if driver availability changes */
   552     SDL_LockJoysticks();
   553 
   554     for (device = SDL_HIDAPI_devices; device; device = device->next) {
   555         if (device->driver && !device->driver->enabled) {
   556             HIDAPI_CleanupDeviceDriver(device);
   557         }
   558         HIDAPI_SetupDeviceDriver(device);
   559     }
   560 
   561     SDL_UnlockJoysticks();
   562 }
   563 
   564 static int
   565 HIDAPI_JoystickInit(void)
   566 {
   567     int i;
   568 
   569     if (initialized) {
   570         return 0;
   571     }
   572 
   573 #if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__)
   574 	/* The hidapi framwork is weak-linked on Apple platforms */
   575     int HID_API_EXPORT HID_API_CALL hid_init(void) __attribute__((weak_import));
   576 
   577     if (hid_init == NULL) {
   578         SDL_SetError("Couldn't initialize hidapi, framework not available");
   579         return -1;
   580     }
   581 #endif /* __MACOSX__ || __IPHONEOS__ || __TVOS__ */
   582 
   583     if (hid_init() < 0) {
   584         SDL_SetError("Couldn't initialize hidapi");
   585         return -1;
   586     }
   587 
   588 #ifdef __WINDOWS__
   589     /* On Windows, turns out HIDAPI for Xbox controllers doesn't allow background input, so off by default */
   590     SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0", SDL_HINT_DEFAULT);
   591 #endif
   592 
   593     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   594         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   595         SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
   596     }
   597     SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
   598                         SDL_HIDAPIDriverHintChanged, NULL);
   599     HIDAPI_InitializeDiscovery();
   600     HIDAPI_JoystickDetect();
   601     HIDAPI_UpdateDevices();
   602 
   603     initialized = SDL_TRUE;
   604 
   605     return 0;
   606 }
   607 
   608 SDL_bool
   609 HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID, SDL_bool is_external)
   610 {
   611     SDL_JoystickID joystickID;
   612     SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks));
   613     if (!joysticks) {
   614         return SDL_FALSE;
   615     }
   616 
   617     joystickID = SDL_GetNextJoystickInstanceID();
   618     device->joysticks = joysticks;
   619     device->joysticks[device->num_joysticks++] = joystickID;
   620     if (!is_external) {
   621         ++SDL_HIDAPI_numjoysticks;
   622     }
   623 
   624     SDL_PrivateJoystickAdded(joystickID);
   625 
   626     if (pJoystickID) {
   627         *pJoystickID = joystickID;
   628     }
   629     return SDL_TRUE;
   630 }
   631 
   632 void
   633 HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID, SDL_bool is_external)
   634 {
   635     int i;
   636 
   637     for (i = 0; i < device->num_joysticks; ++i) {
   638         if (device->joysticks[i] == joystickID) {
   639             SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joystickID);
   640             if (joystick && !is_external) {
   641                 HIDAPI_JoystickClose(joystick);
   642             }
   643 
   644             SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], device->num_joysticks - i - 1);
   645             --device->num_joysticks;
   646             if (!is_external) {
   647                 --SDL_HIDAPI_numjoysticks;
   648             }
   649             if (device->num_joysticks == 0) {
   650                 SDL_free(device->joysticks);
   651                 device->joysticks = NULL;
   652             }
   653 
   654             if (!shutting_down) {
   655                 SDL_PrivateJoystickRemoved(joystickID);
   656             }
   657             return;
   658         }
   659     }
   660 }
   661 
   662 static int
   663 HIDAPI_JoystickGetCount(void)
   664 {
   665     return SDL_HIDAPI_numjoysticks;
   666 }
   667 
   668 static void
   669 HIDAPI_AddDevice(struct hid_device_info *info)
   670 {
   671     SDL_HIDAPI_Device *device;
   672     SDL_HIDAPI_Device *curr, *last = NULL;
   673 
   674     for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
   675         continue;
   676     }
   677 
   678     device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
   679     if (!device) {
   680         return;
   681     }
   682     device->path = SDL_strdup(info->path);
   683     if (!device->path) {
   684         SDL_free(device);
   685         return;
   686     }
   687     device->seen = SDL_TRUE;
   688     device->vendor_id = info->vendor_id;
   689     device->product_id = info->product_id;
   690     device->version = info->release_number;
   691     device->interface_number = info->interface_number;
   692     device->interface_class = info->interface_class;
   693     device->interface_subclass = info->interface_subclass;
   694     device->interface_protocol = info->interface_protocol;
   695     device->usage_page = info->usage_page;
   696     device->usage = info->usage;
   697     {
   698         /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
   699         const Uint16 vendor = device->vendor_id;
   700         const Uint16 product = device->product_id;
   701         const Uint16 version = device->version;
   702         Uint16 *guid16 = (Uint16 *)device->guid.data;
   703 
   704         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
   705         *guid16++ = 0;
   706         *guid16++ = SDL_SwapLE16(vendor);
   707         *guid16++ = 0;
   708         *guid16++ = SDL_SwapLE16(product);
   709         *guid16++ = 0;
   710         *guid16++ = SDL_SwapLE16(version);
   711         *guid16++ = 0;
   712 
   713         /* Note that this is a HIDAPI device for special handling elsewhere */
   714         device->guid.data[14] = 'h';
   715         device->guid.data[15] = 0;
   716     }
   717     device->dev_lock = SDL_CreateMutex();
   718 
   719     /* Need the device name before getting the driver to know whether to ignore this device */
   720     {
   721         char *manufacturer_string = NULL;
   722         char *product_string = NULL;
   723 
   724         if (info->manufacturer_string) {
   725             manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
   726             if (!manufacturer_string) {
   727                 if (sizeof(wchar_t) == sizeof(Uint16)) {
   728                     manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
   729                 } else if (sizeof(wchar_t) == sizeof(Uint32)) {
   730                     manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
   731                 }
   732             }
   733         }
   734         if (info->product_string) {
   735             product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
   736             if (!product_string) {
   737                 if (sizeof(wchar_t) == sizeof(Uint16)) {
   738                     product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
   739                 } else if (sizeof(wchar_t) == sizeof(Uint32)) {
   740                     product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
   741                 }
   742             }
   743         }
   744 
   745         device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string);
   746 
   747         if (manufacturer_string) {
   748             SDL_free(manufacturer_string);
   749         }
   750         if (product_string) {
   751             SDL_free(product_string);
   752         }
   753 
   754         if (!device->name) {
   755             SDL_free(device->path);
   756             SDL_free(device);
   757             return;
   758         }
   759     }
   760 
   761     /* Add it to the list */
   762     if (last) {
   763         last->next = device;
   764     } else {
   765         SDL_HIDAPI_devices = device;
   766     }
   767 
   768     HIDAPI_SetupDeviceDriver(device);
   769 
   770 #ifdef DEBUG_HIDAPI
   771     SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
   772 #endif
   773 }
   774 
   775 
   776 static void
   777 HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
   778 {
   779     SDL_HIDAPI_Device *curr, *last;
   780     for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
   781         if (curr == device) {
   782             if (last) {
   783                 last->next = curr->next;
   784             } else {
   785                 SDL_HIDAPI_devices = curr->next;
   786             }
   787 
   788             HIDAPI_CleanupDeviceDriver(device);
   789 
   790             SDL_DestroyMutex(device->dev_lock);
   791             SDL_free(device->name);
   792             SDL_free(device->path);
   793             SDL_free(device);
   794             return;
   795         }
   796     }
   797 }
   798 
   799 static void
   800 HIDAPI_UpdateDeviceList(void)
   801 {
   802     SDL_HIDAPI_Device *device;
   803     struct hid_device_info *devs, *info;
   804 
   805     SDL_LockJoysticks();
   806 
   807     /* Prepare the existing device list */
   808     device = SDL_HIDAPI_devices;
   809     while (device) {
   810         device->seen = SDL_FALSE;
   811         device = device->next;
   812     }
   813 
   814     /* Enumerate the devices */
   815     if (SDL_HIDAPI_numdrivers > 0) {
   816         devs = hid_enumerate(0, 0);
   817         if (devs) {
   818             for (info = devs; info; info = info->next) {
   819                 device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
   820                 if (device) {
   821                     device->seen = SDL_TRUE;
   822                 } else {
   823                     HIDAPI_AddDevice(info);
   824                 }
   825             }
   826             hid_free_enumeration(devs);
   827         }
   828     }
   829 
   830     /* Remove any devices that weren't seen */
   831     device = SDL_HIDAPI_devices;
   832     while (device) {
   833         SDL_HIDAPI_Device *next = device->next;
   834 
   835         if (!device->seen) {
   836             HIDAPI_DelDevice(device);
   837         }
   838         device = next;
   839     }
   840 
   841     SDL_UnlockJoysticks();
   842 }
   843 
   844 SDL_bool
   845 HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
   846 {
   847     SDL_HIDAPI_Device *device;
   848     SDL_bool supported = SDL_FALSE;
   849     SDL_bool result = SDL_FALSE;
   850 
   851     /* Make sure we're initialized, as this could be called from other drivers during startup */
   852     if (HIDAPI_JoystickInit() < 0) {
   853         return SDL_FALSE;
   854     }
   855 
   856     /* Only update the device list for devices we know might be supported.
   857        If we did this for every device, it would hit the USB driver too hard and potentially 
   858        lock up the system. This won't catch devices that we support but can only detect using 
   859        USB interface details, like Xbox controllers, but hopefully the device list update is
   860        responsive enough to catch those.
   861      */
   862     supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name);
   863 #if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)
   864     if (!supported &&
   865         (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) {
   866         supported = SDL_TRUE;
   867     }
   868 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE */
   869     if (supported) {
   870         if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
   871             HIDAPI_UpdateDeviceList();
   872             SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
   873         }
   874     }
   875 
   876     /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
   877        or a different name than we have it listed here, etc, but if we support the device
   878        and we have something similar in our device list, mark it as present.
   879      */
   880     SDL_LockJoysticks();
   881     device = SDL_HIDAPI_devices;
   882     while (device) {
   883         if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
   884             result = SDL_TRUE;
   885         }
   886         device = device->next;
   887     }
   888     SDL_UnlockJoysticks();
   889 
   890     /* If we're looking for the wireless XBox 360 controller, also look for the dongle */
   891     if (!result && vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x02a1) {
   892         return HIDAPI_IsDevicePresent(USB_VENDOR_MICROSOFT, 0x0719, version, name);
   893     }
   894 
   895 #ifdef DEBUG_HIDAPI
   896     SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x\n", result ? "true" : "false", vendor_id, product_id);
   897 #endif
   898     return result;
   899 }
   900 
   901 static void
   902 HIDAPI_JoystickDetect(void)
   903 {
   904     int i;
   905     if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
   906         HIDAPI_UpdateDiscovery();
   907         if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
   908             /* FIXME: We probably need to schedule an update in a few seconds as well */
   909             HIDAPI_UpdateDeviceList();
   910             SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
   911         }
   912         SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
   913     }
   914     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   915         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   916         if (driver->enabled && driver->PostUpdate) {
   917             driver->PostUpdate();
   918         }
   919     }
   920 }
   921 
   922 void
   923 HIDAPI_UpdateDevices(void)
   924 {
   925     SDL_HIDAPI_Device *device;
   926 
   927     /* Update the devices, which may change connected joysticks and send events */
   928 
   929     /* Prepare the existing device list */
   930     if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
   931         device = SDL_HIDAPI_devices;
   932         while (device) {
   933             if (device->driver) {
   934                 if (SDL_TryLockMutex(device->dev_lock) == 0) {
   935                     device->driver->UpdateDevice(device);
   936                     SDL_UnlockMutex(device->dev_lock);
   937                 }
   938             }
   939             device = device->next;
   940         }
   941         SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
   942     }
   943 }
   944 
   945 static const char *
   946 HIDAPI_JoystickGetDeviceName(int device_index)
   947 {
   948     SDL_HIDAPI_Device *device;
   949     const char *name = NULL;
   950 
   951     device = HIDAPI_GetDeviceByIndex(device_index, NULL);
   952     if (device) {
   953         /* FIXME: The device could be freed after this name is returned... */
   954         name = device->name;
   955     }
   956 
   957     return name;
   958 }
   959 
   960 static int
   961 HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
   962 {
   963     SDL_HIDAPI_Device *device;
   964     SDL_JoystickID instance_id;
   965     int player_index = -1;
   966 
   967     device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
   968     if (device) {
   969         player_index = device->driver->GetDevicePlayerIndex(device, instance_id);
   970     }
   971 
   972     return player_index;
   973 }
   974 
   975 static void
   976 HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
   977 {
   978     SDL_HIDAPI_Device *device;
   979     SDL_JoystickID instance_id;
   980 
   981     device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
   982     if (device) {
   983         device->driver->SetDevicePlayerIndex(device, instance_id, player_index);
   984     }
   985 }
   986 
   987 static SDL_JoystickGUID
   988 HIDAPI_JoystickGetDeviceGUID(int device_index)
   989 {
   990     SDL_HIDAPI_Device *device;
   991     SDL_JoystickGUID guid;
   992 
   993     device = HIDAPI_GetDeviceByIndex(device_index, NULL);
   994     if (device) {
   995         SDL_memcpy(&guid, &device->guid, sizeof(guid));
   996     } else {
   997         SDL_zero(guid);
   998     }
   999 
  1000     return guid;
  1001 }
  1002 
  1003 static SDL_JoystickID
  1004 HIDAPI_JoystickGetDeviceInstanceID(int device_index)
  1005 {
  1006     SDL_JoystickID joystickID = -1;
  1007     HIDAPI_GetDeviceByIndex(device_index, &joystickID);
  1008     return joystickID;
  1009 }
  1010 
  1011 static int
  1012 HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
  1013 {
  1014     SDL_JoystickID joystickID;
  1015     SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
  1016     struct joystick_hwdata *hwdata;
  1017 
  1018     hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
  1019     if (!hwdata) {
  1020         return SDL_OutOfMemory();
  1021     }
  1022     hwdata->device = device;
  1023 
  1024     if (!device->driver->OpenJoystick(device, joystick)) {
  1025         SDL_free(hwdata);
  1026         return -1;
  1027     }
  1028 
  1029     joystick->hwdata = hwdata;
  1030     return 0;
  1031 }
  1032 
  1033 static int
  1034 HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
  1035 {
  1036     int result;
  1037 
  1038     if (joystick->hwdata) {
  1039         SDL_HIDAPI_Device *device = joystick->hwdata->device;
  1040 
  1041         result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble);
  1042     } else {
  1043         SDL_SetError("Rumble failed, device disconnected");
  1044         result = -1;
  1045     }
  1046 
  1047     return result;
  1048 }
  1049 
  1050 static void
  1051 HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
  1052 {
  1053     /* This is handled in SDL_HIDAPI_UpdateDevices() */
  1054 }
  1055 
  1056 static void
  1057 HIDAPI_JoystickClose(SDL_Joystick * joystick)
  1058 {
  1059     if (joystick->hwdata) {
  1060         SDL_HIDAPI_Device *device = joystick->hwdata->device;
  1061 
  1062         /* Wait for pending rumble to complete */
  1063         while (SDL_AtomicGet(&device->rumble_pending) > 0) {
  1064             SDL_Delay(10);
  1065         }
  1066 
  1067         device->driver->CloseJoystick(device, joystick);
  1068 
  1069         SDL_free(joystick->hwdata);
  1070         joystick->hwdata = NULL;
  1071     }
  1072 }
  1073 
  1074 static void
  1075 HIDAPI_JoystickQuit(void)
  1076 {
  1077     int i;
  1078 
  1079     shutting_down = SDL_TRUE;
  1080 
  1081     HIDAPI_ShutdownDiscovery();
  1082 
  1083     while (SDL_HIDAPI_devices) {
  1084         HIDAPI_DelDevice(SDL_HIDAPI_devices);
  1085     }
  1086 
  1087     SDL_HIDAPI_QuitRumble();
  1088 
  1089     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
  1090         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
  1091         SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
  1092     }
  1093     SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
  1094                         SDL_HIDAPIDriverHintChanged, NULL);
  1095 
  1096     hid_exit();
  1097 
  1098     /* Make sure the drivers cleaned up properly */
  1099     SDL_assert(SDL_HIDAPI_numjoysticks == 0);
  1100 
  1101     shutting_down = SDL_FALSE;
  1102     initialized = SDL_FALSE;
  1103 }
  1104 
  1105 SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
  1106 {
  1107     HIDAPI_JoystickInit,
  1108     HIDAPI_JoystickGetCount,
  1109     HIDAPI_JoystickDetect,
  1110     HIDAPI_JoystickGetDeviceName,
  1111     HIDAPI_JoystickGetDevicePlayerIndex,
  1112     HIDAPI_JoystickSetDevicePlayerIndex,
  1113     HIDAPI_JoystickGetDeviceGUID,
  1114     HIDAPI_JoystickGetDeviceInstanceID,
  1115     HIDAPI_JoystickOpen,
  1116     HIDAPI_JoystickRumble,
  1117     HIDAPI_JoystickUpdate,
  1118     HIDAPI_JoystickClose,
  1119     HIDAPI_JoystickQuit,
  1120 };
  1121 
  1122 #endif /* SDL_JOYSTICK_HIDAPI */
  1123 
  1124 /* vi: set ts=4 sw=4 expandtab: */