SDL: Added functions to get the platform dependent name for a joystick or game controller

From e551384a995a82107f97db355fb5653a5a0645d6 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 26 Apr 2022 14:54:14 -0700
Subject: [PATCH] Added functions to get the platform dependent name for a
 joystick or game controller

---
 WhatsNew.txt                                  |  5 ++
 include/SDL_gamecontroller.h                  | 36 ++++++++++++++
 include/SDL_joystick.h                        | 31 ++++++++++++
 src/dynapi/SDL_dynapi_overrides.h             |  4 ++
 src/dynapi/SDL_dynapi_procs.h                 |  4 ++
 src/joystick/SDL_gamecontroller.c             | 23 +++++++++
 src/joystick/SDL_joystick.c                   | 48 +++++++++++++++++++
 src/joystick/SDL_sysjoystick.h                |  4 ++
 src/joystick/android/SDL_sysjoystick.c        |  7 +++
 src/joystick/bsd/SDL_bsdjoystick.c            | 11 ++++-
 src/joystick/darwin/SDL_iokitjoystick.c       |  8 +++-
 src/joystick/dummy/SDL_sysjoystick.c          |  7 +++
 src/joystick/emscripten/SDL_sysjoystick.c     |  7 +++
 src/joystick/haiku/SDL_haikujoystick.cc       | 17 ++++---
 src/joystick/hidapi/SDL_hidapijoystick.c      | 16 +++++++
 src/joystick/iphoneos/SDL_mfijoystick.m       |  7 +++
 src/joystick/linux/SDL_sysjoystick.c          |  8 +++-
 src/joystick/os2/SDL_os2joystick.c            |  9 ++--
 src/joystick/psp/SDL_sysjoystick.c            |  8 +++-
 src/joystick/virtual/SDL_virtualjoystick.c    |  9 +++-
 src/joystick/vita/SDL_sysjoystick.c           |  9 +++-
 src/joystick/windows/SDL_dinputjoystick.c     |  4 +-
 src/joystick/windows/SDL_rawinputjoystick.c   | 16 ++++++-
 .../windows/SDL_windows_gaming_input.c        |  7 +++
 src/joystick/windows/SDL_windowsjoystick.c    | 14 +++++-
 src/joystick/windows/SDL_windowsjoystick_c.h  |  2 +-
 src/joystick/windows/SDL_xinputjoystick.c     |  1 +
 test/testgamecontroller.c                     | 10 ++--
 28 files changed, 306 insertions(+), 26 deletions(-)

diff --git a/WhatsNew.txt b/WhatsNew.txt
index 51e5e27c0d8..727ccc60b73 100644
--- a/WhatsNew.txt
+++ b/WhatsNew.txt
@@ -7,6 +7,11 @@ This is a list of major changes in SDL's version history.
 
 General:
 * Added SDL_bsearch() to the stdlib routines
+* Added functions to get the platform dependent name for a joystick or game controller:
+    * SDL_JoystickPathForIndex()
+    * SDL_JoystickPath()
+    * SDL_GameControllerPathForIndex()
+    * SDL_GameControllerPath()
 * Added joystick event SDL_JOYBATTERYUPDATED for when battery status changes.
 
 ---------------------------------------------------------------------------
diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h
index 548861083cd..6cb103e9265 100644
--- a/include/SDL_gamecontroller.h
+++ b/include/SDL_gamecontroller.h
@@ -289,6 +289,25 @@ extern DECLSPEC SDL_bool SDLCALL SDL_IsGameController(int joystick_index);
  */
 extern DECLSPEC const char *SDLCALL SDL_GameControllerNameForIndex(int joystick_index);
 
+/**
+ * Get the implementation dependent path for the game controller.
+ *
+ * This function can be called before any controllers are opened.
+ *
+ * `joystick_index` is the same as the `device_index` passed to
+ * SDL_JoystickOpen().
+ *
+ * \param joystick_index the device_index of a device, from zero to
+ *                       SDL_NumJoysticks()-1
+ * \returns the implementation-dependent path for the game controller, or NULL
+ *          if there is no path or the index is invalid.
+ *
+ * \since This function is available since SDL 2.0.24.
+ *
+ * \sa SDL_GameControllerPath
+ */
+extern DECLSPEC const char *SDLCALL SDL_GameControllerPathForIndex(int joystick_index);
+
 /**
  * Get the type of a game controller.
  *
@@ -386,6 +405,23 @@ extern DECLSPEC SDL_GameController *SDLCALL SDL_GameControllerFromPlayerIndex(in
  */
 extern DECLSPEC const char *SDLCALL SDL_GameControllerName(SDL_GameController *gamecontroller);
 
+/**
+ * Get the implementation-dependent path for an opened game controller.
+ *
+ * This is the same path as returned by SDL_GameControllerNameForIndex(), but
+ * it takes a controller identifier instead of the (unstable) device index.
+ *
+ * \param gamecontroller a game controller identifier previously returned by
+ *                       SDL_GameControllerOpen()
+ * \returns the implementation dependent path for the game controller, or NULL
+ *          if there is no path or the identifier passed is invalid.
+ *
+ * \since This function is available since SDL 2.0.24.
+ *
+ * \sa SDL_GameControllerPathForIndex
+ */
+extern DECLSPEC const char *SDLCALL SDL_GameControllerPath(SDL_GameController *gamecontroller);
+
 /**
  * Get the type of this currently opened controller
  *
diff --git a/include/SDL_joystick.h b/include/SDL_joystick.h
index 07e2b156154..1aa130f1a32 100644
--- a/include/SDL_joystick.h
+++ b/include/SDL_joystick.h
@@ -153,6 +153,7 @@ extern DECLSPEC void SDLCALL SDL_UnlockJoysticks(void);
  * \since This function is available since SDL 2.0.0.
  *
  * \sa SDL_JoystickName
+ * \sa SDL_JoystickPath
  * \sa SDL_JoystickOpen
  */
 extern DECLSPEC int SDLCALL SDL_NumJoysticks(void);
@@ -174,6 +175,23 @@ extern DECLSPEC int SDLCALL SDL_NumJoysticks(void);
  */
 extern DECLSPEC const char *SDLCALL SDL_JoystickNameForIndex(int device_index);
 
+/**
+ * Get the implementation dependent path of a joystick.
+ *
+ * This can be called before any joysticks are opened.
+ *
+ * \param device_index the index of the joystick to query (the N'th joystick
+ *                     on the system)
+ * \returns the path of the selected joystick. If no path can be found, this
+ *          function returns NULL; call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 2.0.24.
+ *
+ * \sa SDL_JoystickPath
+ * \sa SDL_JoystickOpen
+ */
+extern DECLSPEC const char *SDLCALL SDL_JoystickPathForIndex(int device_index);
+
 /**
  * Get the player index of a joystick, or -1 if it's not available This can be
  * called before any joysticks are opened.
@@ -419,6 +437,19 @@ extern DECLSPEC int SDLCALL SDL_JoystickSetVirtualHat(SDL_Joystick *joystick, in
  */
 extern DECLSPEC const char *SDLCALL SDL_JoystickName(SDL_Joystick *joystick);
 
+/**
+ * Get the implementation dependent path of a joystick.
+ *
+ * \param joystick the SDL_Joystick obtained from SDL_JoystickOpen()
+ * \returns the path of the selected joystick. If no path can be found, this
+ *          function returns NULL; call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 2.0.24.
+ *
+ * \sa SDL_JoystickPathForIndex
+ */
+extern DECLSPEC const char *SDLCALL SDL_JoystickPath(SDL_Joystick *joystick);
+
 /**
  * Get the player index of an opened joystick.
  *
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index ea01ac97471..90b2254edfb 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -866,3 +866,7 @@
 #define SDL_IntersectFRectAndLine SDL_IntersectFRectAndLine_REAL
 #define SDL_RenderGetWindow SDL_RenderGetWindow_REAL
 #define SDL_bsearch SDL_bsearch_REAL
+#define SDL_GameControllerPathForIndex SDL_GameControllerPathForIndex_REAL
+#define SDL_GameControllerPath SDL_GameControllerPath_REAL
+#define SDL_JoystickPathForIndex SDL_JoystickPathForIndex_REAL
+#define SDL_JoystickPath SDL_JoystickPath_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 9f07b6bc7f2..f78c0e4236b 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -937,3 +937,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_EncloseFPoints,(const SDL_FPoint *a, int b, const S
 SDL_DYNAPI_PROC(SDL_bool,SDL_IntersectFRectAndLine,(const SDL_FRect *a, float *b, float *c, float *d, float *e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_RenderGetWindow,(SDL_Renderer *a),(a),return)
 SDL_DYNAPI_PROC(void*,SDL_bsearch,(const void *a, const void *b, size_t c, size_t d, int (*e)(const void *, const void *)),(a,b,c,d,e),return)
+SDL_DYNAPI_PROC(const char*,SDL_GameControllerPathForIndex,(int a),(a),return)
+SDL_DYNAPI_PROC(const char*,SDL_GameControllerPath,(SDL_GameController *a),(a),return)
+SDL_DYNAPI_PROC(const char*,SDL_JoystickPathForIndex,(int a),(a),return)
+SDL_DYNAPI_PROC(const char*,SDL_JoystickPath,(SDL_Joystick *a),(a),return)
diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index e27922a6d2c..fad7d9a0931 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -1732,6 +1732,20 @@ SDL_GameControllerNameForIndex(int device_index)
 }
 
 
+/*
+ * Get the implementation dependent path of a controller
+ */
+const char *
+SDL_GameControllerPathForIndex(int device_index)
+{
+    ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index);
+    if (pSupportedController) {
+        return SDL_JoystickPathForIndex(device_index);
+    }
+    return NULL;
+}
+
+
 /**
  *  Get the type of a game controller.
  */
@@ -2294,6 +2308,15 @@ SDL_GameControllerName(SDL_GameController *gamecontroller)
     }
 }
 
+const char *
+SDL_GameControllerPath(SDL_GameController *gamecontroller)
+{
+    if (!gamecontroller)
+        return NULL;
+
+    return SDL_JoystickPath(SDL_GameControllerGetJoystick(gamecontroller));
+}
+
 SDL_GameControllerType
 SDL_GameControllerGetType(SDL_GameController *gamecontroller)
 {
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 65c8b77329d..e5ec5b7f855 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -320,6 +320,28 @@ SDL_JoystickNameForIndex(int device_index)
     return name;
 }
 
+/*
+ * Get the implementation dependent path of a joystick
+ */
+const char *
+SDL_JoystickPathForIndex(int device_index)
+{
+    SDL_JoystickDriver *driver;
+    const char *path = NULL;
+
+    SDL_LockJoysticks();
+    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+        path = driver->GetDevicePath(device_index);
+    }
+    SDL_UnlockJoysticks();
+
+    /* FIXME: Really we should reference count this path so it doesn't go away after unlock */
+    if (!path) {
+        SDL_Unsupported();
+    }
+    return path;
+}
+
 /*
  *  Get the player index of a joystick, or -1 if it's not available
  */
@@ -386,6 +408,7 @@ SDL_JoystickOpen(int device_index)
     SDL_Joystick *joystick;
     SDL_Joystick *joysticklist;
     const char *joystickname = NULL;
+    const char *joystickpath = NULL;
     SDL_JoystickPowerLevel initial_power_level;
 
     SDL_LockJoysticks();
@@ -436,6 +459,13 @@ SDL_JoystickOpen(int device_index)
         joystick->name = NULL;
     }
 
+    joystickpath = driver->GetDevicePath(device_index);
+    if (joystickpath) {
+        joystick->path = SDL_strdup(joystickpath);
+    } else {
+        joystick->path = NULL;
+    }
+
     joystick->guid = driver->GetDeviceGUID(device_index);
 
     if (joystick->naxes > 0) {
@@ -840,6 +870,23 @@ SDL_JoystickName(SDL_Joystick *joystick)
     return joystick->name;
 }
 
+/*
+ * Get the implementation dependent path of this joystick
+ */
+const char *
+SDL_JoystickPath(SDL_Joystick *joystick)
+{
+    if (!SDL_PrivateJoystickValid(joystick)) {
+        return NULL;
+    }
+
+    if (!joystick->path) {
+        SDL_Unsupported();
+        return NULL;
+    }
+    return joystick->path;
+}
+
 /**
  *  Get the player index of an opened joystick, or -1 if it's not available
  */
@@ -1106,6 +1153,7 @@ SDL_JoystickClose(SDL_Joystick *joystick)
     }
 
     SDL_free(joystick->name);
+    SDL_free(joystick->path);
     SDL_free(joystick->serial);
 
     /* Free the data associated with this joystick */
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index 8b879217990..37025ddde42 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -65,6 +65,7 @@ struct _SDL_Joystick
 {
     SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */
     char *name;                 /* Joystick name - system dependent */
+    char *path;                 /* Joystick path - system dependent */
     char *serial;               /* Joystick serial */
     SDL_JoystickGUID guid;      /* Joystick guid */
 
@@ -146,6 +147,9 @@ typedef struct _SDL_JoystickDriver
     /* Function to get the device-dependent name of a joystick */
     const char *(*GetDeviceName)(int device_index);
 
+    /* Function to get the device-dependent path of a joystick */
+    const char *(*GetDevicePath)(int device_index);
+
     /* Function to get the player index of a joystick */
     int (*GetDevicePlayerIndex)(int device_index);
 
diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c
index aef014d1685..104724afd89 100644
--- a/src/joystick/android/SDL_sysjoystick.c
+++ b/src/joystick/android/SDL_sysjoystick.c
@@ -558,6 +558,12 @@ ANDROID_JoystickGetDeviceName(int device_index)
     return JoystickByDevIndex(device_index)->name;
 }
 
+static const char *
+ANDROID_JoystickGetDevicePath(int device_index)
+{
+    return NULL;
+}
+
 static int
 ANDROID_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -713,6 +719,7 @@ SDL_JoystickDriver SDL_ANDROID_JoystickDriver =
     ANDROID_JoystickGetCount,
     ANDROID_JoystickDetect,
     ANDROID_JoystickGetDeviceName,
+    ANDROID_JoystickGetDevicePath,
     ANDROID_JoystickGetDevicePlayerIndex,
     ANDROID_JoystickSetDevicePlayerIndex,
     ANDROID_JoystickGetDeviceGUID,
diff --git a/src/joystick/bsd/SDL_bsdjoystick.c b/src/joystick/bsd/SDL_bsdjoystick.c
index f8859a5341b..d7b4d473084 100644
--- a/src/joystick/bsd/SDL_bsdjoystick.c
+++ b/src/joystick/bsd/SDL_bsdjoystick.c
@@ -268,9 +268,15 @@ static const char *
 BSD_JoystickGetDeviceName(int device_index)
 {
     if (joydevnames[device_index] != NULL) {
-        return (joydevnames[device_index]);
+        return joydevnames[device_index];
     }
-    return (joynames[device_index]);
+    return joynames[device_index];
+}
+
+static const char *
+BSD_JoystickGetDevicePath(int device_index)
+{
+    return joynames[device_index];
 }
 
 static int
@@ -807,6 +813,7 @@ SDL_JoystickDriver SDL_BSD_JoystickDriver =
     BSD_JoystickGetCount,
     BSD_JoystickDetect,
     BSD_JoystickGetDeviceName,
+    BSD_JoystickGetDevicePath,
     BSD_JoystickGetDevicePlayerIndex,
     BSD_JoystickSetDevicePlayerIndex,
     BSD_JoystickGetDeviceGUID,
diff --git a/src/joystick/darwin/SDL_iokitjoystick.c b/src/joystick/darwin/SDL_iokitjoystick.c
index 7f2514728f7..91072c52e0e 100644
--- a/src/joystick/darwin/SDL_iokitjoystick.c
+++ b/src/joystick/darwin/SDL_iokitjoystick.c
@@ -744,7 +744,6 @@ DARWIN_JoystickDetect(void)
     }
 }
 
-/* Function to get the device-dependent name of a joystick */
 const char *
 DARWIN_JoystickGetDeviceName(int device_index)
 {
@@ -752,6 +751,12 @@ DARWIN_JoystickGetDeviceName(int device_index)
     return device ? device->product : "UNKNOWN";
 }
 
+const char *
+DARWIN_JoystickGetDevicePath(int device_index)
+{
+    return NULL;
+}
+
 static int
 DARWIN_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -1115,6 +1120,7 @@ SDL_JoystickDriver SDL_DARWIN_JoystickDriver =
     DARWIN_JoystickGetCount,
     DARWIN_JoystickDetect,
     DARWIN_JoystickGetDeviceName,
+    DARWIN_JoystickGetDevicePath,
     DARWIN_JoystickGetDevicePlayerIndex,
     DARWIN_JoystickSetDevicePlayerIndex,
     DARWIN_JoystickGetDeviceGUID,
diff --git a/src/joystick/dummy/SDL_sysjoystick.c b/src/joystick/dummy/SDL_sysjoystick.c
index ec1c8e6517f..3cf8da5f982 100644
--- a/src/joystick/dummy/SDL_sysjoystick.c
+++ b/src/joystick/dummy/SDL_sysjoystick.c
@@ -52,6 +52,12 @@ DUMMY_JoystickGetDeviceName(int device_index)
     return NULL;
 }
 
+static const char *
+DUMMY_JoystickGetDevicePath(int device_index)
+{
+    return NULL;
+}
+
 static int
 DUMMY_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -146,6 +152,7 @@ SDL_JoystickDriver SDL_DUMMY_JoystickDriver =
     DUMMY_JoystickGetCount,
     DUMMY_JoystickDetect,
     DUMMY_JoystickGetDeviceName,
+    DUMMY_JoystickGetDevicePath,
     DUMMY_JoystickGetDevicePlayerIndex,
     DUMMY_JoystickSetDevicePlayerIndex,
     DUMMY_JoystickGetDeviceGUID,
diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c
index eb0ff2a74ed..1664a907cf1 100644
--- a/src/joystick/emscripten/SDL_sysjoystick.c
+++ b/src/joystick/emscripten/SDL_sysjoystick.c
@@ -280,6 +280,12 @@ EMSCRIPTEN_JoystickGetDeviceName(int device_index)
     return JoystickByDeviceIndex(device_index)->name;
 }
 
+static const char *
+EMSCRIPTEN_JoystickGetDevicePath(int device_index)
+{
+    return NULL;
+}
+
 static int
 EMSCRIPTEN_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -444,6 +450,7 @@ SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver =
     EMSCRIPTEN_JoystickGetCount,
     EMSCRIPTEN_JoystickDetect,
     EMSCRIPTEN_JoystickGetDeviceName,
+    EMSCRIPTEN_JoystickGetDevicePath,
     EMSCRIPTEN_JoystickGetDevicePlayerIndex,
     EMSCRIPTEN_JoystickSetDevicePlayerIndex,
     EMSCRIPTEN_JoystickGetDeviceGUID,
diff --git a/src/joystick/haiku/SDL_haikujoystick.cc b/src/joystick/haiku/SDL_haikujoystick.cc
index 38a975227b4..c0c6e012cf9 100644
--- a/src/joystick/haiku/SDL_haikujoystick.cc
+++ b/src/joystick/haiku/SDL_haikujoystick.cc
@@ -64,15 +64,15 @@ extern "C"
         char name[B_OS_NAME_LENGTH];
 
         /* Search for attached joysticks */
-          nports = joystick.CountDevices();
-          numjoysticks = 0;
-          SDL_memset(SDL_joyport, 0, (sizeof SDL_joyport));
-          SDL_memset(SDL_joyname, 0, (sizeof SDL_joyname));
+        nports = joystick.CountDevices();
+        numjoysticks = 0;
+        SDL_memset(SDL_joyport, 0, (sizeof SDL_joyport));
+        SDL_memset(SDL_joyname, 0, (sizeof SDL_joyname));
         for (i = 0; (numjoysticks < MAX_JOYSTICKS) && (i < nports); ++i)
         {
             if (joystick.GetDeviceName(i, name) == B_OK) {
                 if (joystick.Open(name) != B_ERROR) {
-                    BString stick_name;
+                      BString stick_name;
                       joystick.GetControllerName(&stick_name);
                       SDL_joyport[numjoysticks] = SDL_strdup(name);
                       SDL_joyname[numjoysticks] = SDL_CreateJoystickName(0, 0, NULL, stick_name.String());
@@ -93,12 +93,16 @@ extern "C"
     {
     }
 
-/* Function to get the device-dependent name of a joystick */
     static const char *HAIKU_JoystickGetDeviceName(int device_index)
     {
         return SDL_joyname[device_index];
     }
 
+    static const char *HAIKU_JoystickGetDevicePath(int device_index)
+    {
+        return SDL_joyport[device_index];
+    }
+
     static int HAIKU_JoystickGetDevicePlayerIndex(int device_index)
     {
         return -1;
@@ -298,6 +302,7 @@ extern "C"
         HAIKU_JoystickGetCount,
         HAIKU_JoystickDetect,
         HAIKU_JoystickGetDeviceName,
+        HAIKU_JoystickGetDevicePath,
         HAIKU_JoystickGetDevicePlayerIndex,
         HAIKU_JoystickSetDevicePlayerIndex,
         HAIKU_JoystickGetDeviceGUID,
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index f54b3c3f2b7..34542f92a04 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -783,6 +783,21 @@ HIDAPI_JoystickGetDeviceName(int device_index)
     return name;
 }
 
+static const char *
+HIDAPI_JoystickGetDevicePath(int device_index)
+{
+    SDL_HIDAPI_Device *device;
+    const char *path = NULL;
+
+    device = HIDAPI_GetDeviceByIndex(device_index, NULL);
+    if (device) {
+        /* FIXME: The device could be freed after this path is returned... */
+        path = device->path;
+    }
+
+    return path;
+}
+
 static int
 HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -1030,6 +1045,7 @@ SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
     HIDAPI_JoystickGetCount,
     HIDAPI_JoystickDetect,
     HIDAPI_JoystickGetDeviceName,
+    HIDAPI_JoystickGetDevicePath,
     HIDAPI_JoystickGetDevicePlayerIndex,
     HIDAPI_JoystickSetDevicePlayerIndex,
     HIDAPI_JoystickGetDeviceGUID,
diff --git a/src/joystick/iphoneos/SDL_mfijoystick.m b/src/joystick/iphoneos/SDL_mfijoystick.m
index 38ab3fe605d..bf9163319ef 100644
--- a/src/joystick/iphoneos/SDL_mfijoystick.m
+++ b/src/joystick/iphoneos/SDL_mfijoystick.m
@@ -651,6 +651,12 @@ static int is_macos11(void)
     return device ? device->name : "Unknown";
 }
 
+static const char *
+IOS_JoystickGetDevicePath(int device_index)
+{
+    return NULL;
+}
+
 static int
 IOS_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -1781,6 +1787,7 @@ SDL_bool IOS_SupportedHIDDevice(IOHIDDeviceRef device)
     IOS_JoystickGetCount,
     IOS_JoystickDetect,
     IOS_JoystickGetDeviceName,
+    IOS_JoystickGetDevicePath,
     IOS_JoystickGetDevicePlayerIndex,
     IOS_JoystickSetDevicePlayerIndex,
     IOS_JoystickGetDeviceGUID,
diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c
index a268c0d9b41..e16ed688d59 100644
--- a/src/joystick/linux/SDL_sysjoystick.c
+++ b/src/joystick/linux/SDL_sysjoystick.c
@@ -854,13 +854,18 @@ JoystickByDevIndex(int device_index)
     return item;
 }
 
-/* Function to get the device-dependent name of a joystick */
 static const char *
 LINUX_JoystickGetDeviceName(int device_index)
 {
     return JoystickByDevIndex(device_index)->name;
 }
 
+static const char *
+LINUX_JoystickGetDevicePath(int device_index)
+{
+    return JoystickByDevIndex(device_index)->path;
+}
+
 static int
 LINUX_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -1846,6 +1851,7 @@ SDL_JoystickDriver SDL_LINUX_JoystickDriver =
     LINUX_JoystickGetCount,
     LINUX_JoystickDetect,
     LINUX_JoystickGetDeviceName,
+    LINUX_JoystickGetDevicePath,
     LINUX_JoystickGetDevicePlayerIndex,
     LINUX_JoystickSetDevicePlayerIndex,
     LINUX_JoystickGetDeviceGUID,
diff --git a/src/joystick/os2/SDL_os2joystick.c b/src/joystick/os2/SDL_os2joystick.c
index 00b7fc554d2..1c292ff4dd7 100644
--- a/src/joystick/os2/SDL_os2joystick.c
+++ b/src/joystick/os2/SDL_os2joystick.c
@@ -377,15 +377,17 @@ static void OS2_JoystickDetect(void)
 {
 }
 
-/***********************************************************/
-/* Function to get the device-dependent name of a joystick */
-/***********************************************************/
 static const char *OS2_JoystickGetDeviceName(int device_index)
 {
 	/* No need to verify if device exists, already done in upper layer */
 	return SYS_JoyData[device_index].szDeviceName;
 }
 
+static const char *OS2_JoystickGetDevicePath(int device_index)
+{
+	return NULL;
+}
+
 static int OS2_JoystickGetDevicePlayerIndex(int device_index)
 {
 	return -1;
@@ -779,6 +781,7 @@ SDL_JoystickDriver SDL_OS2_JoystickDriver =
 	OS2_NumJoysticks,
 	OS2_JoystickDetect,
 	OS2_JoystickGetDeviceName,
+	OS2_JoystickGetDevicePath,
 	OS2_JoystickGetDevicePlayerIndex,
 	OS2_JoystickSetDevicePlayerIndex,
 	OS2_JoystickGetDeviceGUID,
diff --git a/src/joystick/psp/SDL_sysjoystick.c b/src/joystick/psp/SDL_sysjoystick.c
index d7197956066..a5422592b8e 100644
--- a/src/joystick/psp/SDL_sysjoystick.c
+++ b/src/joystick/psp/SDL_sysjoystick.c
@@ -142,7 +142,6 @@ static void PSP_JoystickDetect(void)
 }
 
 #if 0
-/* Function to get the device-dependent name of a joystick */
 static const char *PSP_JoystickName(int idx)
 {
     if (idx == 0) return "PSP controller";
@@ -151,12 +150,16 @@ static const char *PSP_JoystickName(int idx)
 }
 #endif
 
-/* Function to get the device-dependent name of a joystick */
 static const char *PSP_JoystickGetDeviceName(int device_index)
 {
     return "PSP builtin joypad";
 }
 
+static const char *PSP_JoystickGetDevicePath(int device_index)
+{
+    return NULL;
+}
+
 static int PSP_JoystickGetDevicePlayerIndex(int device_index)
 {
     return -1;
@@ -304,6 +307,7 @@ SDL_JoystickDriver SDL_PSP_JoystickDriver =
     PSP_NumJoysticks,
     PSP_JoystickDetect,
     PSP_JoystickGetDeviceName,
+    PSP_JoystickGetDevicePath,
     PSP_JoystickGetDevicePlayerIndex,
     PSP_JoystickSetDevicePlayerIndex,
     PSP_JoystickGetDeviceGUID,
diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c
index 508dd80f984..0e6525d5fb3 100644
--- a/src/joystick/virtual/SDL_virtualjoystick.c
+++ b/src/joystick/virtual/SDL_virtualjoystick.c
@@ -28,7 +28,6 @@
 #include "../SDL_sysjoystick.h"
 #include "../SDL_joystick_c.h"
 
-extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver;
 
 static joystick_hwdata * g_VJoys = NULL;
 
@@ -275,6 +274,13 @@ VIRTUAL_JoystickGetDeviceName(int device_index)
 }
 
 
+static const char *
+VIRTUAL_JoystickGetDevicePath(int device_index)
+{
+    return NULL;
+}
+
+
 static int
 VIRTUAL_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -435,6 +441,7 @@ SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver =
     VIRTUAL_JoystickGetCount,
     VIRTUAL_JoystickDetect,
     VIRTUAL_JoystickGetDeviceName,
+    VIRTUAL_JoystickGetDevicePath,
     VIRTUAL_JoystickGetDevicePlayerIndex,
     VIRTUAL_JoystickSetDevicePlayerIndex,
     VIRTUAL_JoystickGetDeviceGUID,
diff --git a/src/joystick/vita/SDL_sysjoystick.c b/src/joystick/vita/SDL_sysjoystick.c
index aa52cd77f09..b5c7a277896 100644
--- a/src/joystick/vita/SDL_sysjoystick.c
+++ b/src/joystick/vita/SDL_sysjoystick.c
@@ -179,7 +179,6 @@ SDL_JoystickID VITA_JoystickGetDeviceInstanceID(int device_index)
     return device_index;
 }
 
-/* Function to get the device-dependent name of a joystick */
 const char *VITA_JoystickGetDeviceName(int index)
 {
     if (index == 0)
@@ -195,7 +194,12 @@ const char *VITA_JoystickGetDeviceName(int index)
         return "PSVita Controller";
 
     SDL_SetError("No joystick available with that index");
-    return(NULL);
+    return NULL;
+}
+
+const char *VITA_JoystickGetDevicePath(int index)
+{
+    return NULL;
 }
 
 static int
@@ -400,6 +404,7 @@ SDL_JoystickDriver SDL_VITA_JoystickDriver =
     VITA_JoystickGetCount,
     VITA_JoystickDetect,
     VITA_JoystickGetDeviceName,
+    VITA_JoystickGetDevicePath,
     VITA_JoystickGetDevicePlayerIndex,
     VITA_JoystickSetDevicePlayerIndex,
     VITA_JoystickGetDeviceGUID,
diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c
index c2f53a93fe6..353f66e1f86 100644
--- a/src/joystick/windows/SDL_dinputjoystick.c
+++ b/src/joystick/windows/SDL_dinputjoystick.c
@@ -456,7 +456,7 @@ EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)
     pNewJoystick = *(JoyStick_DeviceData**)pContext;
     while (pNewJoystick) {
         /* update GUIDs of joysticks with matching paths, in case they're not open yet */
-        if (SDL_strcmp(pNewJoystick->hidPath, hidPath) == 0) {
+        if (SDL_strcmp(pNewJoystick->path, hidPath) == 0) {
             /* if we are replacing the front of the list then update it */
             if (pNewJoystick == *(JoyStick_DeviceData**)pContext) {
                 *(JoyStick_DeviceData**)pContext = pNewJoystick->pNext;
@@ -483,7 +483,7 @@ EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)
     CHECK(pNewJoystick);
 
     SDL_zerop(pNewJoystick);
-    SDL_strlcpy(pNewJoystick->hidPath, hidPath, SDL_arraysize(pNewJoystick->hidPath));
+    SDL_strlcpy(pNewJoystick->path, hidPath, SDL_arraysize(pNewJoystick->path));
     SDL_memcpy(&pNewJoystick->dxdevice, pDeviceInstance, sizeof(DIDEVICEINSTANCE));
     SDL_memset(pNewJoystick->guid.data, 0, sizeof(pNewJoystick->guid.data));
 
diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c
index c9a92bc4b7a..629093a13ee 100644
--- a/src/joystick/windows/SDL_rawinputjoystick.c
+++ b/src/joystick/windows/SDL_rawinputjoystick.c
@@ -105,6 +105,7 @@ typedef struct _SDL_RAWINPUT_Device
 {
     SDL_atomic_t refcount;
     char *name;
+    char *path;
     Uint16 vendor_id;
     Uint16 product_id;
     Uint16 version;
@@ -691,6 +692,7 @@ RAWINPUT_ReleaseDevice(SDL_RAWINPUT_Device *device)
     if (SDL_AtomicDecRef(&device->refcount)) {
         SDL_free(device->preparsed_data);
         SDL_free(device->name);
+        SDL_free(device->path);
         SDL_free(device);
     }
 }
@@ -796,6 +798,7 @@ RAWINPUT_AddDevice(HANDLE hDevice)
             SDL_free(product_string);
         }
     }
+    device->path = SDL_strdup(dev_name);
 
     CloseHandle(hFile);
     hFile = INVALID_HANDLE_VALUE;
@@ -828,8 +831,12 @@ RAWINPUT_AddDevice(HANDLE hDevice)
         CloseHandle(hFile);
     }
     if (device) {
-        if (device->name)
+        if (device->name) {
             SDL_free(device->name);
+        }
+        if (device->path) {
+            SDL_free(device->path);
+        }
         SDL_free(device);
     }
 #undef CHECK
@@ -1032,6 +1039,12 @@ RAWINPUT_JoystickGetDeviceName(int device_index)
     return RAWINPUT_GetDeviceByIndex(device_index)->name;
 }
 
+static const char *
+RAWINPUT_JoystickGetDevicePath(int device_index)
+{
+    return RAWINPUT_GetDeviceByIndex(device_index)->path;
+}
+
 static int
 RAWINPUT_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -2009,6 +2022,7 @@ SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver =
     RAWINPUT_JoystickGetCount,
     RAWINPUT_JoystickDetect,
     RAWINPUT_JoystickGetDeviceName,
+    RAWINPUT_JoystickGetDevicePath,
     RAWINPUT_JoystickGetDevicePlayerIndex,
     RAWINPUT_JoystickSetDevicePlayerIndex,
     RAWINPUT_JoystickGetDeviceGUID,
diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c
index 6c5ec552868..8b552bb7dad 100644
--- a/src/joystick/windows/SDL_windows_gaming_input.c
+++ b/src/joystick/windows/SDL_windows_gaming_input.c
@@ -586,6 +586,12 @@ WGI_JoystickGetDeviceName(int device_index)
     return wgi.controllers[device_index].name;
 }
 
+static const char *
+WGI_JoystickGetDevicePath(int device_index)
+{
+    return NULL;
+}
+
 static int
 WGI_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -893,6 +899,7 @@ SDL_JoystickDriver SDL_WGI_JoystickDriver =
     WGI_JoystickGetCount,
     WGI_JoystickDetect,
     WGI_JoystickGetDeviceName,
+    WGI_JoystickGetDevicePath,
     WGI_JoystickGetDevicePlayerIndex,
     WGI_JoystickSetDevicePlayerIndex,
     WGI_JoystickGetDeviceGUID,
diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c
index 7dae16d9f2f..a9b71375dc3 100644
--- a/src/joystick/windows/SDL_windowsjoystick.c
+++ b/src/joystick/windows/SDL_windowsjoystick.c
@@ -555,7 +555,6 @@ WINDOWS_JoystickDetect(void)
     }
 }
 
-/* Function to get the device-dependent name of a joystick */
 static const char *
 WINDOWS_JoystickGetDeviceName(int device_index)
 {
@@ -568,6 +567,18 @@ WINDOWS_JoystickGetDeviceName(int device_index)
     return device->joystickname;
 }
 
+static const char *
+WINDOWS_JoystickGetDevicePath(int device_index)
+{
+    JoyStick_DeviceData *device = SYS_Joystick;
+    int index;
+
+    for (index = device_index; index > 0; index--)
+        device = device->pNext;
+
+    return device->path;
+}
+
 static int
 WINDOWS_JoystickGetDevicePlayerIndex(int device_index)
 {
@@ -755,6 +766,7 @@ SDL_JoystickDriver SDL_WINDOWS_JoystickDriver =
     WINDOWS_JoystickGetCount,
     WINDOWS_JoystickDetect,
     WINDOWS_JoystickGetDeviceName,
+    WINDOWS_JoystickGetDevicePath,
     WINDOWS_JoystickGetDevicePlayerIndex,
     WINDOWS_JoystickSetDevicePlayerIndex,
     WINDOWS_JoystickGetDeviceGUID,
diff --git a/src/joystick/windows/SDL_windowsjoystick_c.h b/src/joystick/windows/SDL_windowsjoystick_c.h
index 1b9625385a4..d7e09abde1a 100644
--- a/src/joystick/windows/SDL_windowsjoystick_c.h
+++ b/src/joystick/windows/SDL_windowsjoystick_c.h
@@ -37,7 +37

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