diff --git a/src/core/freebsd/SDL_evdev_kbd_freebsd.c b/src/core/freebsd/SDL_evdev_kbd_freebsd.c index afd3ad05804f4..acc7643399b55 100644 --- a/src/core/freebsd/SDL_evdev_kbd_freebsd.c +++ b/src/core/freebsd/SDL_evdev_kbd_freebsd.c @@ -27,19 +27,26 @@ // This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source, slightly modified to work with FreeBSD +#include #include #include #include #include #include +#include #include #include "../../events/SDL_events_c.h" #include "SDL_evdev_kbd_default_keyaccmap.h" +#ifndef K_OFF +#define K_OFF 0x04 +#endif + typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd); + /* * Keyboard State */ @@ -48,6 +55,7 @@ struct SDL_EVDEV_keyboard_state { int console_fd; int keyboard_fd; + bool muted; unsigned long old_kbd_mode; unsigned short **key_maps; keymap_t *key_map; @@ -63,6 +71,10 @@ struct SDL_EVDEV_keyboard_state char shift_state; char text[128]; unsigned int text_len; + void (*vt_release_callback)(void *); + void *vt_release_callback_data; + void (*vt_acquire_callback)(void *); + void *vt_acquire_callback_data; }; static bool SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd) @@ -83,22 +95,28 @@ static int fatal_signals[] = { SIGSYS }; -static void kbd_cleanup(void) +static void vt_update_mouse(SDL_EVDEV_keyboard_state *kbd, int operation) { struct mouse_info mData; + + SDL_zero(mData); + mData.operation = operation; + ioctl(kbd->console_fd, CONS_MOUSECTL, &mData); +} + +static void kbd_cleanup(void) +{ SDL_EVDEV_keyboard_state *kbd = kbd_cleanup_state; if (!kbd) { return; } kbd_cleanup_state = NULL; - SDL_zero(mData); - mData.operation = MOUSE_SHOW; ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode); if (kbd->keyboard_fd != kbd->console_fd) { close(kbd->keyboard_fd); } ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index)); - ioctl(kbd->console_fd, CONS_MOUSECTL, &mData); + vt_update_mouse(kbd, true); } void SDL_EVDEV_kbd_reraise_signal(int sig) @@ -220,15 +238,108 @@ static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state *kbd) } } +enum { + VT_SIGNAL_NONE, + VT_SIGNAL_RELEASE, + VT_SIGNAL_ACQUIRE, +}; +static int vt_release_signal; +static int vt_acquire_signal; +static SDL_AtomicInt vt_signal_pending; +SDL_AtomicInt vt_current; + +typedef void (*signal_handler)(int signum); + + +static void kbd_vt_release_signal_action(int signum) +{ + SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_RELEASE); + SDL_SetAtomicInt(&vt_current, VT_THEIRS); +} + +static void kbd_vt_acquire_signal_action(int signum) +{ + SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_ACQUIRE); + SDL_SetAtomicInt(&vt_current, VT_OURS); +} + +static bool setup_vt_signal(int signum, signal_handler handler) +{ + struct sigaction *old_action_p; + struct sigaction new_action; + old_action_p = &(old_sigaction[signum]); + SDL_zero(new_action); + new_action.sa_handler = handler; + new_action.sa_flags = SA_RESTART; + if (sigaction(signum, &new_action, old_action_p) < 0) { + return false; + } + if (old_action_p->sa_handler != SIG_DFL) { + // This signal is already in use + if (signum == SIGUSR1 || signum == SIGUSR2) { + /* + * For the moment we have no other options in FreeBSD because + * the vt(4) will not accept signal numbers over 32. + */ + return true; + } + sigaction(signum, old_action_p, NULL); + return false; + } + return true; +} + +static void kbd_vt_quit(int console_fd) +{ + struct vt_mode mode; + + if (vt_release_signal) { + sigaction(vt_release_signal, &old_sigaction[vt_release_signal], NULL); + vt_release_signal = 0; + } + if (vt_acquire_signal) { + sigaction(vt_acquire_signal, &old_sigaction[vt_acquire_signal], NULL); + vt_acquire_signal = 0; + } + + SDL_zero(mode); + mode.mode = VT_AUTO; + ioctl(console_fd, VT_SETMODE, &mode); +} + +static bool kbd_vt_init(int console_fd) +{ + struct vt_mode mode; + + if (setup_vt_signal(SIGUSR1, kbd_vt_release_signal_action)) { + vt_release_signal = SIGUSR1; + } + if (setup_vt_signal(SIGUSR2, kbd_vt_acquire_signal_action)) { + vt_acquire_signal = SIGUSR2; + } + if (!vt_release_signal || !vt_acquire_signal ) { + kbd_vt_quit(console_fd); + return false; + } + + SDL_zero(mode); + mode.mode = VT_PROCESS; + mode.relsig = vt_release_signal; + mode.acqsig = vt_acquire_signal; + mode.frsig = SIGIO; + if (ioctl(console_fd, VT_SETMODE, &mode) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Failed VT_SETMODE ioctl: %s", strerror(errno)); + kbd_vt_quit(console_fd); + return false; + } + return true; +} + SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void) { SDL_EVDEV_keyboard_state *kbd; - struct mouse_info mData; char flag_state; - char *devicePath; - SDL_zero(mData); - mData.operation = MOUSE_HIDE; kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(SDL_EVDEV_keyboard_state)); if (!kbd) { return NULL; @@ -246,7 +357,6 @@ SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void) kbd->kbInfo = SDL_calloc(1, sizeof(keyboard_info_t)); ioctl(kbd->console_fd, KDGKBINFO, kbd->kbInfo); - ioctl(kbd->console_fd, CONS_MOUSECTL, &mData); if (ioctl(kbd->console_fd, KDGKBSTATE, &flag_state) == 0) { kbd->ledflagstate = flag_state; @@ -265,43 +375,31 @@ SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void) kbd->key_map = &keymap_default_us_acc; } - if (SDL_GetHintBoolean(SDL_HINT_MUTE_CONSOLE_KEYBOARD, true)) { - /* Take keyboard from console and open the actual keyboard device. - * Ensures that the keystrokes do not leak through to the console. - */ - ioctl(kbd->console_fd, CONS_RELKBD, 1ul); - SDL_asprintf(&devicePath, "/dev/kbd%d", kbd->kbInfo->kb_index); - kbd->keyboard_fd = open(devicePath, O_WRONLY | O_CLOEXEC); - if (kbd->keyboard_fd == -1) { - // Give keyboard back. - ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index)); - kbd->keyboard_fd = kbd->console_fd; - } + if (!kbd_vt_init(kbd->console_fd)) { + SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "kbd_vt_init failed"); + } - /* 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, false)) { - kbd_register_emerg_cleanup(kbd); - } - SDL_free(devicePath); - } else - kbd->keyboard_fd = kbd->console_fd; + kbd->keyboard_fd = kbd->console_fd; + + if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) { + kbd_register_emerg_cleanup(kbd); + } } + vt_update_mouse(kbd, MOUSE_HIDE); + return kbd; } void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd) { - struct mouse_info mData; - if (!kbd) { return; } - SDL_zero(mData); - mData.operation = MOUSE_SHOW; - ioctl(kbd->console_fd, CONS_MOUSECTL, &mData); + + kbd_vt_quit(kbd->console_fd); + + vt_update_mouse(kbd, MOUSE_SHOW); kbd_unregister_emerg_cleanup(); @@ -322,14 +420,83 @@ void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd) void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted) { + struct termios tios; + + SDL_zero(tios); + + if (!state) { + return; + } + + if (muted == state->muted) { + return; + } + + if (tcgetattr(state->console_fd, &tios) == -1) { + SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Could not get terminal mode: %s", strerror(errno)); + return; + } + + if (muted) { + if (SDL_GetHintBoolean(SDL_HINT_MUTE_CONSOLE_KEYBOARD, true)) { + ioctl(state->console_fd, KDSKBMODE, K_OFF); + cfmakeraw(&tios); + + if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) { + kbd_register_emerg_cleanup(state); + } + } + } else { + kbd_unregister_emerg_cleanup(); + + cfmakesane(&tios); + ioctl(state->console_fd, KDSKBMODE, state->old_kbd_mode); + } + + if (tcsetattr(state->console_fd, TCSAFLUSH, &tios) == -1) { + SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Could not set terminal mode to %s: %s", muted ? "raw" : "sane", strerror(errno)); + return; + } + + state->muted = muted; } void SDL_EVDEV_kbd_set_vt_switch_callbacks(SDL_EVDEV_keyboard_state *state, void (*release_callback)(void *), void *release_callback_data, void (*acquire_callback)(void *), void *acquire_callback_data) { + if (state == NULL) { + return; + } + + state->vt_release_callback = release_callback; + state->vt_release_callback_data = release_callback_data; + state->vt_acquire_callback = acquire_callback; + state->vt_acquire_callback_data = acquire_callback_data; } void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state) { + if (!state) { + return; + } + + int signal_pending = SDL_GetAtomicInt(&vt_signal_pending); + + if (signal_pending != VT_SIGNAL_NONE) { + if (signal_pending == VT_SIGNAL_RELEASE) { + if (state->vt_release_callback) { + vt_update_mouse(state, MOUSE_SHOW); + state->vt_release_callback(state->vt_release_callback_data); + } + ioctl(state->console_fd, VT_RELDISP, 1); + } else { + if (state->vt_acquire_callback) { + state->vt_acquire_callback(state->vt_acquire_callback_data); + vt_update_mouse(state, MOUSE_HIDE); + } + ioctl(state->console_fd, VT_RELDISP, VT_ACKACQ); + } + SDL_CompareAndSwapAtomicInt(&vt_signal_pending, signal_pending, VT_SIGNAL_NONE); + } } /* diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c index 69155ed300798..44bc53ad0bb5e 100644 --- a/src/core/linux/SDL_evdev.c +++ b/src/core/linux/SDL_evdev.c @@ -329,6 +329,11 @@ void SDL_EVDEV_Poll(void) for (item = _this->first; item; item = item->next) { while ((len = read(item->fd, events, sizeof(events))) > 0) { +#ifdef SDL_INPUT_FBSDKBIO + if (SDL_GetAtomicInt(&vt_current) == VT_THEIRS) { + continue; + } +#endif len /= sizeof(events[0]); for (i = 0; i < len; ++i) { struct input_event *event = &events[i]; diff --git a/src/core/linux/SDL_evdev_kbd.h b/src/core/linux/SDL_evdev_kbd.h index b7a2834c8c5a7..681ddefe99576 100644 --- a/src/core/linux/SDL_evdev_kbd.h +++ b/src/core/linux/SDL_evdev_kbd.h @@ -22,6 +22,14 @@ #ifndef SDL_evdev_kbd_h_ #define SDL_evdev_kbd_h_ +#ifdef SDL_INPUT_FBSDKBIO +enum { + VT_OURS, + VT_THEIRS, +}; +extern SDL_AtomicInt vt_current; +#endif + struct SDL_EVDEV_keyboard_state; typedef struct SDL_EVDEV_keyboard_state SDL_EVDEV_keyboard_state;