| | |
| | | #include "../../SDL_internal.h" |
| | | |
| | | #include "SDL_evdev_kbd.h" |
| | | #include "SDL_hints.h" |
| | | |
| | | #ifdef SDL_INPUT_LINUXKD |
| | | |
| | |
| | | #include <linux/keyboard.h> |
| | | #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" |
| | |
| | | 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]; |
| | | |
| | | 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) |
| | | { |
| | |
| | | kbd->key_maps = default_key_maps; |
| | | } |
| | | |
| | | /* 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 |
| | |
| | | if (!kbd) { |
| | | return; |
| | | } |
| | | |
| | | kbd_unregister_emerg_cleanup(); |
| | | |
| | | if (kbd->console_fd >= 0) { |
| | | /* Restore the original keyboard mode */ |
| | |
| | | shift_final = (kbd->shift_state | kbd->slockstate) ^ kbd->lockstate; |
| | | key_map = kbd->key_maps[shift_final]; |
| | | if (!key_map) { |
| | | /* Unsupported shift state (e.g. ctrl = 4, alt = 8), just reset to the default state */ |
| | | kbd->shift_state = 0; |
| | | kbd->slockstate = 0; |
| | | kbd->lockstate = 0; |
| | | return; |
| | | } |
| | | |