SDL: keyboard: Only send SDL_KEYMAPCHANGED when the keymap actually changes

From 8b438f7b51013a6494f385e26dd061e050a65f68 Mon Sep 17 00:00:00 2001
From: Cameron Gutman <[EMAIL REDACTED]>
Date: Sun, 31 Jul 2022 15:34:03 -0500
Subject: [PATCH] keyboard: Only send SDL_KEYMAPCHANGED when the keymap
 actually changes

---
 src/events/SDL_keyboard.c                | 20 ++++++++++++++++----
 src/events/SDL_keyboard_c.h              |  2 +-
 src/video/android/SDL_androidkeyboard.c  |  2 +-
 src/video/cocoa/SDL_cocoakeyboard.m      |  5 +----
 src/video/directfb/SDL_DirectFB_events.c |  4 ++--
 src/video/wayland/SDL_waylandevents.c    |  3 +--
 src/video/windows/SDL_windowsevents.c    |  3 +--
 src/video/windows/SDL_windowskeyboard.c  |  6 +++---
 src/video/windows/SDL_windowskeyboard.h  |  2 +-
 src/video/x11/SDL_x11events.c            |  6 ++----
 src/video/x11/SDL_x11keyboard.c          |  6 +++---
 src/video/x11/SDL_x11keyboard.h          |  2 +-
 12 files changed, 33 insertions(+), 28 deletions(-)

diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c
index aa75f5d82a1..9a8c4033fe2 100644
--- a/src/events/SDL_keyboard.c
+++ b/src/events/SDL_keyboard.c
@@ -590,24 +590,36 @@ SDL_GetDefaultKeymap(SDL_Keycode * keymap)
 }
 
 void
-SDL_SetKeymap(int start, SDL_Keycode * keys, int length)
+SDL_SetKeymap(int start, SDL_Keycode * keys, int length, SDL_bool send_event)
 {
     SDL_Keyboard *keyboard = &SDL_keyboard;
     SDL_Scancode scancode;
+    SDL_Keycode normalized_keymap[SDL_NUM_SCANCODES];
 
     if (start < 0 || start + length > SDL_NUM_SCANCODES) {
         return;
     }
 
-    SDL_memcpy(&keyboard->keymap[start], keys, sizeof(*keys) * length);
+    SDL_memcpy(&normalized_keymap[start], keys, sizeof(*keys) * length);
 
     /* The number key scancodes always map to the number key keycodes.
      * On AZERTY layouts these technically are symbols, but users (and games)
      * always think of them and view them in UI as number keys.
      */
-    keyboard->keymap[SDL_SCANCODE_0] = SDLK_0;
+    normalized_keymap[SDL_SCANCODE_0] = SDLK_0;
     for (scancode = SDL_SCANCODE_1; scancode <= SDL_SCANCODE_9; ++scancode) {
-        keyboard->keymap[scancode] = SDLK_1 + (scancode - SDL_SCANCODE_1);
+        normalized_keymap[scancode] = SDLK_1 + (scancode - SDL_SCANCODE_1);
+    }
+
+    /* If the mapping didn't really change, we're done here */
+    if (!SDL_memcmp(&keyboard->keymap[start], &normalized_keymap[start], sizeof(*keys) * length)) {
+        return;
+    }
+
+    SDL_memcpy(&keyboard->keymap[start], &normalized_keymap[start], sizeof(*keys) * length);
+
+    if (send_event) {
+        SDL_SendKeymapChangedEvent();
     }
 }
 
diff --git a/src/events/SDL_keyboard_c.h b/src/events/SDL_keyboard_c.h
index beee8276d7c..e911312d0e8 100644
--- a/src/events/SDL_keyboard_c.h
+++ b/src/events/SDL_keyboard_c.h
@@ -33,7 +33,7 @@ extern int SDL_KeyboardInit(void);
 extern void SDL_GetDefaultKeymap(SDL_Keycode * keymap);
 
 /* Set the mapping of scancode to key codes */
-extern void SDL_SetKeymap(int start, SDL_Keycode * keys, int length);
+extern void SDL_SetKeymap(int start, SDL_Keycode * keys, int length, SDL_bool send_event);
 
 /* Set a platform-dependent key name, overriding the default platform-agnostic
    name. Encoded as UTF-8. The string is not copied, thus the pointer given to
diff --git a/src/video/android/SDL_androidkeyboard.c b/src/video/android/SDL_androidkeyboard.c
index 7098d2e6bd4..e120360ea30 100644
--- a/src/video/android/SDL_androidkeyboard.c
+++ b/src/video/android/SDL_androidkeyboard.c
@@ -36,7 +36,7 @@ void Android_InitKeyboard(void)
 
     /* Add default scancode to key mapping */
     SDL_GetDefaultKeymap(keymap);
-    SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
+    SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES, SDL_FALSE);
 }
 
 static SDL_Scancode Android_Keycodes[] = {
diff --git a/src/video/cocoa/SDL_cocoakeyboard.m b/src/video/cocoa/SDL_cocoakeyboard.m
index fd96ea3ceb7..02c99e8e72a 100644
--- a/src/video/cocoa/SDL_cocoakeyboard.m
+++ b/src/video/cocoa/SDL_cocoakeyboard.m
@@ -439,10 +439,7 @@ - (NSArray *)validAttributesForMarkedText
                 keymap[scancode] = s[0];
             }
         }
-        SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
-        if (send_event) {
-            SDL_SendKeymapChangedEvent();
-        }
+        SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES, send_event);
         return;
     }
 
diff --git a/src/video/directfb/SDL_DirectFB_events.c b/src/video/directfb/SDL_DirectFB_events.c
index e6ca47ad3e5..4e0ec4d9f66 100644
--- a/src/video/directfb/SDL_DirectFB_events.c
+++ b/src/video/directfb/SDL_DirectFB_events.c
@@ -698,9 +698,9 @@ EnumKeyboards(DFBInputDeviceID device_id,
 
         SDL_GetDefaultKeymap(keymap);
 #if USE_MULTI_API
-        SDL_SetKeymap(devdata->num_keyboard, 0, keymap, SDL_NUM_SCANCODES);
+        SDL_SetKeymap(devdata->num_keyboard, 0, keymap, SDL_NUM_SCANCODES, SDL_FALSE);
 #else
-        SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
+        SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES, SDL_FALSE);
 #endif
         devdata->num_keyboard++;
 
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 08a7e1a3e1d..2b547faf673 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -1111,8 +1111,7 @@ keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
     WAYLAND_xkb_keymap_key_for_each(input->xkb.keymap,
                                     Wayland_keymap_iter,
                                     &keymap);
-    SDL_SetKeymap(0, keymap.keymap, SDL_NUM_SCANCODES);
-    SDL_SendKeymapChangedEvent();
+    SDL_SetKeymap(0, keymap.keymap, SDL_NUM_SCANCODES, SDL_TRUE);
 }
 
 static void
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 60e80a8cec9..4a1cfda89d3 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1044,8 +1044,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 #ifdef WM_INPUTLANGCHANGE
     case WM_INPUTLANGCHANGE:
         {
-            WIN_UpdateKeymap();
-            SDL_SendKeymapChangedEvent();
+            WIN_UpdateKeymap(SDL_TRUE);
         }
         returnCode = 1;
         break;
diff --git a/src/video/windows/SDL_windowskeyboard.c b/src/video/windows/SDL_windowskeyboard.c
index ac17fdf49b1..9ff31d79cf4 100644
--- a/src/video/windows/SDL_windowskeyboard.c
+++ b/src/video/windows/SDL_windowskeyboard.c
@@ -102,7 +102,7 @@ WIN_InitKeyboard(_THIS)
     data->ime_uielemsink = 0;
     data->ime_ippasink = 0;
 
-    WIN_UpdateKeymap();
+    WIN_UpdateKeymap(SDL_FALSE);
 
     SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
     SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows");
@@ -115,7 +115,7 @@ WIN_InitKeyboard(_THIS)
 }
 
 void
-WIN_UpdateKeymap()
+WIN_UpdateKeymap(SDL_bool send_event)
 {
     int i;
     SDL_Scancode scancode;
@@ -152,7 +152,7 @@ WIN_UpdateKeymap()
         }
     }
 
-    SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
+    SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES, send_event);
 }
 
 void
diff --git a/src/video/windows/SDL_windowskeyboard.h b/src/video/windows/SDL_windowskeyboard.h
index 57c4ffaf923..c9b5e10a6f5 100644
--- a/src/video/windows/SDL_windowskeyboard.h
+++ b/src/video/windows/SDL_windowskeyboard.h
@@ -24,7 +24,7 @@
 #define SDL_windowskeyboard_h_
 
 extern void WIN_InitKeyboard(_THIS);
-extern void WIN_UpdateKeymap(void);
+extern void WIN_UpdateKeymap(SDL_bool send_event);
 extern void WIN_QuitKeyboard(_THIS);
 
 extern void WIN_ResetDeadKeys(void);
diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index 6e3c0679b63..b347b079b72 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -842,8 +842,7 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
                 X11_XRefreshKeyboardMapping(&xevent->xmapping);
             }
 
-            X11_UpdateKeymap(_this);
-            SDL_SendKeymapChangedEvent();
+            X11_UpdateKeymap(_this, SDL_TRUE);
         } else if (xevent->type == PropertyNotify && videodata && videodata->windowlist) {
             char* name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
 
@@ -1470,8 +1469,7 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
                    icon). Since it changes the XKLAVIER_STATE property, we
                    notice and reinit our keymap here. This might not be the
                    right approach, but it seems to work. */
-                X11_UpdateKeymap(_this);
-                SDL_SendKeymapChangedEvent();
+                X11_UpdateKeymap(_this, SDL_TRUE);
             } else if (xevent->xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
                 Atom type;
                 int format;
diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c
index 6da6c7d2313..c14e13d1c0c 100644
--- a/src/video/x11/SDL_x11keyboard.c
+++ b/src/video/x11/SDL_x11keyboard.c
@@ -394,7 +394,7 @@ X11_InitKeyboard(_THIS)
         }
     }
 
-    X11_UpdateKeymap(_this);
+    X11_UpdateKeymap(_this, SDL_FALSE);
 
     SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
 
@@ -408,7 +408,7 @@ X11_InitKeyboard(_THIS)
 }
 
 void
-X11_UpdateKeymap(_THIS)
+X11_UpdateKeymap(_THIS, SDL_bool send_event)
 {
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
     int i;
@@ -468,7 +468,7 @@ X11_UpdateKeymap(_THIS)
             }
         }
     }
-    SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
+    SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES, send_event);
 }
 
 void
diff --git a/src/video/x11/SDL_x11keyboard.h b/src/video/x11/SDL_x11keyboard.h
index 9e20ae12ebe..4ce41069b7b 100644
--- a/src/video/x11/SDL_x11keyboard.h
+++ b/src/video/x11/SDL_x11keyboard.h
@@ -24,7 +24,7 @@
 #define SDL_x11keyboard_h_
 
 extern int X11_InitKeyboard(_THIS);
-extern void X11_UpdateKeymap(_THIS);
+extern void X11_UpdateKeymap(_THIS, SDL_bool send_event);
 extern void X11_QuitKeyboard(_THIS);
 extern void X11_StartTextInput(_THIS);
 extern void X11_StopTextInput(_THIS);