SDL_evdev_kbd_freebsd.c (17198B)
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" 40 #include "SDL_evdev_kbd_default_keyaccmap.h" 41 42 typedef void (fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd); 43 44 /* 45 * Keyboard State 46 */ 47 48 struct SDL_EVDEV_keyboard_state 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 220 SDL_EVDEV_keyboard_state * 221 SDL_EVDEV_kbd_init(void) 222 { 223 SDL_EVDEV_keyboard_state *kbd; 224 char flag_state; 225 char* devicePath; 226 227 kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(SDL_EVDEV_keyboard_state)); 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 */ 281 if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, SDL_FALSE)) { 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 293 SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd) 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: */