SDL: evdev_kbd: Use current keymap

From 96a2a6b94515c5a0c920d5ffd64bf83acb74d7a8 Mon Sep 17 00:00:00 2001
From: Michal Suchanek <[EMAIL REDACTED]>
Date: Fri, 3 Mar 2023 19:44:11 +0100
Subject: [PATCH] evdev_kbd: Use current keymap

keymap can change over time, caching the keymap causes wrong keys
returned when user changes keymap during runtime

Signed-off-by: Michal Suchanek <msuchanek@suse.de>
---
 src/core/linux/SDL_evdev_kbd.c | 126 ++++++++++++---------------------
 1 file changed, 47 insertions(+), 79 deletions(-)

diff --git a/src/core/linux/SDL_evdev_kbd.c b/src/core/linux/SDL_evdev_kbd.c
index f7ccca7ef931..6535fdfc819e 100644
--- a/src/core/linux/SDL_evdev_kbd.c
+++ b/src/core/linux/SDL_evdev_kbd.c
@@ -153,45 +153,6 @@ static void SDL_EVDEV_dump_keymap(SDL_EVDEV_keyboard_state *kbd)
 }
 #endif /* DUMP_KEYMAP */
 
-static int SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd)
-{
-    int i, j;
-
-    kbd->key_maps = (unsigned short **)SDL_calloc(MAX_NR_KEYMAPS, sizeof(unsigned short *));
-    if (!kbd->key_maps) {
-        return -1;
-    }
-
-    for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
-        struct kbentry kbe;
-
-        kbe.kb_table = i;
-        kbe.kb_index = 0;
-        if (ioctl(kbd->console_fd, KDGKBENT, &kbe) < 0) {
-            return -1;
-        }
-
-        if (kbe.kb_value == K_NOSUCHMAP) {
-            continue;
-        }
-
-        kbd->key_maps[i] = (unsigned short *)SDL_malloc(NR_KEYS * sizeof(unsigned short));
-        if (!kbd->key_maps[i]) {
-            return -1;
-        }
-
-        for (j = 0; j < NR_KEYS; ++j) {
-            kbe.kb_table = i;
-            kbe.kb_index = j;
-            if (ioctl(kbd->console_fd, KDGKBENT, &kbe) < 0) {
-                return -1;
-            }
-            kbd->key_maps[i][j] = (kbe.kb_value ^ 0xf000);
-        }
-    }
-    return 0;
-}
-
 static SDL_EVDEV_keyboard_state *kbd_cleanup_state = NULL;
 static int kbd_cleanup_sigactions_installed = 0;
 static int kbd_cleanup_atexit_installed = 0;
@@ -339,8 +300,8 @@ SDL_EVDEV_keyboard_state *
 SDL_EVDEV_kbd_init(void)
 {
     SDL_EVDEV_keyboard_state *kbd;
-    int i;
     char flag_state;
+    char kbtype;
     char shift_state[sizeof(long)] = { TIOCL_GETSHIFTSTATE, 0 };
 
     kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(*kbd));
@@ -348,10 +309,14 @@ SDL_EVDEV_kbd_init(void)
         return NULL;
     }
 
-    kbd->npadch = -1;
-
     /* This might fail if we're not connected to a tty (e.g. on the Steam Link) */
     kbd->console_fd = open("/dev/tty", O_RDONLY | O_CLOEXEC);
+    if (!((ioctl(kbd->console_fd, KDGKBTYPE, &kbtype) == 0) && ((kbtype == KB_101) || (kbtype == KB_84)))) {
+        close(kbd->console_fd);
+        kbd->console_fd = -1;
+    }
+
+    kbd->npadch = -1;
 
     if (ioctl(kbd->console_fd, TIOCLINUX, shift_state) == 0) {
         kbd->shift_state = *shift_state;
@@ -362,48 +327,27 @@ SDL_EVDEV_kbd_init(void)
     }
 
     kbd->accents = &default_accents;
-    if (ioctl(kbd->console_fd, KDGKBDIACR, kbd->accents) < 0) {
-        /* No worries, we'll use the default accent table */
-    }
-
     kbd->key_maps = default_key_maps;
+
     if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
         /* Set the keyboard in UNICODE mode and load the keymaps */
         ioctl(kbd->console_fd, KDSKBMODE, K_UNICODE);
+    }
 
-        if (SDL_EVDEV_kbd_load_keymaps(kbd) < 0) {
-            for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
-                if (kbd->key_maps[i]) {
-                    SDL_free(kbd->key_maps[i]);
-                }
-            }
-            SDL_free(kbd->key_maps);
-
-            kbd->key_maps = default_key_maps;
-        }
+    /* Allow inhibiting keyboard mute with env. variable for debugging etc. */
+    if (SDL_getenv("SDL_INPUT_LINUX_KEEP_KBD") == NULL) {
+        /* Mute the keyboard so keystrokes only generate evdev events
+         * and do not leak through to the console
+         */
+        ioctl(kbd->console_fd, KDSKBMODE, K_OFF);
 
-        /* Allow inhibiting keyboard mute with env. variable for debugging etc. */
-        if (SDL_getenv("SDL_INPUT_LINUX_KEEP_KBD") == NULL) {
-            /* Mute the keyboard so keystrokes only generate evdev events
-             * and do not leak through to the console
-             */
-            ioctl(kbd->console_fd, KDSKBMODE, K_OFF);
-
-            /* Make sure to restore keyboard if application fails to call
-             * SDL_Quit before exit or fatal signal is raised.
-             */
-            if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, SDL_FALSE)) {
-                kbd_register_emerg_cleanup(kbd);
-            }
+        /* Make sure to restore keyboard if application fails to call
+         * SDL_Quit before exit or fatal signal is raised.
+         */
+        if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, SDL_FALSE)) {
+            kbd_register_emerg_cleanup(kbd);
         }
     }
-
-#ifdef DUMP_ACCENTS
-    SDL_EVDEV_dump_accents(kbd);
-#endif
-#ifdef DUMP_KEYMAP
-    SDL_EVDEV_dump_keymap(kbd);
-#endif
     return kbd;
 }
 
@@ -489,6 +433,11 @@ static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
 
     kbd->diacr = 0;
 
+    if (kbd->console_fd >= 0)
+        if (ioctl(kbd->console_fd, KDGKBDIACR, kbd->accents) < 0) {
+            /* No worries, we'll use the default accent table */
+        }
+
     for (i = 0; i < kbd->accents->kb_cnt; i++) {
         if (kbd->accents->kbdiacr[i].diacr == d &&
             kbd->accents->kbdiacr[i].base == ch) {
@@ -795,7 +744,17 @@ void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode
     }
 
     if (keycode < NR_KEYS) {
-        keysym = key_map[keycode];
+        if (state->console_fd < 0) {
+            keysym = key_map[keycode];
+        } else {
+            struct kbentry kbe;
+            kbe.kb_table = shift_final;
+            kbe.kb_index = keycode;
+            if (ioctl(state->console_fd, KDGKBENT, &kbe) == 0)
+                keysym = (kbe.kb_value ^ 0xf000);
+            else
+                return;
+        }
     } else {
         return;
     }
@@ -814,9 +773,18 @@ void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode
             type = KT_LATIN;
 
             if (vc_kbd_led(state, K_CAPSLOCK)) {
-                key_map = state->key_maps[shift_final ^ (1 << KG_SHIFT)];
+                shift_final = shift_final ^ (1 << KG_SHIFT);
+                key_map = state->key_maps[shift_final];
                 if (key_map) {
-                    keysym = key_map[keycode];
+                    if (state->console_fd < 0) {
+                        keysym = key_map[keycode];
+                    } else {
+                        struct kbentry kbe;
+                        kbe.kb_table = shift_final;
+                        kbe.kb_index = keycode;
+                        if (ioctl(state->console_fd, KDGKBENT, &kbe) == 0)
+                            keysym = (kbe.kb_value ^ 0xf000);
+                    }
                 }
             }
         }