2727
2828// This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source, slightly modified to work with FreeBSD
2929
30+ #include <errno.h>
3031#include <unistd.h>
3132#include <fcntl.h>
3233#include <sys/ioctl.h>
3334#include <sys/kbio.h>
3435#include <sys/consio.h>
36+ #include <termios.h>
3537
3638#include <signal.h>
3739
3840#include "../../events/SDL_events_c.h"
3941#include "SDL_evdev_kbd_default_keyaccmap.h"
4042
43+ #ifndef K_OFF
44+ #define K_OFF 0x04
45+ #endif
46+
4147typedef void (fn_handler_fn )(SDL_EVDEV_keyboard_state * kbd );
4248
49+
4350/*
4451 * Keyboard State
4552 */
@@ -48,6 +55,7 @@ struct SDL_EVDEV_keyboard_state
4855{
4956 int console_fd ;
5057 int keyboard_fd ;
58+ bool muted ;
5159 unsigned long old_kbd_mode ;
5260 unsigned short * * key_maps ;
5361 keymap_t * key_map ;
@@ -63,6 +71,10 @@ struct SDL_EVDEV_keyboard_state
6371 char shift_state ;
6472 char text [128 ];
6573 unsigned int text_len ;
74+ void (* vt_release_callback )(void * );
75+ void * vt_release_callback_data ;
76+ void (* vt_acquire_callback )(void * );
77+ void * vt_acquire_callback_data ;
6678};
6779
6880static bool SDL_EVDEV_kbd_load_keymaps (SDL_EVDEV_keyboard_state * kbd )
@@ -83,22 +95,28 @@ static int fatal_signals[] = {
8395 SIGSYS
8496};
8597
86- static void kbd_cleanup ( void )
98+ static void vt_update_mouse ( SDL_EVDEV_keyboard_state * kbd , int operation )
8799{
88100 struct mouse_info mData ;
101+
102+ SDL_zero (mData );
103+ mData .operation = operation ;
104+ ioctl (kbd -> console_fd , CONS_MOUSECTL , & mData );
105+ }
106+
107+ static void kbd_cleanup (void )
108+ {
89109 SDL_EVDEV_keyboard_state * kbd = kbd_cleanup_state ;
90110 if (!kbd ) {
91111 return ;
92112 }
93113 kbd_cleanup_state = NULL ;
94- SDL_zero (mData );
95- mData .operation = MOUSE_SHOW ;
96114 ioctl (kbd -> keyboard_fd , KDSKBMODE , kbd -> old_kbd_mode );
97115 if (kbd -> keyboard_fd != kbd -> console_fd ) {
98116 close (kbd -> keyboard_fd );
99117 }
100118 ioctl (kbd -> console_fd , CONS_SETKBD , (unsigned long )(kbd -> kbInfo -> kb_index ));
101- ioctl (kbd -> console_fd , CONS_MOUSECTL , & mData );
119+ vt_update_mouse (kbd , true );
102120}
103121
104122void SDL_EVDEV_kbd_reraise_signal (int sig )
@@ -220,15 +238,108 @@ static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state *kbd)
220238 }
221239}
222240
241+ enum {
242+ VT_SIGNAL_NONE ,
243+ VT_SIGNAL_RELEASE ,
244+ VT_SIGNAL_ACQUIRE ,
245+ };
246+ static int vt_release_signal ;
247+ static int vt_acquire_signal ;
248+ static SDL_AtomicInt vt_signal_pending ;
249+ SDL_AtomicInt vt_current ;
250+
251+ typedef void (* signal_handler )(int signum );
252+
253+
254+ static void kbd_vt_release_signal_action (int signum )
255+ {
256+ SDL_SetAtomicInt (& vt_signal_pending , VT_SIGNAL_RELEASE );
257+ SDL_SetAtomicInt (& vt_current , VT_THEIRS );
258+ }
259+
260+ static void kbd_vt_acquire_signal_action (int signum )
261+ {
262+ SDL_SetAtomicInt (& vt_signal_pending , VT_SIGNAL_ACQUIRE );
263+ SDL_SetAtomicInt (& vt_current , VT_OURS );
264+ }
265+
266+ static bool setup_vt_signal (int signum , signal_handler handler )
267+ {
268+ struct sigaction * old_action_p ;
269+ struct sigaction new_action ;
270+ old_action_p = & (old_sigaction [signum ]);
271+ SDL_zero (new_action );
272+ new_action .sa_handler = handler ;
273+ new_action .sa_flags = SA_RESTART ;
274+ if (sigaction (signum , & new_action , old_action_p ) < 0 ) {
275+ return false;
276+ }
277+ if (old_action_p -> sa_handler != SIG_DFL ) {
278+ // This signal is already in use
279+ if (signum == SIGUSR1 || signum == SIGUSR2 ) {
280+ /*
281+ * For the moment we have no other options in FreeBSD because
282+ * the vt(4) will not accept signal numbers over 32.
283+ */
284+ return true;
285+ }
286+ sigaction (signum , old_action_p , NULL );
287+ return false;
288+ }
289+ return true;
290+ }
291+
292+ static void kbd_vt_quit (int console_fd )
293+ {
294+ struct vt_mode mode ;
295+
296+ if (vt_release_signal ) {
297+ sigaction (vt_release_signal , & old_sigaction [vt_release_signal ], NULL );
298+ vt_release_signal = 0 ;
299+ }
300+ if (vt_acquire_signal ) {
301+ sigaction (vt_acquire_signal , & old_sigaction [vt_acquire_signal ], NULL );
302+ vt_acquire_signal = 0 ;
303+ }
304+
305+ SDL_zero (mode );
306+ mode .mode = VT_AUTO ;
307+ ioctl (console_fd , VT_SETMODE , & mode );
308+ }
309+
310+ static bool kbd_vt_init (int console_fd )
311+ {
312+ struct vt_mode mode ;
313+
314+ if (setup_vt_signal (SIGUSR1 , kbd_vt_release_signal_action )) {
315+ vt_release_signal = SIGUSR1 ;
316+ }
317+ if (setup_vt_signal (SIGUSR2 , kbd_vt_acquire_signal_action )) {
318+ vt_acquire_signal = SIGUSR2 ;
319+ }
320+ if (!vt_release_signal || !vt_acquire_signal ) {
321+ kbd_vt_quit (console_fd );
322+ return false;
323+ }
324+
325+ SDL_zero (mode );
326+ mode .mode = VT_PROCESS ;
327+ mode .relsig = vt_release_signal ;
328+ mode .acqsig = vt_acquire_signal ;
329+ mode .frsig = SIGIO ;
330+ if (ioctl (console_fd , VT_SETMODE , & mode ) < 0 ) {
331+ SDL_LogError (SDL_LOG_CATEGORY_INPUT , "Failed VT_SETMODE ioctl: %s" , strerror (errno ));
332+ kbd_vt_quit (console_fd );
333+ return false;
334+ }
335+ return true;
336+ }
337+
223338SDL_EVDEV_keyboard_state * SDL_EVDEV_kbd_init (void )
224339{
225340 SDL_EVDEV_keyboard_state * kbd ;
226- struct mouse_info mData ;
227341 char flag_state ;
228- char * devicePath ;
229342
230- SDL_zero (mData );
231- mData .operation = MOUSE_HIDE ;
232343 kbd = (SDL_EVDEV_keyboard_state * )SDL_calloc (1 , sizeof (SDL_EVDEV_keyboard_state ));
233344 if (!kbd ) {
234345 return NULL ;
@@ -246,7 +357,6 @@ SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
246357 kbd -> kbInfo = SDL_calloc (1 , sizeof (keyboard_info_t ));
247358
248359 ioctl (kbd -> console_fd , KDGKBINFO , kbd -> kbInfo );
249- ioctl (kbd -> console_fd , CONS_MOUSECTL , & mData );
250360
251361 if (ioctl (kbd -> console_fd , KDGKBSTATE , & flag_state ) == 0 ) {
252362 kbd -> ledflagstate = flag_state ;
@@ -265,43 +375,31 @@ SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
265375 kbd -> key_map = & keymap_default_us_acc ;
266376 }
267377
268- if (SDL_GetHintBoolean (SDL_HINT_MUTE_CONSOLE_KEYBOARD , true)) {
269- /* Take keyboard from console and open the actual keyboard device.
270- * Ensures that the keystrokes do not leak through to the console.
271- */
272- ioctl (kbd -> console_fd , CONS_RELKBD , 1ul );
273- SDL_asprintf (& devicePath , "/dev/kbd%d" , kbd -> kbInfo -> kb_index );
274- kbd -> keyboard_fd = open (devicePath , O_WRONLY | O_CLOEXEC );
275- if (kbd -> keyboard_fd == -1 ) {
276- // Give keyboard back.
277- ioctl (kbd -> console_fd , CONS_SETKBD , (unsigned long )(kbd -> kbInfo -> kb_index ));
278- kbd -> keyboard_fd = kbd -> console_fd ;
279- }
378+ if (!kbd_vt_init (kbd -> console_fd )) {
379+ SDL_LogInfo (SDL_LOG_CATEGORY_INPUT , "kbd_vt_init failed" );
380+ }
280381
281- /* Make sure to restore keyboard if application fails to call
282- * SDL_Quit before exit or fatal signal is raised.
283- */
284- if (!SDL_GetHintBoolean (SDL_HINT_NO_SIGNAL_HANDLERS , false)) {
285- kbd_register_emerg_cleanup (kbd );
286- }
287- SDL_free (devicePath );
288- } else
289- kbd -> keyboard_fd = kbd -> console_fd ;
382+ kbd -> keyboard_fd = kbd -> console_fd ;
383+
384+ if (!SDL_GetHintBoolean (SDL_HINT_NO_SIGNAL_HANDLERS , false)) {
385+ kbd_register_emerg_cleanup (kbd );
386+ }
290387 }
291388
389+ vt_update_mouse (kbd , MOUSE_HIDE );
390+
292391 return kbd ;
293392}
294393
295394void SDL_EVDEV_kbd_quit (SDL_EVDEV_keyboard_state * kbd )
296395{
297- struct mouse_info mData ;
298-
299396 if (!kbd ) {
300397 return ;
301398 }
302- SDL_zero (mData );
303- mData .operation = MOUSE_SHOW ;
304- ioctl (kbd -> console_fd , CONS_MOUSECTL , & mData );
399+
400+ kbd_vt_quit (kbd -> console_fd );
401+
402+ vt_update_mouse (kbd , MOUSE_SHOW );
305403
306404 kbd_unregister_emerg_cleanup ();
307405
@@ -322,14 +420,83 @@ void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
322420
323421void SDL_EVDEV_kbd_set_muted (SDL_EVDEV_keyboard_state * state , bool muted )
324422{
423+ struct termios tios ;
424+
425+ SDL_zero (tios );
426+
427+ if (!state ) {
428+ return ;
429+ }
430+
431+ if (muted == state -> muted ) {
432+ return ;
433+ }
434+
435+ if (tcgetattr (state -> console_fd , & tios ) == -1 ) {
436+ SDL_LogError (SDL_LOG_CATEGORY_INPUT , "Could not get terminal mode: %s" , strerror (errno ));
437+ return ;
438+ }
439+
440+ if (muted ) {
441+ if (SDL_GetHintBoolean (SDL_HINT_MUTE_CONSOLE_KEYBOARD , true)) {
442+ ioctl (state -> console_fd , KDSKBMODE , K_OFF );
443+ cfmakeraw (& tios );
444+
445+ if (!SDL_GetHintBoolean (SDL_HINT_NO_SIGNAL_HANDLERS , false)) {
446+ kbd_register_emerg_cleanup (state );
447+ }
448+ }
449+ } else {
450+ kbd_unregister_emerg_cleanup ();
451+
452+ cfmakesane (& tios );
453+ ioctl (state -> console_fd , KDSKBMODE , state -> old_kbd_mode );
454+ }
455+
456+ if (tcsetattr (state -> console_fd , TCSAFLUSH , & tios ) == -1 ) {
457+ SDL_LogError (SDL_LOG_CATEGORY_INPUT , "Could not set terminal mode to %s: %s" , muted ? "raw" : "sane" , strerror (errno ));
458+ return ;
459+ }
460+
461+ state -> muted = muted ;
325462}
326463
327464void 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 )
328465{
466+ if (state == NULL ) {
467+ return ;
468+ }
469+
470+ state -> vt_release_callback = release_callback ;
471+ state -> vt_release_callback_data = release_callback_data ;
472+ state -> vt_acquire_callback = acquire_callback ;
473+ state -> vt_acquire_callback_data = acquire_callback_data ;
329474}
330475
331476void SDL_EVDEV_kbd_update (SDL_EVDEV_keyboard_state * state )
332477{
478+ if (!state ) {
479+ return ;
480+ }
481+
482+ int signal_pending = SDL_GetAtomicInt (& vt_signal_pending );
483+
484+ if (signal_pending != VT_SIGNAL_NONE ) {
485+ if (signal_pending == VT_SIGNAL_RELEASE ) {
486+ if (state -> vt_release_callback ) {
487+ vt_update_mouse (state , MOUSE_SHOW );
488+ state -> vt_release_callback (state -> vt_release_callback_data );
489+ }
490+ ioctl (state -> console_fd , VT_RELDISP , 1 );
491+ } else {
492+ if (state -> vt_acquire_callback ) {
493+ state -> vt_acquire_callback (state -> vt_acquire_callback_data );
494+ vt_update_mouse (state , MOUSE_HIDE );
495+ }
496+ ioctl (state -> console_fd , VT_RELDISP , VT_ACKACQ );
497+ }
498+ SDL_CompareAndSwapAtomicInt (& vt_signal_pending , signal_pending , VT_SIGNAL_NONE );
499+ }
333500}
334501
335502/*
0 commit comments