SDL: x11: Avoid excessive keymap reconstruction

From f79d0adfc9300173669db646d68a567ed7196508 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 24 Jun 2024 16:39:20 -0400
Subject: [PATCH] x11: Avoid excessive keymap reconstruction

KeymapNotify events happen on focus events, as well as when the keymap group changes. Query the current group and don't rebuild the keymap if it hasn't changed.

Note that some IME changes, such as activating intelligent Japanese or Chinese input methods on Gnome, will only trigger IBus activation, and won't send a keymap or group update as they use the existing layout.
---
 src/video/x11/SDL_x11events.c   | 12 +++++++++++-
 src/video/x11/SDL_x11keyboard.c |  5 ++---
 src/video/x11/SDL_x11video.h    |  1 +
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index abb75622bc0cb..251f6781412ed 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -1131,7 +1131,17 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
             printf("window %p: KeymapNotify!\n", data);
 #endif
             if (SDL_GetKeyboardFocus() != NULL) {
-                X11_UpdateKeymap(_this, SDL_TRUE);
+#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
+                if (videodata->xkb) {
+                    XkbStateRec state;
+                    if (X11_XkbGetState(videodata->display, XkbUseCoreKbd, &state) == Success) {
+                        if (state.group != videodata->xkb_group) {
+                            /* Only rebuild the keymap if the layout has changed. */
+                            X11_UpdateKeymap(_this, SDL_TRUE);
+                        }
+                    }
+                }
+#endif
                 X11_ReconcileKeyboardState(_this);
             }
         } else if (xevent->type == MappingNotify) {
diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c
index 2d772449c2def..6f7a6d3e0c362 100644
--- a/src/video/x11/SDL_x11keyboard.c
+++ b/src/video/x11/SDL_x11keyboard.c
@@ -347,7 +347,6 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, SDL_bool send_event)
     int i;
     SDL_Scancode scancode;
     SDL_Keymap *keymap;
-    unsigned char group = 0;
 
     keymap = SDL_CreateKeymap();
 
@@ -357,7 +356,7 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, SDL_bool send_event)
         X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb);
 
         if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) {
-            group = state.group;
+            data->xkb_group = state.group;
         }
     }
 #endif
@@ -372,7 +371,7 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, SDL_bool send_event)
                 continue;
             }
 
-            KeySym keysym = X11_KeyCodeToSym(_this, i, group, keymod_masks[m].xkb_mask);
+            KeySym keysym = X11_KeyCodeToSym(_this, i, data->xkb_group, keymod_masks[m].xkb_mask);
 
             /* Note: The default SDL scancode table sets this to right alt instead of AltGr/Mode, so handle it separately. */
             if (keysym != XK_ISO_Level3_Shift) {
diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h
index 80b5c47997923..f8e567c777cc2 100644
--- a/src/video/x11/SDL_x11video.h
+++ b/src/video/x11/SDL_x11video.h
@@ -125,6 +125,7 @@ struct SDL_VideoData
     XkbDescPtr xkb;
 #endif
     int xkb_event;
+    unsigned int xkb_group;
 
     KeyCode filter_code;
     Time filter_time;