SDL: Keep the simplest mapping of scancode + modifer for a given keycode

From 16f8122a0d481825cfd77dea02ec75d3e02ddce4 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 31 Jan 2025 12:07:08 -0800
Subject: [PATCH] Keep the simplest mapping of scancode + modifer for a given
 keycode

Fixes https://github.com/libsdl-org/sdl2-compat/issues/259
---
 src/events/SDL_keymap.c | 32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

diff --git a/src/events/SDL_keymap.c b/src/events/SDL_keymap.c
index 5c3f784c6c7b8..bc21c33103476 100644
--- a/src/events/SDL_keymap.c
+++ b/src/events/SDL_keymap.c
@@ -72,20 +72,36 @@ void SDL_SetKeymapEntry(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod mo
         return;
     }
 
-    if (keycode == SDL_GetKeymapKeycode(keymap, scancode, modstate)) {
-        return;
-    }
-
-    Uint32 key = ((Uint32)NormalizeModifierStateForKeymap(modstate) << 16) | scancode;
+    modstate = NormalizeModifierStateForKeymap(modstate);
+    Uint32 key = ((Uint32)modstate << 16) | scancode;
     const void *value;
     if (SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
+        SDL_Keycode existing_keycode = (SDL_Keycode)(uintptr_t)value;
+        if (existing_keycode == keycode) {
+            // We already have this mapping
+            return;
+        }
+
         // 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);
+
+    bool update_keycode = true;
+    if (SDL_FindInHashTable(keymap->keycode_to_scancode, (void *)(uintptr_t)keycode, &value)) {
+        Uint32 existing_value = (Uint32)(uintptr_t)value;
+        SDL_Keymod existing_modstate = (SDL_Keymod)(existing_value >> 16);
+
+        // Keep the simplest combination of scancode and modifiers to generate this keycode
+        if (existing_modstate <= modstate) {
+            update_keycode = false;
+        } else {
+            SDL_RemoveFromHashTable(keymap->keycode_to_scancode, (void *)(uintptr_t)keycode);
+        }
+    }
+    if (update_keycode) {
+        SDL_InsertIntoHashTable(keymap->keycode_to_scancode, (void *)(uintptr_t)keycode, (void *)(uintptr_t)key);
+    }
 }
 
 SDL_Keycode SDL_GetKeymapKeycode(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate)