SDL: Added SDL_GetKeyboardName() and SDL_GetMouseName()

From c0f456555287623b935ab553404f0391b26389c4 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 22 Mar 2024 09:17:17 -0700
Subject: [PATCH] Added SDL_GetKeyboardName() and SDL_GetMouseName()

---
 include/SDL3/SDL_joystick.h                |  20 ++--
 include/SDL3/SDL_keyboard.h                |  15 +++
 include/SDL3/SDL_mouse.h                   |  15 +++
 src/core/linux/SDL_evdev.c                 |  18 +++-
 src/core/openbsd/SDL_wscons_kbd.c          |   2 +-
 src/core/openbsd/SDL_wscons_mouse.c        |   2 +-
 src/dynapi/SDL_dynapi.sym                  |   2 +
 src/dynapi/SDL_dynapi_overrides.h          |   2 +
 src/dynapi/SDL_dynapi_procs.h              |   2 +
 src/events/SDL_keyboard.c                  |  61 ++++++------
 src/events/SDL_keyboard_c.h                |   2 +-
 src/events/SDL_mouse.c                     |  56 ++++++-----
 src/events/SDL_mouse_c.h                   |   2 +-
 src/video/cocoa/SDL_cocoavideo.m           |   4 +-
 src/video/emscripten/SDL_emscriptenvideo.c |   4 +-
 src/video/haiku/SDL_bvideo.cc              |   4 +-
 src/video/qnx/SDL_qnxvideo.c               |   4 +-
 src/video/riscos/SDL_riscosvideo.c         |   4 +-
 src/video/uikit/SDL_uikitevents.m          |   4 +-
 src/video/vita/SDL_vitakeyboard.c          |   2 +-
 src/video/vita/SDL_vitamouse.c             |   2 +-
 src/video/wayland/SDL_waylandevents.c      |   4 +-
 src/video/windows/SDL_windowsevents.c      | 102 ++++++++++++++++-----
 src/video/winrt/SDL_winrtvideo.cpp         |   4 +-
 src/video/x11/SDL_x11video.c               |   4 +-
 src/video/x11/SDL_x11xinput2.c             |   8 +-
 26 files changed, 239 insertions(+), 110 deletions(-)

diff --git a/include/SDL3/SDL_joystick.h b/include/SDL3/SDL_joystick.h
index 4708aa7ba40bf..44857c974b394 100644
--- a/include/SDL3/SDL_joystick.h
+++ b/include/SDL3/SDL_joystick.h
@@ -216,8 +216,8 @@ extern DECLSPEC int SDLCALL SDL_GetJoystickInstancePlayerIndex(SDL_JoystickID in
  * This can be called before any joysticks are opened.
  *
  * \param instance_id the joystick instance ID
- * \returns the GUID of the selected joystick. If called on an invalid index,
- *          this function returns a zero GUID
+ * \returns the GUID of the selected joystick. If called with an invalid instance_id,
+ *          this function returns a zero GUID.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -233,8 +233,8 @@ extern DECLSPEC SDL_JoystickGUID SDLCALL SDL_GetJoystickInstanceGUID(SDL_Joystic
  * available this function returns 0.
  *
  * \param instance_id the joystick instance ID
- * \returns the USB vendor ID of the selected joystick. If called on an
- *          invalid index, this function returns zero
+ * \returns the USB vendor ID of the selected joystick. If called with an
+ *          invalid instance_id, this function returns 0.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -250,8 +250,8 @@ extern DECLSPEC Uint16 SDLCALL SDL_GetJoystickInstanceVendor(SDL_JoystickID inst
  * available this function returns 0.
  *
  * \param instance_id the joystick instance ID
- * \returns the USB product ID of the selected joystick. If called on an
- *          invalid index, this function returns zero
+ * \returns the USB product ID of the selected joystick. If called with an
+ *          invalid instance_id, this function returns 0.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -267,8 +267,8 @@ extern DECLSPEC Uint16 SDLCALL SDL_GetJoystickInstanceProduct(SDL_JoystickID ins
  * isn't available this function returns 0.
  *
  * \param instance_id the joystick instance ID
- * \returns the product version of the selected joystick. If called on an
- *          invalid index, this function returns zero
+ * \returns the product version of the selected joystick. If called with an
+ *          invalid instance_id, this function returns 0.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -283,8 +283,8 @@ extern DECLSPEC Uint16 SDLCALL SDL_GetJoystickInstanceProductVersion(SDL_Joystic
  * This can be called before any joysticks are opened.
  *
  * \param instance_id the joystick instance ID
- * \returns the SDL_JoystickType of the selected joystick. If called on an
- *          invalid index, this function returns `SDL_JOYSTICK_TYPE_UNKNOWN`
+ * \returns the SDL_JoystickType of the selected joystick. If called with an
+ *          invalid instance_id, this function returns `SDL_JOYSTICK_TYPE_UNKNOWN`.
  *
  * \since This function is available since SDL 3.0.0.
  *
diff --git a/include/SDL3/SDL_keyboard.h b/include/SDL3/SDL_keyboard.h
index d2a8b1cfbaeef..84e84098328bf 100644
--- a/include/SDL3/SDL_keyboard.h
+++ b/include/SDL3/SDL_keyboard.h
@@ -77,10 +77,25 @@ extern DECLSPEC SDL_bool SDLCALL SDL_HasKeyboard(void);
  *
  * \since This function is available since SDL 3.0.0.
  *
+ * \sa SDL_GetKeyboardInstanceName
  * \sa SDL_HasKeyboard
  */
 extern DECLSPEC SDL_KeyboardID *SDLCALL SDL_GetKeyboards(int *count);
 
+/**
+ * Get the name of a keyboard.
+ *
+ * This function returns "" if the keyboard doesn't have a name.
+ *
+ * \param instance_id the keyboard instance ID
+ * \returns the name of the selected keyboard, or NULL on failure; call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetKeyboards
+ */
+extern DECLSPEC const char *SDLCALL SDL_GetKeyboardInstanceName(SDL_KeyboardID instance_id);
+
 /**
  * Query the window which currently has keyboard focus.
  *
diff --git a/include/SDL3/SDL_mouse.h b/include/SDL3/SDL_mouse.h
index da1eb3b907d51..0c97fc1a56860 100644
--- a/include/SDL3/SDL_mouse.h
+++ b/include/SDL3/SDL_mouse.h
@@ -102,10 +102,25 @@ extern DECLSPEC SDL_bool SDLCALL SDL_HasMouse(void);
  *
  * \since This function is available since SDL 3.0.0.
  *
+ * \sa SDL_GetMouseInstanceName
  * \sa SDL_HasMouse
  */
 extern DECLSPEC SDL_MouseID *SDLCALL SDL_GetMice(int *count);
 
+/**
+ * Get the name of a mouse.
+ *
+ * This function returns "" if the mouse doesn't have a name.
+ *
+ * \param instance_id the mouse instance ID
+ * \returns the name of the selected mouse, or NULL on failure; call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetMice
+ */
+extern DECLSPEC const char *SDLCALL SDL_GetMouseInstanceName(SDL_MouseID instance_id);
+
 /**
  * Get the window which currently has mouse focus.
  *
diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c
index fa9b141315150..b39630d9262fc 100644
--- a/src/core/linux/SDL_evdev.c
+++ b/src/core/linux/SDL_evdev.c
@@ -607,17 +607,22 @@ static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode)
 
 static int SDL_EVDEV_init_keyboard(SDL_evdevlist_item *item, int udev_class)
 {
-    SDL_AddKeyboard((SDL_KeyboardID)item->fd, SDL_TRUE);
+    SDL_AddKeyboard((SDL_KeyboardID)item->fd, NULL, SDL_TRUE);
 
     return 0;
 }
 
+static void SDL_EVDEV_destroy_keyboard(SDL_evdevlist_item *item)
+{
+    SDL_RemoveKeyboard((SDL_KeyboardID)item->fd);
+}
+
 static int SDL_EVDEV_init_mouse(SDL_evdevlist_item *item, int udev_class)
 {
     int ret;
     struct input_absinfo abs_info;
 
-    SDL_AddMouse((SDL_MouseID)item->fd, SDL_TRUE);
+    SDL_AddMouse((SDL_MouseID)item->fd, NULL, SDL_TRUE);
 
     ret = ioctl(item->fd, EVIOCGABS(ABS_X), &abs_info);
     if (ret < 0) {
@@ -640,6 +645,11 @@ static int SDL_EVDEV_init_mouse(SDL_evdevlist_item *item, int udev_class)
     return 0;
 }
 
+static void SDL_EVDEV_destroy_mouse(SDL_evdevlist_item *item)
+{
+    SDL_RemoveMouse((SDL_MouseID)item->fd);
+}
+
 static int SDL_EVDEV_init_touchscreen(SDL_evdevlist_item *item, int udev_class)
 {
     int ret;
@@ -974,6 +984,10 @@ static int SDL_EVDEV_device_removed(const char *dev_path)
             }
             if (item->is_touchscreen) {
                 SDL_EVDEV_destroy_touchscreen(item);
+            } else if (item->udev_class & SDL_UDEV_DEVICE_MOUSE) {
+                SDL_EVDEV_destroy_mouse(item);
+            } else if (item->udev_class & SDL_UDEV_DEVICE_KEYBOARD) {
+                SDL_EVDEV_destroy_keyboard(item);
             }
             close(item->fd);
             SDL_free(item->path);
diff --git a/src/core/openbsd/SDL_wscons_kbd.c b/src/core/openbsd/SDL_wscons_kbd.c
index 541ad2cbef34d..81f94448cdaa4 100644
--- a/src/core/openbsd/SDL_wscons_kbd.c
+++ b/src/core/openbsd/SDL_wscons_kbd.c
@@ -433,7 +433,7 @@ static SDL_WSCONS_input_data *SDL_WSCONS_Init_Keyboard(const char *dev)
     }
 
     input->keyboardID = SDL_GetNextObjectID();
-    SDL_AddKeyboard(input->keyboardID, SDL_FALSE);
+    SDL_AddKeyboard(input->keyboardID, NULL, SDL_FALSE);
 
     input->keymap.map = SDL_calloc(sizeof(struct wscons_keymap), KS_NUMKEYCODES);
     if (!input->keymap.map) {
diff --git a/src/core/openbsd/SDL_wscons_mouse.c b/src/core/openbsd/SDL_wscons_mouse.c
index 435de1e099b3d..6b0c96261d4c7 100644
--- a/src/core/openbsd/SDL_wscons_mouse.c
+++ b/src/core/openbsd/SDL_wscons_mouse.c
@@ -52,7 +52,7 @@ SDL_WSCONS_mouse_input_data *SDL_WSCONS_Init_Mouse()
     }
 
     input->mouseID = SDL_GetNextObjectID();
-    SDL_AddMouse(input->mouseID, SDL_FALSE);
+    SDL_AddMouse(input->mouseID, NULL, SDL_FALSE);
 
 #ifdef WSMOUSEIO_SETMODE
     ioctl(input->fd, WSMOUSEIO_SETMODE, WSMOUSE_COMPAT);
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index be498a820f930..5d065e4a5655b 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -319,6 +319,7 @@ SDL3_0.0.0 {
     SDL_GetKeyFromScancode;
     SDL_GetKeyName;
     SDL_GetKeyboardFocus;
+    SDL_GetKeyboardInstanceName;
     SDL_GetKeyboardState;
     SDL_GetKeyboards;
     SDL_GetLogOutputFunction;
@@ -329,6 +330,7 @@ SDL3_0.0.0 {
     SDL_GetMice;
     SDL_GetModState;
     SDL_GetMouseFocus;
+    SDL_GetMouseInstanceName;
     SDL_GetMouseState;
     SDL_GetNaturalDisplayOrientation;
     SDL_GetNumAllocations;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 8f140360d0603..935170151a4f5 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -344,6 +344,7 @@
 #define SDL_GetKeyFromScancode SDL_GetKeyFromScancode_REAL
 #define SDL_GetKeyName SDL_GetKeyName_REAL
 #define SDL_GetKeyboardFocus SDL_GetKeyboardFocus_REAL
+#define SDL_GetKeyboardInstanceName SDL_GetKeyboardInstanceName_REAL
 #define SDL_GetKeyboardState SDL_GetKeyboardState_REAL
 #define SDL_GetKeyboards SDL_GetKeyboards_REAL
 #define SDL_GetLogOutputFunction SDL_GetLogOutputFunction_REAL
@@ -354,6 +355,7 @@
 #define SDL_GetMice SDL_GetMice_REAL
 #define SDL_GetModState SDL_GetModState_REAL
 #define SDL_GetMouseFocus SDL_GetMouseFocus_REAL
+#define SDL_GetMouseInstanceName SDL_GetMouseInstanceName_REAL
 #define SDL_GetMouseState SDL_GetMouseState_REAL
 #define SDL_GetNaturalDisplayOrientation SDL_GetNaturalDisplayOrientation_REAL
 #define SDL_GetNumAllocations SDL_GetNumAllocations_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 8a2804a249efb..8f7b319ea5696 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -382,6 +382,7 @@ SDL_DYNAPI_PROC(SDL_Keycode,SDL_GetKeyFromName,(const char *a),(a),return)
 SDL_DYNAPI_PROC(SDL_Keycode,SDL_GetKeyFromScancode,(SDL_Scancode a),(a),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetKeyName,(SDL_Keycode a),(a),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_GetKeyboardFocus,(void),(),return)
+SDL_DYNAPI_PROC(const char*,SDL_GetKeyboardInstanceName,(SDL_KeyboardID a),(a),return)
 SDL_DYNAPI_PROC(const Uint8*,SDL_GetKeyboardState,(int *a),(a),return)
 SDL_DYNAPI_PROC(SDL_MouseID*,SDL_GetKeyboards,(int *a),(a),return)
 SDL_DYNAPI_PROC(void,SDL_GetLogOutputFunction,(SDL_LogOutputFunction *a, void **b),(a,b),)
@@ -392,6 +393,7 @@ SDL_DYNAPI_PROC(void,SDL_GetMemoryFunctions,(SDL_malloc_func *a, SDL_calloc_func
 SDL_DYNAPI_PROC(SDL_MouseID*,SDL_GetMice,(int *a),(a),return)
 SDL_DYNAPI_PROC(SDL_Keymod,SDL_GetModState,(void),(),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_GetMouseFocus,(void),(),return)
+SDL_DYNAPI_PROC(const char*,SDL_GetMouseInstanceName,(SDL_MouseID a),(a),return)
 SDL_DYNAPI_PROC(Uint32,SDL_GetMouseState,(float *a, float *b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetNaturalDisplayOrientation,(SDL_DisplayID a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetNumAllocations,(void),(),return)
diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c
index 1e5eb02a09cf4..577e6ba5f1b59 100644
--- a/src/events/SDL_keyboard.c
+++ b/src/events/SDL_keyboard.c
@@ -40,9 +40,13 @@ typedef enum
 
 #define KEYBOARD_SOURCE_MASK (KEYBOARD_HARDWARE | KEYBOARD_AUTORELEASE)
 
-typedef struct SDL_Keyboard SDL_Keyboard;
+typedef struct SDL_KeyboardInstance
+{
+    SDL_KeyboardID instance_id;
+    char *name;
+} SDL_KeyboardInstance;
 
-struct SDL_Keyboard
+typedef struct SDL_Keyboard
 {
     /* Data common to all keyboards */
     SDL_Window *focus;
@@ -52,11 +56,11 @@ struct SDL_Keyboard
     SDL_Keycode keymap[SDL_NUM_SCANCODES];
     SDL_bool autorelease_pending;
     Uint64 hardware_timestamp;
-};
+} SDL_Keyboard;
 
 static SDL_Keyboard SDL_keyboard;
 static int SDL_keyboard_count;
-static SDL_KeyboardID *SDL_keyboards;
+static SDL_KeyboardInstance *SDL_keyboards;
 
 static const SDL_Keycode SDL_default_keymap[SDL_NUM_SCANCODES] = {
     /* 0 */ SDLK_UNKNOWN,
@@ -691,29 +695,33 @@ SDL_bool SDL_IsKeyboard(Uint16 vendor, Uint16 product, int num_keys)
     return SDL_TRUE;
 }
 
-void SDL_AddKeyboard(SDL_KeyboardID keyboardID, SDL_bool send_event)
+static int SDL_GetKeyboardIndex(SDL_KeyboardID keyboardID)
 {
-    int keyboard_index = -1;
-
-    SDL_assert(keyboardID != 0);
-
     for (int i = 0; i < SDL_keyboard_count; ++i) {
-        if (keyboardID == SDL_keyboards[i]) {
-            keyboard_index = i;
-            break;
+        if (keyboardID == SDL_keyboards[i].instance_id) {
+            return i;
         }
     }
+    return -1;
+}
 
+void SDL_AddKeyboard(SDL_KeyboardID keyboardID, const char *name, SDL_bool send_event)
+{
+    int keyboard_index = SDL_GetKeyboardIndex(keyboardID);
     if (keyboard_index >= 0) {
         /* We already know about this keyboard */
         return;
     }
 
-    SDL_KeyboardID *keyboards = (SDL_KeyboardID *)SDL_realloc(SDL_keyboards, (SDL_keyboard_count + 1) * sizeof(*keyboards));
+    SDL_assert(keyboardID != 0);
+
+    SDL_KeyboardInstance *keyboards = (SDL_KeyboardInstance *)SDL_realloc(SDL_keyboards, (SDL_keyboard_count + 1) * sizeof(*keyboards));
     if (!keyboards) {
         return;
     }
-    keyboards[SDL_keyboard_count] = keyboardID;
+    SDL_KeyboardInstance *instance = &keyboards[SDL_keyboard_count];
+    instance->instance_id = keyboardID;
+    instance->name = SDL_strdup(name ? name : "");
     SDL_keyboards = keyboards;
     ++SDL_keyboard_count;
 
@@ -728,22 +736,14 @@ void SDL_AddKeyboard(SDL_KeyboardID keyboardID, SDL_bool send_event)
 
 void SDL_RemoveKeyboard(SDL_KeyboardID keyboardID)
 {
-    int keyboard_index = -1;
-
-    SDL_assert(keyboardID != 0);
-
-    for (int i = 0; i < SDL_keyboard_count; ++i) {
-        if (keyboardID == SDL_keyboards[i]) {
-            keyboard_index = i;
-            break;
-        }
-    }
-
+    int keyboard_index = SDL_GetKeyboardIndex(keyboardID);
     if (keyboard_index < 0) {
         /* We don't know about this keyboard */
         return;
     }
 
+    SDL_free(SDL_keyboards[keyboard_index].name);
+
     if (keyboard_index != SDL_keyboard_count - 1) {
         SDL_memcpy(&SDL_keyboards[keyboard_index], &SDL_keyboards[keyboard_index + 1], (SDL_keyboard_count - keyboard_index - 1) * sizeof(SDL_keyboards[keyboard_index]));
     }
@@ -773,7 +773,7 @@ SDL_KeyboardID *SDL_GetKeyboards(int *count)
         }
 
         for (i = 0; i < SDL_keyboard_count; ++i) {
-            keyboards[i] = SDL_keyboards[i];
+            keyboards[i] = SDL_keyboards[i].instance_id;
         }
         keyboards[i] = 0;
     } else {
@@ -785,6 +785,15 @@ SDL_KeyboardID *SDL_GetKeyboards(int *count)
     return keyboards;
 }
 
+const char *SDL_GetKeyboardInstanceName(SDL_KeyboardID instance_id)
+{
+    int keyboard_index = SDL_GetKeyboardIndex(instance_id);
+    if (keyboard_index < 0) {
+        return NULL;
+    }
+    return SDL_keyboards[keyboard_index].name;
+}
+
 void SDL_ResetKeyboard(void)
 {
     SDL_Keyboard *keyboard = &SDL_keyboard;
diff --git a/src/events/SDL_keyboard_c.h b/src/events/SDL_keyboard_c.h
index 584951a947579..b44ec8276fbd0 100644
--- a/src/events/SDL_keyboard_c.h
+++ b/src/events/SDL_keyboard_c.h
@@ -30,7 +30,7 @@ extern int SDL_InitKeyboard(void);
 extern SDL_bool SDL_IsKeyboard(Uint16 vendor, Uint16 product, int num_keys);
 
 /* A keyboard has been added to the system */
-extern void SDL_AddKeyboard(SDL_KeyboardID keyboardID, SDL_bool send_event);
+extern void SDL_AddKeyboard(SDL_KeyboardID keyboardID, const char *name, SDL_bool send_event);
 
 /* A keyboard has been removed from the system */
 extern void SDL_RemoveKeyboard(SDL_KeyboardID keyboardID);
diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c
index 616115bf7a369..1edaab464f1de 100644
--- a/src/events/SDL_mouse.c
+++ b/src/events/SDL_mouse.c
@@ -33,10 +33,16 @@
 
 /* #define DEBUG_MOUSE */
 
+typedef struct SDL_MouseInstance
+{
+    SDL_MouseID instance_id;
+    char *name;
+} SDL_MouseInstance;
+
 /* The mouse state */
 static SDL_Mouse SDL_mouse;
 static int SDL_mouse_count;
-static SDL_MouseID *SDL_mice;
+static SDL_MouseInstance *SDL_mice;
 
 /* for mapping mouse events to touch */
 static SDL_bool track_mouse_down = SDL_FALSE;
@@ -235,29 +241,33 @@ SDL_bool SDL_IsMouse(Uint16 vendor, Uint16 product)
     return SDL_TRUE;
 }
 
-void SDL_AddMouse(SDL_MouseID mouseID, SDL_bool send_event)
+static int SDL_GetMouseIndex(SDL_MouseID mouseID)
 {
-    int mouse_index = -1;
-
-    SDL_assert(mouseID != 0);
-
     for (int i = 0; i < SDL_mouse_count; ++i) {
-        if (mouseID == SDL_mice[i]) {
-            mouse_index = i;
-            break;
+        if (mouseID == SDL_mice[i].instance_id) {
+            return i;
         }
     }
+    return -1;
+}
 
+void SDL_AddMouse(SDL_MouseID mouseID, const char *name, SDL_bool send_event)
+{
+    int mouse_index = SDL_GetMouseIndex(mouseID);
     if (mouse_index >= 0) {
         /* We already know about this mouse */
         return;
     }
 
-    SDL_MouseID *mice = (SDL_MouseID *)SDL_realloc(SDL_mice, (SDL_mouse_count + 1) * sizeof(*mice));
+    SDL_assert(mouseID != 0);
+
+    SDL_MouseInstance *mice = (SDL_MouseInstance *)SDL_realloc(SDL_mice, (SDL_mouse_count + 1) * sizeof(*mice));
     if (!mice) {
         return;
     }
-    mice[SDL_mouse_count] = mouseID;
+    SDL_MouseInstance *instance = &mice[SDL_mouse_count];
+    instance->instance_id = mouseID;
+    instance->name = SDL_strdup(name ? name : "");
     SDL_mice = mice;
     ++SDL_mouse_count;
 
@@ -272,22 +282,14 @@ void SDL_AddMouse(SDL_MouseID mouseID, SDL_bool send_event)
 
 void SDL_RemoveMouse(SDL_MouseID mouseID)
 {
-    int mouse_index = -1;
-
-    SDL_assert(mouseID != 0);
-
-    for (int i = 0; i < SDL_mouse_count; ++i) {
-        if (mouseID == SDL_mice[i]) {
-            mouse_index = i;
-            break;
-        }
-    }
-
+    int mouse_index = SDL_GetMouseIndex(mouseID);
     if (mouse_index < 0) {
         /* We don't know about this mouse */
         return;
     }
 
+    SDL_free(SDL_mice[mouse_index].name);
+
     if (mouse_index != SDL_mouse_count - 1) {
         SDL_memcpy(&SDL_mice[mouse_index], &SDL_mice[mouse_index + 1], (SDL_mouse_count - mouse_index - 1) * sizeof(SDL_mice[mouse_index]));
     }
@@ -330,7 +332,7 @@ SDL_MouseID *SDL_GetMice(int *count)
         }
 
         for (i = 0; i < SDL_mouse_count; ++i) {
-            mice[i] = SDL_mice[i];
+            mice[i] = SDL_mice[i].instance_id;
         }
         mice[i] = 0;
     } else {
@@ -342,6 +344,14 @@ SDL_MouseID *SDL_GetMice(int *count)
     return mice;
 }
 
+const char *SDL_GetMouseInstanceName(SDL_MouseID instance_id)
+{
+    int mouse_index = SDL_GetMouseIndex(instance_id);
+    if (mouse_index < 0) {
+        return NULL;
+    }
+    return SDL_mice[mouse_index].name;
+}
 
 void SDL_SetDefaultCursor(SDL_Cursor *cursor)
 {
diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h
index ea700a30b3ac8..f02811a5ef7c4 100644
--- a/src/events/SDL_mouse_c.h
+++ b/src/events/SDL_mouse_c.h
@@ -131,7 +131,7 @@ extern void SDL_PostInitMouse(void);
 extern SDL_bool SDL_IsMouse(Uint16 vendor, Uint16 product);
 
 /* A mouse has been added to the system */
-extern void SDL_AddMouse(SDL_MouseID mouseID, SDL_bool send_event);
+extern void SDL_AddMouse(SDL_MouseID mouseID, const char *name, SDL_bool send_event);
 
 /* A mouse has been removed from the system */
 extern void SDL_RemoveMouse(SDL_MouseID mouseID);
diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m
index 20c46b5cae894..eb96d3526677d 100644
--- a/src/video/cocoa/SDL_cocoavideo.m
+++ b/src/video/cocoa/SDL_cocoavideo.m
@@ -204,9 +204,9 @@ int Cocoa_VideoInit(SDL_VideoDevice *_this)
         // Assume we have a mouse and keyboard
         // We could use GCMouse and GCKeyboard if we needed to, as is done in SDL_uikitevents.m
         data.keyboardID = SDL_GetNextObjectID();
-        SDL_AddKeyboard(data.keyboardID, SDL_FALSE);
+        SDL_AddKeyboard(data.keyboardID, NULL, SDL_FALSE);
         data.mouseID = SDL_GetNextObjectID();
-        SDL_AddMouse(data.mouseID, SDL_FALSE);
+        SDL_AddMouse(data.mouseID, NULL, SDL_FALSE);
 
         data.allow_spaces = SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, SDL_TRUE);
         data.trackpad_is_touch_only = SDL_GetHintBoolean(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, SDL_FALSE);
diff --git a/src/video/emscripten/SDL_emscriptenvideo.c b/src/video/emscripten/SDL_emscriptenvideo.c
index 67bf340754cb2..1437b0a66904b 100644
--- a/src/video/emscripten/SDL_emscriptenvideo.c
+++ b/src/video/emscripten/SDL_emscriptenvideo.c
@@ -137,8 +137,8 @@ int Emscripten_VideoInit(SDL_VideoDevice *_this)
     Emscripten_InitMouse();
 
     /* Assume we have a mouse and keyboard */
-    SDL_AddKeyboard(EMSCRIPTEN_KEYBOARD_ID, SDL_FALSE);
-    SDL_AddMouse(EMSCRIPTEN_MOUSE_ID, SDL_FALSE);
+    SDL_AddKeyboard(EMSCRIPTEN_KEYBOARD_ID, NULL, SDL_FALSE);
+    SDL_AddMouse(EMSCRIPTEN_MOUSE_ID, NULL, SDL_FALSE);
 
     /* We're done! */
     return 0;
diff --git a/src/video/haiku/SDL_bvideo.cc b/src/video/haiku/SDL_bvideo.cc
index ea495af7aaa2c..9720e41d648f3 100644
--- a/src/video/haiku/SDL_bvideo.cc
+++ b/src/video/haiku/SDL_bvideo.cc
@@ -282,8 +282,8 @@ int HAIKU_VideoInit(SDL_VideoDevice *_this)
     HAIKU_MouseInit(_this);
 
     /* Assume we have a mouse and keyboard */
-    SDL_AddKeyboard(BAPP_KEYBOARD_ID, SDL_FALSE);
-    SDL_AddMouse(BAPP_MOUSE_ID, SDL_FALSE);
+    SDL_AddKeyboard(BAPP_KEYBOARD_ID, NULL, SDL_FALSE);
+    SDL_AddMouse(BAPP_MOUSE_ID, NULL, SDL_FALSE);
 
 #ifdef SDL_VIDEO_OPENGL
         /* testgl application doesn't load library, just tries to load symbols */
diff --git a/src/video/qnx/SDL_qnxvideo.c b/src/video/qnx/SDL_qnxvideo.c
index b0cd9fd795784..43e8a5ec0ba15 100644
--- a/src/video/qnx/SDL_qnxvideo.c
+++ b/src/video/qnx/SDL_qnxvideo.c
@@ -53,8 +53,8 @@ static int videoInit(SDL_VideoDevice *_this)
     }
 
     /* Assume we have a mouse and keyboard */
-    SDL_AddKeyboard(QNX_KEYBOARD_ID, SDL_FALSE);
-    SDL_AddMouse(QNX_MOUSE_ID, SDL_FALSE);
+    SDL_AddKeyboard(QNX_KEYBOARD_ID, NULL, SDL_FALSE);
+    SDL_AddMouse(QNX_MOUSE_ID, NULL, SDL_FALSE);
 
     return 0;
 }
diff --git a/src/video/riscos/SDL_riscosvideo.c b/src/video/riscos/SDL_riscosvideo.c
index 02b0abbc4fbcc..2068f17c4ae15 100644
--- a/src/video/riscos/SDL_riscosvideo.c
+++ b/src/video/riscos/SDL_riscosvideo.c
@@ -110,9 +110,9 @@ static int RISCOS_VideoInit(SDL_VideoDevice *_this)
 
     /* Assume we have a mouse and keyboard */
     data->keyboardID = SDL_GetNextObjectID();
-    SDL_AddKeyboard(data->keyboardID, SDL_FALSE);
+    SDL_AddKeyboard(data->keyboardID, NULL, SDL_FALSE);
     data->mouseID = SDL_GetNextObjectID();
-    SDL_AddMouse(data->mouseID, SDL_FALSE);
+    SDL_AddMouse(data->mouseID, NULL, SDL_FALSE);
 
     if (RISCOS_InitModes(_this) < 0) {
         return -1;
diff --git a/src/video/uikit/SDL_uikitevents.m b/src/video/uikit/SDL_uikitevents.m
index 233216486ac15..5c025e0b45422 100644
--- a/src/video/uikit/SDL_uikitevents.m
+++ b/src/video/uikit/SDL_uikitevents.m
@@ -178,7 +178,7 @@ static void OnGCKeyboardConnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0
 {
     SDL_KeyboardID keyboardID = (SDL_KeyboardID)(uintptr_t)keyboard;
 
-    SDL_AddKeyboard(keyboardID, SDL_TRUE);
+    SDL_AddKeyboard(keyboardID, NULL, SDL_TRUE);
 
     keyboard.keyboardInput.keyChangedHandler = ^(GCKeyboardInput *kbrd, GCControllerButtonInput *key, GCKeyCode keyCode, BOOL pressed) {
         SDL_SendKeyboardKey(0, keyboardID, pressed ? SDL_PRESSED : SDL_RELEASED, (SDL_Scancode)keyCode);
@@ -319,7 +319,7 @@ static void OnGCMouseConnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14
 {
     SDL_MouseID mouseID = (SDL_MouseID)(uintptr_t)mouse;
 
-    SDL_AddMouse(mouseID, SDL_TRUE);
+    SDL_AddMouse(mouseID, NULL, SDL_TRUE);
 
     mouse.mouseInput.leftButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
       OnGCMouseButtonChanged(mouseID, SDL_BUTTON_LEFT, pressed);
diff --git a/src/video/vita/SDL_vitakeyboard.c b/src/video/vita/SDL_vitakeyboard.c
index cb0ccc4e95bea..93dffc8f24979 100644
--- a/src/video/vita/SDL_vitakeyboard.c
+++ b/src/video/vita/SDL_vitakeyboard.c
@@ -45,7 +45,7 @@ void VITA_InitKeyboard(void)
     sceHidKeyboardEnumerate(&keyboard_hid_handle, 1);
 
     if (keyboard_hid_handle > 0) {
-        SDL_AddKeyboard((SDL_KeyboardID)keyboard_hid_handle, SDL_FALSE);
+        SDL_AddKeyboard((SDL_KeyboardID)keyboard_hid_handle, NULL, SDL_FALSE);
     }
 }
 
diff --git a/src/video/vita/SDL_vitamouse.c b/src/video/vita/SDL_vitamouse.c
index 46d736252f81b..dfac1601f0166 100644
--- a/src/video/vita/SDL_vitamouse.c
+++ b/src/video/vita/SDL_vitamouse.c
@@ -39,7 +39,7 @@ void VITA_InitMouse(void)
     sceHidMouseEnumerate(&mouse_hid_handle, 1);
 
     if (mouse_hid_handle > 0) {
-        SDL_AddMouse((SDL_MouseID)mouse_hid_handle, SDL_FALSE);
+        SDL_AddMouse((SDL_MouseID)mouse_hid_handle, NULL, SDL_FALSE);
     }
 }
 
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 24dde270f6e4d..a6f94d01b0e85 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -1715,7 +1715,7 @@ static void seat_handle_capabilities(void *data, struct wl_seat *seat,
         wl_pointer_add_listener(input->pointer, &pointer_listener, input);
 
         input->pointer_id = SDL_GetNextObjectID();
-        SDL_AddMouse(input->pointer_id, SDL_TRUE);
+        SDL_AddMouse(input->pointer_id, NULL, SDL_TRUE);
     } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
         if (input->cursor_shape) {
             wp_cursor_shape_device_v1_destroy(input->cursor_shape);
@@ -1748,7 +1748,7 @@ static void seat_handle_capabilities(void *data, struct wl_seat *seat,
                                  input);
 
         input->keyboard_id = SDL_GetNextObjectID();
-        SDL_AddKeyboard(input->keyboard_id, SDL_TRUE);
+        SDL_AddKeyboard(input->keyboard_id, NULL, SDL_TRUE);
     } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
         wl_keyboard_destroy(input->keyboard);
         input->keyboard = NULL;
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index f6d80544e57d9..11f31eb303b1b 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -32,6 +32,9 @@
 /* Dropfile support */
 #include <shellapi.h>
 
+/* Device names */
+#include <setupapi.h>
+
 /* For GET_X_LPARAM, GET_Y_LPARAM. */
 #include <windowsx.h>
 
@@ -720,6 +723,33 @@ static SDL_bool HasDeviceID(Uint32 deviceID, Uint32 *list, int count)
     return SDL_FALSE;
 }
 
+static void GetDeviceName(HDEVINFO devinfo, const char *instance, char *name, size_t len)
+{
+    name[0] = '\0';
+
+    SP_DEVINFO_DATA data;
+    SDL_zero(data);
+    data.cbSize = sizeof(data);
+    for (DWORD i = 0;; ++i) {
+        if (!SetupDiEnumDeviceInfo(devinfo, i, &data)) {
+            if (GetLastError() == ERROR_NO_MORE_ITEMS) {
+                break;
+            } else {
+                continue;
+            }
+        }
+
+        char DeviceInstanceId[64];
+        if (!SetupDiGetDeviceInstanceIdA(devinfo, &data, DeviceInstanceId, sizeof(DeviceInstanceId), NULL))
+            continue;
+
+        if (SDL_strcasecmp(instance, DeviceInstanceId) == 0) {
+            SetupDiGetDeviceRegistryPropertyA(devinfo, &data, SPDRP_DEVICEDESC, NULL, (PBYTE)name, len, NULL);
+            return;
+        }
+    }
+}
+
 void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, SDL_bool initial_check)
 {
     SDL_VideoData *data = _this->driverdata;
@@ -759,76 +789,106 @@ void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, SDL_bool initial_c
         return; /* oh well. */
     }
 
+    HDEVINFO devinfo = SetupDiGetClassDevsA(NULL, NULL, NULL, (DIGCF_ALLCLASSES | DIGCF_PRESENT));
+
+    old_keyboards = SDL_GetKeyboards(&old_keyboard_count);
+    old_mice = SDL_GetMice(&old_mouse_count);
+
     for (UINT i = 0; i < raw_device_count; i++) {
         RID_DEVICE_INFO rdi;
         char devName[MAX_PATH] = { 0 };
         UINT rdiSize = sizeof(rdi);
         UINT nameSize = SDL_arraysize(devName);
         int vendor = 0, product = 0;
+        DWORD dwType = raw_devices[i].dwType;
+        char *instance, *ptr, name[64];
 
-        rdi.cbSize = sizeof(rdi);
+        if (dwType != RIM_TYPEKEYBOARD && dwType != RIM_TYPEMOUSE) {
+            continue;
+        }
 
+        rdi.cbSize = sizeof(rdi);
         if (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) == ((UINT)-1) ||
             GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) == ((UINT)-1)) {
             continue;
         }
 
-        SDL_sscanf(devName, "\\\\?\\HID#VID_%X&PID_%X&", &vendor, &product);
+        /* Extract the device instance */
+        instance = devName;
+        while (*instance == '\\' || *instance == '?') {
+            ++instance;
+        }
+        for (ptr = instance; *ptr; ++ptr) {
+            if (*ptr == '#') {
+                *ptr = '\\';
+            }
+            if (*ptr == '{') {
+                if (ptr > instance && ptr[-1] == '\\') {
+                    --ptr;
+                }
+                break;
+            }
+        }
+        *ptr = '\0';
+
+        SDL_sscanf(instance, "HID\\VID_%X&PID_%X&", &vendor, &product);
 
-        switch (raw_devices[i].dwType) {
+        switch (dwType) {
         case RIM_TYPEKEYBOARD:
             if (SDL_IsKeyboard((Uint16)vendor, (Uint16)product, rdi.keyboard.dwNumberOfKeysTotal)) {
-                AddDeviceID((Uint32)(uintptr_t)raw_devices[i].hDevice, &new_keyboards, &new_keyboard_count);
+                SDL_KeyboardID keyboardID = (Uint32)(uintptr_t)raw_devices[i].hDevice;
+                AddDeviceID(keyboardID, &new_keyboards, &new_keyboard_count);
+                if (!HasDeviceID(keyboardID, old_keyboards, old_keyboard_count)) {
+                    GetDeviceName(devinfo, instance, name, sizeof(name));
+                    SDL_AddKeyboard(keyboardID, name, send_event);
+                }
             }
             break;
         case RIM_TYPEMOUSE:
             if (SDL_IsMouse((Uint16)vendor, (Uint16)product)

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