Browse Source

Don't mute the console input if we can't read the keyboard

This makes sure you can hit Ctrl-C if you don't have permission to access the raw keyboard device.

Fixes https://github.com/libsdl-org/SDL/issues/4812
Sam Lantinga 1 year ago
parent
commit
ce9e1bd324

+ 32 - 2
src/core/linux/SDL_evdev.c

@@ -73,6 +73,7 @@ typedef struct SDL_evdevlist_item
 {
     char *path;
     int fd;
+    int udev_class;
 
     /* TODO: use this for every device, not just touchscreen */
     SDL_bool out_of_sync;
@@ -155,6 +156,15 @@ static int SDL_EVDEV_SetRelativeMouseMode(SDL_bool enabled)
     return 0;
 }
 
+static void SDL_EVDEV_UpdateKeyboardMute(void)
+{
+    if (SDL_EVDEV_GetDeviceCount(SDL_UDEV_DEVICE_KEYBOARD) > 0) {
+        SDL_EVDEV_kbd_set_muted(_this->kbd, SDL_TRUE);
+    } else {
+        SDL_EVDEV_kbd_set_muted(_this->kbd, SDL_FALSE);
+    }
+}
+
 int SDL_EVDEV_Init(void)
 {
     if (_this == NULL) {
@@ -208,6 +218,8 @@ int SDL_EVDEV_Init(void)
 #endif /* SDL_USE_LIBUDEV */
 
         _this->kbd = SDL_EVDEV_kbd_init();
+
+        SDL_EVDEV_UpdateKeyboardMute();
     }
 
     SDL_GetMouse()->SetRelativeMouseMode = SDL_EVDEV_SetRelativeMouseMode;
@@ -231,13 +243,13 @@ void SDL_EVDEV_Quit(void)
         SDL_UDEV_Quit();
 #endif /* SDL_USE_LIBUDEV */
 
-        SDL_EVDEV_kbd_quit(_this->kbd);
-
         /* Remove existing devices */
         while (_this->first != NULL) {
             SDL_EVDEV_device_removed(_this->first->path);
         }
 
+        SDL_EVDEV_kbd_quit(_this->kbd);
+
         SDL_assert(_this->first == NULL);
         SDL_assert(_this->last == NULL);
         SDL_assert(_this->num_devices == 0);
@@ -276,6 +288,19 @@ static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_cl
 }
 #endif /* SDL_USE_LIBUDEV */
 
+int SDL_EVDEV_GetDeviceCount(int device_class)
+{
+    SDL_evdevlist_item *item;
+    int count = 0;
+
+    for (item = _this->first; item != NULL; item = item->next) {
+        if ((item->udev_class & device_class) == device_class) {
+            ++count;
+        }
+    }
+    return count;
+}
+
 void SDL_EVDEV_Poll(void)
 {
     struct input_event events[32];
@@ -864,6 +889,8 @@ static int SDL_EVDEV_device_added(const char *dev_path, int udev_class)
         return SDL_OutOfMemory();
     }
 
+    item->udev_class = udev_class;
+
     if (ioctl(item->fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0) {
         item->relative_mouse = test_bit(REL_X, relbit) && test_bit(REL_Y, relbit);
         item->high_res_wheel = test_bit(REL_WHEEL_HI_RES, relbit);
@@ -900,6 +927,8 @@ static int SDL_EVDEV_device_added(const char *dev_path, int udev_class)
 
     SDL_EVDEV_sync_device(item);
 
+    SDL_EVDEV_UpdateKeyboardMute();
+
     return _this->num_devices++;
 }
 
@@ -926,6 +955,7 @@ static int SDL_EVDEV_device_removed(const char *dev_path)
             close(item->fd);
             SDL_free(item->path);
             SDL_free(item);
+            SDL_EVDEV_UpdateKeyboardMute();
             _this->num_devices--;
             return 0;
         }

+ 1 - 0
src/core/linux/SDL_evdev.h

@@ -30,6 +30,7 @@ struct input_event;
 
 extern int SDL_EVDEV_Init(void);
 extern void SDL_EVDEV_Quit(void);
+extern int SDL_EVDEV_GetDeviceCount(int device_class);
 extern void SDL_EVDEV_Poll(void);
 extern Uint64 SDL_EVDEV_GetEventTimestamp(struct input_event *event);
 

+ 35 - 18
src/core/linux/SDL_evdev_kbd.c

@@ -84,6 +84,7 @@ static fn_handler_fn *fn_handler[] = {
 struct SDL_EVDEV_keyboard_state
 {
     int console_fd;
+    SDL_bool muted;
     int old_kbd_mode;
     unsigned short **key_maps;
     unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
@@ -332,20 +333,6 @@ SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
         ioctl(kbd->console_fd, KDSKBMODE, K_UNICODE);
     }
 
-    /* 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);
-        }
-    }
     return kbd;
 }
 
@@ -355,12 +342,9 @@ void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
         return;
     }
 
-    kbd_unregister_emerg_cleanup();
+    SDL_EVDEV_kbd_set_muted(state, SDL_FALSE);
 
     if (state->console_fd >= 0) {
-        /* Restore the original keyboard mode */
-        ioctl(state->console_fd, KDSKBMODE, state->old_kbd_mode);
-
         close(state->console_fd);
         state->console_fd = -1;
     }
@@ -378,6 +362,39 @@ void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
     SDL_free(state);
 }
 
+void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, SDL_bool muted)
+{
+    if (state == NULL) {
+        return;
+    }
+
+    if (muted == state->muted) {
+        return;
+    }
+
+    if (muted) {
+        /* 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(state->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(state);
+            }
+        }
+    } else {
+        kbd_unregister_emerg_cleanup();
+
+        /* Restore the original keyboard mode */
+        ioctl(state->console_fd, KDSKBMODE, state->old_kbd_mode);
+    }
+}
+
 /*
  * Helper Functions.
  */

+ 1 - 0
src/core/linux/SDL_evdev_kbd.h

@@ -26,6 +26,7 @@ struct SDL_EVDEV_keyboard_state;
 typedef struct SDL_EVDEV_keyboard_state SDL_EVDEV_keyboard_state;
 
 extern SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void);
+extern void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, SDL_bool muted);
 extern void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down);
 extern void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state);