SDL: Add the raw platform specific key code to SDL_Keysym

From ef9bd8b609e08fbf7138da0d2a177d6b809767e0 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 18 Jun 2024 18:34:55 -0700
Subject: [PATCH] Add the raw platform specific key code to SDL_Keysym

This allows applications to handle keys that SDL doesn't recognize, in a platform dependent way.

Fixes https://github.com/libsdl-org/SDL/issues/6390
---
 include/SDL3/SDL_keyboard.h                 |  2 +-
 src/core/haiku/SDL_BApp.h                   |  2 +-
 src/core/linux/SDL_evdev.c                  | 10 ++-
 src/core/openbsd/SDL_wscons_kbd.c           | 12 ++--
 src/core/winrt/SDL_winrtapp_direct3d.cpp    |  4 +-
 src/events/SDL_keyboard.c                   | 75 +++++++++++----------
 src/events/SDL_keyboard_c.h                 |  6 +-
 src/joystick/android/SDL_sysjoystick.c      |  4 +-
 src/video/android/SDL_androidkeyboard.c     |  4 +-
 src/video/cocoa/SDL_cocoakeyboard.m         |  8 +--
 src/video/cocoa/SDL_cocoawindow.m           |  4 +-
 src/video/emscripten/SDL_emscriptenevents.c |  4 +-
 src/video/ngage/SDL_ngageevents.cpp         |  4 +-
 src/video/psp/SDL_pspevents.c               |  4 +-
 src/video/qnx/SDL_qnxkeyboard.c             |  4 +-
 src/video/riscos/SDL_riscosevents.c         |  4 +-
 src/video/uikit/SDL_uikitevents.m           |  2 +-
 src/video/uikit/SDL_uikitview.m             |  6 +-
 src/video/vita/SDL_vitakeyboard.c           | 56 +++++++--------
 src/video/wayland/SDL_waylandevents.c       |  6 +-
 src/video/windows/SDL_windowsevents.c       | 51 +++++++++-----
 src/video/winrt/SDL_winrtkeyboard.cpp       |  8 ++-
 src/video/x11/SDL_x11events.c               | 12 ++--
 test/checkkeys.c                            | 10 +--
 24 files changed, 163 insertions(+), 139 deletions(-)

diff --git a/include/SDL3/SDL_keyboard.h b/include/SDL3/SDL_keyboard.h
index c84eb78388cf8..ffa15949504c6 100644
--- a/include/SDL3/SDL_keyboard.h
+++ b/include/SDL3/SDL_keyboard.h
@@ -68,7 +68,7 @@ typedef struct SDL_Keysym
     SDL_Scancode scancode;      /**< SDL physical key code - see SDL_Scancode for details */
     SDL_Keycode sym;            /**< SDL virtual key code - see SDL_Keycode for details */
     SDL_Keymod mod;             /**< current key modifiers */
-    Uint16 unused;
+    Uint16 raw;                 /**< The platform dependent scancode for this event */
 } SDL_Keysym;
 
 /* Function prototypes */
diff --git a/src/core/haiku/SDL_BApp.h b/src/core/haiku/SDL_BApp.h
index 8af12eec502c0..37384fdb699d0 100644
--- a/src/core/haiku/SDL_BApp.h
+++ b/src/core/haiku/SDL_BApp.h
@@ -304,7 +304,7 @@ class SDL_BLooper : public BLooper
             return;
         }
         HAIKU_SetKeyState(scancode, state);
-        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, state, HAIKU_GetScancodeFromBeKey(scancode));
+        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, scancode, HAIKU_GetScancodeFromBeKey(scancode), state);
 
         if (state == SDL_PRESSED && SDL_TextInputActive()) {
             const int8 *keyUtf8;
diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c
index 1ed054ff4d0dd..f0f31b395afd1 100644
--- a/src/core/linux/SDL_evdev.c
+++ b/src/core/linux/SDL_evdev.c
@@ -373,12 +373,10 @@ void SDL_EVDEV_Poll(void)
 
                     /* Probably keyboard */
                     scan_code = SDL_EVDEV_translate_keycode(event->code);
-                    if (scan_code != SDL_SCANCODE_UNKNOWN) {
-                        if (event->value == 0) {
-                            SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, SDL_RELEASED, scan_code);
-                        } else if (event->value == 1 || event->value == 2 /* key repeated */) {
-                            SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, SDL_PRESSED, scan_code);
-                        }
+                    if (event->value == 0) {
+                        SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, event->code, scan_code, 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_EVDEV_kbd_keycode(_this->kbd, event->code, event->value);
                     break;
diff --git a/src/core/openbsd/SDL_wscons_kbd.c b/src/core/openbsd/SDL_wscons_kbd.c
index 81f94448cdaa4..b00e25ec8c4d1 100644
--- a/src/core/openbsd/SDL_wscons_kbd.c
+++ b/src/core/openbsd/SDL_wscons_kbd.c
@@ -560,22 +560,22 @@ static void Translate_to_keycode(SDL_WSCONS_input_data *input, int type, keysym_
     switch (keyDesc.command) {
     case KS_Cmd_ScrollBack:
     {
-        SDL_SendKeyboardKey(0, input->keyboardID, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_PAGEUP);
+        SDL_SendKeyboardKey(0, input->keyboardID, 0, SDL_SCANCODE_PAGEUP, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED);
         return;
     }
     case KS_Cmd_ScrollFwd:
     {
-        SDL_SendKeyboardKey(0, input->keyboardID, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_PAGEDOWN);
+        SDL_SendKeyboardKey(0, input->keyboardID, 0, SDL_SCANCODE_PAGEDOWN, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED);
         return;
     }
     }
     for (i = 0; i < sizeof(conversion_table) / sizeof(struct wscons_keycode_to_SDL); i++) {
         if (conversion_table[i].sourcekey == group[0]) {
-            SDL_SendKeyboardKey(0, input->keyboardID, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, conversion_table[i].targetKey);
+            SDL_SendKeyboardKey(0, input->keyboardID, group[0], conversion_table[i].targetKey, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED);
             return;
         }
     }
-    SDL_SendKeyboardKey(0, input->keyboardID, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_UNKNOWN);
+    SDL_SendKeyboardKey(0, input->keyboardID, group[0], SDL_SCANCODE_UNKNOWN, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED);
 }
 
 static void updateKeyboard(SDL_WSCONS_input_data *input)
@@ -809,13 +809,13 @@ static void updateKeyboard(SDL_WSCONS_input_data *input)
             } break;
             case WSCONS_EVENT_ALL_KEYS_UP:
                 for (i = 0; i < SDL_NUM_SCANCODES; i++) {
-                    SDL_SendKeyboardKey(0, input->keyboardID, SDL_RELEASED, i);
+                    SDL_SendKeyboardKey(0, input->keyboardID, 0, (SDL_Scancode)i, SDL_RELEASED);
                 }
                 break;
             }
 
             if (input->type == WSKBD_TYPE_USB && events[i].value <= 0xE7)
-                SDL_SendKeyboardKey(0, input->keyboardID, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, (SDL_Scancode)events[i].value);
+                SDL_SendKeyboardKey(0, input->keyboardID, 0, (SDL_Scancode)events[i].value, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED);
             else
                 Translate_to_keycode(input, type, events[i].value);
 
diff --git a/src/core/winrt/SDL_winrtapp_direct3d.cpp b/src/core/winrt/SDL_winrtapp_direct3d.cpp
index 3562794254643..38cbb08648a6e 100644
--- a/src/core/winrt/SDL_winrtapp_direct3d.cpp
+++ b/src/core/winrt/SDL_winrtapp_direct3d.cpp
@@ -724,8 +724,8 @@ void SDL_WinRTApp::OnCharacterReceived(Windows::UI::Core::CoreWindow ^ sender, W
 template <typename BackButtonEventArgs>
 static void WINRT_OnBackButtonPressed(BackButtonEventArgs ^ args)
 {
-    SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, SDL_SCANCODE_AC_BACK);
-    SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, SDL_SCANCODE_AC_BACK);
+    SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_AC_BACK, SDL_PRESSED);
+    SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_AC_BACK, SDL_RELEASED);
 
     if (SDL_GetHintBoolean(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, SDL_FALSE)) {
         args->Handled = true;
diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c
index c96058710d306..3492adc8c7b14 100644
--- a/src/events/SDL_keyboard.c
+++ b/src/events/SDL_keyboard.c
@@ -806,7 +806,7 @@ void SDL_ResetKeyboard(void)
 #endif
     for (scancode = (SDL_Scancode)0; scancode < SDL_NUM_SCANCODES; ++scancode) {
         if (keyboard->keystate[scancode] == SDL_PRESSED) {
-            SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, scancode);
+            SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDL_RELEASED);
         }
     }
 }
@@ -944,7 +944,7 @@ int SDL_SetKeyboardFocus(SDL_Window *window)
     return 0;
 }
 
-static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_KeyboardID keyboardID, Uint8 state, SDL_Scancode scancode, SDL_Keycode keycode)
+static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, SDL_Keycode keycode, Uint8 state)
 {
     SDL_Keyboard *keyboard = &SDL_keyboard;
     int posted;
@@ -953,10 +953,6 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_Keybo
     Uint8 repeat = SDL_FALSE;
     const Uint8 source = flags & KEYBOARD_SOURCE_MASK;
 
-    if (scancode == SDL_SCANCODE_UNKNOWN || scancode >= SDL_NUM_SCANCODES) {
-        return 0;
-    }
-
 #ifdef DEBUG_KEYBOARD
     printf("The '%s' key has been %s\n", SDL_GetScancodeName(scancode),
            state == SDL_PRESSED ? "pressed" : "released");
@@ -975,28 +971,34 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_Keybo
         return 0;
     }
 
-    /* Drop events that don't change state */
-    if (state) {
-        if (keyboard->keystate[scancode]) {
-            if (!(keyboard->keysource[scancode] & source)) {
-                keyboard->keysource[scancode] |= source;
+    if (scancode != SDL_SCANCODE_UNKNOWN && scancode < SDL_NUM_SCANCODES) {
+        /* Drop events that don't change state */
+        if (state) {
+            if (keyboard->keystate[scancode]) {
+                if (!(keyboard->keysource[scancode] & source)) {
+                    keyboard->keysource[scancode] |= source;
+                    return 0;
+                }
+                repeat = SDL_TRUE;
+            }
+            keyboard->keysource[scancode] |= source;
+        } else {
+            if (!keyboard->keystate[scancode]) {
                 return 0;
             }
-            repeat = SDL_TRUE;
-        }
-        keyboard->keysource[scancode] |= source;
-    } else {
-        if (!keyboard->keystate[scancode]) {
-            return 0;
+            keyboard->keysource[scancode] = 0;
         }
-        keyboard->keysource[scancode] = 0;
-    }
 
-    /* Update internal keyboard state */
-    keyboard->keystate[scancode] = state;
+        /* Update internal keyboard state */
+        keyboard->keystate[scancode] = state;
 
-    if (keycode == SDLK_UNKNOWN) {
-        keycode = keyboard->keymap[scancode];
+        if (keycode == SDLK_UNKNOWN) {
+            keycode = keyboard->keymap[scancode];
+        }
+
+    } else if (keycode == SDLK_UNKNOWN && rawcode == 0) {
+        /* Nothing to do! */
+        return 0;
     }
 
     if (source == KEYBOARD_HARDWARE) {
@@ -1070,6 +1072,7 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_Keybo
         event.key.keysym.scancode = scancode;
         event.key.keysym.sym = keycode;
         event.key.keysym.mod = keyboard->modstate;
+        event.key.keysym.raw = (Uint16)rawcode;
         event.key.windowID = keyboard->focus ? keyboard->focus->id : 0;
         event.key.which = keyboardID;
         posted = (SDL_PushEvent(&event) > 0);
@@ -1105,43 +1108,43 @@ int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch)
 
     if (mod & SDL_KMOD_SHIFT) {
         /* If the character uses shift, press shift down */
-        SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, SDL_SCANCODE_LSHIFT, SDLK_UNKNOWN);
+        SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, SDLK_UNKNOWN, SDL_PRESSED);
     }
 
     /* Send a keydown and keyup for the character */
-    SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, code, SDLK_UNKNOWN);
-    SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, code, SDLK_UNKNOWN);
+    SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, code, SDLK_UNKNOWN, SDL_PRESSED);
+    SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, code, SDLK_UNKNOWN, SDL_RELEASED);
 
     if (mod & SDL_KMOD_SHIFT) {
         /* If the character uses shift, release shift */
-        SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, SDL_SCANCODE_LSHIFT, SDLK_UNKNOWN);
+        SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, SDLK_UNKNOWN, SDL_RELEASED);
     }
     return 0;
 }
 
 int SDL_SendVirtualKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode)
 {
-    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, state, scancode, SDLK_UNKNOWN);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDLK_UNKNOWN, state);
 }
 
-int SDL_SendKeyboardKey(Uint64 timestamp, SDL_KeyboardID keyboardID, Uint8 state, SDL_Scancode scancode)
+int SDL_SendKeyboardKey(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, Uint8 state)
 {
-    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, state, scancode, SDLK_UNKNOWN);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, SDLK_UNKNOWN, state);
 }
 
-int SDL_SendKeyboardKeyAndKeycode(Uint64 timestamp, SDL_KeyboardID keyboardID, Uint8 state, SDL_Scancode scancode, SDL_Keycode keycode)
+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, state, scancode, keycode);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, keycode, state);
 }
 
 int SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode)
 {
-    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, scancode, SDLK_UNKNOWN);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDLK_UNKNOWN, SDL_PRESSED);
 }
 
-int SDL_SendKeyboardKeyIgnoreModifiers(Uint64 timestamp, SDL_KeyboardID keyboardID, Uint8 state, SDL_Scancode scancode)
+int SDL_SendKeyboardKeyIgnoreModifiers(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, Uint8 state)
 {
-    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE | KEYBOARD_IGNOREMODIFIERS, keyboardID, state, scancode, SDLK_UNKNOWN);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE | KEYBOARD_IGNOREMODIFIERS, keyboardID, rawcode, scancode, SDLK_UNKNOWN, state);
 }
 
 void SDL_ReleaseAutoReleaseKeys(void)
@@ -1152,7 +1155,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, SDL_RELEASED, scancode, SDLK_UNKNOWN);
+                SDL_SendKeyboardKeyInternal(0, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDLK_UNKNOWN, SDL_RELEASED);
             }
         }
         keyboard->autorelease_pending = SDL_FALSE;
diff --git a/src/events/SDL_keyboard_c.h b/src/events/SDL_keyboard_c.h
index 14b8dea124d76..3233b1b33a45e 100644
--- a/src/events/SDL_keyboard_c.h
+++ b/src/events/SDL_keyboard_c.h
@@ -68,13 +68,13 @@ extern int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch);
 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, Uint8 state, SDL_Scancode scancode);
+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, Uint8 state, SDL_Scancode scancode);
+extern int SDL_SendKeyboardKeyIgnoreModifiers(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, Uint8 state);
 
 /* 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. */
-extern int SDL_SendKeyboardKeyAndKeycode(Uint64 timestamp, SDL_KeyboardID keyboardID, Uint8 state, SDL_Scancode scancode, SDL_Keycode keycode);
+extern int SDL_SendKeyboardKeyAndKeycode(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, SDL_Keycode keycode, Uint8 state);
 
 /* Release all the autorelease keys */
 extern void SDL_ReleaseAutoReleaseKeys(void);
diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c
index 62bac58fadec2..d2c063deeb70f 100644
--- a/src/joystick/android/SDL_sysjoystick.c
+++ b/src/joystick/android/SDL_sysjoystick.c
@@ -205,7 +205,7 @@ int Android_OnPadDown(int device_id, int keycode)
         if (item && item->joystick) {
             SDL_SendJoystickButton(timestamp, item->joystick, button, SDL_PRESSED);
         } else {
-            SDL_SendKeyboardKey(timestamp, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, button_to_scancode(button));
+            SDL_SendKeyboardKey(timestamp, SDL_GLOBAL_KEYBOARD_ID, keycode, button_to_scancode(button), SDL_PRESSED);
         }
         SDL_UnlockJoysticks();
         return 0;
@@ -225,7 +225,7 @@ int Android_OnPadUp(int device_id, int keycode)
         if (item && item->joystick) {
             SDL_SendJoystickButton(timestamp, item->joystick, button, SDL_RELEASED);
         } else {
-            SDL_SendKeyboardKey(timestamp, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, button_to_scancode(button));
+            SDL_SendKeyboardKey(timestamp, SDL_GLOBAL_KEYBOARD_ID, keycode, button_to_scancode(button), SDL_RELEASED);
         }
         SDL_UnlockJoysticks();
         return 0;
diff --git a/src/video/android/SDL_androidkeyboard.c b/src/video/android/SDL_androidkeyboard.c
index 4c86c22b4bc86..45bf4021aa564 100644
--- a/src/video/android/SDL_androidkeyboard.c
+++ b/src/video/android/SDL_androidkeyboard.c
@@ -330,12 +330,12 @@ static SDL_Scancode TranslateKeycode(int keycode)
 
 int Android_OnKeyDown(int keycode)
 {
-    return SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_PRESSED, TranslateKeycode(keycode));
+    return SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, keycode, TranslateKeycode(keycode), SDL_PRESSED);
 }
 
 int Android_OnKeyUp(int keycode)
 {
-    return SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_RELEASED, TranslateKeycode(keycode));
+    return SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, keycode, TranslateKeycode(keycode), SDL_RELEASED);
 }
 
 SDL_bool Android_HasScreenKeyboardSupport(SDL_VideoDevice *_this)
diff --git a/src/video/cocoa/SDL_cocoakeyboard.m b/src/video/cocoa/SDL_cocoakeyboard.m
index 61866a413799f..5da5db1277ac5 100644
--- a/src/video/cocoa/SDL_cocoakeyboard.m
+++ b/src/video/cocoa/SDL_cocoakeyboard.m
@@ -231,9 +231,9 @@ static void HandleModifiers(SDL_VideoDevice *_this, SDL_Scancode code, unsigned
     }
 
     if (pressed) {
-        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_PRESSED, code);
+        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, 0, code, SDL_PRESSED);
     } else {
-        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_RELEASED, code);
+        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, 0, code, SDL_RELEASED);
     }
 }
 
@@ -414,7 +414,7 @@ void Cocoa_HandleKeyEvent(SDL_VideoDevice *_this, NSEvent *event)
             UpdateKeymap(data, SDL_TRUE);
         }
 
-        SDL_SendKeyboardKey(Cocoa_GetEventTimestamp([event timestamp]), SDL_DEFAULT_KEYBOARD_ID, SDL_PRESSED, code);
+        SDL_SendKeyboardKey(Cocoa_GetEventTimestamp([event timestamp]), SDL_DEFAULT_KEYBOARD_ID, scancode, code, SDL_PRESSED);
 #ifdef DEBUG_SCANCODES
         if (code == SDL_SCANCODE_UNKNOWN) {
             SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL forums/mailing list <https://discourse.libsdl.org/> or to Christian Walther <cwalther@gmx.ch>. Mac virtual key code is %d.\n", scancode);
@@ -433,7 +433,7 @@ void Cocoa_HandleKeyEvent(SDL_VideoDevice *_this, NSEvent *event)
         }
         break;
     case NSEventTypeKeyUp:
-        SDL_SendKeyboardKey(Cocoa_GetEventTimestamp([event timestamp]), SDL_DEFAULT_KEYBOARD_ID, SDL_RELEASED, code);
+        SDL_SendKeyboardKey(Cocoa_GetEventTimestamp([event timestamp]), SDL_DEFAULT_KEYBOARD_ID, scancode, code, SDL_RELEASED);
         break;
     case NSEventTypeFlagsChanged: {
         // see if the new modifierFlags mean any existing keys should be pressed/released...
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 452c6c4763a4b..f3a17b47686ac 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -1408,8 +1408,8 @@ - (void)flagsChanged:(NSEvent *)theEvent
     const SDL_bool osenabled = ([theEvent modifierFlags] & NSEventModifierFlagCapsLock) ? SDL_TRUE : SDL_FALSE;
     const SDL_bool sdlenabled = (SDL_GetModState() & SDL_KMOD_CAPS) ? SDL_TRUE : SDL_FALSE;
     if (osenabled ^ sdlenabled) {
-        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_PRESSED, SDL_SCANCODE_CAPSLOCK);
-        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
+        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, 0, SDL_SCANCODE_CAPSLOCK, SDL_PRESSED);
+        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, 0, SDL_SCANCODE_CAPSLOCK, SDL_RELEASED);
     }
 }
 - (void)keyDown:(NSEvent *)theEvent
diff --git a/src/video/emscripten/SDL_emscriptenevents.c b/src/video/emscripten/SDL_emscriptenevents.c
index d5edfe9b2811b..99ed3efbb738f 100644
--- a/src/video/emscripten/SDL_emscriptenevents.c
+++ b/src/video/emscripten/SDL_emscriptenevents.c
@@ -836,9 +836,7 @@ static EM_BOOL Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent
         }
     }
 
-    if (scancode != SDL_SCANCODE_UNKNOWN) {
-        SDL_SendKeyboardKeyAndKeycode(0, SDL_DEFAULT_KEYBOARD_ID, eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_PRESSED : SDL_RELEASED, scancode, keycode);
-    }
+    SDL_SendKeyboardKeyAndKeycode(0, SDL_DEFAULT_KEYBOARD_ID, 0, scancode, keycode, eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_PRESSED : SDL_RELEASED);
 
     /* if TEXTINPUT events are enabled we can't prevent keydown or we won't get keypress
      * we need to ALWAYS prevent backspace and tab otherwise chrome takes action and does bad navigation UX
diff --git a/src/video/ngage/SDL_ngageevents.cpp b/src/video/ngage/SDL_ngageevents.cpp
index 86deba4757404..cd82b6b4a4611 100644
--- a/src/video/ngage/SDL_ngageevents.cpp
+++ b/src/video/ngage/SDL_ngageevents.cpp
@@ -154,10 +154,10 @@ int HandleWsEvent(SDL_VideoDevice *_this, const TWsEvent &aWsEvent)
 
     switch (aWsEvent.Type()) {
     case EEventKeyDown: /* Key events */
-        SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, ConvertScancode(_this, aWsEvent.Key()->iScanCode));
+        SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, aWsEvent.Key()->iScanCode, ConvertScancode(_this, aWsEvent.Key()->iScanCode), SDL_PRESSED);
         break;
     case EEventKeyUp: /* Key events */
-        SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, ConvertScancode(_this, aWsEvent.Key()->iScanCode));
+        SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, aWsEvent.Key()->iScanCode, ConvertScancode(_this, aWsEvent.Key()->iScanCode), SDL_RELEASED);
         break;
     case EEventFocusGained: /* SDL window got focus */
         data->NGAGE_IsWindowFocused = ETrue;
diff --git a/src/video/psp/SDL_pspevents.c b/src/video/psp/SDL_pspevents.c
index 80ec27b017f95..ecc441c1ff1e3 100644
--- a/src/video/psp/SDL_pspevents.c
+++ b/src/video/psp/SDL_pspevents.c
@@ -90,7 +90,7 @@ void PSP_PumpEvents(SDL_VideoDevice *_this)
     if (changed) {
         for (i = 0; i < sizeof(keymap_psp) / sizeof(keymap_psp[0]); i++) {
             if (changed & keymap_psp[i].id) {
-                SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, (keys & keymap_psp[i].id) ? SDL_PRESSED : SDL_RELEASED, SDL_GetScancodeFromKey(keymap_psp[i].sym));
+                SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, keymap_psp[i].id, SDL_GetScancodeFromKey(keymap_psp[i].sym), (keys & keymap_psp[i].id) ? SDL_PRESSED : SDL_RELEASED);
             }
         }
     }
@@ -113,7 +113,7 @@ void PSP_PumpEvents(SDL_VideoDevice *_this)
                     sym.sym = keymap[raw];
                     /* not tested */
                     /* SDL_PrivateKeyboard(pressed?SDL_PRESSED:SDL_RELEASED, &sym); */
-                    SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, (keys & keymap_psp[i].id) ? SDL_PRESSED : SDL_RELEASED, SDL_GetScancodeFromKey(keymap[raw]));
+                    SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, raw, SDL_GetScancodeFromKey(keymap[raw]), (keys & keymap_psp[i].id) ? SDL_PRESSED : SDL_RELEASED);
                 }
             }
         }
diff --git a/src/video/qnx/SDL_qnxkeyboard.c b/src/video/qnx/SDL_qnxkeyboard.c
index 706b1fff9b05f..6b9bbf561bc7d 100644
--- a/src/video/qnx/SDL_qnxkeyboard.c
+++ b/src/video/qnx/SDL_qnxkeyboard.c
@@ -125,8 +125,8 @@ void handleKeyboardEvent(screen_event_t event)
     // FIXME:
     // Need to handle more key states (such as key combinations).
     if (val & KEY_DOWN) {
-        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_PRESSED, scancode);
+        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, val, scancode, SDL_PRESSED);
     } else {
-        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_RELEASED, scancode);
+        SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, val, scancode, SDL_RELEASED);
     }
 }
diff --git a/src/video/riscos/SDL_riscosevents.c b/src/video/riscos/SDL_riscosevents.c
index 3771fe5912b67..be05989a6720b 100644
--- a/src/video/riscos/SDL_riscosevents.c
+++ b/src/video/riscos/SDL_riscosevents.c
@@ -58,7 +58,7 @@ void RISCOS_PollKeyboard(SDL_VideoDevice *_this)
     for (i = 0; i < RISCOS_MAX_KEYS_PRESSED; i++) {
         if (driverdata->key_pressed[i] != 255) {
             if ((_kernel_osbyte(129, driverdata->key_pressed[i] ^ 0xff, 0xff) & 0xff) != 255) {
-                SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_RELEASED, SDL_RISCOS_translate_keycode(driverdata->key_pressed[i]));
+                SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, driverdata->key_pressed[i], SDL_RISCOS_translate_keycode(driverdata->key_pressed[i]), SDL_RELEASED);
                 driverdata->key_pressed[i] = 255;
             }
         }
@@ -81,7 +81,7 @@ void RISCOS_PollKeyboard(SDL_VideoDevice *_this)
             break;
 
         default:
-            SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, SDL_PRESSED, SDL_RISCOS_translate_keycode(key));
+            SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, key, SDL_RISCOS_translate_keycode(key), SDL_PRESSED);
 
             /* Record the press so we can detect release later. */
             for (i = 0; i < RISCOS_MAX_KEYS_PRESSED; i++) {
diff --git a/src/video/uikit/SDL_uikitevents.m b/src/video/uikit/SDL_uikitevents.m
index 5ce8e6d030d1d..97a1887cb7ee9 100644
--- a/src/video/uikit/SDL_uikitevents.m
+++ b/src/video/uikit/SDL_uikitevents.m
@@ -181,7 +181,7 @@ static void OnGCKeyboardConnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0
     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);
+        SDL_SendKeyboardKey(0, keyboardID, 0, (SDL_Scancode)keyCode, pressed ? SDL_PRESSED : SDL_RELEASED);
     };
 
     dispatch_queue_t queue = dispatch_queue_create("org.libsdl.input.keyboard", DISPATCH_QUEUE_SERIAL);
diff --git a/src/video/uikit/SDL_uikitview.m b/src/video/uikit/SDL_uikitview.m
index 0db9d3be4fedb..92d3dd3461bcc 100644
--- a/src/video/uikit/SDL_uikitview.m
+++ b/src/video/uikit/SDL_uikitview.m
@@ -414,7 +414,7 @@ - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)eve
     if (!SDL_HasKeyboard()) {
         for (UIPress *press in presses) {
             SDL_Scancode scancode = [self scancodeFromPress:press];
-            SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, scancode);
+            SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDL_PRESSED);
         }
     }
     if (SDL_TextInputActive()) {
@@ -427,7 +427,7 @@ - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)eve
     if (!SDL_HasKeyboard()) {
         for (UIPress *press in presses) {
             SDL_Scancode scancode = [self scancodeFromPress:press];
-            SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, scancode);
+            SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDL_RELEASED);
         }
     }
     if (SDL_TextInputActive()) {
@@ -440,7 +440,7 @@ - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *
     if (!SDL_HasKeyboard()) {
         for (UIPress *press in presses) {
             SDL_Scancode scancode = [self scancodeFromPress:press];
-            SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, scancode);
+            SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_GLOBAL_KEYBOARD_ID, 0, scancode, SDL_RELEASED);
         }
     }
     if (SDL_TextInputActive()) {
diff --git a/src/video/vita/SDL_vitakeyboard.c b/src/video/vita/SDL_vitakeyboard.c
index 93dffc8f24979..4c5a4bea6feb8 100644
--- a/src/video/vita/SDL_vitakeyboard.c
+++ b/src/video/vita/SDL_vitakeyboard.c
@@ -67,40 +67,40 @@ void VITA_PollKeyboard(void)
             // The k_report only reports the state of the LED
             if (k_reports[numReports - 1].modifiers[1] & 0x1) {
                 if (!(locks & 0x1)) {
-                    SDL_SendKeyboardKey(0, keyboardID, SDL_PRESSED, SDL_SCANCODE_NUMLOCKCLEAR);
+                    SDL_SendKeyboardKey(0, keyboardID, 0, SDL_SCANCODE_NUMLOCKCLEAR, SDL_PRESSED);
                     locks |= 0x1;
                 }
             } else {
                 if (locks & 0x1) {
-                    SDL_SendKeyboardKey(0, keyboardID, SDL_RELEASED, SDL_SCANCODE_NUMLOCKCLEAR);
-                    SDL_SendKeyboardKey(0, keyboardID, SDL_PRESSED, SDL_SCANCODE_NUMLOCKCLEAR)

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