SDL: Generalized the idea of joystick driver priority

From f35ede72810b8c5843752a776df9ae8869bf7a79 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 17 Feb 2024 15:41:18 -0800
Subject: [PATCH] Generalized the idea of joystick driver priority

Joystick drivers are sorted by priority in the driver list, and higher priority drivers report whether they are handling a device when lower priority drivers want to add it to their device list.

This has been handled ad-hoc with the Windows and HIDAPI drivers, but this formalizes the idea and makes sure that GameInput has the highest priority of the Windows drivers.
---
 src/joystick/SDL_joystick.c                   |  35 ++++-
 src/joystick/SDL_joystick_c.h                 |   3 +
 src/joystick/SDL_sysjoystick.h                |   3 +
 src/joystick/android/SDL_sysjoystick.c        |  12 +-
 src/joystick/apple/SDL_mfijoystick.m          |   7 +
 src/joystick/bsd/SDL_bsdjoystick.c            |  18 +--
 src/joystick/darwin/SDL_iokitjoystick.c       |  16 ++-
 src/joystick/dummy/SDL_sysjoystick.c          |   6 +
 src/joystick/emscripten/SDL_sysjoystick.c     |   7 +
 src/joystick/gdk/SDL_gameinputjoystick.c      |  64 +++++----
 src/joystick/haiku/SDL_haikujoystick.cc       |   7 +
 src/joystick/hidapi/SDL_hidapijoystick.c      |   1 +
 src/joystick/linux/SDL_sysjoystick.c          |  12 +-
 src/joystick/n3ds/SDL_sysjoystick.c           |  47 ++++---
 src/joystick/ps2/SDL_sysjoystick.c            |   7 +
 src/joystick/psp/SDL_sysjoystick.c            |   7 +
 src/joystick/virtual/SDL_virtualjoystick.c    |   7 +
 src/joystick/vita/SDL_sysjoystick.c           |  27 ++--
 src/joystick/windows/SDL_dinputjoystick.c     |   8 +-
 src/joystick/windows/SDL_rawinputjoystick.c   |  78 +++++------
 src/joystick/windows/SDL_rawinputjoystick_c.h |   3 -
 .../windows/SDL_windows_gaming_input.c        | 131 ++----------------
 src/joystick/windows/SDL_windowsjoystick.c    |  12 ++
 src/joystick/windows/SDL_xinputjoystick.c     |  49 +++++--
 src/joystick/windows/SDL_xinputjoystick_c.h   |   1 +
 25 files changed, 297 insertions(+), 271 deletions(-)

diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index c1c1a3843cae..1e0ca4466535 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -49,16 +49,16 @@
 #endif
 
 static SDL_JoystickDriver *SDL_joystick_drivers[] = {
-#ifdef SDL_JOYSTICK_HIDAPI /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */
+#ifdef SDL_JOYSTICK_HIDAPI /* Highest priority driver for supported devices */
     &SDL_HIDAPI_JoystickDriver,
 #endif
-#ifdef SDL_JOYSTICK_RAWINPUT /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */
-    &SDL_RAWINPUT_JoystickDriver,
-#endif
-#ifdef SDL_JOYSTICK_GAMEINPUT /* Before WINDOWS_ driver, as GameInput takes priority over XInputOnGameInput for GDK platforms */
+#ifdef SDL_JOYSTICK_GAMEINPUT /* Higher priority than other Windows drivers */
     &SDL_GAMEINPUT_JoystickDriver,
 #endif
-#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) /* Before WGI driver, as WGI wants to check if this driver is handling things */
+#ifdef SDL_JOYSTICK_RAWINPUT
+    &SDL_RAWINPUT_JoystickDriver,
+#endif
+#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
     &SDL_WINDOWS_JoystickDriver,
 #endif
 #ifdef SDL_JOYSTICK_WGI
@@ -659,6 +659,29 @@ SDL_bool SDL_JoysticksOpened(void)
     return opened;
 }
 
+SDL_bool SDL_JoystickHandledByAnotherDriver(struct SDL_JoystickDriver *driver, Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    int i;
+    SDL_bool result = SDL_FALSE;
+
+    SDL_LockJoysticks();
+    {
+        for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+            if (driver == SDL_joystick_drivers[i]) {
+                /* Higher priority drivers do not have this device */
+                break;
+            }
+            if (SDL_joystick_drivers[i]->IsDevicePresent(vendor_id, product_id, version, name)) {
+                result = SDL_TRUE;
+                break;
+            }
+        }
+    }
+    SDL_UnlockJoysticks();
+
+    return result;
+}
+
 SDL_JoystickID *SDL_GetJoysticks(int *count)
 {
     int i, num_joysticks, device_index;
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index 3004bb1485a9..0f00d180b68b 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -54,6 +54,9 @@ extern void SDL_AssertJoysticksLocked(void) SDL_ASSERT_CAPABILITY(SDL_joystick_l
 /* Function to return whether there are any joysticks opened by the application */
 extern SDL_bool SDL_JoysticksOpened(void);
 
+/* Function to determine whether a device is currently detected by this driver */
+extern SDL_bool SDL_JoystickHandledByAnotherDriver(struct SDL_JoystickDriver *driver, Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name);
+
 /* Function to standardize the name for a controller
    This should be freed with SDL_free() when no longer needed
  */
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index 739d6973d132..d3d8e71de5c5 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -158,6 +158,9 @@ typedef struct SDL_JoystickDriver
     /* Function to cause any queued joystick insertions to be processed */
     void (*Detect)(void);
 
+    /* Function to determine whether a device is currently detected by this driver */
+    SDL_bool (*IsDevicePresent)(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name);
+
     /* Function to get the device-dependent name of a joystick */
     const char *(*GetDeviceName)(int device_index);
 
diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c
index 0d2972f5ee83..fe085a749d76 100644
--- a/src/joystick/android/SDL_sysjoystick.c
+++ b/src/joystick/android/SDL_sysjoystick.c
@@ -321,12 +321,9 @@ int Android_AddJoystick(int device_id, const char *name, const char *desc, int v
         goto done;
     }
 
-#ifdef SDL_JOYSTICK_HIDAPI
-    if (HIDAPI_IsDevicePresent(vendor_id, product_id, 0, name)) {
-        /* The HIDAPI driver is taking care of this device */
+    if (SDL_JoystickHandledByAnotherDriver(&SDL_ANDROID_JoystickDriver, vendor_id, product_id, 0, name)) {
         goto done;
     }
-#endif
 
 #ifdef DEBUG_JOYSTICK
     SDL_Log("Joystick: %s, descriptor %s, vendor = 0x%.4x, product = 0x%.4x, %d axes, %d hats\n", name, desc, vendor_id, product_id, naxes, nhats);
@@ -482,6 +479,12 @@ static void ANDROID_JoystickDetect(void)
     }
 }
 
+static SDL_bool ANDROID_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers */
+    return SDL_FALSE;
+}
+
 static SDL_joylist_item *GetJoystickByDevIndex(int device_index)
 {
     SDL_joylist_item *item = SDL_joylist;
@@ -646,6 +649,7 @@ SDL_JoystickDriver SDL_ANDROID_JoystickDriver = {
     ANDROID_JoystickInit,
     ANDROID_JoystickGetCount,
     ANDROID_JoystickDetect,
+    ANDROID_JoystickIsDevicePresent,
     ANDROID_JoystickGetDeviceName,
     ANDROID_JoystickGetDevicePath,
     ANDROID_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/apple/SDL_mfijoystick.m b/src/joystick/apple/SDL_mfijoystick.m
index 9f43a6e6398f..c497fc606428 100644
--- a/src/joystick/apple/SDL_mfijoystick.m
+++ b/src/joystick/apple/SDL_mfijoystick.m
@@ -879,6 +879,12 @@ static void IOS_JoystickDetect(void)
 {
 }
 
+static SDL_bool IOS_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers through this method */
+    return SDL_FALSE;
+}
+
 static const char *IOS_JoystickGetDeviceName(int device_index)
 {
     SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
@@ -2079,6 +2085,7 @@ static void GetAppleSFSymbolsNameForElement(GCControllerElement *element, char *
     IOS_JoystickInit,
     IOS_JoystickGetCount,
     IOS_JoystickDetect,
+    IOS_JoystickIsDevicePresent,
     IOS_JoystickGetDeviceName,
     IOS_JoystickGetDevicePath,
     IOS_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/bsd/SDL_bsdjoystick.c b/src/joystick/bsd/SDL_bsdjoystick.c
index 647015c470b2..c5c924594243 100644
--- a/src/joystick/bsd/SDL_bsdjoystick.c
+++ b/src/joystick/bsd/SDL_bsdjoystick.c
@@ -427,15 +427,8 @@ static int MaybeAddDevice(const char *path)
             name = SDL_CreateJoystickName(di.udi_vendorNo, di.udi_productNo, di.udi_vendor, di.udi_product);
             guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, di.udi_vendorNo, di.udi_productNo, di.udi_releaseNo, di.udi_vendor, di.udi_product, 0, 0);
 
-#ifdef SDL_JOYSTICK_HIDAPI
-            if (HIDAPI_IsDevicePresent(di.udi_vendorNo, di.udi_productNo, di.udi_releaseNo, name)) {
-                /* The HIDAPI driver is taking care of this device */
-                SDL_free(name);
-                FreeHwData(hw);
-                return -1;
-            }
-#endif
-            if (SDL_ShouldIgnoreJoystick(name, guid)) {
+            if (SDL_ShouldIgnoreJoystick(name, guid) ||
+                SDL_JoystickHandledByAnotherDriver(&SDL_BSD_JoystickDriver, di.udi_vendorNo, di.udi_productNo, di.udi_releaseNo, name)) {
                 SDL_free(name);
                 FreeHwData(hw);
                 return -1;
@@ -516,6 +509,12 @@ static void BSD_JoystickDetect(void)
 {
 }
 
+static SDL_bool BSD_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers */
+    return SDL_FALSE;
+}
+
 static SDL_joylist_item *GetJoystickByDevIndex(int device_index)
 {
     SDL_joylist_item *item = SDL_joylist;
@@ -848,6 +847,7 @@ SDL_JoystickDriver SDL_BSD_JoystickDriver = {
     BSD_JoystickInit,
     BSD_JoystickGetCount,
     BSD_JoystickDetect,
+    BSD_JoystickIsDevicePresent,
     BSD_JoystickGetDeviceName,
     BSD_JoystickGetDevicePath,
     BSD_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/darwin/SDL_iokitjoystick.c b/src/joystick/darwin/SDL_iokitjoystick.c
index 9900ecf44680..99c9565fd2dc 100644
--- a/src/joystick/darwin/SDL_iokitjoystick.c
+++ b/src/joystick/darwin/SDL_iokitjoystick.c
@@ -490,12 +490,9 @@ static SDL_bool GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
         SDL_free(name);
     }
 
-#ifdef SDL_JOYSTICK_HIDAPI
-    if (HIDAPI_IsDevicePresent(vendor, product, version, pDevice->product)) {
-        /* The HIDAPI driver is taking care of this device */
+    if (SDL_JoystickHandledByAnotherDriver(&SDL_DARWIN_JoystickDriver, vendor, product, version, pDevice->product)) {
         return SDL_FALSE;
     }
-#endif
 
     pDevice->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, (Uint16)vendor, (Uint16)product, (Uint16)version, manufacturer_string, product_string, 0, 0);
     pDevice->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot((Uint16)vendor, (Uint16)product, product_string);
@@ -714,13 +711,19 @@ static void DARWIN_JoystickDetect(void)
     }
 }
 
-const char *DARWIN_JoystickGetDeviceName(int device_index)
+static SDL_bool DARWIN_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers */
+    return SDL_FALSE;
+}
+
+static const char *DARWIN_JoystickGetDeviceName(int device_index)
 {
     recDevice *device = GetDeviceForIndex(device_index);
     return device ? device->product : "UNKNOWN";
 }
 
-const char *DARWIN_JoystickGetDevicePath(int device_index)
+static const char *DARWIN_JoystickGetDevicePath(int device_index)
 {
     return NULL;
 }
@@ -1063,6 +1066,7 @@ SDL_JoystickDriver SDL_DARWIN_JoystickDriver = {
     DARWIN_JoystickInit,
     DARWIN_JoystickGetCount,
     DARWIN_JoystickDetect,
+    DARWIN_JoystickIsDevicePresent,
     DARWIN_JoystickGetDeviceName,
     DARWIN_JoystickGetDevicePath,
     DARWIN_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/dummy/SDL_sysjoystick.c b/src/joystick/dummy/SDL_sysjoystick.c
index 0d6a850d2c09..9338757507ee 100644
--- a/src/joystick/dummy/SDL_sysjoystick.c
+++ b/src/joystick/dummy/SDL_sysjoystick.c
@@ -41,6 +41,11 @@ static void DUMMY_JoystickDetect(void)
 {
 }
 
+static SDL_bool DUMMY_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    return SDL_FALSE;
+}
+
 static const char *DUMMY_JoystickGetDeviceName(int device_index)
 {
     return NULL;
@@ -128,6 +133,7 @@ SDL_JoystickDriver SDL_DUMMY_JoystickDriver = {
     DUMMY_JoystickInit,
     DUMMY_JoystickGetCount,
     DUMMY_JoystickDetect,
+    DUMMY_JoystickIsDevicePresent,
     DUMMY_JoystickGetDeviceName,
     DUMMY_JoystickGetDevicePath,
     DUMMY_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c
index a9be05618f96..e4a5603a9fc0 100644
--- a/src/joystick/emscripten/SDL_sysjoystick.c
+++ b/src/joystick/emscripten/SDL_sysjoystick.c
@@ -259,6 +259,12 @@ static void EMSCRIPTEN_JoystickDetect(void)
 {
 }
 
+static SDL_bool EMSCRIPTEN_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers */
+    return SDL_FALSE;
+}
+
 static const char *EMSCRIPTEN_JoystickGetDeviceName(int device_index)
 {
     return JoystickByDeviceIndex(device_index)->name;
@@ -414,6 +420,7 @@ SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver = {
     EMSCRIPTEN_JoystickInit,
     EMSCRIPTEN_JoystickGetCount,
     EMSCRIPTEN_JoystickDetect,
+    EMSCRIPTEN_JoystickIsDevicePresent,
     EMSCRIPTEN_JoystickGetDeviceName,
     EMSCRIPTEN_JoystickGetDevicePath,
     EMSCRIPTEN_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c
index 230f5c96ff8d..c90635e6611f 100644
--- a/src/joystick/gdk/SDL_gameinputjoystick.c
+++ b/src/joystick/gdk/SDL_gameinputjoystick.c
@@ -24,11 +24,6 @@
 
 #if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT
 
-/* Set up for C function definitions, even when using C++ */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /* Public APIs: GAMEINPUT_Joystick... */
 /* Private APIs: GAMEINPUT_InternalJoystick... */
 
@@ -38,6 +33,8 @@ typedef struct GAMEINPUT_InternalDevice
 {
     IGameInputDevice *device;
     const char *deviceName;        /* this is a constant string literal */
+    Uint16 vendor;
+    Uint16 product;
     SDL_JoystickGUID joystickGuid; /* generated by SDL. */
     SDL_JoystickID instanceId;     /* generated by SDL. */
     int playerIndex;
@@ -71,6 +68,10 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
     GAMEINPUT_InternalDevice **devicelist = NULL;
     GAMEINPUT_InternalDevice *elem = NULL;
     const GameInputDeviceInfo *devinfo = NULL;
+    Uint16 bus = SDL_HARDWARE_BUS_USB;
+    Uint16 vendor = 0;
+    Uint16 product = 0;
+    Uint16 version = 0;
     char tmpbuff[4];
     int idx = 0;
 
@@ -96,33 +97,34 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
         return SDL_OutOfMemory();
     }
 
+    devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(elem) * (g_GameInputList.count + 1LL));
+    if (!devicelist) {
+        SDL_free(elem);
+        return SDL_OutOfMemory();
+    }
+
     /* generate a device name */
     for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) {
         SDL_snprintf(tmpbuff, SDL_arraysize(tmpbuff), "%02hhX", devinfo->deviceId.value[idx]);
         SDL_strlcat(elem->devicePath, tmpbuff, SDL_arraysize(tmpbuff));
     }
-
-    devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(elem) * (g_GameInputList.count + 1LL));
-    if (!devicelist) {
-        SDL_free(elem);
-        return SDL_OutOfMemory();
+    if (devinfo->capabilities & GameInputDeviceCapabilityWireless) {
+        bus = SDL_HARDWARE_BUS_BLUETOOTH;
+    } else {
+        bus = SDL_HARDWARE_BUS_USB;
     }
+    vendor = devinfo->vendorId;
+    product = devinfo->productId;
+    version = (devinfo->major << 8) | devinfo->minor;
 
     g_GameInputList.devices = devicelist;
     IGameInputDevice_AddRef(pDevice);
     elem->device = pDevice;
     elem->deviceName = "GameInput Gamepad";
+    elem->vendor = vendor;
+    elem->product = product;
     elem->supportedRumbleMotors = devinfo->supportedRumbleMotors;
-    elem->joystickGuid = SDL_CreateJoystickGUID(
-        SDL_HARDWARE_BUS_BLUETOOTH,
-        USB_VENDOR_MICROSOFT,
-        USB_PRODUCT_XBOX_SERIES_X_BLE,
-        1,
-        "GameInput",
-        "Gamepad",
-        'g',
-        0
-    );
+    elem->joystickGuid = SDL_CreateJoystickGUID(bus, vendor, product, version, "GameInput", "Gamepad", 'g', 0);
     elem->instanceId = SDL_GetNextObjectID();
     g_GameInputList.devices[g_GameInputList.count] = elem;
 
@@ -274,6 +276,20 @@ static void GAMEINPUT_JoystickDetect(void)
     }
 }
 
+static SDL_bool GAMEINPUT_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    int idx = 0;
+    GAMEINPUT_InternalDevice *elem = NULL;
+
+    for (idx = 0; idx < g_GameInputList.count; ++idx) {
+        elem = g_GameInputList.devices[idx];
+        if (elem && vendor_id == elem->vendor && product_id == elem->product) {
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
+
 static const char *GAMEINPUT_JoystickGetDeviceName(int device_index)
 {
     GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index);
@@ -312,7 +328,7 @@ static int GAMEINPUT_JoystickGetDevicePlayerIndex(int device_index)
      * you're meant to assign some index to a player yourself.
      *
      * GameMaker, for example, seems to do this in the order of plugging in.
-     * 
+     *
      * Sorry for the trouble!
      */
     GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index);
@@ -566,6 +582,7 @@ SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver =
     GAMEINPUT_JoystickInit,
     GAMEINPUT_JoystickGetCount,
     GAMEINPUT_JoystickDetect,
+    GAMEINPUT_JoystickIsDevicePresent,
     GAMEINPUT_JoystickGetDeviceName,
     GAMEINPUT_JoystickGetDevicePath,
     GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot,
@@ -586,9 +603,4 @@ SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver =
 };
 
 
-/* Ends C function definitions when using C++ */
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */
diff --git a/src/joystick/haiku/SDL_haikujoystick.cc b/src/joystick/haiku/SDL_haikujoystick.cc
index 77961036ecb3..db40918c047f 100644
--- a/src/joystick/haiku/SDL_haikujoystick.cc
+++ b/src/joystick/haiku/SDL_haikujoystick.cc
@@ -91,6 +91,12 @@ extern "C"
     {
     }
 
+    static SDL_bool HAIKU_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+    {
+        /* We don't override any other drivers */
+        return SDL_FALSE;
+    }
+
     static const char *HAIKU_JoystickGetDeviceName(int device_index)
     {
         return SDL_joyname[device_index];
@@ -293,6 +299,7 @@ extern "C"
         HAIKU_JoystickInit,
         HAIKU_JoystickGetCount,
         HAIKU_JoystickDetect,
+        HAIKU_JoystickIsDevicePresent,
         HAIKU_JoystickGetDeviceName,
         HAIKU_JoystickGetDevicePath,
         HAIKU_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index e0a9adb59858..0fadbe4aa0e3 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -1699,6 +1699,7 @@ SDL_JoystickDriver SDL_HIDAPI_JoystickDriver = {
     HIDAPI_JoystickInit,
     HIDAPI_JoystickGetCount,
     HIDAPI_JoystickDetect,
+    HIDAPI_IsDevicePresent,
     HIDAPI_JoystickGetDeviceName,
     HIDAPI_JoystickGetDevicePath,
     HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c
index 0c8573a1a732..17935745bb0c 100644
--- a/src/joystick/linux/SDL_sysjoystick.c
+++ b/src/joystick/linux/SDL_sysjoystick.c
@@ -310,14 +310,11 @@ static int IsJoystick(const char *path, int fd, char **name_return, Uint16 *vend
         return 0;
     }
 
-#ifdef SDL_JOYSTICK_HIDAPI
     if (!IsVirtualJoystick(inpid.vendor, inpid.product, inpid.version, name) &&
-        HIDAPI_IsDevicePresent(inpid.vendor, inpid.product, inpid.version, name)) {
-        /* The HIDAPI driver is taking care of this device */
+        SDL_JoystickHandledByAnotherDriver(&SDL_LINUX_JoystickDriver, inpid.vendor, inpid.product, inpid.version, name)) {
         SDL_free(name);
         return 0;
     }
-#endif
 
     FixupDeviceInfoForMapping(fd, &inpid);
 
@@ -987,6 +984,12 @@ static void LINUX_JoystickDetect(void)
     SDL_UpdateSteamControllers();
 }
 
+static SDL_bool LINUX_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers */
+    return SDL_FALSE;
+}
+
 static int LINUX_JoystickInit(void)
 {
     const char *devices = SDL_GetHint(SDL_HINT_JOYSTICK_DEVICE);
@@ -2686,6 +2689,7 @@ SDL_JoystickDriver SDL_LINUX_JoystickDriver = {
     LINUX_JoystickInit,
     LINUX_JoystickGetCount,
     LINUX_JoystickDetect,
+    LINUX_JoystickIsDevicePresent,
     LINUX_JoystickGetDeviceName,
     LINUX_JoystickGetDevicePath,
     LINUX_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/n3ds/SDL_sysjoystick.c b/src/joystick/n3ds/SDL_sysjoystick.c
index 863a1c174782..271b2627fdb8 100644
--- a/src/joystick/n3ds/SDL_sysjoystick.c
+++ b/src/joystick/n3ds/SDL_sysjoystick.c
@@ -226,6 +226,12 @@ static void N3DS_JoystickDetect(void)
 {
 }
 
+static SDL_bool N3DS_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers */
+    return SDL_FALSE;
+}
+
 static const char *N3DS_JoystickGetDevicePath(int device_index)
 {
     return NULL;
@@ -266,26 +272,27 @@ static int N3DS_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int
 }
 
 SDL_JoystickDriver SDL_N3DS_JoystickDriver = {
-    .Init = N3DS_JoystickInit,
-    .GetCount = N3DS_JoystickGetCount,
-    .Detect = N3DS_JoystickDetect,
-    .GetDeviceName = N3DS_JoystickGetDeviceName,
-    .GetDevicePath = N3DS_JoystickGetDevicePath,
-    .GetDeviceSteamVirtualGamepadSlot = N3DS_JoystickGetDeviceSteamVirtualGamepadSlot,
-    .GetDevicePlayerIndex = N3DS_JoystickGetDevicePlayerIndex,
-    .SetDevicePlayerIndex = N3DS_JoystickSetDevicePlayerIndex,
-    .GetDeviceGUID = N3DS_JoystickGetDeviceGUID,
-    .GetDeviceInstanceID = N3DS_JoystickGetDeviceInstanceID,
-    .Open = N3DS_JoystickOpen,
-    .Rumble = N3DS_JoystickRumble,
-    .RumbleTriggers = N3DS_JoystickRumbleTriggers,
-    .SetLED = N3DS_JoystickSetLED,
-    .SendEffect = N3DS_JoystickSendEffect,
-    .SetSensorsEnabled = N3DS_JoystickSetSensorsEnabled,
-    .Update = N3DS_JoystickUpdate,
-    .Close = N3DS_JoystickClose,
-    .Quit = N3DS_JoystickQuit,
-    .GetGamepadMapping = N3DS_JoystickGetGamepadMapping
+    N3DS_JoystickInit,
+    N3DS_JoystickGetCount,
+    N3DS_JoystickDetect,
+    N3DS_JoystickIsDevicePresent,
+    N3DS_JoystickGetDeviceName,
+    N3DS_JoystickGetDevicePath,
+    N3DS_JoystickGetDeviceSteamVirtualGamepadSlot,
+    N3DS_JoystickGetDevicePlayerIndex,
+    N3DS_JoystickSetDevicePlayerIndex,
+    N3DS_JoystickGetDeviceGUID,
+    N3DS_JoystickGetDeviceInstanceID,
+    N3DS_JoystickOpen,
+    N3DS_JoystickRumble,
+    N3DS_JoystickRumbleTriggers,
+    N3DS_JoystickSetLED,
+    N3DS_JoystickSendEffect,
+    N3DS_JoystickSetSensorsEnabled,
+    N3DS_JoystickUpdate,
+    N3DS_JoystickClose,
+    N3DS_JoystickQuit,
+    N3DS_JoystickGetGamepadMapping
 };
 
 #endif /* SDL_JOYSTICK_N3DS */
diff --git a/src/joystick/ps2/SDL_sysjoystick.c b/src/joystick/ps2/SDL_sysjoystick.c
index ad264584e6c6..cf54852f1501 100644
--- a/src/joystick/ps2/SDL_sysjoystick.c
+++ b/src/joystick/ps2/SDL_sysjoystick.c
@@ -140,6 +140,12 @@ static void PS2_JoystickDetect()
 {
 }
 
+static SDL_bool PS2_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers */
+    return SDL_FALSE;
+}
+
 /* Function to get the device-dependent name of a joystick */
 static const char *PS2_JoystickGetDeviceName(int index)
 {
@@ -341,6 +347,7 @@ SDL_JoystickDriver SDL_PS2_JoystickDriver = {
     PS2_JoystickInit,
     PS2_JoystickGetCount,
     PS2_JoystickDetect,
+    PS2_JoystickIsDevicePresent,
     PS2_JoystickGetDeviceName,
     PS2_JoystickGetDevicePath,
     PS2_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/psp/SDL_sysjoystick.c b/src/joystick/psp/SDL_sysjoystick.c
index 2ae252865b99..31cbb8a7129a 100644
--- a/src/joystick/psp/SDL_sysjoystick.c
+++ b/src/joystick/psp/SDL_sysjoystick.c
@@ -105,6 +105,12 @@ static void PSP_JoystickDetect(void)
 {
 }
 
+static SDL_bool PSP_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers */
+    return SDL_FALSE;
+}
+
 /* Function to get the device-dependent name of a joystick */
 static const char *PSP_JoystickGetDeviceName(int device_index)
 {
@@ -251,6 +257,7 @@ SDL_JoystickDriver SDL_PSP_JoystickDriver = {
     PSP_JoystickInit,
     PSP_JoystickGetCount,
     PSP_JoystickDetect,
+    PSP_JoystickIsDevicePresent,
     PSP_JoystickGetDeviceName,
     PSP_JoystickGetDevicePath,
     PSP_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c
index 526ec24cc9bf..6c014673f09b 100644
--- a/src/joystick/virtual/SDL_virtualjoystick.c
+++ b/src/joystick/virtual/SDL_virtualjoystick.c
@@ -353,6 +353,12 @@ static void VIRTUAL_JoystickDetect(void)
 {
 }
 
+static SDL_bool VIRTUAL_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers... or do we? */
+    return SDL_FALSE;
+}
+
 static const char *VIRTUAL_JoystickGetDeviceName(int device_index)
 {
     joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
@@ -749,6 +755,7 @@ SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver = {
     VIRTUAL_JoystickInit,
     VIRTUAL_JoystickGetCount,
     VIRTUAL_JoystickDetect,
+    VIRTUAL_JoystickIsDevicePresent,
     VIRTUAL_JoystickGetDeviceName,
     VIRTUAL_JoystickGetDevicePath,
     VIRTUAL_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/vita/SDL_sysjoystick.c b/src/joystick/vita/SDL_sysjoystick.c
index 35ec7ebdd89a..58fef1788419 100644
--- a/src/joystick/vita/SDL_sysjoystick.c
+++ b/src/joystick/vita/SDL_sysjoystick.c
@@ -101,7 +101,7 @@ static int calc_bezier_y(float t)
  * Joystick 0 should be the system default joystick.
  * It should return number of joysticks, or -1 on an unrecoverable fatal error.
  */
-int VITA_JoystickInit(void)
+static int VITA_JoystickInit(void)
 {
     int i;
     SceCtrlPortInfo myPortInfo;
@@ -139,22 +139,28 @@ int VITA_JoystickInit(void)
     return SDL_numjoysticks;
 }
 
-int VITA_JoystickGetCount()
+static int VITA_JoystickGetCount()
 {
     return SDL_numjoysticks;
 }
 
-void VITA_JoystickDetect()
+static void VITA_JoystickDetect()
 {
 }
 
+static SDL_bool VITA_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
+{
+    /* We don't override any other drivers */
+    return SDL_FALSE;
+}
+
 /* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID VITA_JoystickGetDeviceInstanceID(int device_index)
+static SDL_JoystickID VITA_JoystickGetDeviceInstanceID(int device_index)
 {
     return device_index + 1;
 }
 
-const char *VITA_JoystickGetDeviceName(int index)
+static const char *VITA_JoystickGetDeviceName(int index)
 {
     if (index == 0) {
         return "PSVita Controller";
@@ -176,7 +182,7 @@ const char *VITA_JoystickGetDeviceName(int index)
     return NULL;
 }
 
-const char *VITA_JoystickGetDevicePath(int index)
+static const char *VITA_JoystickGetDevicePath(int index)
 {
     return NULL;
 }
@@ -200,7 +206,7 @@ static void VITA_JoystickSetDevicePlayerIndex(int device_index, int player_index
    This should fill the nbuttons and naxes fields of the joystick structure.
    It returns 0, or -1 if there is an error.
  */
-int VITA_JoystickOpen(SDL_Joystick *joystick, int device_index)
+static int VITA_JoystickOpen(SDL_Joystick *joystick, int device_index)
 {
     joystick->nbuttons = SDL_arraysize(ext_button_map);
     joystick->naxes = 6;
@@ -309,16 +315,16 @@ static void VITA_JoystickUpdate(SDL_Joystick *joystick)
 }
 
 /* Function to close a joystick after use */
-void VITA_JoystickClose(SDL_Joystick *joystick)
+static void VITA_JoystickClose(SDL_Joystick *joystick)
 {
 }
 
 /* Function to perform any system-specific joystick related cleanup */
-void VITA_JoystickQuit(void)
+static void VITA_JoystickQuit(void)
 {
 }
 
-SDL_JoystickGUID VITA_JoystickGetDeviceGUID(int device_index)
+static SDL_JoystickGUID VITA_JoystickGetDeviceGUID(int device_index)
 {
     /* the GUID is just the name for now */
     const char *name = VITA_JoystickGetDeviceName(device_index);
@@ -378,6 +384,7 @@ SDL_JoystickDriver SDL_VITA_JoystickDriver = {
     VITA_JoystickInit,
     VITA_JoystickGetCount,
     VITA_JoystickDetect,
+    VITA_JoystickIsDevicePresent,
     VITA_JoystickGetDeviceName,
     VITA_JoystickGetDevicePath,
     VITA_JoystickGetDeviceSteamVirtualGamepadSlot,
diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c
index 150f59be22e6..08d9fdebc134 100644
--- a/src/joystick/windows/SDL_dinputjoystick.c
+++ b/src/joystick/windows/SDL_dinputjoystick.c
@@ -516,13 +516,7 @@ static BOOL CALLBACK EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInsta
 
     CHECK(!SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid));
 
-#ifdef SDL_JOYSTICK_HIDAPI
-    CHECK(!HIDAPI_IsDevicePresent(vendor, product, version, pNewJoystick->joystickname));
-#endif
-
-#ifdef SDL_JOYSTICK_RAWINPUT
-    CHECK(!RAWINPUT_IsDevicePresent(vendor, product, version, pNewJoystick->joystickname));
-#endif
+    CHECK(!SDL_JoystickHandledByAnotherDriver(&SDL_WINDOWS_JoystickDriver, vendor, product, version, pNewJoystick->joystickname));
 
     WINDOWS_AddJoystickDevice(pNewJoystick);
     pNewJoystick = NULL;
diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c
index 671e3a376d39..e59df1d9740f 100644
--- a/src/joystick/windows/SDL_rawinputjoystick.c
+++ b/src/joystick/windows/SDL_rawinputjoystick.c
@@ -886,10 +886,7 @@ static void RAWINPUT_AddDevice(HANDLE hDevice)
     CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICENAME, dev_name, &size) != (UINT)-1);
     /* Only take XInput-capable devices */
     CHECK(SDL_strstr(dev_name, "IG_") != NULL);
-#ifdef SDL_JOYSTICK_HIDAPI
-    /* Don't take devices handled by HIDAPI */
-    CHECK(!HIDAPI_IsDevicePresent((Uint16)rdi.hid.dwVendorId, (Uint16)rdi.hid.dwProductId, (Uint16)rdi.hid.dwVersionNumber, ""));
-#endif
+    CHECK(!SDL_JoystickHandledByAnotherDriver(&SDL_RAWINPUT_JoystickDriver, (Uint16)rdi.hid.dwVendorId, (Uint16)rdi.hid.dwProductId, (Uint16)rdi.hid.dwVersionNumber, ""));
     device = (SDL_RAWINPUT_Device *)SDL_calloc(1, sizeof(SDL_RAWINPUT_Device));
     CHECK(device);
     device->hDevice = hDevice;
@@ -1062,42 +1059,6 @@ SDL_bool RAWINPUT_IsEnabled()
     return SDL_RAWINPUT_inited && !SDL_RAWINPUT_remote_desktop;
 

(Patch may be truncated, please check the link at the top of this post.)