From 03f8528315fa46c95991a34f3325d7b33ae5538c Mon Sep 17 00:00:00 2001
From: Edward Rudd <urkle@outoforder.cc>
Date: Sat, 02 May 2020 21:48:36 +0000
Subject: [PATCH] Update source to SDL2 2.0.12

---
 source/src/joystick/hidapi/SDL_hidapijoystick.c |  719 +++++++++++++++++++++++++++++++----------------------------
 1 files changed, 380 insertions(+), 339 deletions(-)

diff --git a/source/src/joystick/hidapi/SDL_hidapijoystick.c b/source/src/joystick/hidapi/SDL_hidapijoystick.c
index 064cb82..579d096 100644
--- a/source/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/source/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -22,15 +22,18 @@
 
 #ifdef SDL_JOYSTICK_HIDAPI
 
+#include "SDL_assert.h"
+#include "SDL_atomic.h"
 #include "SDL_endian.h"
 #include "SDL_hints.h"
 #include "SDL_log.h"
-#include "SDL_mutex.h"
 #include "SDL_thread.h"
 #include "SDL_timer.h"
 #include "SDL_joystick.h"
 #include "../SDL_sysjoystick.h"
 #include "SDL_hidapijoystick_c.h"
+#include "SDL_hidapi_rumble.h"
+#include "../../SDL_hints_c.h"
 
 #if defined(__WIN32__)
 #include "../../core/windows/SDL_windows.h"
@@ -40,6 +43,7 @@
 #include <CoreFoundation/CoreFoundation.h>
 #include <mach/mach.h>
 #include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDDevice.h>
 #include <IOKit/usb/USBSpec.h>
 #endif
 
@@ -52,32 +56,8 @@
 
 struct joystick_hwdata
 {
-    SDL_HIDAPI_DeviceDriver *driver;
-    void *context;
-
-    SDL_mutex *mutex;
-    hid_device *dev;
+    SDL_HIDAPI_Device *device;
 };
-
-typedef struct _SDL_HIDAPI_Device
-{
-    SDL_JoystickID instance_id;
-    char *name;
-    char *path;
-    Uint16 vendor_id;
-    Uint16 product_id;
-    Uint16 version;
-    SDL_JoystickGUID guid;
-    int interface_number;   /* Available on Windows and Linux */
-    Uint16 usage_page;      /* Available on Windows and Mac OS X */
-    Uint16 usage;           /* Available on Windows and Mac OS X */
-    SDL_HIDAPI_DeviceDriver *driver;
-
-    /* Used during scanning for device changes */
-    SDL_bool seen;
-
-    struct _SDL_HIDAPI_Device *next;
-} SDL_HIDAPI_Device;
 
 static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
 #ifdef SDL_JOYSTICK_HIDAPI_PS4
@@ -91,13 +71,21 @@
 #endif
 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
     &SDL_HIDAPI_DriverXbox360,
+    &SDL_HIDAPI_DriverXbox360W,
 #endif
 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
     &SDL_HIDAPI_DriverXboxOne,
 #endif
+#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
+    &SDL_HIDAPI_DriverGameCube,
+#endif
 };
+static int SDL_HIDAPI_numdrivers = 0;
+static SDL_SpinLock SDL_HIDAPI_spinlock;
 static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
 static int SDL_HIDAPI_numjoysticks = 0;
+static SDL_bool initialized = SDL_FALSE;
+static SDL_bool shutting_down = SDL_FALSE;
 
 #if defined(SDL_USE_LIBUDEV)
 static const SDL_UDEV_Symbols * usyms = NULL;
@@ -231,12 +219,15 @@
     SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
     if (SDL_HIDAPI_discovery.m_notificationPort) {
         {
-            CFMutableDictionaryRef matchingDict = IOServiceMatching("IOUSBDevice");
-
-            /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
             io_iterator_t portIterator = 0;
             io_object_t entry;
-            if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
+            IOReturn result = IOServiceAddMatchingNotification(
+                SDL_HIDAPI_discovery.m_notificationPort,
+                kIOFirstMatchNotification,
+                IOServiceMatching(kIOHIDDeviceKey),
+                CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
+
+            if (result == 0) {
                 /* Must drain the existing iterator, or we won't receive new notifications */
                 while ((entry = IOIteratorNext(portIterator)) != 0) {
                     IOObjectRelease(entry);
@@ -247,12 +238,15 @@
             }
         }
         {
-            CFMutableDictionaryRef matchingDict = IOServiceMatching("IOBluetoothDevice");
-
-            /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
             io_iterator_t portIterator = 0;
             io_object_t entry;
-            if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
+            IOReturn result = IOServiceAddMatchingNotification(
+                SDL_HIDAPI_discovery.m_notificationPort,
+                kIOTerminatedNotification,
+                IOServiceMatching(kIOHIDDeviceKey),
+                CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
+
+            if (result == 0) {
                 /* Must drain the existing iterator, or we won't receive new notifications */
                 while ((entry = IOIteratorNext(portIterator)) != 0) {
                     IOObjectRelease(entry);
@@ -393,182 +387,18 @@
 #endif
 }
 
-
-const char *
-HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id)
-{
-    static struct
-    {
-        Uint32 vidpid;
-        const char *name;
-    } names[] = {
-        { MAKE_VIDPID(0x0079, 0x18d4), "GPD Win 2 X-Box Controller" },
-        { MAKE_VIDPID(0x044f, 0xb326), "Thrustmaster Gamepad GP XID" },
-        { MAKE_VIDPID(0x045e, 0x028e), "Microsoft X-Box 360 pad" },
-        { MAKE_VIDPID(0x045e, 0x028f), "Microsoft X-Box 360 pad v2" },
-        { MAKE_VIDPID(0x045e, 0x0291), "Xbox 360 Wireless Receiver (XBOX)" },
-        { MAKE_VIDPID(0x045e, 0x02d1), "Microsoft X-Box One pad" },
-        { MAKE_VIDPID(0x045e, 0x02dd), "Microsoft X-Box One pad (Firmware 2015)" },
-        { MAKE_VIDPID(0x045e, 0x02e3), "Microsoft X-Box One Elite pad" },
-        { MAKE_VIDPID(0x045e, 0x02ea), "Microsoft X-Box One S pad" },
-        { MAKE_VIDPID(0x045e, 0x02ff), "Microsoft X-Box One pad" },
-        { MAKE_VIDPID(0x045e, 0x0719), "Xbox 360 Wireless Receiver" },
-        { MAKE_VIDPID(0x046d, 0xc21d), "Logitech Gamepad F310" },
-        { MAKE_VIDPID(0x046d, 0xc21e), "Logitech Gamepad F510" },
-        { MAKE_VIDPID(0x046d, 0xc21f), "Logitech Gamepad F710" },
-        { MAKE_VIDPID(0x046d, 0xc242), "Logitech Chillstream Controller" },
-        { MAKE_VIDPID(0x046d, 0xcaa3), "Logitech DriveFx Racing Wheel" },
-        { MAKE_VIDPID(0x056e, 0x2004), "Elecom JC-U3613M" },
-        { MAKE_VIDPID(0x06a3, 0xf51a), "Saitek P3600" },
-        { MAKE_VIDPID(0x0738, 0x4716), "Mad Catz Wired Xbox 360 Controller" },
-        { MAKE_VIDPID(0x0738, 0x4718), "Mad Catz Street Fighter IV FightStick SE" },
-        { MAKE_VIDPID(0x0738, 0x4726), "Mad Catz Xbox 360 Controller" },
-        { MAKE_VIDPID(0x0738, 0x4728), "Mad Catz Street Fighter IV FightPad" },
-        { MAKE_VIDPID(0x0738, 0x4736), "Mad Catz MicroCon Gamepad" },
-        { MAKE_VIDPID(0x0738, 0x4738), "Mad Catz Wired Xbox 360 Controller (SFIV)" },
-        { MAKE_VIDPID(0x0738, 0x4740), "Mad Catz Beat Pad" },
-        { MAKE_VIDPID(0x0738, 0x4758), "Mad Catz Arcade Game Stick" },
-        { MAKE_VIDPID(0x0738, 0x4a01), "Mad Catz FightStick TE 2" },
-        { MAKE_VIDPID(0x0738, 0x9871), "Mad Catz Portable Drum" },
-        { MAKE_VIDPID(0x0738, 0xb726), "Mad Catz Xbox controller - MW2" },
-        { MAKE_VIDPID(0x0738, 0xb738), "Mad Catz MVC2TE Stick 2" },
-        { MAKE_VIDPID(0x0738, 0xbeef), "Mad Catz JOYTECH NEO SE Advanced GamePad" },
-        { MAKE_VIDPID(0x0738, 0xcb02), "Saitek Cyborg Rumble Pad - PC/Xbox 360" },
-        { MAKE_VIDPID(0x0738, 0xcb03), "Saitek P3200 Rumble Pad - PC/Xbox 360" },
-        { MAKE_VIDPID(0x0738, 0xcb29), "Saitek Aviator Stick AV8R02" },
-        { MAKE_VIDPID(0x0738, 0xf738), "Super SFIV FightStick TE S" },
-        { MAKE_VIDPID(0x07ff, 0xffff), "Mad Catz GamePad" },
-        { MAKE_VIDPID(0x0e6f, 0x0105), "HSM3 Xbox360 dancepad" },
-        { MAKE_VIDPID(0x0e6f, 0x0113), "Afterglow AX.1 Gamepad for Xbox 360" },
-        { MAKE_VIDPID(0x0e6f, 0x011f), "Rock Candy Gamepad Wired Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0131), "PDP EA Sports Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0133), "Xbox 360 Wired Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0139), "Afterglow Prismatic Wired Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x013a), "PDP Xbox One Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0146), "Rock Candy Wired Controller for Xbox One" },
-        { MAKE_VIDPID(0x0e6f, 0x0147), "PDP Marvel Xbox One Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x015c), "PDP Xbox One Arcade Stick" },
-        { MAKE_VIDPID(0x0e6f, 0x0161), "PDP Xbox One Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0162), "PDP Xbox One Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0163), "PDP Xbox One Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0164), "PDP Battlefield One" },
-        { MAKE_VIDPID(0x0e6f, 0x0165), "PDP Titanfall 2" },
-        { MAKE_VIDPID(0x0e6f, 0x0201), "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0213), "Afterglow Gamepad for Xbox 360" },
-        { MAKE_VIDPID(0x0e6f, 0x021f), "Rock Candy Gamepad for Xbox 360" },
-        { MAKE_VIDPID(0x0e6f, 0x0246), "Rock Candy Gamepad for Xbox One 2015" },
-        { MAKE_VIDPID(0x0e6f, 0x02a4), "PDP Wired Controller for Xbox One - Stealth Series" },
-        { MAKE_VIDPID(0x0e6f, 0x02ab), "PDP Controller for Xbox One" },
-        { MAKE_VIDPID(0x0e6f, 0x0301), "Logic3 Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0346), "Rock Candy Gamepad for Xbox One 2016" },
-        { MAKE_VIDPID(0x0e6f, 0x0401), "Logic3 Controller" },
-        { MAKE_VIDPID(0x0e6f, 0x0413), "Afterglow AX.1 Gamepad for Xbox 360" },
-        { MAKE_VIDPID(0x0e6f, 0x0501), "PDP Xbox 360 Controller" },
-        { MAKE_VIDPID(0x0e6f, 0xf900), "PDP Afterglow AX.1" },
-        { MAKE_VIDPID(0x0f0d, 0x000a), "Hori Co. DOA4 FightStick" },
-        { MAKE_VIDPID(0x0f0d, 0x000c), "Hori PadEX Turbo" },
-        { MAKE_VIDPID(0x0f0d, 0x000d), "Hori Fighting Stick EX2" },
-        { MAKE_VIDPID(0x0f0d, 0x0016), "Hori Real Arcade Pro.EX" },
-        { MAKE_VIDPID(0x0f0d, 0x001b), "Hori Real Arcade Pro VX" },
-        { MAKE_VIDPID(0x0f0d, 0x0063), "Hori Real Arcade Pro Hayabusa (USA) Xbox One" },
-        { MAKE_VIDPID(0x0f0d, 0x0067), "HORIPAD ONE" },
-        { MAKE_VIDPID(0x0f0d, 0x0078), "Hori Real Arcade Pro V Kai Xbox One" },
-        { MAKE_VIDPID(0x11c9, 0x55f0), "Nacon GC-100XF" },
-        { MAKE_VIDPID(0x12ab, 0x0004), "Honey Bee Xbox360 dancepad" },
-        { MAKE_VIDPID(0x12ab, 0x0301), "PDP AFTERGLOW AX.1" },
-        { MAKE_VIDPID(0x12ab, 0x0303), "Mortal Kombat Klassic FightStick" },
-        { MAKE_VIDPID(0x1430, 0x4748), "RedOctane Guitar Hero X-plorer" },
-        { MAKE_VIDPID(0x1430, 0xf801), "RedOctane Controller" },
-        { MAKE_VIDPID(0x146b, 0x0601), "BigBen Interactive XBOX 360 Controller" },
-        { MAKE_VIDPID(0x1532, 0x0037), "Razer Sabertooth" },
-        { MAKE_VIDPID(0x1532, 0x0a00), "Razer Atrox Arcade Stick" },
-        { MAKE_VIDPID(0x1532, 0x0a03), "Razer Wildcat" },
-        { MAKE_VIDPID(0x15e4, 0x3f00), "Power A Mini Pro Elite" },
-        { MAKE_VIDPID(0x15e4, 0x3f0a), "Xbox Airflo wired controller" },
-        { MAKE_VIDPID(0x15e4, 0x3f10), "Batarang Xbox 360 controller" },
-        { MAKE_VIDPID(0x162e, 0xbeef), "Joytech Neo-Se Take2" },
-        { MAKE_VIDPID(0x1689, 0xfd00), "Razer Onza Tournament Edition" },
-        { MAKE_VIDPID(0x1689, 0xfd01), "Razer Onza Classic Edition" },
-        { MAKE_VIDPID(0x1689, 0xfe00), "Razer Sabertooth" },
-        { MAKE_VIDPID(0x1bad, 0x0002), "Harmonix Rock Band Guitar" },
-        { MAKE_VIDPID(0x1bad, 0x0003), "Harmonix Rock Band Drumkit" },
-        { MAKE_VIDPID(0x1bad, 0x0130), "Ion Drum Rocker" },
-        { MAKE_VIDPID(0x1bad, 0xf016), "Mad Catz Xbox 360 Controller" },
-        { MAKE_VIDPID(0x1bad, 0xf018), "Mad Catz Street Fighter IV SE Fighting Stick" },
-        { MAKE_VIDPID(0x1bad, 0xf019), "Mad Catz Brawlstick for Xbox 360" },
-        { MAKE_VIDPID(0x1bad, 0xf021), "Mad Cats Ghost Recon FS GamePad" },
-        { MAKE_VIDPID(0x1bad, 0xf023), "MLG Pro Circuit Controller (Xbox)" },
-        { MAKE_VIDPID(0x1bad, 0xf025), "Mad Catz Call Of Duty" },
-        { MAKE_VIDPID(0x1bad, 0xf027), "Mad Catz FPS Pro" },
-        { MAKE_VIDPID(0x1bad, 0xf028), "Street Fighter IV FightPad" },
-        { MAKE_VIDPID(0x1bad, 0xf02e), "Mad Catz Fightpad" },
-        { MAKE_VIDPID(0x1bad, 0xf030), "Mad Catz Xbox 360 MC2 MicroCon Racing Wheel" },
-        { MAKE_VIDPID(0x1bad, 0xf036), "Mad Catz MicroCon GamePad Pro" },
-        { MAKE_VIDPID(0x1bad, 0xf038), "Street Fighter IV FightStick TE" },
-        { MAKE_VIDPID(0x1bad, 0xf039), "Mad Catz MvC2 TE" },
-        { MAKE_VIDPID(0x1bad, 0xf03a), "Mad Catz SFxT Fightstick Pro" },
-        { MAKE_VIDPID(0x1bad, 0xf03d), "Street Fighter IV Arcade Stick TE - Chun Li" },
-        { MAKE_VIDPID(0x1bad, 0xf03e), "Mad Catz MLG FightStick TE" },
-        { MAKE_VIDPID(0x1bad, 0xf03f), "Mad Catz FightStick SoulCaliber" },
-        { MAKE_VIDPID(0x1bad, 0xf042), "Mad Catz FightStick TES+" },
-        { MAKE_VIDPID(0x1bad, 0xf080), "Mad Catz FightStick TE2" },
-        { MAKE_VIDPID(0x1bad, 0xf501), "HoriPad EX2 Turbo" },
-        { MAKE_VIDPID(0x1bad, 0xf502), "Hori Real Arcade Pro.VX SA" },
-        { MAKE_VIDPID(0x1bad, 0xf503), "Hori Fighting Stick VX" },
-        { MAKE_VIDPID(0x1bad, 0xf504), "Hori Real Arcade Pro. EX" },
-        { MAKE_VIDPID(0x1bad, 0xf505), "Hori Fighting Stick EX2B" },
-        { MAKE_VIDPID(0x1bad, 0xf506), "Hori Real Arcade Pro.EX Premium VLX" },
-        { MAKE_VIDPID(0x1bad, 0xf900), "Harmonix Xbox 360 Controller" },
-        { MAKE_VIDPID(0x1bad, 0xf901), "Gamestop Xbox 360 Controller" },
-        { MAKE_VIDPID(0x1bad, 0xf903), "Tron Xbox 360 controller" },
-        { MAKE_VIDPID(0x1bad, 0xf904), "PDP Versus Fighting Pad" },
-        { MAKE_VIDPID(0x1bad, 0xf906), "MortalKombat FightStick" },
-        { MAKE_VIDPID(0x1bad, 0xfa01), "MadCatz GamePad" },
-        { MAKE_VIDPID(0x1bad, 0xfd00), "Razer Onza TE" },
-        { MAKE_VIDPID(0x1bad, 0xfd01), "Razer Onza" },
-        { MAKE_VIDPID(0x24c6, 0x5000), "Razer Atrox Arcade Stick" },
-        { MAKE_VIDPID(0x24c6, 0x5300), "PowerA MINI PROEX Controller" },
-        { MAKE_VIDPID(0x24c6, 0x5303), "Xbox Airflo wired controller" },
-        { MAKE_VIDPID(0x24c6, 0x530a), "Xbox 360 Pro EX Controller" },
-        { MAKE_VIDPID(0x24c6, 0x531a), "PowerA Pro Ex" },
-        { MAKE_VIDPID(0x24c6, 0x5397), "FUS1ON Tournament Controller" },
-        { MAKE_VIDPID(0x24c6, 0x541a), "PowerA Xbox One Mini Wired Controller" },
-        { MAKE_VIDPID(0x24c6, 0x542a), "Xbox ONE spectra" },
-        { MAKE_VIDPID(0x24c6, 0x543a), "PowerA Xbox One wired controller" },
-        { MAKE_VIDPID(0x24c6, 0x5500), "Hori XBOX 360 EX 2 with Turbo" },
-        { MAKE_VIDPID(0x24c6, 0x5501), "Hori Real Arcade Pro VX-SA" },
-        { MAKE_VIDPID(0x24c6, 0x5502), "Hori Fighting Stick VX Alt" },
-        { MAKE_VIDPID(0x24c6, 0x5503), "Hori Fighting Edge" },
-        { MAKE_VIDPID(0x24c6, 0x5506), "Hori SOULCALIBUR V Stick" },
-        { MAKE_VIDPID(0x24c6, 0x550d), "Hori GEM Xbox controller" },
-        { MAKE_VIDPID(0x24c6, 0x550e), "Hori Real Arcade Pro V Kai 360" },
-        { MAKE_VIDPID(0x24c6, 0x551a), "PowerA FUSION Pro Controller" },
-        { MAKE_VIDPID(0x24c6, 0x561a), "PowerA FUSION Controller" },
-        { MAKE_VIDPID(0x24c6, 0x5b00), "ThrustMaster Ferrari 458 Racing Wheel" },
-        { MAKE_VIDPID(0x24c6, 0x5b02), "Thrustmaster, Inc. GPX Controller" },
-        { MAKE_VIDPID(0x24c6, 0x5b03), "Thrustmaster Ferrari 458 Racing Wheel" },
-        { MAKE_VIDPID(0x24c6, 0x5d04), "Razer Sabertooth" },
-        { MAKE_VIDPID(0x24c6, 0xfafe), "Rock Candy Gamepad for Xbox 360" },
-    };
-    int i;
-    Uint32 vidpid = MAKE_VIDPID(vendor_id, product_id);
-
-    for (i = 0; i < SDL_arraysize(names); ++i) {
-        if (vidpid == names[i].vidpid) {
-            return names[i].name;
-        }
-    }
-    return NULL;
-}
+static void HIDAPI_JoystickDetect(void);
+static void HIDAPI_JoystickClose(SDL_Joystick * joystick);
 
 static SDL_bool
-HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version)
+HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
 {
     int i;
+    SDL_GameControllerType type = SDL_GetJoystickGameControllerType(name, vendor_id, product_id, -1, 0, 0, 0);
 
     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
-        if (driver->enabled && driver->IsSupportedDevice(vendor_id, product_id, version, -1)) {
+        if (driver->enabled && driver->IsSupportedDevice(name, type, vendor_id, product_id, version, -1, 0, 0, 0)) {
             return SDL_TRUE;
         }
     }
@@ -583,6 +413,7 @@
     const Uint16 USAGE_GAMEPAD = 0x0005;
     const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
     int i;
+    SDL_GameControllerType type;
 
     if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
         return NULL;
@@ -595,9 +426,10 @@
         return NULL;
     }
 
+    type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
-        if (driver->enabled && driver->IsSupportedDevice(device->vendor_id, device->product_id, device->version, device->interface_number)) {
+        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)) {
             return driver;
         }
     }
@@ -605,19 +437,22 @@
 }
 
 static SDL_HIDAPI_Device *
-HIDAPI_GetJoystickByIndex(int device_index)
+HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID)
 {
     SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
     while (device) {
         if (device->driver) {
-            if (device_index == 0) {
-                break;
+            if (device_index < device->num_joysticks) {
+                if (pJoystickID) {
+                    *pJoystickID = device->joysticks[device_index];
+                }
+                return device;
             }
-            --device_index;
+            device_index -= device->num_joysticks;
         }
         device = device->next;
     }
-    return device;
+    return NULL;
 }
 
 static SDL_HIDAPI_Device *
@@ -634,12 +469,52 @@
     return device;
 }
 
+static void
+HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device)
+{
+    if (device->driver) {
+        /* Already setup */
+        return;
+    }
+
+    device->driver = HIDAPI_GetDeviceDriver(device);
+    if (device->driver) {
+        const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
+        if (name) {
+            SDL_free(device->name);
+            device->name = SDL_strdup(name);
+        }
+    }
+
+    /* Initialize the device, which may cause a connected event */
+    if (device->driver && !device->driver->InitDevice(device)) {
+        device->driver = NULL;
+    }
+}
+
+static void
+HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
+{
+    if (!device->driver) {
+        /* Already cleaned up */
+        return;
+    }
+
+    /* Disconnect any joysticks */
+    while (device->num_joysticks) {
+        HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
+    }
+
+    device->driver->FreeDevice(device);
+    device->driver = NULL;
+}
+
 static void SDLCALL
 SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
 {
     int i;
-    SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
-    SDL_bool enabled = (!hint || !*hint || ((*hint != '0') && (SDL_strcasecmp(hint, "false") != 0)));
+    SDL_HIDAPI_Device *device;
+    SDL_bool enabled = SDL_GetStringBoolean(hint, SDL_TRUE);
 
     if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
         for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
@@ -651,41 +526,49 @@
             SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
             if (SDL_strcmp(name, driver->hint) == 0) {
                 driver->enabled = enabled;
-                break;
             }
+        }
+    }
+
+    SDL_HIDAPI_numdrivers = 0;
+    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+        if (driver->enabled) {
+            ++SDL_HIDAPI_numdrivers;
         }
     }
 
     /* Update device list if driver availability changes */
-    while (device) {
-        if (device->driver) {
-            if (!device->driver->enabled) {
-                device->driver = NULL;
+    SDL_LockJoysticks();
 
-                --SDL_HIDAPI_numjoysticks;
-
-                SDL_PrivateJoystickRemoved(device->instance_id);
-            }
-        } else {
-            device->driver = HIDAPI_GetDeviceDriver(device);
-            if (device->driver) {
-                device->instance_id = SDL_GetNextJoystickInstanceID();
-
-                ++SDL_HIDAPI_numjoysticks;
-
-                SDL_PrivateJoystickAdded(device->instance_id);
-            }
+    for (device = SDL_HIDAPI_devices; device; device = device->next) {
+        if (device->driver && !device->driver->enabled) {
+            HIDAPI_CleanupDeviceDriver(device);
         }
-        device = device->next;
+        HIDAPI_SetupDeviceDriver(device);
     }
-}
 
-static void HIDAPI_JoystickDetect(void);
+    SDL_UnlockJoysticks();
+}
 
 static int
 HIDAPI_JoystickInit(void)
 {
     int i;
+
+    if (initialized) {
+        return 0;
+    }
+
+#if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__)
+	/* The hidapi framwork is weak-linked on Apple platforms */
+    int HID_API_EXPORT HID_API_CALL hid_init(void) __attribute__((weak_import));
+
+    if (hid_init == NULL) {
+        SDL_SetError("Couldn't initialize hidapi, framework not available");
+        return -1;
+    }
+#endif /* __MACOSX__ || __IPHONEOS__ || __TVOS__ */
 
     if (hid_init() < 0) {
         SDL_SetError("Couldn't initialize hidapi");
@@ -700,7 +583,61 @@
                         SDL_HIDAPIDriverHintChanged, NULL);
     HIDAPI_InitializeDiscovery();
     HIDAPI_JoystickDetect();
+    HIDAPI_UpdateDevices();
+
+    initialized = SDL_TRUE;
+
     return 0;
+}
+
+SDL_bool
+HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
+{
+    SDL_JoystickID joystickID;
+    SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks));
+    if (!joysticks) {
+        return SDL_FALSE;
+    }
+
+    joystickID = SDL_GetNextJoystickInstanceID();
+    device->joysticks = joysticks;
+    device->joysticks[device->num_joysticks++] = joystickID;
+    ++SDL_HIDAPI_numjoysticks;
+
+    SDL_PrivateJoystickAdded(joystickID);
+
+    if (pJoystickID) {
+        *pJoystickID = joystickID;
+    }
+    return SDL_TRUE;
+}
+
+void
+HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
+{
+    int i;
+
+    for (i = 0; i < device->num_joysticks; ++i) {
+        if (device->joysticks[i] == joystickID) {
+            SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joystickID);
+            if (joystick) {
+                HIDAPI_JoystickClose(joystick);
+            }
+
+            SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], device->num_joysticks - i - 1);
+            --device->num_joysticks;
+            --SDL_HIDAPI_numjoysticks;
+            if (device->num_joysticks == 0) {
+                SDL_free(device->joysticks);
+                device->joysticks = NULL;
+            }
+
+            if (!shutting_down) {
+                SDL_PrivateJoystickRemoved(joystickID);
+            }
+            return;
+        }
+    }
 }
 
 static int
@@ -723,12 +660,19 @@
     if (!device) {
         return;
     }
-    device->instance_id = -1;
+    device->path = SDL_strdup(info->path);
+    if (!device->path) {
+        SDL_free(device);
+        return;
+    }
     device->seen = SDL_TRUE;
     device->vendor_id = info->vendor_id;
     device->product_id = info->product_id;
     device->version = info->release_number;
     device->interface_number = info->interface_number;
+    device->interface_class = info->interface_class;
+    device->interface_subclass = info->interface_subclass;
+    device->interface_protocol = info->interface_protocol;
     device->usage_page = info->usage_page;
     device->usage = info->usage;
     {
@@ -751,9 +695,17 @@
         device->guid.data[14] = 'h';
         device->guid.data[15] = 0;
     }
+    device->dev_lock = SDL_CreateMutex();
 
     /* Need the device name before getting the driver to know whether to ignore this device */
+    if (!device->name) {
+        const char *name = SDL_GetCustomJoystickName(device->vendor_id, device->product_id);
+        if (name) {
+            device->name = SDL_strdup(name);
+        }
+    }
     if (!device->name && info->manufacturer_string && info->product_string) {
+        const char *manufacturer_remapped;
         char *manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
         char *product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
         if (!manufacturer_string && !product_string) {
@@ -765,11 +717,22 @@
                 product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
             }
         }
+
+        manufacturer_remapped = SDL_GetCustomJoystickManufacturer(manufacturer_string);
+        if (manufacturer_remapped != manufacturer_string) {
+            SDL_free(manufacturer_string);
+            manufacturer_string = SDL_strdup(manufacturer_remapped);
+        }
+
         if (manufacturer_string && product_string) {
             size_t name_size = (SDL_strlen(manufacturer_string) + 1 + SDL_strlen(product_string) + 1);
             device->name = (char *)SDL_malloc(name_size);
             if (device->name) {
-                SDL_snprintf(device->name, name_size, "%s %s", manufacturer_string, product_string);
+                if (SDL_strncasecmp(manufacturer_string, product_string, SDL_strlen(manufacturer_string)) == 0) {
+                    SDL_strlcpy(device->name, product_string, name_size);
+                } else {
+                    SDL_snprintf(device->name, name_size, "%s %s", manufacturer_string, product_string);
+                }
             }
         }
         if (manufacturer_string) {
@@ -783,32 +746,12 @@
         size_t name_size = (6 + 1 + 6 + 1);
         device->name = (char *)SDL_malloc(name_size);
         if (!device->name) {
+            SDL_free(device->path);
             SDL_free(device);
             return;
         }
         SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
     }
-
-    device->driver = HIDAPI_GetDeviceDriver(device);
-
-    if (device->driver) {
-        const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
-        if (name) {
-            SDL_free(device->name);
-            device->name = SDL_strdup(name);
-        }
-    }
-
-    device->path = SDL_strdup(info->path);
-    if (!device->path) {
-        SDL_free(device->name);
-        SDL_free(device);
-        return;
-    }
-
-#ifdef DEBUG_HIDAPI
-    SDL_Log("Adding HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, usage page 0x%.4x, usage 0x%.4x, driver = %s\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->usage_page, device->usage, device->driver ? device->driver->hint : "NONE");
-#endif
 
     /* Add it to the list */
     if (last) {
@@ -817,19 +760,16 @@
         SDL_HIDAPI_devices = device;
     }
 
-    if (device->driver) {
-        /* It's a joystick! */
-        device->instance_id = SDL_GetNextJoystickInstanceID();
+    HIDAPI_SetupDeviceDriver(device);
 
-        ++SDL_HIDAPI_numjoysticks;
-
-        SDL_PrivateJoystickAdded(device->instance_id);
-    }
+#ifdef DEBUG_HIDAPI
+    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");
+#endif
 }
 
 
 static void
-HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event)
+HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
 {
     SDL_HIDAPI_Device *curr, *last;
     for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
@@ -840,13 +780,9 @@
                 SDL_HIDAPI_devices = curr->next;
             }
 
-            if (device->driver && send_event) {
-                /* Need to decrement the joystick count before we post the event */
-                --SDL_HIDAPI_numjoysticks;
+            HIDAPI_CleanupDeviceDriver(device);
 
-                SDL_PrivateJoystickRemoved(device->instance_id);
-            }
-
+            SDL_DestroyMutex(device->dev_lock);
             SDL_free(device->name);
             SDL_free(device->path);
             SDL_free(device);
@@ -861,6 +797,8 @@
     SDL_HIDAPI_Device *device;
     struct hid_device_info *devs, *info;
 
+    SDL_LockJoysticks();
+
     /* Prepare the existing device list */
     device = SDL_HIDAPI_devices;
     while (device) {
@@ -869,17 +807,19 @@
     }
 
     /* Enumerate the devices */
-    devs = hid_enumerate(0, 0);
-    if (devs) {
-        for (info = devs; info; info = info->next) {
-            device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
-            if (device) {
-                device->seen = SDL_TRUE;
-            } else {
-                HIDAPI_AddDevice(info);
+    if (SDL_HIDAPI_numdrivers > 0) {
+        devs = hid_enumerate(0, 0);
+        if (devs) {
+            for (info = devs; info; info = info->next) {
+                device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
+                if (device) {
+                    device->seen = SDL_TRUE;
+                } else {
+                    HIDAPI_AddDevice(info);
+                }
             }
+            hid_free_enumeration(devs);
         }
-        hid_free_enumeration(devs);
     }
 
     /* Remove any devices that weren't seen */
@@ -888,91 +828,188 @@
         SDL_HIDAPI_Device *next = device->next;
 
         if (!device->seen) {
-            HIDAPI_DelDevice(device, SDL_TRUE);
+            HIDAPI_DelDevice(device);
         }
         device = next;
     }
+
+    SDL_UnlockJoysticks();
 }
 
 SDL_bool
-HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
+HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
 {
     SDL_HIDAPI_Device *device;
+    SDL_bool supported = SDL_FALSE;
+    SDL_bool result = SDL_FALSE;
 
-    /* Don't update the device list for devices we know aren't supported */
-    if (!HIDAPI_IsDeviceSupported(vendor_id, product_id, version)) {
+    /* Make sure we're initialized, as this could be called from other drivers during startup */
+    if (HIDAPI_JoystickInit() < 0) {
         return SDL_FALSE;
     }
 
-    /* Make sure the device list is completely up to date when we check for device presence */
-    HIDAPI_UpdateDeviceList();
+    /* Only update the device list for devices we know might be supported.
+       If we did this for every device, it would hit the USB driver too hard and potentially 
+       lock up the system. This won't catch devices that we support but can only detect using 
+       USB interface details, like Xbox controllers, but hopefully the device list update is
+       responsive enough to catch those.
+     */
+    supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name);
+#if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)
+    if (!supported &&
+        (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) {
+        supported = SDL_TRUE;
+    }
+#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE */
+    if (supported) {
+        if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
+            HIDAPI_UpdateDeviceList();
+            SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
+        }
+    }
 
+    /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
+       or a different name than we have it listed here, etc, but if we support the device
+       and we have something similar in our device list, mark it as present.
+     */
+    SDL_LockJoysticks();
     device = SDL_HIDAPI_devices;
     while (device) {
         if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
-            return SDL_TRUE;
+            result = SDL_TRUE;
         }
         device = device->next;
     }
-    return SDL_FALSE;
+    SDL_UnlockJoysticks();
+
+    /* If we're looking for the wireless XBox 360 controller, also look for the dongle */
+    if (!result && vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x02a1) {
+        return HIDAPI_IsDevicePresent(USB_VENDOR_MICROSOFT, 0x0719, version, name);
+    }
+
+#ifdef DEBUG_HIDAPI
+    SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x\n", result ? "true" : "false", vendor_id, product_id);
+#endif
+    return result;
 }
 
 static void
 HIDAPI_JoystickDetect(void)
 {
-    HIDAPI_UpdateDiscovery();
-    if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
-        /* FIXME: We probably need to schedule an update in a few seconds as well */
-        HIDAPI_UpdateDeviceList();
-        SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
+    if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
+        HIDAPI_UpdateDiscovery();
+        if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
+            /* FIXME: We probably need to schedule an update in a few seconds as well */
+            HIDAPI_UpdateDeviceList();
+            SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
+        }
+        SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
+    }
+}
+
+void
+HIDAPI_UpdateDevices(void)
+{
+    SDL_HIDAPI_Device *device;
+
+    /* Update the devices, which may change connected joysticks and send events */
+
+    /* Prepare the existing device list */
+    if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
+        device = SDL_HIDAPI_devices;
+        while (device) {
+            if (device->driver) {
+                if (SDL_TryLockMutex(device->dev_lock) == 0) {
+                    device->driver->UpdateDevice(device);
+                    SDL_UnlockMutex(device->dev_lock);
+                }
+            }
+            device = device->next;
+        }
+        SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
     }
 }
 
 static const char *
 HIDAPI_JoystickGetDeviceName(int device_index)
 {
-    return HIDAPI_GetJoystickByIndex(device_index)->name;
+    SDL_HIDAPI_Device *device;
+    const char *name = NULL;
+
+    device = HIDAPI_GetDeviceByIndex(device_index, NULL);
+    if (device) {
+        /* FIXME: The device could be freed after this name is returned... */
+        name = device->name;
+    }
+
+    return name;
 }
 
 static int
 HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
 {
-    return -1;
+    SDL_HIDAPI_Device *device;
+    SDL_JoystickID instance_id;
+    int player_index = -1;
+
+    device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
+    if (device) {
+        player_index = device->driver->GetDevicePlayerIndex(device, instance_id);
+    }
+
+    return player_index;
+}
+
+static void
+HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
+{
+    SDL_HIDAPI_Device *device;
+    SDL_JoystickID instance_id;
+
+    device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
+    if (device) {
+        device->driver->SetDevicePlayerIndex(device, instance_id, player_index);
+    }
 }
 
 static SDL_JoystickGUID
 HIDAPI_JoystickGetDeviceGUID(int device_index)
 {
-    return HIDAPI_GetJoystickByIndex(device_index)->guid;
+    SDL_HIDAPI_Device *device;
+    SDL_JoystickGUID guid;
+
+    device = HIDAPI_GetDeviceByIndex(device_index, NULL);
+    if (device) {
+        SDL_memcpy(&guid, &device->guid, sizeof(guid));
+    } else {
+        SDL_zero(guid);
+    }
+
+    return guid;
 }
 
 static SDL_JoystickID
 HIDAPI_JoystickGetDeviceInstanceID(int device_index)
 {
-    return HIDAPI_GetJoystickByIndex(device_index)->instance_id;
+    SDL_JoystickID joystickID = -1;
+    HIDAPI_GetDeviceByIndex(device_index, &joystickID);
+    return joystickID;
 }
 
 static int
 HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
 {
-    SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index);
+    SDL_JoystickID joystickID;
+    SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
     struct joystick_hwdata *hwdata;
 
     hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
     if (!hwdata) {
         return SDL_OutOfMemory();
     }
+    hwdata->device = device;
 
-    hwdata->driver = device->driver;
-    hwdata->dev = hid_open_path(device->path, 0);
-    if (!hwdata->dev) {
-        SDL_free(hwdata);
-        return SDL_SetError("Couldn't open HID device %s", device->path);
-    }
-    hwdata->mutex = SDL_CreateMutex();
-
-    if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) {
-        hid_close(hwdata->dev);
+    if (!device->driver->OpenJoystick(device, joystick)) {
         SDL_free(hwdata);
         return -1;
     }
@@ -982,51 +1019,44 @@
 }
 
 static int
-HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
 {
-    struct joystick_hwdata *hwdata = joystick->hwdata;
-    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
     int result;
 
-    SDL_LockMutex(hwdata->mutex);
-    result = driver->Rumble(joystick, hwdata->dev, hwdata->context, low_frequency_rumble, high_frequency_rumble, duration_ms);
-    SDL_UnlockMutex(hwdata->mutex);
+    if (joystick->hwdata) {
+        SDL_HIDAPI_Device *device = joystick->hwdata->device;
+
+        result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble);
+    } else {
+        SDL_SetError("Rumble failed, device disconnected");
+        result = -1;
+    }
+
     return result;
 }
 
 static void
 HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
 {
-    struct joystick_hwdata *hwdata = joystick->hwdata;
-    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
-    SDL_bool succeeded;
-
-    SDL_LockMutex(hwdata->mutex);
-    succeeded = driver->Update(joystick, hwdata->dev, hwdata->context);
-    SDL_UnlockMutex(hwdata->mutex);
-    
-    if (!succeeded) {
-        SDL_HIDAPI_Device *device;
-        for (device = SDL_HIDAPI_devices; device; device = device->next) {
-            if (device->instance_id == joystick->instance_id) {
-                HIDAPI_DelDevice(device, SDL_TRUE);
-                break;
-            }
-        }
-    }
+    /* This is handled in SDL_HIDAPI_UpdateDevices() */
 }
 
 static void
 HIDAPI_JoystickClose(SDL_Joystick * joystick)
 {
-    struct joystick_hwdata *hwdata = joystick->hwdata;
-    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
-    driver->Quit(joystick, hwdata->dev, hwdata->context);
+    if (joystick->hwdata) {
+        SDL_HIDAPI_Device *device = joystick->hwdata->device;
 
-    hid_close(hwdata->dev);
-    SDL_DestroyMutex(hwdata->mutex);
-    SDL_free(hwdata);
-    joystick->hwdata = NULL;
+        /* Wait for pending rumble to complete */
+        while (SDL_AtomicGet(&device->rumble_pending) > 0) {
+            SDL_Delay(10);
+        }
+
+        device->driver->CloseJoystick(device, joystick);
+
+        SDL_free(joystick->hwdata);
+        joystick->hwdata = NULL;
+    }
 }
 
 static void
@@ -1034,20 +1064,30 @@
 {
     int i;
 
+    shutting_down = SDL_TRUE;
+
     HIDAPI_ShutdownDiscovery();
 
     while (SDL_HIDAPI_devices) {
-        HIDAPI_DelDevice(SDL_HIDAPI_devices, SDL_FALSE);
+        HIDAPI_DelDevice(SDL_HIDAPI_devices);
     }
+
+    SDL_HIDAPI_QuitRumble();
+
     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
         SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
     }
     SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
                         SDL_HIDAPIDriverHintChanged, NULL);
-    SDL_HIDAPI_numjoysticks = 0;
 
     hid_exit();
+
+    /* Make sure the drivers cleaned up properly */
+    SDL_assert(SDL_HIDAPI_numjoysticks == 0);
+
+    shutting_down = SDL_FALSE;
+    initialized = SDL_FALSE;
 }
 
 SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
@@ -1057,6 +1097,7 @@
     HIDAPI_JoystickDetect,
     HIDAPI_JoystickGetDeviceName,
     HIDAPI_JoystickGetDevicePlayerIndex,
+    HIDAPI_JoystickSetDevicePlayerIndex,
     HIDAPI_JoystickGetDeviceGUID,
     HIDAPI_JoystickGetDeviceInstanceID,
     HIDAPI_JoystickOpen,

--
Gitblit v1.9.3