|
@@ -21,6 +21,7 @@
|
|
|
#include "../../SDL_internal.h"
|
|
|
|
|
|
#include "SDL_evdev_kbd.h"
|
|
|
+#include "SDL_hints.h"
|
|
|
|
|
|
#ifdef SDL_INPUT_LINUXKD
|
|
|
|
|
@@ -34,6 +35,8 @@
|
|
|
#include <linux/vt.h>
|
|
|
#include <linux/tiocl.h> /* for TIOCL_GETSHIFTSTATE */
|
|
|
|
|
|
+#include <signal.h>
|
|
|
+
|
|
|
#include "../../events/SDL_events_c.h"
|
|
|
#include "SDL_evdev_kbd_default_accents.h"
|
|
|
#include "SDL_evdev_kbd_default_keymap.h"
|
|
@@ -191,6 +194,151 @@ static int SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd)
|
|
|
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;
|
|
|
+
|
|
|
+static struct sigaction old_sigaction[NSIG] = { 0 };
|
|
|
+
|
|
|
+static int fatal_signals[] =
|
|
|
+{
|
|
|
+ /* Handlers for SIGTERM and SIGINT are installed in SDL_QuitInit. */
|
|
|
+ SIGHUP, SIGQUIT, SIGILL, SIGABRT,
|
|
|
+ SIGFPE, SIGSEGV, SIGPIPE, SIGBUS,
|
|
|
+ SIGSYS
|
|
|
+};
|
|
|
+
|
|
|
+static void kbd_cleanup(void)
|
|
|
+{
|
|
|
+ SDL_EVDEV_keyboard_state* kbd = kbd_cleanup_state;
|
|
|
+ if (kbd == NULL) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ kbd_cleanup_state = NULL;
|
|
|
+
|
|
|
+ fprintf(stderr, "(SDL restoring keyboard) ");
|
|
|
+ ioctl(kbd->console_fd, KDSKBMODE, kbd->old_kbd_mode);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+SDL_EVDEV_kbd_reraise_signal(int sig)
|
|
|
+{
|
|
|
+ raise(sig);
|
|
|
+}
|
|
|
+
|
|
|
+siginfo_t* SDL_EVDEV_kdb_cleanup_siginfo = NULL;
|
|
|
+void* SDL_EVDEV_kdb_cleanup_ucontext = NULL;
|
|
|
+
|
|
|
+static void kbd_cleanup_signal_action(int signum, siginfo_t* info, void* ucontext)
|
|
|
+{
|
|
|
+ struct sigaction* old_action_p = &(old_sigaction[signum]);
|
|
|
+ sigset_t sigset;
|
|
|
+
|
|
|
+ /* Restore original signal handler before going any further. */
|
|
|
+ sigaction(signum, old_action_p, NULL);
|
|
|
+
|
|
|
+ /* Unmask current signal. */
|
|
|
+ sigemptyset(&sigset);
|
|
|
+ sigaddset(&sigset, signum);
|
|
|
+ sigprocmask(SIG_UNBLOCK, &sigset, NULL);
|
|
|
+
|
|
|
+ /* Save original signal info and context for archeologists. */
|
|
|
+ SDL_EVDEV_kdb_cleanup_siginfo = info;
|
|
|
+ SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
|
|
|
+
|
|
|
+ /* Restore keyboard. */
|
|
|
+ kbd_cleanup();
|
|
|
+
|
|
|
+ /* Reraise signal. */
|
|
|
+ SDL_EVDEV_kbd_reraise_signal(signum);
|
|
|
+}
|
|
|
+
|
|
|
+static void kbd_unregister_emerg_cleanup()
|
|
|
+{
|
|
|
+ int tabidx, signum;
|
|
|
+
|
|
|
+ kbd_cleanup_state = NULL;
|
|
|
+
|
|
|
+ if (!kbd_cleanup_sigactions_installed) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ kbd_cleanup_sigactions_installed = 0;
|
|
|
+
|
|
|
+ for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
|
|
|
+ struct sigaction* old_action_p;
|
|
|
+ struct sigaction cur_action;
|
|
|
+ signum = fatal_signals[tabidx];
|
|
|
+ old_action_p = &(old_sigaction[signum]);
|
|
|
+
|
|
|
+ /* Examine current signal action */
|
|
|
+ if (sigaction(signum, NULL, &cur_action))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Check if action installed and not modifed */
|
|
|
+ if (!(cur_action.sa_flags & SA_SIGINFO)
|
|
|
+ || cur_action.sa_sigaction != &kbd_cleanup_signal_action)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Restore original action */
|
|
|
+ sigaction(signum, old_action_p, NULL);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void kbd_cleanup_atexit(void)
|
|
|
+{
|
|
|
+ /* Restore keyboard. */
|
|
|
+ kbd_cleanup();
|
|
|
+
|
|
|
+ /* Try to restore signal handlers in case shared library is being unloaded */
|
|
|
+ kbd_unregister_emerg_cleanup();
|
|
|
+}
|
|
|
+
|
|
|
+static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state * kbd)
|
|
|
+{
|
|
|
+ int tabidx, signum;
|
|
|
+
|
|
|
+ if (kbd_cleanup_state != NULL) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ kbd_cleanup_state = kbd;
|
|
|
+
|
|
|
+ if (!kbd_cleanup_atexit_installed) {
|
|
|
+ /* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
|
|
|
+ * functions that are called when the shared library is unloaded.
|
|
|
+ * -- man atexit(3)
|
|
|
+ */
|
|
|
+ atexit(kbd_cleanup_atexit);
|
|
|
+ kbd_cleanup_atexit_installed = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (kbd_cleanup_sigactions_installed) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ kbd_cleanup_sigactions_installed = 1;
|
|
|
+
|
|
|
+ for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
|
|
|
+ struct sigaction* old_action_p;
|
|
|
+ struct sigaction new_action;
|
|
|
+ signum = fatal_signals[tabidx];
|
|
|
+ old_action_p = &(old_sigaction[signum]);
|
|
|
+ if (sigaction(signum, NULL, old_action_p))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Skip SIGHUP and SIGPIPE if handler is already installed
|
|
|
+ * - assume the handler will do the cleanup
|
|
|
+ */
|
|
|
+ if ((signum == SIGHUP || signum == SIGPIPE)
|
|
|
+ && (old_action_p->sa_handler != SIG_DFL
|
|
|
+ || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ new_action = *old_action_p;
|
|
|
+ new_action.sa_flags |= SA_SIGINFO;
|
|
|
+ new_action.sa_sigaction = &kbd_cleanup_signal_action;
|
|
|
+ sigaction(signum, &new_action, NULL);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
SDL_EVDEV_keyboard_state *
|
|
|
SDL_EVDEV_kbd_init(void)
|
|
|
{
|
|
@@ -238,10 +386,20 @@ SDL_EVDEV_kbd_init(void)
|
|
|
kbd->key_maps = default_key_maps;
|
|
|
}
|
|
|
|
|
|
- /* 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 (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);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#ifdef DUMP_ACCENTS
|
|
@@ -260,6 +418,8 @@ SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ kbd_unregister_emerg_cleanup();
|
|
|
+
|
|
|
if (kbd->console_fd >= 0) {
|
|
|
/* Restore the original keyboard mode */
|
|
|
ioctl(kbd->console_fd, KDSKBMODE, kbd->old_kbd_mode);
|