SDL: The keycode in key events is the base, unmodified, keycode for the current keyboard layout

From d9dc4b320ad4023b51806faf419b3e2b4403fe0c Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 30 Jun 2024 19:58:48 -0700
Subject: [PATCH] The keycode in key events is the base, unmodified, keycode
 for the current keyboard layout

---
 include/SDL3/SDL_events.h                 |   5 +
 include/SDL3/SDL_hints.h                  |   8 +-
 include/SDL3/SDL_keycode.h                |  26 -----
 include/SDL3/SDL_scancode.h               |   2 +
 src/core/haiku/SDL_BApp.h                 |   5 -
 src/core/linux/SDL_evdev.c                |   8 +-
 src/events/SDL_keyboard.c                 | 125 +++++++++++++---------
 src/events/SDL_keyboard_c.h               |   5 +-
 src/events/SDL_keymap.c                   |  13 ++-
 src/test/SDL_test_common.c                |  13 ---
 src/video/haiku/SDL_bkeyboard.cc          |  19 ----
 src/video/haiku/SDL_bkeyboard.h           |   2 -
 src/video/uikit/SDL_uikitviewcontroller.m |   4 +-
 test/testaudiostreamdynamicresample.c     |  14 +--
 test/testintersections.c                  |  34 +++---
 15 files changed, 124 insertions(+), 159 deletions(-)

diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h
index d3e84af0fa362..112816717698b 100644
--- a/include/SDL3/SDL_events.h
+++ b/include/SDL3/SDL_events.h
@@ -315,6 +315,11 @@ typedef struct SDL_KeyboardDeviceEvent
 /**
  * Keyboard button event structure (event.key.*)
  *
+ * The `key` is the base SDL_Keycode generated by pressing the `scancode` using the current keyboard layout, applying any options specified in SDL_HINT_KEYCODE_OPTIONS. You can get the SDL_Keycode corresponding to the event scancode and modifiers directly from the keyboard layout, bypassing SDL_HINT_KEYCODE_OPTIONS, by calling SDL_GetKeyFromScancode().
+ *
+ * \sa SDL_GetKeyFromScancode
+ * \sa SDL_HINT_KEYCODE_OPTIONS
+ *
  * \since This struct is available since SDL 3.0.0.
  */
 typedef struct SDL_KeyboardEvent
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index e5913bf4bfd56..62847ea24c9ce 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -1999,11 +1999,7 @@ extern "C" {
  * This variable is a comma separated set of options for translating keycodes
  * in events:
  *
- * - "unmodified": The keycode is the symbol generated by pressing the key
- *   without any modifiers applied. e.g. Shift+A would yield the keycode
- *   SDLK_a, or 'a'.
- * - "modified": The keycode is the symbol generated by pressing the key with
- *   modifiers applied. e.g. Shift+A would yield the keycode SDLK_A, or 'A'.
+ * - "none": Keycode options are cleared, this overrides other options.
  * - "french_numbers": The number row on French keyboards is inverted, so
  *   pressing the 1 key would yield the keycode SDLK_1, or '1', instead of
  *   SDLK_AMPERSAND, or '&'
@@ -2012,7 +2008,7 @@ extern "C" {
  *   layout. e.g. pressing the key associated with SDL_SCANCODE_A on a Russian
  *   keyboard would yield 'a' instead of 'ф'.
  *
- * The default value for this hint is equivalent to "modified,french_numbers"
+ * The default value for this hint is "french_numbers"
  *
  * Some platforms like Emscripten only provide modified keycodes and the
  * options are not used.
diff --git a/include/SDL3/SDL_keycode.h b/include/SDL3/SDL_keycode.h
index e2a14951e6c29..f3a98a34b1721 100644
--- a/include/SDL3/SDL_keycode.h
+++ b/include/SDL3/SDL_keycode.h
@@ -88,32 +88,6 @@ typedef Uint32 SDL_Keycode;
 #define SDLK_GREATER                0x0000003eu /* '>' */
 #define SDLK_QUESTION               0x0000003fu /* '?' */
 #define SDLK_AT                     0x00000040u /* '@' */
-#define SDLK_A                      0x00000041u /* 'A' */
-#define SDLK_B                      0x00000042u /* 'B' */
-#define SDLK_C                      0x00000043u /* 'C' */
-#define SDLK_D                      0x00000044u /* 'D' */
-#define SDLK_E                      0x00000045u /* 'E' */
-#define SDLK_F                      0x00000046u /* 'F' */
-#define SDLK_G                      0x00000047u /* 'G' */
-#define SDLK_H                      0x00000048u /* 'H' */
-#define SDLK_I                      0x00000049u /* 'I' */
-#define SDLK_J                      0x0000004au /* 'J' */
-#define SDLK_K                      0x0000004bu /* 'K' */
-#define SDLK_L                      0x0000004cu /* 'L' */
-#define SDLK_M                      0x0000004du /* 'M' */
-#define SDLK_N                      0x0000004eu /* 'N' */
-#define SDLK_O                      0x0000004fu /* 'O' */
-#define SDLK_P                      0x00000050u /* 'P' */
-#define SDLK_Q                      0x00000051u /* 'Q' */
-#define SDLK_R                      0x00000052u /* 'R' */
-#define SDLK_S                      0x00000053u /* 'S' */
-#define SDLK_T                      0x00000054u /* 'T' */
-#define SDLK_U                      0x00000055u /* 'U' */
-#define SDLK_V                      0x00000056u /* 'V' */
-#define SDLK_W                      0x00000057u /* 'W' */
-#define SDLK_X                      0x00000058u /* 'X' */
-#define SDLK_Y                      0x00000059u /* 'Y' */
-#define SDLK_Z                      0x0000005au /* 'Z' */
 #define SDLK_LEFTBRACKET            0x0000005bu /* '[' */
 #define SDLK_BACKSLASH              0x0000005cu /* '\\' */
 #define SDLK_RIGHTBRACKET           0x0000005du /* ']' */
diff --git a/include/SDL3/SDL_scancode.h b/include/SDL3/SDL_scancode.h
index 62b098257945c..ccf8bb5f79daa 100644
--- a/include/SDL3/SDL_scancode.h
+++ b/include/SDL3/SDL_scancode.h
@@ -415,6 +415,8 @@ typedef enum SDL_Scancode
 
     /* Add any other keys here. */
 
+    SDL_SCANCODE_RESERVED = 400,    /**< 400-500 reserved for dynamic keycodes */
+
     SDL_NUM_SCANCODES = 512 /**< not a key, just marks the number of scancodes
                                  for array bounds */
 } SDL_Scancode;
diff --git a/src/core/haiku/SDL_BApp.h b/src/core/haiku/SDL_BApp.h
index e4f3b8dcce427..80a61c338bd62 100644
--- a/src/core/haiku/SDL_BApp.h
+++ b/src/core/haiku/SDL_BApp.h
@@ -302,11 +302,6 @@ class SDL_BLooper : public BLooper
             return;
         }
 
-        /* Make sure this isn't a repeated event (key pressed and held) */
-        if (state == SDL_PRESSED && HAIKU_GetKeyState(scancode) == SDL_PRESSED) {
-            return;
-        }
-        HAIKU_SetKeyState(scancode, state);
         SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, scancode, HAIKU_GetScancodeFromBeKey(scancode), state);
 
         win = GetSDLWindow(winID);
diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c
index f0f31b395afd1..43b3b3b77d06a 100644
--- a/src/core/linux/SDL_evdev.c
+++ b/src/core/linux/SDL_evdev.c
@@ -314,7 +314,7 @@ void SDL_EVDEV_Poll(void)
     struct input_event events[32];
     int i, j, len;
     SDL_evdevlist_item *item;
-    SDL_Scancode scan_code;
+    SDL_Scancode scancode;
     int mouse_button;
     SDL_Mouse *mouse;
     float norm_x, norm_y, norm_pressure;
@@ -372,11 +372,11 @@ void SDL_EVDEV_Poll(void)
                     }
 
                     /* Probably keyboard */
-                    scan_code = SDL_EVDEV_translate_keycode(event->code);
+                    scancode = SDL_EVDEV_translate_keycode(event->code);
                     if (event->value == 0) {
-                        SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, event->code, scan_code, SDL_RELEASED);
+                        SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, event->code, scancode, SDL_RELEASED);
                     } else if (event->value == 1 || event->value == 2 /* key repeated */) {
-                        SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, event->code, scan_code, SDL_PRESSED);
+                        SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, event->code, scancode, SDL_PRESSED);
                     }
                     SDL_EVDEV_kbd_keycode(_this->kbd, event->code, event->value);
                     break;
diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c
index 44fc1377879ed..f3530a27040a5 100644
--- a/src/events/SDL_keyboard.c
+++ b/src/events/SDL_keyboard.c
@@ -37,10 +37,9 @@
 
 #define KEYBOARD_SOURCE_MASK (KEYBOARD_HARDWARE | KEYBOARD_AUTORELEASE)
 
-#define KEYCODE_OPTION_APPLY_MODIFIERS  0x01
-#define KEYCODE_OPTION_FRENCH_NUMBERS   0x02
-#define KEYCODE_OPTION_LATIN_LETTERS    0x04
-#define DEFAULT_KEYCODE_OPTIONS (KEYCODE_OPTION_APPLY_MODIFIERS | KEYCODE_OPTION_FRENCH_NUMBERS)
+#define KEYCODE_OPTION_FRENCH_NUMBERS   0x01
+#define KEYCODE_OPTION_LATIN_LETTERS    0x02
+#define DEFAULT_KEYCODE_OPTIONS (KEYCODE_OPTION_FRENCH_NUMBERS)
 
 typedef struct SDL_KeyboardInstance
 {
@@ -61,6 +60,7 @@ typedef struct SDL_Keyboard
     Uint32 keycode_options;
     SDL_bool autorelease_pending;
     Uint64 hardware_timestamp;
+    int next_reserved_scancode;
 } SDL_Keyboard;
 
 static SDL_Keyboard SDL_keyboard;
@@ -73,16 +73,13 @@ static void SDLCALL SDL_KeycodeOptionsChanged(void *userdata, const char *name,
 
     if (hint && *hint) {
         keyboard->keycode_options = 0;
-        if (SDL_strstr(hint, "unmodified")) {
-            keyboard->keycode_options &= ~KEYCODE_OPTION_APPLY_MODIFIERS;
-        } else if (SDL_strstr(hint, "modified")) {
-            keyboard->keycode_options |= KEYCODE_OPTION_APPLY_MODIFIERS;
-        }
-        if (SDL_strstr(hint, "french_numbers")) {
-            keyboard->keycode_options |= KEYCODE_OPTION_FRENCH_NUMBERS;
-        }
-        if (SDL_strstr(hint, "latin_letters")) {
-            keyboard->keycode_options |= KEYCODE_OPTION_LATIN_LETTERS;
+        if (!SDL_strstr(hint, "none")) {
+            if (SDL_strstr(hint, "french_numbers")) {
+                keyboard->keycode_options |= KEYCODE_OPTION_FRENCH_NUMBERS;
+            }
+            if (SDL_strstr(hint, "latin_letters")) {
+                keyboard->keycode_options |= KEYCODE_OPTION_LATIN_LETTERS;
+            }
         }
     } else {
         keyboard->keycode_options = DEFAULT_KEYCODE_OPTIONS;
@@ -258,6 +255,32 @@ void SDL_SetKeymap(SDL_Keymap *keymap, SDL_bool send_event)
     }
 }
 
+static SDL_Scancode GetNextReservedScancode(void)
+{
+    SDL_Keyboard *keyboard = &SDL_keyboard;
+    SDL_Scancode scancode;
+
+    if (keyboard->next_reserved_scancode && keyboard->next_reserved_scancode < SDL_SCANCODE_RESERVED + 100) {
+        scancode = (SDL_Scancode)keyboard->next_reserved_scancode;
+    } else {
+        scancode = SDL_SCANCODE_RESERVED;
+    }
+    keyboard->next_reserved_scancode = (int)scancode + 1;
+
+    return scancode;
+}
+
+static void SetKeymapEntry(SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keycode keycode)
+{
+    SDL_Keyboard *keyboard = &SDL_keyboard;
+
+    if (!keyboard->keymap) {
+        keyboard->keymap = SDL_CreateKeymap();
+    }
+
+    SDL_SetKeymapEntry(keyboard->keymap, scancode, modstate, keycode);
+}
+
 SDL_Window *SDL_GetKeyboardFocus(void)
 {
     SDL_Keyboard *keyboard = &SDL_keyboard;
@@ -309,23 +332,21 @@ int SDL_SetKeyboardFocus(SDL_Window *window)
 
 static SDL_Keycode SDL_GetEventKeycode(SDL_Keyboard *keyboard, SDL_Scancode scancode, SDL_Keymod modstate)
 {
-    SDL_Keycode keycode;
+    SDL_bool shifted = (modstate & SDL_KMOD_SHIFT) != 0;
+
+    // We won't be applying any modifiers by default
+    modstate = SDL_KMOD_NONE;
 
     if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
         if (keyboard->non_latin_letters && (keyboard->keycode_options & KEYCODE_OPTION_LATIN_LETTERS)) {
-            if (keyboard->keycode_options & KEYCODE_OPTION_APPLY_MODIFIERS) {
-                keycode = SDL_GetDefaultKeyFromScancode(scancode, modstate);
-            } else {
-                keycode = SDL_GetDefaultKeyFromScancode(scancode, SDL_KMOD_NONE);
-            }
-            return keycode;
+            return SDL_GetDefaultKeyFromScancode(scancode, modstate);
         }
     }
 
     if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0) {
         if (keyboard->french_numbers && (keyboard->keycode_options & KEYCODE_OPTION_FRENCH_NUMBERS)) {
             // Invert the shift state to generate the correct keycode
-            if (modstate & SDL_KMOD_SHIFT) {
+            if (shifted) {
                 modstate &= ~SDL_KMOD_SHIFT;
             } else {
                 modstate |= SDL_KMOD_SHIFT;
@@ -333,19 +354,14 @@ static SDL_Keycode SDL_GetEventKeycode(SDL_Keyboard *keyboard, SDL_Scancode scan
         }
     }
 
-    if (keyboard->keycode_options & KEYCODE_OPTION_APPLY_MODIFIERS) {
-        keycode = SDL_GetKeyFromScancode(scancode, modstate);
-    } else {
-        keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE);
-    }
-    return keycode;
+    return SDL_GetKeyFromScancode(scancode, modstate);
 }
 
-static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, SDL_Keycode keycode, Uint8 state)
+static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, Uint8 state)
 {
     SDL_Keyboard *keyboard = &SDL_keyboard;
     int posted;
-    SDL_Keymod modifier;
+    SDL_Keycode keycode = SDLK_UNKNOWN;
     Uint32 type;
     Uint8 repeat = SDL_FALSE;
     const Uint8 source = flags & KEYBOARD_SOURCE_MASK;
@@ -368,7 +384,7 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_Keybo
         return 0;
     }
 
-    if (scancode != SDL_SCANCODE_UNKNOWN && scancode < SDL_NUM_SCANCODES) {
+    if (scancode > SDL_SCANCODE_UNKNOWN && scancode < SDL_NUM_SCANCODES) {
         /* Drop events that don't change state */
         if (state) {
             if (keyboard->keystate[scancode]) {
@@ -389,11 +405,9 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_Keybo
         /* Update internal keyboard state */
         keyboard->keystate[scancode] = state;
 
-        if (keycode == SDLK_UNKNOWN) {
-            keycode = SDL_GetEventKeycode(keyboard, scancode, keyboard->modstate);
-        }
+        keycode = SDL_GetEventKeycode(keyboard, scancode, keyboard->modstate);
 
-    } else if (keycode == SDLK_UNKNOWN && rawcode == 0) {
+    } else if (rawcode == 0) {
         /* Nothing to do! */
         return 0;
     }
@@ -406,6 +420,8 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_Keybo
 
     /* Update modifiers state if applicable */
     if (!(flags & KEYBOARD_IGNOREMODIFIERS) && !repeat) {
+        SDL_Keymod modifier;
+
         switch (keycode) {
         case SDLK_LCTRL:
             modifier = SDL_KMOD_LCTRL;
@@ -496,48 +512,53 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_Keybo
 int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch)
 {
     SDL_Keymod modstate = SDL_KMOD_NONE;
-    SDL_Scancode scancode = SDL_GetDefaultScancodeFromKey(ch, &modstate);
+    SDL_Scancode scancode = SDL_GetScancodeFromKey(ch, &modstate);
 
+    // Make sure we have this keycode in our keymap
+    if (scancode == SDL_SCANCODE_UNKNOWN && ch < SDLK_SCANCODE_MASK) {
+        scancode = GetNextReservedScancode();
+        SetKeymapEntry(scancode, modstate, ch);
+    }
 
     if (modstate & SDL_KMOD_SHIFT) {
         /* If the character uses shift, press shift down */
-        SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, SDLK_LSHIFT, SDL_PRESSED);
+        SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, SDL_PRESSED);
     }
 
     /* Send a keydown and keyup for the character */
-    SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, ch, SDL_PRESSED);
-    SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, ch, SDL_RELEASED);
+    SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDL_PRESSED);
+    SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDL_RELEASED);
 
     if (modstate & SDL_KMOD_SHIFT) {
         /* If the character uses shift, release shift */
-        SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, SDLK_LSHIFT, SDL_RELEASED);
+        SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, SDL_RELEASED);
     }
     return 0;
 }
 
-int SDL_SendVirtualKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode)
-{
-    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDLK_UNKNOWN, state);
-}
-
 int SDL_SendKeyboardKey(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, Uint8 state)
 {
-    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, SDLK_UNKNOWN, state);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, state);
 }
 
 int SDL_SendKeyboardKeyAndKeycode(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, SDL_Keycode keycode, Uint8 state)
 {
-    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, keycode, state);
+    if (state == SDL_PRESSED) {
+        // Make sure we have this keycode in our keymap
+        SetKeymapEntry(scancode, SDL_GetModState(), keycode);
+    }
+
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, state);
 }
 
-int SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode)
+int SDL_SendKeyboardKeyIgnoreModifiers(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, Uint8 state)
 {
-    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDLK_UNKNOWN, SDL_PRESSED);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE | KEYBOARD_IGNOREMODIFIERS, keyboardID, rawcode, scancode, state);
 }
 
-int SDL_SendKeyboardKeyIgnoreModifiers(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, Uint8 state)
+int SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode)
 {
-    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE | KEYBOARD_IGNOREMODIFIERS, keyboardID, rawcode, scancode, SDLK_UNKNOWN, state);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDL_PRESSED);
 }
 
 void SDL_ReleaseAutoReleaseKeys(void)
@@ -548,7 +569,7 @@ void SDL_ReleaseAutoReleaseKeys(void)
     if (keyboard->autorelease_pending) {
         for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_NUM_SCANCODES; ++scancode) {
             if (keyboard->keysource[scancode] == KEYBOARD_AUTORELEASE) {
-                SDL_SendKeyboardKeyInternal(0, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDLK_UNKNOWN, SDL_RELEASED);
+                SDL_SendKeyboardKeyInternal(0, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDL_RELEASED);
             }
         }
         keyboard->autorelease_pending = SDL_FALSE;
diff --git a/src/events/SDL_keyboard_c.h b/src/events/SDL_keyboard_c.h
index d305641f78b45..de3f162ecb19d 100644
--- a/src/events/SDL_keyboard_c.h
+++ b/src/events/SDL_keyboard_c.h
@@ -54,13 +54,10 @@ extern int SDL_SetKeyboardFocus(SDL_Window *window);
  */
 extern int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch);
 
-/* Send a key from a virtual key source, like an on-screen keyboard */
-extern int SDL_SendVirtualKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode);
-
 /* Send a keyboard key event */
 extern int SDL_SendKeyboardKey(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, Uint8 state);
-extern int SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode);
 extern int SDL_SendKeyboardKeyIgnoreModifiers(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, Uint8 state);
+extern int SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode);
 
 /* This is for platforms that don't know the keymap but can report scancode and keycode directly.
    Most platforms should prefer to optionally call SDL_SetKeymap and then use SDL_SendKeyboardKey. */
diff --git a/src/events/SDL_keymap.c b/src/events/SDL_keymap.c
index 60120270ecd85..b2182ebb585fe 100644
--- a/src/events/SDL_keymap.c
+++ b/src/events/SDL_keymap.c
@@ -69,11 +69,18 @@ void SDL_SetKeymapEntry(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod mo
         return;
     }
 
-    if (keycode == SDL_GetDefaultKeyFromScancode(scancode, modstate)) {
+    if (keycode == SDL_GetKeymapKeycode(keymap, scancode, modstate)) {
         return;
     }
 
     Uint32 key = ((Uint32)NormalizeModifierStateForKeymap(modstate) << 16) | scancode;
+    const void *value;
+    if (SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
+        // Changing the mapping, need to remove the existing entry from the keymap
+        SDL_RemoveFromHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key);
+        SDL_RemoveFromHashTable(keymap->keycode_to_scancode, value);
+    }
+
     SDL_InsertIntoHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, (void *)(uintptr_t)keycode);
     SDL_InsertIntoHashTable(keymap->keycode_to_scancode, (void *)(uintptr_t)keycode, (void *)(uintptr_t)key);
 }
@@ -254,11 +261,11 @@ SDL_Scancode SDL_GetDefaultScancodeFromKey(SDL_Keycode key, SDL_Keymod *modstate
         return (SDL_Scancode)(SDL_SCANCODE_A + key - SDLK_a);
     }
 
-    if (key >= SDLK_Z && key <= SDLK_Z) {
+    if (key >= 'A' && key <= 'Z') {
         if (modstate) {
             *modstate = SDL_KMOD_SHIFT;
         }
-        return (SDL_Scancode)(SDL_SCANCODE_A + key - SDLK_Z);
+        return (SDL_Scancode)(SDL_SCANCODE_A + key - 'Z');
     }
 
     for (int i = 0; i < SDL_arraysize(normal_default_symbols); ++i) {
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 791c5b4c386fd..f713a5671d19a 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -2206,7 +2206,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_O:
         case SDLK_o:
             if (withControl) {
                 /* Ctrl-O (or Ctrl-Shift-O) changes window opacity. */
@@ -2224,7 +2223,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_H:
         case SDLK_h:
             if (withControl) {
                 /* Ctrl-H changes cursor visibility. */
@@ -2235,7 +2233,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_C:
         case SDLK_c:
             if (withAlt) {
                 /* Alt-C copy awesome text to the primary selection! */
@@ -2261,7 +2258,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 break;
             }
             break;
-        case SDLK_V:
         case SDLK_v:
             if (withAlt) {
                 /* Alt-V paste awesome text from the primary selection! */
@@ -2289,7 +2285,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_F:
         case SDLK_f:
             if (withControl) {
                 /* Ctrl-F flash the window */
@@ -2299,7 +2294,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_G:
         case SDLK_g:
             if (withControl) {
                 /* Ctrl-G toggle mouse grab */
@@ -2309,7 +2303,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_K:
         case SDLK_k:
             if (withControl) {
                 /* Ctrl-K toggle keyboard grab */
@@ -2319,7 +2312,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_M:
         case SDLK_m:
             if (withControl) {
                 /* Ctrl-M maximize */
@@ -2342,14 +2334,12 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_R:
         case SDLK_r:
             if (withControl) {
                 /* Ctrl-R toggle mouse relative mode */
                 SDL_SetRelativeMouseMode(!SDL_GetRelativeMouseMode());
             }
             break;
-        case SDLK_T:
         case SDLK_t:
             if (withControl) {
                 /* Ctrl-T toggle topmost mode */
@@ -2364,7 +2354,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_Z:
         case SDLK_z:
             if (withControl) {
                 /* Ctrl-Z minimize */
@@ -2404,7 +2393,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
             }
 
             break;
-        case SDLK_B:
         case SDLK_b:
             if (withControl) {
                 /* Ctrl-B toggle window border */
@@ -2416,7 +2404,6 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
-        case SDLK_A:
         case SDLK_a:
             if (withControl) {
                 /* Ctrl-A toggle aspect ratio */
diff --git a/src/video/haiku/SDL_bkeyboard.cc b/src/video/haiku/SDL_bkeyboard.cc
index 5f3fbaf17a2af..45b444dc4e7f8 100644
--- a/src/video/haiku/SDL_bkeyboard.cc
+++ b/src/video/haiku/SDL_bkeyboard.cc
@@ -37,7 +37,6 @@ extern "C" {
 
 
 static SDL_Scancode keymap[KEYMAP_SIZE];
-static int8 keystate[KEYMAP_SIZE];
 
 void HAIKU_InitOSKeymap(void)
 {
@@ -45,10 +44,6 @@ void HAIKU_InitOSKeymap(void)
             keymap[i] = SDL_SCANCODE_UNKNOWN;
         }
 
-        for ( uint i = 0; i < KEYMAP_SIZE; ++i ) {
-            keystate[i] = SDL_RELEASED;
-        }
-
         keymap[0x01]        = SDL_SCANCODE_ESCAPE;
         keymap[B_F1_KEY]    = SDL_SCANCODE_F1;
         keymap[B_F2_KEY]    = SDL_SCANCODE_F2;
@@ -166,20 +161,6 @@ SDL_Scancode HAIKU_GetScancodeFromBeKey(int32 bkey) {
     }
 }
 
-int8 HAIKU_GetKeyState(int32 bkey) {
-    if (bkey > 0 && bkey < KEYMAP_SIZE) {
-        return keystate[bkey];
-    } else {
-        return SDL_RELEASED;
-    }
-}
-
-void HAIKU_SetKeyState(int32 bkey, int8 state) {
-    if (bkey > 0 && bkey < KEYMAP_SIZE) {
-        keystate[bkey] = state;
-    }
-}
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/video/haiku/SDL_bkeyboard.h b/src/video/haiku/SDL_bkeyboard.h
index d6bf261243479..e1896858d8c60 100644
--- a/src/video/haiku/SDL_bkeyboard.h
+++ b/src/video/haiku/SDL_bkeyboard.h
@@ -30,8 +30,6 @@ extern "C" {
 
 extern void HAIKU_InitOSKeymap(void);
 extern SDL_Scancode HAIKU_GetScancodeFromBeKey(int32 bkey);
-extern int8 HAIKU_GetKeyState(int32 bkey);
-extern void HAIKU_SetKeyState(int32 bkey, int8 state);
 
 #ifdef __cplusplus
 }
diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m
index 0d69f50fbb44e..4b6cd845a830e 100644
--- a/src/video/uikit/SDL_uikitviewcontroller.m
+++ b/src/video/uikit/SDL_uikitviewcontroller.m
@@ -487,8 +487,8 @@ - (void)textFieldTextDidChange:(NSNotification *)notification
             size_t deleteLength = SDL_utf8strlen([[committedText substringFromIndex:matchLength] UTF8String]);
             while (deleteLength > 0) {
                 /* Send distinct down and up events for each backspace action */
-                SDL_SendVirtualKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
-                SDL_SendVirtualKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
+                SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, SDL_PRESSED);
+                SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, SDL_RELEASED);
                 --deleteLength;
             }
         }
diff --git a/test/testaudiostreamdynamicresample.c b/test/testaudiostreamdynamicresample.c
index 4bea5ac68b220..b22af114d981e 100644
--- a/test/testaudiostreamdynamicresample.c
+++ b/test/testaudiostreamdynamicresample.c
@@ -218,23 +218,23 @@ static void loop(void)
         }
 #endif
         if (e.type == SDL_EVENT_KEY_DOWN) {
-            SDL_Keycode sym = e.key.key;
-            if (sym == SDLK_q) {
+            SDL_Keycode key = e.key.key;
+            if (key == SDLK_q) {
                 if (SDL_AudioDevicePaused(state->audio_id)) {
                     SDL_ResumeAudioDevice(state->audio_id);
                 } else {
                     SDL_PauseAudioDevice(state->audio_id);
                 }
-            } else if (sym == SDLK_w) {
+            } else if (key == SDLK_w) {
                 auto_loop = !auto_loop;
-            } else if (sym == SDLK_e) {
+            } else if (key == SDLK_e) {
                 auto_flush = !auto_flush;
-            } else if (sym == SDLK_a) {
+            } else if (key == SDLK_a) {
                 SDL_ClearAudioStream(stream);
                 SDL_Log("Cleared audio stream");
-            } else if (sym == SDLK_s) {
+            } else if (key == SDLK_s) {
                 queue_audio();
-            } else if (sym == SDLK_d || sym == SDLK_D) {
+            } else if (key == SDLK_d) {
                 float amount = 1.0f;
                 amount *= (e.key.mod & SDL_KMOD_CTRL) ? 10.0f : 1.0f;
                 amount *= (e.key.mod & SDL_KMOD_SHIFT) ? 10.0f : 1.0f;
diff --git a/test/testintersections.c b/test/testintersections.c
index ad16951ccadfb..9e62b9cea70be 100644
--- a/test/testintersections.c
+++ b/test/testintersections.c
@@ -226,25 +226,27 @@ static void loop(void *arg)
             break;
         case SDL_EVENT_KEY_DOWN:
             switch (event.key.key) {
-            case SDLK_L:
-                num_lines = 0;
-                break;
             case SDLK_l:
-                add_line(
-                    (float)SDL_rand(640),
-                    (float)SDL_rand(480),
-                    (float)SDL_rand(640),
-                    (float)SDL_rand(480));
-                break;
-            case SDLK_R:
-                num_rects = 0;
+                if (event.key.mod & SDL_KMOD_SHIFT) {
+                    num_lines = 0;
+                } else {
+                    add_line(
+                        (float)SDL_rand(640),
+                        (float)SDL_rand(480),
+                        (float)SDL_rand(640),
+                        (float)SDL_rand(480));
+                }
                 break;
             case SDLK_r:
-                add_rect(
-                    (float)SDL_rand(640),
-                    (float)SDL_rand(480),
-                    (float)SDL_rand(640),
-                    (float)SDL_rand(480));
+                if (event.key.mod & SDL_KMOD_SHIFT) {
+                    num_rects = 0;
+                } else {
+                    add_rect(
+                        (float)SDL_rand(640),
+                        (float)SDL_rand(480),
+                        (float)SDL_rand(640),
+     

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