sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_hidapijoystick.c (41677B)


      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_atomic.h"
     26 #include "SDL_endian.h"
     27 #include "SDL_hints.h"
     28 #include "SDL_thread.h"
     29 #include "SDL_timer.h"
     30 #include "SDL_joystick.h"
     31 #include "../SDL_sysjoystick.h"
     32 #include "SDL_hidapijoystick_c.h"
     33 #include "SDL_hidapi_rumble.h"
     34 #include "../../SDL_hints_c.h"
     35 
     36 #if defined(__WIN32__)
     37 #include "../../core/windows/SDL_windows.h"
     38 #include "../windows/SDL_rawinputjoystick_c.h"
     39 #endif
     40 
     41 #if defined(__MACOSX__)
     42 #include <CoreFoundation/CoreFoundation.h>
     43 #include <mach/mach.h>
     44 #include <IOKit/IOKitLib.h>
     45 #include <IOKit/hid/IOHIDDevice.h>
     46 #include <IOKit/usb/USBSpec.h>
     47 #endif
     48 
     49 #if defined(__LINUX__)
     50 #include "../../core/linux/SDL_udev.h"
     51 #ifdef SDL_USE_LIBUDEV
     52 #include <poll.h>
     53 #endif
     54 #endif
     55 
     56 struct joystick_hwdata
     57 {
     58     SDL_HIDAPI_Device *device;
     59 };
     60 
     61 static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
     62 #ifdef SDL_JOYSTICK_HIDAPI_PS4
     63     &SDL_HIDAPI_DriverPS4,
     64 #endif
     65 #ifdef SDL_JOYSTICK_HIDAPI_PS5
     66     &SDL_HIDAPI_DriverPS5,
     67 #endif
     68 #ifdef SDL_JOYSTICK_HIDAPI_STEAM
     69     &SDL_HIDAPI_DriverSteam,
     70 #endif
     71 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH
     72     &SDL_HIDAPI_DriverSwitch,
     73 #endif
     74 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
     75     &SDL_HIDAPI_DriverXbox360,
     76     &SDL_HIDAPI_DriverXbox360W,
     77 #endif
     78 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
     79     &SDL_HIDAPI_DriverXboxOne,
     80 #endif
     81 #ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
     82     &SDL_HIDAPI_DriverGameCube,
     83 #endif
     84 };
     85 static int SDL_HIDAPI_numdrivers = 0;
     86 static SDL_SpinLock SDL_HIDAPI_spinlock;
     87 static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
     88 static int SDL_HIDAPI_numjoysticks = 0;
     89 static SDL_bool initialized = SDL_FALSE;
     90 static SDL_bool shutting_down = SDL_FALSE;
     91 
     92 #if defined(SDL_USE_LIBUDEV)
     93 static const SDL_UDEV_Symbols * usyms = NULL;
     94 #endif
     95 
     96 static struct
     97 {
     98     SDL_bool m_bHaveDevicesChanged;
     99     SDL_bool m_bCanGetNotifications;
    100     Uint32 m_unLastDetect;
    101 
    102 #if defined(__WIN32__)
    103     SDL_threadID m_nThreadID;
    104     WNDCLASSEXA m_wndClass;
    105     HWND m_hwndMsg;
    106     HDEVNOTIFY m_hNotify;
    107     double m_flLastWin32MessageCheck;
    108 #endif
    109 
    110 #if defined(__MACOSX__)
    111     IONotificationPortRef m_notificationPort;
    112     mach_port_t m_notificationMach;
    113 #endif
    114 
    115 #if defined(SDL_USE_LIBUDEV)
    116     struct udev *m_pUdev;
    117     struct udev_monitor *m_pUdevMonitor;
    118     int m_nUdevFd;
    119 #endif
    120 } SDL_HIDAPI_discovery;
    121 
    122 
    123 #ifdef __WIN32__
    124 struct _DEV_BROADCAST_HDR
    125 {
    126     DWORD       dbch_size;
    127     DWORD       dbch_devicetype;
    128     DWORD       dbch_reserved;
    129 };
    130 
    131 typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
    132 {
    133     DWORD       dbcc_size;
    134     DWORD       dbcc_devicetype;
    135     DWORD       dbcc_reserved;
    136     GUID        dbcc_classguid;
    137     char        dbcc_name[ 1 ];
    138 } DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
    139 
    140 typedef struct  _DEV_BROADCAST_HDR      DEV_BROADCAST_HDR;
    141 #define DBT_DEVICEARRIVAL               0x8000  /* system detected a new device */
    142 #define DBT_DEVICEREMOVECOMPLETE        0x8004  /* device was removed from the system */
    143 #define DBT_DEVTYP_DEVICEINTERFACE      0x00000005  /* device interface class */
    144 #define DBT_DEVNODES_CHANGED            0x0007
    145 #define DBT_CONFIGCHANGED               0x0018
    146 #define DBT_DEVICETYPESPECIFIC          0x8005  /* type specific event */
    147 #define DBT_DEVINSTSTARTED              0x8008  /* device installed and started */
    148 
    149 #include <initguid.h>
    150 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
    151 
    152 static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    153 {
    154     switch (message) {
    155     case WM_DEVICECHANGE:
    156         switch (wParam) {
    157         case DBT_DEVICEARRIVAL:
    158         case DBT_DEVICEREMOVECOMPLETE:
    159             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
    160                 SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
    161             }
    162             break;
    163         }
    164         return TRUE;
    165     }
    166 
    167     return DefWindowProc(hwnd, message, wParam, lParam);
    168 }
    169 #endif /* __WIN32__ */
    170 
    171 
    172 #if defined(__MACOSX__)
    173 static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator)
    174 {
    175     /* Must drain the iterator, or we won't receive new notifications */
    176     io_object_t entry;
    177     while ((entry = IOIteratorNext(portIterator)) != 0) {
    178         IOObjectRelease(entry);
    179         *(SDL_bool*)context = SDL_TRUE;
    180     }
    181 }
    182 #endif /* __MACOSX__ */
    183 
    184 static void
    185 HIDAPI_InitializeDiscovery()
    186 {
    187     SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
    188     SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_FALSE;
    189     SDL_HIDAPI_discovery.m_unLastDetect = 0;
    190 
    191 #if defined(__WIN32__)
    192     SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
    193 
    194     SDL_zero(SDL_HIDAPI_discovery.m_wndClass);
    195     SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
    196     SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
    197     SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc;      /* This function is called by windows */
    198     SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX);
    199 
    200     RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass);
    201     SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
    202 
    203     {
    204         DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
    205 
    206         SDL_zero(devBroadcast);
    207         devBroadcast.dbcc_size = sizeof( devBroadcast );
    208         devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    209         devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
    210 
    211         /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored,
    212          * but that seems to be necessary to get a notice after each individual usb input device actually
    213          * installs, rather than just as the composite device is seen.
    214          */
    215         SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification( SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES );
    216         SDL_HIDAPI_discovery.m_bCanGetNotifications = ( SDL_HIDAPI_discovery.m_hNotify != 0 );
    217     }
    218 #endif /* __WIN32__ */
    219 
    220 #if defined(__MACOSX__)
    221     SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
    222     if (SDL_HIDAPI_discovery.m_notificationPort) {
    223         {
    224             io_iterator_t portIterator = 0;
    225             io_object_t entry;
    226             IOReturn result = IOServiceAddMatchingNotification(
    227                 SDL_HIDAPI_discovery.m_notificationPort,
    228                 kIOFirstMatchNotification,
    229                 IOServiceMatching(kIOHIDDeviceKey),
    230                 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
    231 
    232             if (result == 0) {
    233                 /* Must drain the existing iterator, or we won't receive new notifications */
    234                 while ((entry = IOIteratorNext(portIterator)) != 0) {
    235                     IOObjectRelease(entry);
    236                 }
    237             } else {
    238                 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
    239                 SDL_HIDAPI_discovery.m_notificationPort = nil;
    240             }
    241         }
    242         {
    243             io_iterator_t portIterator = 0;
    244             io_object_t entry;
    245             IOReturn result = IOServiceAddMatchingNotification(
    246                 SDL_HIDAPI_discovery.m_notificationPort,
    247                 kIOTerminatedNotification,
    248                 IOServiceMatching(kIOHIDDeviceKey),
    249                 CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
    250 
    251             if (result == 0) {
    252                 /* Must drain the existing iterator, or we won't receive new notifications */
    253                 while ((entry = IOIteratorNext(portIterator)) != 0) {
    254                     IOObjectRelease(entry);
    255                 }
    256             } else {
    257                 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
    258                 SDL_HIDAPI_discovery.m_notificationPort = nil;
    259             }
    260         }
    261     }
    262 
    263     SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL;
    264     if (SDL_HIDAPI_discovery.m_notificationPort) {
    265         SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort);
    266     }
    267 
    268     SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL);
    269 
    270 #endif // __MACOSX__
    271 
    272 #if defined(SDL_USE_LIBUDEV)
    273     SDL_HIDAPI_discovery.m_pUdev = NULL;
    274     SDL_HIDAPI_discovery.m_pUdevMonitor = NULL;
    275     SDL_HIDAPI_discovery.m_nUdevFd = -1;
    276 
    277     usyms = SDL_UDEV_GetUdevSyms();
    278     if (usyms) {
    279         SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new();
    280     }
    281     if (SDL_HIDAPI_discovery.m_pUdev) {
    282         SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev");
    283         if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
    284             usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor);
    285             SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor);
    286             SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_TRUE;
    287         }
    288     }
    289 
    290 #endif /* SDL_USE_LIBUDEV */
    291 }
    292 
    293 static void
    294 HIDAPI_UpdateDiscovery()
    295 {
    296     if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) {
    297         const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000;  /* Update every 3 seconds */
    298         Uint32 now = SDL_GetTicks();
    299         if (!SDL_HIDAPI_discovery.m_unLastDetect || SDL_TICKS_PASSED(now, SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) {
    300             SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
    301             SDL_HIDAPI_discovery.m_unLastDetect = now;
    302         }
    303         return;
    304     }
    305 
    306 #if defined(__WIN32__)
    307 #if 0 /* just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan. */
    308     /* We'll only get messages on the same thread that created the window */
    309     if (SDL_ThreadID() == SDL_HIDAPI_discovery.m_nThreadID) {
    310         MSG msg;
    311         while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) {
    312             if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) {
    313                 TranslateMessage(&msg);
    314                 DispatchMessage(&msg);
    315             }
    316         }
    317     }
    318 #endif
    319 #endif /* __WIN32__ */
    320 
    321 #if defined(__MACOSX__)
    322     if (SDL_HIDAPI_discovery.m_notificationPort) {
    323         struct { mach_msg_header_t hdr; char payload[ 4096 ]; } msg;
    324         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) {
    325             IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort);
    326         }
    327     }
    328 #endif
    329 
    330 #if defined(SDL_USE_LIBUDEV)
    331     if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) {
    332         /* Drain all notification events.
    333          * We don't expect a lot of device notifications so just
    334          * do a new discovery on any kind or number of notifications.
    335          * This could be made more restrictive if necessary.
    336          */
    337         for (;;) {
    338             struct pollfd PollUdev;
    339             struct udev_device *pUdevDevice;
    340 
    341             PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd;
    342             PollUdev.events = POLLIN;
    343             if (poll(&PollUdev, 1, 0) != 1) {
    344                 break;
    345             }
    346 
    347             SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
    348 
    349             pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor);
    350             if (pUdevDevice) {
    351                 usyms->udev_device_unref(pUdevDevice);
    352             }
    353         }
    354     }
    355 #endif
    356 }
    357 
    358 static void
    359 HIDAPI_ShutdownDiscovery()
    360 {
    361 #if defined(__WIN32__)
    362     if (SDL_HIDAPI_discovery.m_hNotify)
    363         UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify);
    364 
    365     if (SDL_HIDAPI_discovery.m_hwndMsg) {
    366         DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg);
    367     }
    368 
    369     UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance);
    370 #endif
    371 
    372 #if defined(__MACOSX__)
    373     if (SDL_HIDAPI_discovery.m_notificationPort) {
    374         IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
    375     }
    376 #endif
    377 
    378 #if defined(SDL_USE_LIBUDEV)
    379     if (usyms) {
    380         if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
    381             usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor);
    382         }
    383         if (SDL_HIDAPI_discovery.m_pUdev) {
    384             usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev);
    385         }
    386         SDL_UDEV_ReleaseUdevSyms();
    387         usyms = NULL;
    388     }
    389 #endif
    390 }
    391 
    392 void
    393 HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size)
    394 {
    395     int i;
    396     char *buffer;
    397     size_t length = SDL_strlen(prefix) + 11*(USB_PACKET_LENGTH/8) + (5*USB_PACKET_LENGTH*2) + 1 + 1;
    398     int start = 0, amount = size;
    399 
    400     buffer = (char *)SDL_malloc(length);
    401     SDL_snprintf(buffer, length, prefix, size);
    402     for (i = start; i < start+amount; ++i) {
    403         if ((i % 8) == 0) {
    404             SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), "\n%.2d:      ", i);
    405         }
    406         SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), " 0x%.2x", data[i]);
    407     }
    408     SDL_strlcat(buffer, "\n", length);
    409     SDL_Log("%s", buffer);
    410     SDL_free(buffer);
    411 }
    412 
    413 static void HIDAPI_JoystickDetect(void);
    414 static void HIDAPI_JoystickClose(SDL_Joystick * joystick);
    415 
    416 static SDL_bool
    417 HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
    418 {
    419     int i;
    420     SDL_GameControllerType type = SDL_GetJoystickGameControllerType(name, vendor_id, product_id, -1, 0, 0, 0);
    421 
    422     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
    423         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
    424         if (driver->enabled && driver->IsSupportedDevice(name, type, vendor_id, product_id, version, -1, 0, 0, 0)) {
    425             return SDL_TRUE;
    426         }
    427     }
    428     return SDL_FALSE;
    429 }
    430 
    431 static SDL_HIDAPI_DeviceDriver *
    432 HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
    433 {
    434     const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;
    435     const Uint16 USAGE_JOYSTICK = 0x0004;
    436     const Uint16 USAGE_GAMEPAD = 0x0005;
    437     const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
    438     int i;
    439     SDL_GameControllerType type;
    440 
    441     if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
    442         return NULL;
    443     }
    444 
    445     if (device->vendor_id != USB_VENDOR_VALVE) {
    446         if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
    447             return NULL;
    448         }
    449         if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
    450             return NULL;
    451         }
    452     }
    453 
    454     type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
    455     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
    456         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
    457         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)) {
    458             return driver;
    459         }
    460     }
    461     return NULL;
    462 }
    463 
    464 static SDL_HIDAPI_Device *
    465 HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID)
    466 {
    467     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
    468     while (device) {
    469         if (device->driver) {
    470             if (device_index < device->num_joysticks) {
    471                 if (pJoystickID) {
    472                     *pJoystickID = device->joysticks[device_index];
    473                 }
    474                 return device;
    475             }
    476             device_index -= device->num_joysticks;
    477         }
    478         device = device->next;
    479     }
    480     return NULL;
    481 }
    482 
    483 static SDL_HIDAPI_Device *
    484 HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
    485 {
    486     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
    487     while (device) {
    488         if (device->vendor_id == vendor_id && device->product_id == product_id &&
    489             SDL_strcmp(device->path, path) == 0) {
    490             break;
    491         }
    492         device = device->next;
    493     }
    494     return device;
    495 }
    496 
    497 static void
    498 HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device)
    499 {
    500     if (device->driver) {
    501         /* Already setup */
    502         return;
    503     }
    504 
    505     device->driver = HIDAPI_GetDeviceDriver(device);
    506     if (device->driver) {
    507         const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
    508         if (name) {
    509             SDL_free(device->name);
    510             device->name = SDL_strdup(name);
    511         }
    512     }
    513 
    514     /* Initialize the device, which may cause a connected event */
    515     if (device->driver && !device->driver->InitDevice(device)) {
    516         device->driver = NULL;
    517     }
    518 }
    519 
    520 static void
    521 HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
    522 {
    523     if (!device->driver) {
    524         /* Already cleaned up */
    525         return;
    526     }
    527 
    528     /* Disconnect any joysticks */
    529     while (device->num_joysticks) {
    530         HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
    531     }
    532 
    533     device->driver->FreeDevice(device);
    534     device->driver = NULL;
    535 }
    536 
    537 static void SDLCALL
    538 SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
    539 {
    540     int i;
    541     SDL_HIDAPI_Device *device;
    542     SDL_bool enabled = SDL_GetStringBoolean(hint, SDL_TRUE);
    543 
    544     if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
    545         for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
    546             SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
    547             driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
    548         }
    549     } else {
    550         for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
    551             SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
    552             if (SDL_strcmp(name, driver->hint) == 0) {
    553                 driver->enabled = enabled;
    554             }
    555         }
    556     }
    557 
    558     SDL_HIDAPI_numdrivers = 0;
    559     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
    560         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
    561         if (driver->enabled) {
    562             ++SDL_HIDAPI_numdrivers;
    563         }
    564     }
    565 
    566     /* Update device list if driver availability changes */
    567     SDL_LockJoysticks();
    568 
    569     for (device = SDL_HIDAPI_devices; device; device = device->next) {
    570         if (device->driver && !device->driver->enabled) {
    571             HIDAPI_CleanupDeviceDriver(device);
    572         }
    573         HIDAPI_SetupDeviceDriver(device);
    574     }
    575 
    576     SDL_UnlockJoysticks();
    577 }
    578 
    579 static int
    580 HIDAPI_JoystickInit(void)
    581 {
    582     int i;
    583 
    584     if (initialized) {
    585         return 0;
    586     }
    587 
    588 #if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__)
    589     /* The hidapi framwork is weak-linked on Apple platforms */
    590     int HID_API_EXPORT HID_API_CALL hid_init(void) __attribute__((weak_import));
    591 
    592     if (hid_init == NULL) {
    593         SDL_SetError("Couldn't initialize hidapi, framework not available");
    594         return -1;
    595     }
    596 #endif /* __MACOSX__ || __IPHONEOS__ || __TVOS__ */
    597 
    598     if (hid_init() < 0) {
    599         SDL_SetError("Couldn't initialize hidapi");
    600         return -1;
    601     }
    602 
    603     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
    604         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
    605         SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
    606     }
    607     SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
    608                         SDL_HIDAPIDriverHintChanged, NULL);
    609     HIDAPI_InitializeDiscovery();
    610     HIDAPI_JoystickDetect();
    611     HIDAPI_UpdateDevices();
    612 
    613     initialized = SDL_TRUE;
    614 
    615     return 0;
    616 }
    617 
    618 SDL_bool
    619 HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
    620 {
    621     SDL_JoystickID joystickID;
    622     SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks));
    623     if (!joysticks) {
    624         return SDL_FALSE;
    625     }
    626 
    627     joystickID = SDL_GetNextJoystickInstanceID();
    628     device->joysticks = joysticks;
    629     device->joysticks[device->num_joysticks++] = joystickID;
    630     ++SDL_HIDAPI_numjoysticks;
    631 
    632     SDL_PrivateJoystickAdded(joystickID);
    633 
    634     if (pJoystickID) {
    635         *pJoystickID = joystickID;
    636     }
    637     return SDL_TRUE;
    638 }
    639 
    640 void
    641 HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
    642 {
    643     int i, size;
    644 
    645     for (i = 0; i < device->num_joysticks; ++i) {
    646         if (device->joysticks[i] == joystickID) {
    647             SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joystickID);
    648             if (joystick) {
    649                 HIDAPI_JoystickClose(joystick);
    650             }
    651 
    652             size = (device->num_joysticks - i - 1) * sizeof(SDL_JoystickID);
    653             SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], size);
    654             --device->num_joysticks;
    655 
    656             --SDL_HIDAPI_numjoysticks;
    657             if (device->num_joysticks == 0) {
    658                 SDL_free(device->joysticks);
    659                 device->joysticks = NULL;
    660             }
    661 
    662             if (!shutting_down) {
    663                 SDL_PrivateJoystickRemoved(joystickID);
    664             }
    665             return;
    666         }
    667     }
    668 }
    669 
    670 static int
    671 HIDAPI_JoystickGetCount(void)
    672 {
    673     return SDL_HIDAPI_numjoysticks;
    674 }
    675 
    676 static char *
    677 HIDAPI_ConvertString(const wchar_t *wide_string)
    678 {
    679     char *string = NULL;
    680 
    681     if (wide_string) {
    682         string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
    683         if (!string) {
    684             if (sizeof(wchar_t) == sizeof(Uint16)) {
    685                 string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
    686             } else if (sizeof(wchar_t) == sizeof(Uint32)) {
    687                 string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
    688             }
    689         }
    690     }
    691     return string;
    692 }
    693 
    694 static void
    695 HIDAPI_AddDevice(struct hid_device_info *info)
    696 {
    697     SDL_HIDAPI_Device *device;
    698     SDL_HIDAPI_Device *curr, *last = NULL;
    699 
    700     for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
    701         continue;
    702     }
    703 
    704     device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
    705     if (!device) {
    706         return;
    707     }
    708     device->path = SDL_strdup(info->path);
    709     if (!device->path) {
    710         SDL_free(device);
    711         return;
    712     }
    713     device->seen = SDL_TRUE;
    714     device->vendor_id = info->vendor_id;
    715     device->product_id = info->product_id;
    716     device->version = info->release_number;
    717     device->interface_number = info->interface_number;
    718     device->interface_class = info->interface_class;
    719     device->interface_subclass = info->interface_subclass;
    720     device->interface_protocol = info->interface_protocol;
    721     device->usage_page = info->usage_page;
    722     device->usage = info->usage;
    723     {
    724         /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
    725         const Uint16 vendor = device->vendor_id;
    726         const Uint16 product = device->product_id;
    727         const Uint16 version = device->version;
    728         Uint16 *guid16 = (Uint16 *)device->guid.data;
    729 
    730         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
    731         *guid16++ = 0;
    732         *guid16++ = SDL_SwapLE16(vendor);
    733         *guid16++ = 0;
    734         *guid16++ = SDL_SwapLE16(product);
    735         *guid16++ = 0;
    736         *guid16++ = SDL_SwapLE16(version);
    737         *guid16++ = 0;
    738 
    739         /* Note that this is a HIDAPI device for special handling elsewhere */
    740         device->guid.data[14] = 'h';
    741         device->guid.data[15] = 0;
    742     }
    743     device->dev_lock = SDL_CreateMutex();
    744 
    745     /* Need the device name before getting the driver to know whether to ignore this device */
    746     {
    747         char *manufacturer_string = HIDAPI_ConvertString(info->manufacturer_string);
    748         char *product_string = HIDAPI_ConvertString(info->product_string);
    749         char *serial_number = HIDAPI_ConvertString(info->serial_number);
    750 
    751         device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string);
    752         if (SDL_strncmp(device->name, "0x", 2) == 0) {
    753             /* Couldn't find a controller name, try to give it one based on device type */
    754             const char *name = NULL;
    755 
    756             switch (SDL_GetJoystickGameControllerType(NULL, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
    757             case SDL_CONTROLLER_TYPE_XBOX360:
    758                 name = "Xbox 360 Controller";
    759                 break;
    760             case SDL_CONTROLLER_TYPE_XBOXONE:
    761                 name = "Xbox One Controller";
    762                 break;
    763             case SDL_CONTROLLER_TYPE_PS3:
    764                 name = "PS3 Controller";
    765                 break;
    766             case SDL_CONTROLLER_TYPE_PS4:
    767                 name = "PS4 Controller";
    768                 break;
    769             case SDL_CONTROLLER_TYPE_PS5:
    770                 name = "PS5 Controller";
    771                 break;
    772             case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
    773                 name = "Nintendo Switch Pro Controller";
    774                 break;
    775             default:
    776                 break;
    777             }
    778 
    779             if (name) {
    780                 SDL_free(device->name);
    781                 device->name = SDL_strdup(name);
    782             }
    783         }
    784 
    785         if (manufacturer_string) {
    786             SDL_free(manufacturer_string);
    787         }
    788         if (product_string) {
    789             SDL_free(product_string);
    790         }
    791 
    792         if (serial_number && *serial_number) {
    793             device->serial = serial_number;
    794         } else {
    795             SDL_free(serial_number);
    796         }
    797 
    798         if (!device->name) {
    799             SDL_free(device->serial);
    800             SDL_free(device->path);
    801             SDL_free(device);
    802             return;
    803         }
    804     }
    805 
    806     /* Add it to the list */
    807     if (last) {
    808         last->next = device;
    809     } else {
    810         SDL_HIDAPI_devices = device;
    811     }
    812 
    813     HIDAPI_SetupDeviceDriver(device);
    814 
    815 #ifdef DEBUG_HIDAPI
    816     SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, serial %s, 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->serial ? device->serial : "NONE", 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");
    817 #endif
    818 }
    819 
    820 
    821 static void
    822 HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
    823 {
    824     SDL_HIDAPI_Device *curr, *last;
    825 
    826 #ifdef DEBUG_HIDAPI
    827     SDL_Log("Removing HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, serial %s, 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->serial ? device->serial : "NONE", 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");
    828 #endif
    829 
    830     for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
    831         if (curr == device) {
    832             if (last) {
    833                 last->next = curr->next;
    834             } else {
    835                 SDL_HIDAPI_devices = curr->next;
    836             }
    837 
    838             HIDAPI_CleanupDeviceDriver(device);
    839 
    840             /* Make sure the rumble thread is done with this device */
    841             while (SDL_AtomicGet(&device->rumble_pending) > 0) {
    842                 SDL_Delay(10);
    843             }
    844 
    845             SDL_DestroyMutex(device->dev_lock);
    846             SDL_free(device->serial);
    847             SDL_free(device->name);
    848             SDL_free(device->path);
    849             SDL_free(device);
    850             return;
    851         }
    852     }
    853 }
    854 
    855 static void
    856 HIDAPI_UpdateDeviceList(void)
    857 {
    858     SDL_HIDAPI_Device *device;
    859     struct hid_device_info *devs, *info;
    860 
    861     SDL_LockJoysticks();
    862 
    863     /* Prepare the existing device list */
    864     device = SDL_HIDAPI_devices;
    865     while (device) {
    866         device->seen = SDL_FALSE;
    867         device = device->next;
    868     }
    869 
    870     /* Enumerate the devices */
    871     if (SDL_HIDAPI_numdrivers > 0) {
    872         devs = hid_enumerate(0, 0);
    873         if (devs) {
    874             for (info = devs; info; info = info->next) {
    875                 device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
    876                 if (device) {
    877                     device->seen = SDL_TRUE;
    878                 } else {
    879                     HIDAPI_AddDevice(info);
    880                 }
    881             }
    882             hid_free_enumeration(devs);
    883         }
    884     }
    885 
    886     /* Remove any devices that weren't seen */
    887     device = SDL_HIDAPI_devices;
    888     while (device) {
    889         SDL_HIDAPI_Device *next = device->next;
    890 
    891         if (!device->seen) {
    892             HIDAPI_DelDevice(device);
    893         }
    894         device = next;
    895     }
    896 
    897     SDL_UnlockJoysticks();
    898 }
    899 
    900 static SDL_bool
    901 HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Device *device)
    902 {
    903     if (vendor_id == device->vendor_id && product_id == device->product_id) {
    904         return SDL_TRUE;
    905     }
    906 
    907     if (vendor_id == USB_VENDOR_MICROSOFT) {
    908         /* If we're looking for the wireless XBox 360 controller, also look for the dongle */
    909         if (product_id == 0x02a1 && device->product_id == 0x0719) {
    910             return SDL_TRUE;
    911         }
    912 
    913         /* If we're looking for the raw input Xbox One controller, match it against any other Xbox One controller */
    914         if (product_id == USB_PRODUCT_XBOX_ONE_RAW_INPUT_CONTROLLER &&
    915             SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol) == SDL_CONTROLLER_TYPE_XBOXONE) {
    916             return SDL_TRUE;
    917         }
    918 
    919         /* If we're looking for an XInput controller, match it against any other Xbox controller */
    920         if (product_id == USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER) {
    921             SDL_GameControllerType type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
    922             if (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) {
    923                 return SDL_TRUE;
    924             }
    925         }
    926     }
    927     return SDL_FALSE;
    928 }
    929 
    930 SDL_bool
    931 HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
    932 {
    933     SDL_HIDAPI_Device *device;
    934     SDL_bool supported = SDL_FALSE;
    935     SDL_bool result = SDL_FALSE;
    936 
    937     /* Make sure we're initialized, as this could be called from other drivers during startup */
    938     if (HIDAPI_JoystickInit() < 0) {
    939         return SDL_FALSE;
    940     }
    941 
    942     /* Only update the device list for devices we know might be supported.
    943        If we did this for every device, it would hit the USB driver too hard and potentially 
    944        lock up the system. This won't catch devices that we support but can only detect using 
    945        USB interface details, like Xbox controllers, but hopefully the device list update is
    946        responsive enough to catch those.
    947      */
    948     supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name);
    949 #if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)
    950     if (!supported &&
    951         (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) {
    952         supported = SDL_TRUE;
    953     }
    954 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE */
    955     if (supported) {
    956         if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
    957             HIDAPI_UpdateDeviceList();
    958             SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
    959         }
    960     }
    961 
    962     /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
    963        or a different name than we have it listed here, etc, but if we support the device
    964        and we have something similar in our device list, mark it as present.
    965      */
    966     SDL_LockJoysticks();
    967     device = SDL_HIDAPI_devices;
    968     while (device) {
    969         if (device->driver &&
    970             HIDAPI_IsEquivalentToDevice(vendor_id, product_id, device)) {
    971             result = SDL_TRUE;
    972         }
    973         device = device->next;
    974     }
    975     SDL_UnlockJoysticks();
    976 
    977 #ifdef DEBUG_HIDAPI
    978     SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x\n", result ? "true" : "false", vendor_id, product_id);
    979 #endif
    980     return result;
    981 }
    982 
    983 static void
    984 HIDAPI_JoystickDetect(void)
    985 {
    986     if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
    987         HIDAPI_UpdateDiscovery();
    988         if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
    989             /* FIXME: We probably need to schedule an update in a few seconds as well */
    990             HIDAPI_UpdateDeviceList();
    991             SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
    992         }
    993         SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
    994     }
    995 }
    996 
    997 void
    998 HIDAPI_UpdateDevices(void)
    999 {
   1000     SDL_HIDAPI_Device *device;
   1001 
   1002     /* Update the devices, which may change connected joysticks and send events */
   1003 
   1004     /* Prepare the existing device list */
   1005     if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
   1006         device = SDL_HIDAPI_devices;
   1007         while (device) {
   1008             if (device->driver) {
   1009                 if (SDL_TryLockMutex(device->dev_lock) == 0) {
   1010                     device->updating = SDL_TRUE;
   1011                     device->driver->UpdateDevice(device);
   1012                     device->updating = SDL_FALSE;
   1013                     SDL_UnlockMutex(device->dev_lock);
   1014                 }
   1015             }
   1016             device = device->next;
   1017         }
   1018         SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
   1019     }
   1020 }
   1021 
   1022 static const char *
   1023 HIDAPI_JoystickGetDeviceName(int device_index)
   1024 {
   1025     SDL_HIDAPI_Device *device;
   1026     const char *name = NULL;
   1027 
   1028     device = HIDAPI_GetDeviceByIndex(device_index, NULL);
   1029     if (device) {
   1030         /* FIXME: The device could be freed after this name is returned... */
   1031         name = device->name;
   1032     }
   1033 
   1034     return name;
   1035 }
   1036 
   1037 static int
   1038 HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
   1039 {
   1040     SDL_HIDAPI_Device *device;
   1041     SDL_JoystickID instance_id;
   1042     int player_index = -1;
   1043 
   1044     device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
   1045     if (device) {
   1046         player_index = device->driver->GetDevicePlayerIndex(device, instance_id);
   1047     }
   1048 
   1049     return player_index;
   1050 }
   1051 
   1052 static void
   1053 HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
   1054 {
   1055     SDL_HIDAPI_Device *device;
   1056     SDL_JoystickID instance_id;
   1057 
   1058     device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
   1059     if (device) {
   1060         device->driver->SetDevicePlayerIndex(device, instance_id, player_index);
   1061     }
   1062 }
   1063 
   1064 static SDL_JoystickGUID
   1065 HIDAPI_JoystickGetDeviceGUID(int device_index)
   1066 {
   1067     SDL_HIDAPI_Device *device;
   1068     SDL_JoystickGUID guid;
   1069 
   1070     device = HIDAPI_GetDeviceByIndex(device_index, NULL);
   1071     if (device) {
   1072         SDL_memcpy(&guid, &device->guid, sizeof(guid));
   1073     } else {
   1074         SDL_zero(guid);
   1075     }
   1076 
   1077     return guid;
   1078 }
   1079 
   1080 static SDL_JoystickID
   1081 HIDAPI_JoystickGetDeviceInstanceID(int device_index)
   1082 {
   1083     SDL_JoystickID joystickID = -1;
   1084     HIDAPI_GetDeviceByIndex(device_index, &joystickID);
   1085     return joystickID;
   1086 }
   1087 
   1088 static int
   1089 HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
   1090 {
   1091     SDL_JoystickID joystickID;
   1092     SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
   1093     struct joystick_hwdata *hwdata;
   1094 
   1095     hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
   1096     if (!hwdata) {
   1097         return SDL_OutOfMemory();
   1098     }
   1099     hwdata->device = device;
   1100 
   1101     if (!device->driver->OpenJoystick(device, joystick)) {
   1102         SDL_free(hwdata);
   1103         return -1;
   1104     }
   1105 
   1106     if (!joystick->serial && device->serial) {
   1107         joystick->serial = SDL_strdup(device->serial);
   1108     }
   1109 
   1110     joystick->hwdata = hwdata;
   1111     return 0;
   1112 }
   1113 
   1114 static int
   1115 HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
   1116 {
   1117     int result;
   1118 
   1119     if (joystick->hwdata) {
   1120         SDL_HIDAPI_Device *device = joystick->hwdata->device;
   1121 
   1122         result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble);
   1123     } else {
   1124         SDL_SetError("Rumble failed, device disconnected");
   1125         result = -1;
   1126     }
   1127 
   1128     return result;
   1129 }
   1130 
   1131 static int
   1132 HIDAPI_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
   1133 {
   1134     int result;
   1135 
   1136     if (joystick->hwdata) {
   1137         SDL_HIDAPI_Device *device = joystick->hwdata->device;
   1138 
   1139         result = device->driver->RumbleJoystickTriggers(device, joystick, left_rumble, right_rumble);
   1140     } else {
   1141         SDL_SetError("Rumble failed, device disconnected");
   1142         result = -1;
   1143     }
   1144 
   1145     return result;
   1146 }
   1147 
   1148 static SDL_bool
   1149 HIDAPI_JoystickHasLED(SDL_Joystick * joystick)
   1150 {
   1151     SDL_bool result = SDL_FALSE;
   1152 
   1153     if (joystick->hwdata) {
   1154         SDL_HIDAPI_Device *device = joystick->hwdata->device;
   1155 
   1156         result = device->driver->HasJoystickLED(device, joystick);
   1157     }
   1158 
   1159     return result;
   1160 }
   1161 
   1162 static int
   1163 HIDAPI_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue)
   1164 {
   1165     int result;
   1166 
   1167     if (joystick->hwdata) {
   1168         SDL_HIDAPI_Device *device = joystick->hwdata->device;
   1169 
   1170         result = device->driver->SetJoystickLED(device, joystick, red, green, blue);
   1171     } else {
   1172         SDL_SetError("SetLED failed, device disconnected");
   1173         result = -1;
   1174     }
   1175 
   1176     return result;
   1177 }
   1178 
   1179 static int
   1180 HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick * joystick, SDL_bool enabled)
   1181 {
   1182     int result;
   1183 
   1184     if (joystick->hwdata) {
   1185         SDL_HIDAPI_Device *device = joystick->hwdata->device;
   1186 
   1187         result = device->driver->SetJoystickSensorsEnabled(device, joystick, enabled);
   1188     } else {
   1189         SDL_SetError("SetSensorsEnabled failed, device disconnected");
   1190         result = -1;
   1191     }
   1192 
   1193     return result;
   1194 }
   1195 
   1196 static void
   1197 HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
   1198 {
   1199     /* This is handled in SDL_HIDAPI_UpdateDevices() */
   1200 }
   1201 
   1202 static void
   1203 HIDAPI_JoystickClose(SDL_Joystick * joystick)
   1204 {
   1205     if (joystick->hwdata) {
   1206         SDL_HIDAPI_Device *device = joystick->hwdata->device;
   1207         int i;
   1208 
   1209         /* Wait up to 30 ms for pending rumble to complete */
   1210         if (device->updating) {
   1211             /* Unlock the device so rumble can complete */
   1212             SDL_UnlockMutex(device->dev_lock);
   1213         }
   1214         for (i = 0; i < 3; ++i) {
   1215             if (SDL_AtomicGet(&device->rumble_pending) > 0) {
   1216                 SDL_Delay(10);
   1217             }
   1218         }
   1219         if (device->updating) {
   1220             /* Relock the device */
   1221             SDL_LockMutex(device->dev_lock);
   1222         }
   1223 
   1224         device->driver->CloseJoystick(device, joystick);
   1225 
   1226         SDL_free(joystick->hwdata);
   1227         joystick->hwdata = NULL;
   1228     }
   1229 }
   1230 
   1231 static void
   1232 HIDAPI_JoystickQuit(void)
   1233 {
   1234     int i;
   1235 
   1236     shutting_down = SDL_TRUE;
   1237 
   1238     HIDAPI_ShutdownDiscovery();
   1239 
   1240     SDL_HIDAPI_QuitRumble();
   1241 
   1242     while (SDL_HIDAPI_devices) {
   1243         HIDAPI_DelDevice(SDL_HIDAPI_devices);
   1244     }
   1245 
   1246     /* Make sure the drivers cleaned up properly */
   1247     SDL_assert(SDL_HIDAPI_numjoysticks == 0);
   1248 
   1249     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   1250         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   1251         SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
   1252     }
   1253     SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
   1254                         SDL_HIDAPIDriverHintChanged, NULL);
   1255 
   1256     hid_exit();
   1257 
   1258     shutting_down = SDL_FALSE;
   1259     initialized = SDL_FALSE;
   1260 }
   1261 
   1262 static SDL_bool
   1263 HIDAPI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
   1264 {
   1265     return SDL_FALSE;
   1266 }
   1267 
   1268 SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
   1269 {
   1270     HIDAPI_JoystickInit,
   1271     HIDAPI_JoystickGetCount,
   1272     HIDAPI_JoystickDetect,
   1273     HIDAPI_JoystickGetDeviceName,
   1274     HIDAPI_JoystickGetDevicePlayerIndex,
   1275     HIDAPI_JoystickSetDevicePlayerIndex,
   1276     HIDAPI_JoystickGetDeviceGUID,
   1277     HIDAPI_JoystickGetDeviceInstanceID,
   1278     HIDAPI_JoystickOpen,
   1279     HIDAPI_JoystickRumble,
   1280     HIDAPI_JoystickRumbleTriggers,
   1281     HIDAPI_JoystickHasLED,
   1282     HIDAPI_JoystickSetLED,
   1283     HIDAPI_JoystickSetSensorsEnabled,
   1284     HIDAPI_JoystickUpdate,
   1285     HIDAPI_JoystickClose,
   1286     HIDAPI_JoystickQuit,
   1287     HIDAPI_JoystickGetGamepadMapping
   1288 };
   1289 
   1290 #endif /* SDL_JOYSTICK_HIDAPI */
   1291 
   1292 /* vi: set ts=4 sw=4 expandtab: */