SDL  2.0
SDL_evdev_kbd_freebsd.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 
22 #include "../../SDL_internal.h"
23 
24 #include "../linux/SDL_evdev_kbd.h"
25 #include "SDL_hints.h"
26 
27 #ifdef SDL_INPUT_FBSDKBIO
28 
29 /* This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source, slightly modified to work with FreeBSD */
30 
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <sys/ioctl.h>
34 #include <sys/kbio.h>
35 #include <sys/consio.h>
36 
37 #include <signal.h>
38 
39 #include "../../events/SDL_events_c.h"
41 
42 typedef void (fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
43 
44 /*
45  * Keyboard State
46  */
47 
49 {
50  int console_fd;
51  int keyboard_fd;
52  unsigned long old_kbd_mode;
53  unsigned short **key_maps;
54  keymap_t* key_map;
55  keyboard_info_t* kbInfo;
56  unsigned char shift_down[4]; /* shift state counters.. */
57  SDL_bool dead_key_next;
58  int npadch; /* -1 or number assembled on pad */
59  accentmap_t *accents;
60  unsigned int diacr;
61  SDL_bool rep; /* flag telling character repeat */
62  unsigned char lockstate;
63  unsigned char ledflagstate;
64  char shift_state;
65  char text[128];
66  unsigned int text_len;
67 };
68 
69 static int SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd)
70 {
71  return (ioctl(kbd->keyboard_fd, GIO_KEYMAP, kbd->key_map) >= 0);
72 }
73 
74 static SDL_EVDEV_keyboard_state * kbd_cleanup_state = NULL;
75 static int kbd_cleanup_sigactions_installed = 0;
76 static int kbd_cleanup_atexit_installed = 0;
77 
78 static struct sigaction old_sigaction[NSIG];
79 
80 static int fatal_signals[] =
81 {
82  /* Handlers for SIGTERM and SIGINT are installed in SDL_QuitInit. */
83  SIGHUP, SIGQUIT, SIGILL, SIGABRT,
84  SIGFPE, SIGSEGV, SIGPIPE, SIGBUS,
85  SIGSYS
86 };
87 
88 static void kbd_cleanup(void)
89 {
90  SDL_EVDEV_keyboard_state* kbd = kbd_cleanup_state;
91  if (kbd == NULL) {
92  return;
93  }
94  kbd_cleanup_state = NULL;
95 
96  ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
97  if (kbd->keyboard_fd != kbd->console_fd) close(kbd->keyboard_fd);
98  ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
99 }
100 
101 void
102 SDL_EVDEV_kbd_reraise_signal(int sig)
103 {
104  raise(sig);
105 }
106 
107 siginfo_t* SDL_EVDEV_kdb_cleanup_siginfo = NULL;
108 void* SDL_EVDEV_kdb_cleanup_ucontext = NULL;
109 
110 static void kbd_cleanup_signal_action(int signum, siginfo_t* info, void* ucontext)
111 {
112  struct sigaction* old_action_p = &(old_sigaction[signum]);
113  sigset_t sigset;
114 
115  /* Restore original signal handler before going any further. */
116  sigaction(signum, old_action_p, NULL);
117 
118  /* Unmask current signal. */
119  sigemptyset(&sigset);
120  sigaddset(&sigset, signum);
121  sigprocmask(SIG_UNBLOCK, &sigset, NULL);
122 
123  /* Save original signal info and context for archeologists. */
124  SDL_EVDEV_kdb_cleanup_siginfo = info;
125  SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
126 
127  /* Restore keyboard. */
128  kbd_cleanup();
129 
130  /* Reraise signal. */
131  SDL_EVDEV_kbd_reraise_signal(signum);
132 }
133 
134 static void kbd_unregister_emerg_cleanup()
135 {
136  int tabidx, signum;
137 
138  kbd_cleanup_state = NULL;
139 
140  if (!kbd_cleanup_sigactions_installed) {
141  return;
142  }
143  kbd_cleanup_sigactions_installed = 0;
144 
145  for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
146  struct sigaction* old_action_p;
147  struct sigaction cur_action;
148  signum = fatal_signals[tabidx];
149  old_action_p = &(old_sigaction[signum]);
150 
151  /* Examine current signal action */
152  if (sigaction(signum, NULL, &cur_action))
153  continue;
154 
155  /* Check if action installed and not modifed */
156  if (!(cur_action.sa_flags & SA_SIGINFO)
157  || cur_action.sa_sigaction != &kbd_cleanup_signal_action)
158  continue;
159 
160  /* Restore original action */
161  sigaction(signum, old_action_p, NULL);
162  }
163 }
164 
165 static void kbd_cleanup_atexit(void)
166 {
167  /* Restore keyboard. */
168  kbd_cleanup();
169 
170  /* Try to restore signal handlers in case shared library is being unloaded */
171  kbd_unregister_emerg_cleanup();
172 }
173 
174 static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state * kbd)
175 {
176  int tabidx, signum;
177 
178  if (kbd_cleanup_state != NULL) {
179  return;
180  }
181  kbd_cleanup_state = kbd;
182 
183  if (!kbd_cleanup_atexit_installed) {
184  /* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
185  * functions that are called when the shared library is unloaded.
186  * -- man atexit(3)
187  */
188  atexit(kbd_cleanup_atexit);
189  kbd_cleanup_atexit_installed = 1;
190  }
191 
192  if (kbd_cleanup_sigactions_installed) {
193  return;
194  }
195  kbd_cleanup_sigactions_installed = 1;
196 
197  for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
198  struct sigaction* old_action_p;
199  struct sigaction new_action;
200  signum = fatal_signals[tabidx];
201  old_action_p = &(old_sigaction[signum]);
202  if (sigaction(signum, NULL, old_action_p))
203  continue;
204 
205  /* Skip SIGHUP and SIGPIPE if handler is already installed
206  * - assume the handler will do the cleanup
207  */
208  if ((signum == SIGHUP || signum == SIGPIPE)
209  && (old_action_p->sa_handler != SIG_DFL
210  || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL))
211  continue;
212 
213  new_action = *old_action_p;
214  new_action.sa_flags |= SA_SIGINFO;
215  new_action.sa_sigaction = &kbd_cleanup_signal_action;
216  sigaction(signum, &new_action, NULL);
217  }
218 }
219 
221 SDL_EVDEV_kbd_init(void)
222 {
224  char flag_state;
225  char* devicePath;
226 
228  if (!kbd) {
229  return NULL;
230  }
231 
232  kbd->npadch = -1;
233 
234  /* This might fail if we're not connected to a tty (e.g. on the Steam Link) */
235  kbd->keyboard_fd = kbd->console_fd = open("/dev/tty", O_RDONLY);
236 
237  kbd->shift_state = 0;
238 
239  kbd->accents = SDL_calloc(sizeof(accentmap_t), 1);
240  kbd->key_map = SDL_calloc(sizeof(keymap_t), 1);
241  kbd->kbInfo = SDL_calloc(sizeof(keyboard_info_t), 1);
242 
243  ioctl(kbd->console_fd, KDGKBINFO, kbd->kbInfo);
244 
245  if (ioctl(kbd->console_fd, KDGKBSTATE, &flag_state) == 0) {
246  kbd->ledflagstate = flag_state;
247  }
248 
249  if (ioctl(kbd->console_fd, GIO_DEADKEYMAP, kbd->accents) < 0)
250  {
251  SDL_free(kbd->accents);
252  kbd->accents = &accentmap_default_us_acc;
253  }
254 
255  if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
256  /* Set the keyboard in XLATE mode and load the keymaps */
257  ioctl(kbd->console_fd, KDSKBMODE, (unsigned long)(K_XLATE));
258  if(!SDL_EVDEV_kbd_load_keymaps(kbd))
259  {
260  SDL_free(kbd->key_map);
261  kbd->key_map = &keymap_default_us_acc;
262  }
263  /* Allow inhibiting keyboard mute with env. variable for debugging etc. */
264  if (getenv("SDL_INPUT_FREEBSD_KEEP_KBD") == NULL) {
265  /* Take keyboard from console and open the actual keyboard device.
266  * Ensures that the keystrokes do not leak through to the console.
267  */
268  ioctl(kbd->console_fd, CONS_RELKBD, 1ul);
269  asprintf(&devicePath, "/dev/kbd%d", kbd->kbInfo->kb_index);
270  kbd->keyboard_fd = open(devicePath, O_WRONLY);
271  if (kbd->keyboard_fd == -1)
272  {
273  // Give keyboard back.
274  ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
275  kbd->keyboard_fd = kbd->console_fd;
276  }
277 
278  /* Make sure to restore keyboard if application fails to call
279  * SDL_Quit before exit or fatal signal is raised.
280  */
282  kbd_register_emerg_cleanup(kbd);
283  }
284  free(devicePath);
285  }
286  else kbd->keyboard_fd = kbd->console_fd;
287  }
288 
289  return kbd;
290 }
291 
292 void
294 {
295  if (!kbd) {
296  return;
297  }
298 
299  kbd_unregister_emerg_cleanup();
300 
301  if (kbd->keyboard_fd >= 0) {
302  /* Restore the original keyboard mode */
303  ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
304 
305  close(kbd->keyboard_fd);
306  if (kbd->console_fd != kbd->keyboard_fd && kbd->console_fd >= 0)
307  {
308  // Give back keyboard.
309  ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
310  }
311  kbd->console_fd = kbd->keyboard_fd = -1;
312  }
313 
314  SDL_free(kbd);
315 }
316 
317 /*
318  * Helper Functions.
319  */
320 static void put_queue(SDL_EVDEV_keyboard_state *kbd, uint c)
321 {
322  /* c is already part of a UTF-8 sequence and safe to add as a character */
323  if (kbd->text_len < (sizeof(kbd->text)-1)) {
324  kbd->text[kbd->text_len++] = (char)c;
325  }
326 }
327 
328 static void put_utf8(SDL_EVDEV_keyboard_state *kbd, uint c)
329 {
330  if (c < 0x80)
331  /* 0******* */
332  put_queue(kbd, c);
333  else if (c < 0x800) {
334  /* 110***** 10****** */
335  put_queue(kbd, 0xc0 | (c >> 6));
336  put_queue(kbd, 0x80 | (c & 0x3f));
337  } else if (c < 0x10000) {
338  if (c >= 0xD800 && c < 0xE000)
339  return;
340  if (c == 0xFFFF)
341  return;
342  /* 1110**** 10****** 10****** */
343  put_queue(kbd, 0xe0 | (c >> 12));
344  put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
345  put_queue(kbd, 0x80 | (c & 0x3f));
346  } else if (c < 0x110000) {
347  /* 11110*** 10****** 10****** 10****** */
348  put_queue(kbd, 0xf0 | (c >> 18));
349  put_queue(kbd, 0x80 | ((c >> 12) & 0x3f));
350  put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
351  put_queue(kbd, 0x80 | (c & 0x3f));
352  }
353 }
354 
355 /*
356  * We have a combining character DIACR here, followed by the character CH.
357  * If the combination occurs in the table, return the corresponding value.
358  * Otherwise, if CH is a space or equals DIACR, return DIACR.
359  * Otherwise, conclude that DIACR was not combining after all,
360  * queue it and return CH.
361  */
362 static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
363 {
364  unsigned int d = kbd->diacr;
365  unsigned int i, j;
366 
367  kbd->diacr = 0;
368 
369  for (i = 0; i < kbd->accents->n_accs; i++) {
370  if (kbd->accents->acc[i].accchar == d)
371  {
372  for (j = 0; j < NUM_ACCENTCHARS; ++j) {
373  if (kbd->accents->acc[i].map[j][0] == 0) /* end of table */
374  break;
375  if (kbd->accents->acc[i].map[j][0] == ch)
376  return kbd->accents->acc[i].map[j][1];
377  }
378  }
379  }
380 
381  if (ch == ' ' || ch == d) {
382  put_utf8(kbd, d);
383  return 0;
384  }
385  put_utf8(kbd, d);
386 
387  return ch;
388 }
389 
390 static int vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
391 {
392  return (kbd->ledflagstate & flag) != 0;
393 }
394 
395 static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
396 {
397  kbd->ledflagstate ^= flag;
398  ioctl(kbd->keyboard_fd, KDSKBSTATE, (unsigned long)(kbd->ledflagstate));
399 }
400 
401 /*
402  * Special function handlers
403  */
404 
405 static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
406 {
407  if (up_flag)
408  return; /* no action, if this is a key release */
409 
410  if (kbd->diacr)
411  value = handle_diacr(kbd, value);
412 
413  if (kbd->dead_key_next) {
414  kbd->dead_key_next = SDL_FALSE;
415  kbd->diacr = value;
416  return;
417  }
418  put_utf8(kbd, value);
419 }
420 
421 static void k_deadunicode(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
422 {
423  if (up_flag)
424  return;
425 
426  kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
427 }
428 
429 static void k_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
430 {
431  int old_state = kbd->shift_state;
432 
433  if (kbd->rep)
434  return;
435 
436  if (up_flag) {
437  /*
438  * handle the case that two shift or control
439  * keys are depressed simultaneously
440  */
441  if (kbd->shift_down[value])
442  kbd->shift_down[value]--;
443  } else
444  kbd->shift_down[value]++;
445 
446  if (kbd->shift_down[value])
447  kbd->shift_state |= (1 << value);
448  else
449  kbd->shift_state &= ~(1 << value);
450 
451  /* kludge */
452  if (up_flag && kbd->shift_state != old_state && kbd->npadch != -1) {
453  put_utf8(kbd, kbd->npadch);
454  kbd->npadch = -1;
455  }
456 }
457 
458 void
459 SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *kbd, unsigned int keycode, int down)
460 {
461  keymap_t key_map;
462  struct keyent_t keysym;
463  unsigned int final_key_state;
464  unsigned int map_from_key_sym;
465 
466  key_map = *kbd->key_map;
467 
468  if (!kbd) {
469  return;
470  }
471 
472  kbd->rep = (down == 2);
473 
474  if (keycode < NUM_KEYS) {
475  if (keycode >= 89 && keycode <= 95) {
476  /* These constitute unprintable language-related keys, so ignore them. */
477  return;
478  }
479  if (keycode > 95)
480  keycode -= 7;
481  if (vc_kbd_led(kbd, ALKED) || (kbd->shift_state & 0x8))
482  {
483  keycode += ALTGR_OFFSET;
484  }
485  keysym = key_map.key[keycode];
486  } else {
487  return;
488  }
489 
490  final_key_state = kbd->shift_state & 0x7;
491  if ((keysym.flgs & FLAG_LOCK_C) && vc_kbd_led(kbd, LED_CAP))
492  final_key_state ^= 0x1;
493  if ((keysym.flgs & FLAG_LOCK_N) && vc_kbd_led(kbd, LED_NUM))
494  final_key_state ^= 0x1;
495 
496  map_from_key_sym = keysym.map[final_key_state];
497  if ((keysym.spcl & (0x80 >> final_key_state)) || (map_from_key_sym & SPCLKEY)) {
498  /* Special function.*/
499  if (map_from_key_sym == 0)
500  return; /* Nothing to do. */
501  if (map_from_key_sym & SPCLKEY)
502  map_from_key_sym &= ~SPCLKEY;
503  if (map_from_key_sym >= F_ACC && map_from_key_sym <= L_ACC) {
504  /* Accent function.*/
505  unsigned int accent_index = map_from_key_sym - F_ACC;
506  if (kbd->accents->acc[accent_index].accchar != 0) {
507  k_deadunicode(kbd, kbd->accents->acc[accent_index].accchar, !down);
508  }
509  } else {
510  switch(map_from_key_sym) {
511  case ASH: /* alt/meta shift */
512  k_shift(kbd, 3, down == 0);
513  break;
514  case LSHA: /* left shift + alt lock */
515  case RSHA: /* right shift + alt lock */
516  if (down == 0) chg_vc_kbd_led(kbd, ALKED);
517  case LSH: /* left shift */
518  case RSH: /* right shift */
519  k_shift(kbd, 0, down == 0);
520  break;
521  case LCTRA: /* left ctrl + alt lock */
522  case RCTRA: /* right ctrl + alt lock */
523  if (down == 0) chg_vc_kbd_led(kbd, ALKED);
524  case LCTR: /* left ctrl */
525  case RCTR: /* right ctrl */
526  k_shift(kbd, 1, down == 0);
527  break;
528  case LALTA: /* left alt + alt lock */
529  case RALTA: /* right alt + alt lock */
530  if (down == 0) chg_vc_kbd_led(kbd, ALKED);
531  case LALT: /* left alt */
532  case RALT: /* right alt */
533  k_shift(kbd, 2, down == 0);
534  break;
535  case ALK: /* alt lock */
536  if (down == 1) chg_vc_kbd_led(kbd, ALKED);
537  break;
538  case CLK: /* caps lock*/
539  if (down == 1) chg_vc_kbd_led(kbd, CLKED);
540  break;
541  case NLK: /* num lock */
542  if (down == 1) chg_vc_kbd_led(kbd, NLKED);
543  break;
544  case SLK: /* scroll lock */
545  if (down == 1) chg_vc_kbd_led(kbd, SLKED);
546  break;
547  default:
548  return;
549  }
550  }
551  } else {
552  if (map_from_key_sym == '\n' || map_from_key_sym == '\r') {
553  if (kbd->diacr) {
554  kbd->diacr = 0;
555  return;
556  }
557  }
558  if (map_from_key_sym >= ' ' && map_from_key_sym != 127) {
559  k_self(kbd, map_from_key_sym, !down);
560  }
561  }
562 
563  if (kbd->text_len > 0) {
564  kbd->text[kbd->text_len] = '\0';
565  SDL_SendKeyboardText(kbd->text);
566  kbd->text_len = 0;
567  }
568 }
569 
570 #endif /* SDL_INPUT_FBSDKBIO */
571 
572 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_free
#define SDL_GetHintBoolean
#define SDL_calloc
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 SDL_AssertionHandler void SDL_SpinLock SDL_atomic_t int int return SDL_atomic_t return void void void return void return int return SDL_AudioSpec SDL_AudioSpec return int int return return int SDL_RWops int SDL_AudioSpec Uint8 ** d
SDL_EVDEV_keyboard_state * SDL_EVDEV_kbd_init(void)
void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down)
struct SDL_EVDEV_keyboard_state SDL_EVDEV_keyboard_state
Definition: SDL_evdev_kbd.h:26
static keymap_t keymap_default_us_acc
static accentmap_t accentmap_default_us_acc
SDL_EventEntry * free
Definition: SDL_events.c:89
#define uint
#define SDL_HINT_NO_SIGNAL_HANDLERS
Tell SDL not to catch the SIGINT or SIGTERM signals.
Definition: SDL_hints.h:1138
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:848
const GLubyte * c
GLsizei const GLfloat * value
SDL_bool
Definition: SDL_stdinc.h:168
@ SDL_FALSE
Definition: SDL_stdinc.h:169
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int int in j)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:163
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
typedef int(__stdcall *FARPROC)()