1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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#include "SDL_internal.h"
22
23#include "SDL_evdev_kbd.h"
24
25#ifdef SDL_INPUT_LINUXKD
26
27// This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source
28
29#include <unistd.h>
30#include <fcntl.h>
31#include <sys/ioctl.h>
32#include <linux/kd.h>
33#include <linux/keyboard.h>
34#include <linux/vt.h>
35#include <linux/tiocl.h> // for TIOCL_GETSHIFTSTATE
36
37#include <signal.h>
38
39#include "../../events/SDL_events_c.h"
40#include "SDL_evdev_kbd_default_accents.h"
41#include "SDL_evdev_kbd_default_keymap.h"
42
43// These are not defined in older Linux kernel headers
44#ifndef K_UNICODE
45#define K_UNICODE 0x03
46#endif
47#ifndef K_OFF
48#define K_OFF 0x04
49#endif
50
51/*
52 * Handler Tables.
53 */
54
55#define K_HANDLERS \
56 k_self, k_fn, k_spec, k_pad, \
57 k_dead, k_cons, k_cur, k_shift, \
58 k_meta, k_ascii, k_lock, k_lowercase, \
59 k_slock, k_dead2, k_brl, k_ignore
60
61typedef void(k_handler_fn)(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag);
62static k_handler_fn K_HANDLERS;
63static k_handler_fn *k_handler[16] = { K_HANDLERS };
64
65typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
66static void fn_enter(SDL_EVDEV_keyboard_state *kbd);
67static void fn_caps_toggle(SDL_EVDEV_keyboard_state *kbd);
68static void fn_caps_on(SDL_EVDEV_keyboard_state *kbd);
69static void fn_num(SDL_EVDEV_keyboard_state *kbd);
70static void fn_compose(SDL_EVDEV_keyboard_state *kbd);
71
72static fn_handler_fn *fn_handler[] = {
73 NULL, fn_enter, NULL, NULL,
74 NULL, NULL, NULL, fn_caps_toggle,
75 fn_num, NULL, NULL, NULL,
76 NULL, fn_caps_on, fn_compose, NULL,
77 NULL, NULL, NULL, fn_num
78};
79
80/*
81 * Keyboard State
82 */
83
84struct SDL_EVDEV_keyboard_state
85{
86 int console_fd;
87 bool muted;
88 int old_kbd_mode;
89 unsigned short **key_maps;
90 unsigned char shift_down[NR_SHIFT]; // shift state counters..
91 bool dead_key_next;
92 int npadch; // -1 or number assembled on pad
93 struct kbdiacrs *accents;
94 unsigned int diacr;
95 bool rep; // flag telling character repeat
96 unsigned char lockstate;
97 unsigned char slockstate;
98 unsigned char ledflagstate;
99 char shift_state;
100 char text[128];
101 unsigned int text_len;
102 void (*vt_release_callback)(void *);
103 void *vt_release_callback_data;
104 void (*vt_acquire_callback)(void *);
105 void *vt_acquire_callback_data;
106};
107
108#ifdef DUMP_ACCENTS
109static void SDL_EVDEV_dump_accents(SDL_EVDEV_keyboard_state *kbd)
110{
111 unsigned int i;
112
113 printf("static struct kbdiacrs default_accents = {\n");
114 printf(" %d,\n", kbd->accents->kb_cnt);
115 printf(" {\n");
116 for (i = 0; i < kbd->accents->kb_cnt; ++i) {
117 struct kbdiacr *diacr = &kbd->accents->kbdiacr[i];
118 printf(" { 0x%.2x, 0x%.2x, 0x%.2x },\n",
119 diacr->diacr, diacr->base, diacr->result);
120 }
121 while (i < 256) {
122 printf(" { 0x00, 0x00, 0x00 },\n");
123 ++i;
124 }
125 printf(" }\n");
126 printf("};\n");
127}
128#endif // DUMP_ACCENTS
129
130#ifdef DUMP_KEYMAP
131static void SDL_EVDEV_dump_keymap(SDL_EVDEV_keyboard_state *kbd)
132{
133 int i, j;
134
135 for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
136 if (kbd->key_maps[i]) {
137 printf("static unsigned short default_key_map_%d[NR_KEYS] = {", i);
138 for (j = 0; j < NR_KEYS; ++j) {
139 if ((j % 8) == 0) {
140 printf("\n ");
141 }
142 printf("0x%.4x, ", kbd->key_maps[i][j]);
143 }
144 printf("\n};\n");
145 }
146 }
147 printf("\n");
148 printf("static unsigned short *default_key_maps[MAX_NR_KEYMAPS] = {\n");
149 for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
150 if (kbd->key_maps[i]) {
151 printf(" default_key_map_%d,\n", i);
152 } else {
153 printf(" NULL,\n");
154 }
155 }
156 printf("};\n");
157}
158#endif // DUMP_KEYMAP
159
160static SDL_EVDEV_keyboard_state *kbd_cleanup_state = NULL;
161static int kbd_cleanup_sigactions_installed = 0;
162static int kbd_cleanup_atexit_installed = 0;
163
164static struct sigaction old_sigaction[NSIG];
165
166static int fatal_signals[] = {
167 // Handlers for SIGTERM and SIGINT are installed in SDL_InitQuit.
168 SIGHUP, SIGQUIT, SIGILL, SIGABRT,
169 SIGFPE, SIGSEGV, SIGPIPE, SIGBUS,
170 SIGSYS
171};
172
173static void kbd_cleanup(void)
174{
175 SDL_EVDEV_keyboard_state *kbd = kbd_cleanup_state;
176 if (!kbd) {
177 return;
178 }
179 kbd_cleanup_state = NULL;
180
181 ioctl(kbd->console_fd, KDSKBMODE, kbd->old_kbd_mode);
182}
183
184static void SDL_EVDEV_kbd_reraise_signal(int sig)
185{
186 (void)raise(sig);
187}
188
189static siginfo_t *SDL_EVDEV_kdb_cleanup_siginfo = NULL;
190static void *SDL_EVDEV_kdb_cleanup_ucontext = NULL;
191
192static void kbd_cleanup_signal_action(int signum, siginfo_t *info, void *ucontext)
193{
194 struct sigaction *old_action_p = &(old_sigaction[signum]);
195 sigset_t sigset;
196
197 // Restore original signal handler before going any further.
198 sigaction(signum, old_action_p, NULL);
199
200 // Unmask current signal.
201 sigemptyset(&sigset);
202 sigaddset(&sigset, signum);
203 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
204
205 // Save original signal info and context for archeologists.
206 SDL_EVDEV_kdb_cleanup_siginfo = info;
207 SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
208
209 // Restore keyboard.
210 kbd_cleanup();
211
212 // Reraise signal.
213 SDL_EVDEV_kbd_reraise_signal(signum);
214}
215
216static void kbd_unregister_emerg_cleanup(void)
217{
218 int tabidx;
219
220 kbd_cleanup_state = NULL;
221
222 if (!kbd_cleanup_sigactions_installed) {
223 return;
224 }
225 kbd_cleanup_sigactions_installed = 0;
226
227 for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
228 struct sigaction *old_action_p;
229 struct sigaction cur_action;
230 int signum = fatal_signals[tabidx];
231 old_action_p = &(old_sigaction[signum]);
232
233 // Examine current signal action
234 if (sigaction(signum, NULL, &cur_action)) {
235 continue;
236 }
237
238 // Check if action installed and not modified
239 if (!(cur_action.sa_flags & SA_SIGINFO) || cur_action.sa_sigaction != &kbd_cleanup_signal_action) {
240 continue;
241 }
242
243 // Restore original action
244 sigaction(signum, old_action_p, NULL);
245 }
246}
247
248static void kbd_cleanup_atexit(void)
249{
250 // Restore keyboard.
251 kbd_cleanup();
252
253 // Try to restore signal handlers in case shared library is being unloaded
254 kbd_unregister_emerg_cleanup();
255}
256
257static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state *kbd)
258{
259 int tabidx;
260
261 if (kbd_cleanup_state) {
262 return;
263 }
264 kbd_cleanup_state = kbd;
265
266 if (!kbd_cleanup_atexit_installed) {
267 /* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
268 * functions that are called when the shared library is unloaded.
269 * -- man atexit(3)
270 */
271 (void)atexit(kbd_cleanup_atexit);
272 kbd_cleanup_atexit_installed = 1;
273 }
274
275 if (kbd_cleanup_sigactions_installed) {
276 return;
277 }
278 kbd_cleanup_sigactions_installed = 1;
279
280 for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
281 struct sigaction *old_action_p;
282 struct sigaction new_action;
283 int signum = fatal_signals[tabidx];
284 old_action_p = &(old_sigaction[signum]);
285 if (sigaction(signum, NULL, old_action_p)) {
286 continue;
287 }
288
289 /* Skip SIGHUP and SIGPIPE if handler is already installed
290 * - assume the handler will do the cleanup
291 */
292 if ((signum == SIGHUP || signum == SIGPIPE) && (old_action_p->sa_handler != SIG_DFL || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL)) {
293 continue;
294 }
295
296 new_action = *old_action_p;
297 new_action.sa_flags |= SA_SIGINFO;
298 new_action.sa_sigaction = &kbd_cleanup_signal_action;
299 sigaction(signum, &new_action, NULL);
300 }
301}
302
303enum {
304 VT_SIGNAL_NONE,
305 VT_SIGNAL_RELEASE,
306 VT_SIGNAL_ACQUIRE,
307};
308static int vt_release_signal;
309static int vt_acquire_signal;
310static SDL_AtomicInt vt_signal_pending;
311
312typedef void (*signal_handler)(int signum);
313
314static void kbd_vt_release_signal_action(int signum)
315{
316 SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_RELEASE);
317}
318
319static void kbd_vt_acquire_signal_action(int signum)
320{
321 SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_ACQUIRE);
322}
323
324static bool setup_vt_signal(int signum, signal_handler handler)
325{
326 struct sigaction *old_action_p;
327 struct sigaction new_action;
328 old_action_p = &(old_sigaction[signum]);
329 SDL_zero(new_action);
330 new_action.sa_handler = handler;
331 new_action.sa_flags = SA_RESTART;
332 if (sigaction(signum, &new_action, old_action_p) < 0) {
333 return false;
334 }
335 if (old_action_p->sa_handler != SIG_DFL) {
336 // This signal is already in use
337 sigaction(signum, old_action_p, NULL);
338 return false;
339 }
340 return true;
341}
342
343static int find_free_signal(signal_handler handler)
344{
345#ifdef SIGRTMIN
346 int i;
347
348 for (i = SIGRTMIN + 2; i <= SIGRTMAX; ++i) {
349 if (setup_vt_signal(i, handler)) {
350 return i;
351 }
352 }
353#endif
354 if (setup_vt_signal(SIGUSR1, handler)) {
355 return SIGUSR1;
356 }
357 if (setup_vt_signal(SIGUSR2, handler)) {
358 return SIGUSR2;
359 }
360 return 0;
361}
362
363static void kbd_vt_quit(int console_fd)
364{
365 struct vt_mode mode;
366
367 if (vt_release_signal) {
368 sigaction(vt_release_signal, &old_sigaction[vt_release_signal], NULL);
369 vt_release_signal = 0;
370 }
371 if (vt_acquire_signal) {
372 sigaction(vt_acquire_signal, &old_sigaction[vt_acquire_signal], NULL);
373 vt_acquire_signal = 0;
374 }
375
376 SDL_zero(mode);
377 mode.mode = VT_AUTO;
378 ioctl(console_fd, VT_SETMODE, &mode);
379}
380
381static bool kbd_vt_init(int console_fd)
382{
383 struct vt_mode mode;
384
385 vt_release_signal = find_free_signal(kbd_vt_release_signal_action);
386 vt_acquire_signal = find_free_signal(kbd_vt_acquire_signal_action);
387 if (!vt_release_signal || !vt_acquire_signal ) {
388 kbd_vt_quit(console_fd);
389 return false;
390 }
391
392 SDL_zero(mode);
393 mode.mode = VT_PROCESS;
394 mode.relsig = vt_release_signal;
395 mode.acqsig = vt_acquire_signal;
396 mode.frsig = SIGIO;
397 if (ioctl(console_fd, VT_SETMODE, &mode) < 0) {
398 kbd_vt_quit(console_fd);
399 return false;
400 }
401 return true;
402}
403
404static void kbd_vt_update(SDL_EVDEV_keyboard_state *state)
405{
406 int signal_pending = SDL_GetAtomicInt(&vt_signal_pending);
407 if (signal_pending != VT_SIGNAL_NONE) {
408 if (signal_pending == VT_SIGNAL_RELEASE) {
409 if (state->vt_release_callback) {
410 state->vt_release_callback(state->vt_release_callback_data);
411 }
412 ioctl(state->console_fd, VT_RELDISP, 1);
413 } else {
414 if (state->vt_acquire_callback) {
415 state->vt_acquire_callback(state->vt_acquire_callback_data);
416 }
417 ioctl(state->console_fd, VT_RELDISP, VT_ACKACQ);
418 }
419 SDL_CompareAndSwapAtomicInt(&vt_signal_pending, signal_pending, VT_SIGNAL_NONE);
420 }
421}
422
423SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
424{
425 SDL_EVDEV_keyboard_state *kbd;
426 char flag_state;
427 char kbtype;
428 char shift_state[sizeof(long)] = { TIOCL_GETSHIFTSTATE, 0 };
429
430 kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(*kbd));
431 if (!kbd) {
432 return NULL;
433 }
434
435 // This might fail if we're not connected to a tty (e.g. on the Steam Link)
436 kbd->console_fd = open("/dev/tty", O_RDONLY | O_CLOEXEC);
437 if (!((ioctl(kbd->console_fd, KDGKBTYPE, &kbtype) == 0) && ((kbtype == KB_101) || (kbtype == KB_84)))) {
438 close(kbd->console_fd);
439 kbd->console_fd = -1;
440 }
441
442 kbd->npadch = -1;
443
444 if (ioctl(kbd->console_fd, TIOCLINUX, shift_state) == 0) {
445 kbd->shift_state = *shift_state;
446 }
447
448 if (ioctl(kbd->console_fd, KDGKBLED, &flag_state) == 0) {
449 kbd->ledflagstate = flag_state;
450 }
451
452 kbd->accents = &default_accents;
453 kbd->key_maps = default_key_maps;
454
455 if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
456 // Set the keyboard in UNICODE mode and load the keymaps
457 ioctl(kbd->console_fd, KDSKBMODE, K_UNICODE);
458 }
459
460 kbd_vt_init(kbd->console_fd);
461
462 return kbd;
463}
464
465void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted)
466{
467 if (!state) {
468 return;
469 }
470
471 if (muted == state->muted) {
472 return;
473 }
474
475 if (muted) {
476 if (SDL_GetHintBoolean(SDL_HINT_MUTE_CONSOLE_KEYBOARD, true)) {
477 /* Mute the keyboard so keystrokes only generate evdev events
478 * and do not leak through to the console
479 */
480 ioctl(state->console_fd, KDSKBMODE, K_OFF);
481
482 /* Make sure to restore keyboard if application fails to call
483 * SDL_Quit before exit or fatal signal is raised.
484 */
485 if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) {
486 kbd_register_emerg_cleanup(state);
487 }
488 }
489 } else {
490 kbd_unregister_emerg_cleanup();
491
492 // Restore the original keyboard mode
493 ioctl(state->console_fd, KDSKBMODE, state->old_kbd_mode);
494 }
495 state->muted = muted;
496}
497
498void 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)
499{
500 if (state == NULL) {
501 return;
502 }
503
504 state->vt_release_callback = release_callback;
505 state->vt_release_callback_data = release_callback_data;
506 state->vt_acquire_callback = acquire_callback;
507 state->vt_acquire_callback_data = acquire_callback_data;
508}
509
510void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state)
511{
512 if (!state) {
513 return;
514 }
515
516 kbd_vt_update(state);
517}
518
519void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
520{
521 if (state == NULL) {
522 return;
523 }
524
525 SDL_EVDEV_kbd_set_muted(state, false);
526
527 kbd_vt_quit(state->console_fd);
528
529 if (state->console_fd >= 0) {
530 close(state->console_fd);
531 state->console_fd = -1;
532 }
533
534 if (state->key_maps && state->key_maps != default_key_maps) {
535 int i;
536 for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
537 if (state->key_maps[i]) {
538 SDL_free(state->key_maps[i]);
539 }
540 }
541 SDL_free(state->key_maps);
542 }
543
544 SDL_free(state);
545}
546
547/*
548 * Helper Functions.
549 */
550static void put_queue(SDL_EVDEV_keyboard_state *kbd, uint c)
551{
552 // c is already part of a UTF-8 sequence and safe to add as a character
553 if (kbd->text_len < (sizeof(kbd->text) - 1)) {
554 kbd->text[kbd->text_len++] = (char)c;
555 }
556}
557
558static void put_utf8(SDL_EVDEV_keyboard_state *kbd, uint c)
559{
560 if (c < 0x80) {
561 put_queue(kbd, c); /* 0******* */
562 } else if (c < 0x800) {
563 /* 110***** 10****** */
564 put_queue(kbd, 0xc0 | (c >> 6));
565 put_queue(kbd, 0x80 | (c & 0x3f));
566 } else if (c < 0x10000) {
567 if (c >= 0xD800 && c < 0xE000) {
568 return;
569 }
570 if (c == 0xFFFF) {
571 return;
572 }
573 /* 1110**** 10****** 10****** */
574 put_queue(kbd, 0xe0 | (c >> 12));
575 put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
576 put_queue(kbd, 0x80 | (c & 0x3f));
577 } else if (c < 0x110000) {
578 /* 11110*** 10****** 10****** 10****** */
579 put_queue(kbd, 0xf0 | (c >> 18));
580 put_queue(kbd, 0x80 | ((c >> 12) & 0x3f));
581 put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
582 put_queue(kbd, 0x80 | (c & 0x3f));
583 }
584}
585
586/*
587 * We have a combining character DIACR here, followed by the character CH.
588 * If the combination occurs in the table, return the corresponding value.
589 * Otherwise, if CH is a space or equals DIACR, return DIACR.
590 * Otherwise, conclude that DIACR was not combining after all,
591 * queue it and return CH.
592 */
593static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
594{
595 unsigned int d = kbd->diacr;
596 unsigned int i;
597
598 kbd->diacr = 0;
599
600 if (kbd->console_fd >= 0)
601 if (ioctl(kbd->console_fd, KDGKBDIACR, kbd->accents) < 0) {
602 // No worries, we'll use the default accent table
603 }
604
605 for (i = 0; i < kbd->accents->kb_cnt; i++) {
606 if (kbd->accents->kbdiacr[i].diacr == d &&
607 kbd->accents->kbdiacr[i].base == ch) {
608 return kbd->accents->kbdiacr[i].result;
609 }
610 }
611
612 if (ch == ' ' || ch == d) {
613 return d;
614 }
615
616 put_utf8(kbd, d);
617
618 return ch;
619}
620
621static bool vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
622{
623 return (kbd->ledflagstate & flag) != 0;
624}
625
626static void set_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
627{
628 kbd->ledflagstate |= flag;
629 ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
630}
631
632static void clr_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
633{
634 kbd->ledflagstate &= ~flag;
635 ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
636}
637
638static void chg_vc_kbd_lock(SDL_EVDEV_keyboard_state *kbd, int flag)
639{
640 kbd->lockstate ^= 1 << flag;
641}
642
643static void chg_vc_kbd_slock(SDL_EVDEV_keyboard_state *kbd, int flag)
644{
645 kbd->slockstate ^= 1 << flag;
646}
647
648static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
649{
650 kbd->ledflagstate ^= flag;
651 ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
652}
653
654/*
655 * Special function handlers
656 */
657
658static void fn_enter(SDL_EVDEV_keyboard_state *kbd)
659{
660 if (kbd->diacr) {
661 put_utf8(kbd, kbd->diacr);
662 kbd->diacr = 0;
663 }
664}
665
666static void fn_caps_toggle(SDL_EVDEV_keyboard_state *kbd)
667{
668 if (kbd->rep) {
669 return;
670 }
671
672 chg_vc_kbd_led(kbd, K_CAPSLOCK);
673}
674
675static void fn_caps_on(SDL_EVDEV_keyboard_state *kbd)
676{
677 if (kbd->rep) {
678 return;
679 }
680
681 set_vc_kbd_led(kbd, K_CAPSLOCK);
682}
683
684static void fn_num(SDL_EVDEV_keyboard_state *kbd)
685{
686 if (!kbd->rep) {
687 chg_vc_kbd_led(kbd, K_NUMLOCK);
688 }
689}
690
691static void fn_compose(SDL_EVDEV_keyboard_state *kbd)
692{
693 kbd->dead_key_next = true;
694}
695
696/*
697 * Special key handlers
698 */
699
700static void k_ignore(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
701{
702}
703
704static void k_spec(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
705{
706 if (up_flag) {
707 return;
708 }
709 if (value >= SDL_arraysize(fn_handler)) {
710 return;
711 }
712 if (fn_handler[value]) {
713 fn_handler[value](kbd);
714 }
715}
716
717static void k_lowercase(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
718{
719}
720
721static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
722{
723 if (up_flag) {
724 return; // no action, if this is a key release
725 }
726
727 if (kbd->diacr) {
728 value = handle_diacr(kbd, value);
729 }
730
731 if (kbd->dead_key_next) {
732 kbd->dead_key_next = false;
733 kbd->diacr = value;
734 return;
735 }
736 put_utf8(kbd, value);
737}
738
739static void k_deadunicode(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
740{
741 if (up_flag) {
742 return;
743 }
744
745 kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
746}
747
748static void k_dead(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
749{
750 const unsigned char ret_diacr[NR_DEAD] = { '`', '\'', '^', '~', '"', ',' };
751
752 k_deadunicode(kbd, ret_diacr[value], up_flag);
753}
754
755static void k_dead2(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
756{
757 k_deadunicode(kbd, value, up_flag);
758}
759
760static void k_cons(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
761{
762}
763
764static void k_fn(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
765{
766}
767
768static void k_cur(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
769{
770}
771
772static void k_pad(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
773{
774 static const char pad_chars[] = "0123456789+-*/\015,.?()#";
775
776 if (up_flag) {
777 return; // no action, if this is a key release
778 }
779
780 if (!vc_kbd_led(kbd, K_NUMLOCK)) {
781 // unprintable action
782 return;
783 }
784
785 put_queue(kbd, pad_chars[value]);
786}
787
788static void k_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
789{
790 int old_state = kbd->shift_state;
791
792 if (kbd->rep) {
793 return;
794 }
795 /*
796 * Mimic typewriter:
797 * a CapsShift key acts like Shift but undoes CapsLock
798 */
799 if (value == KVAL(K_CAPSSHIFT)) {
800 value = KVAL(K_SHIFT);
801 if (!up_flag) {
802 clr_vc_kbd_led(kbd, K_CAPSLOCK);
803 }
804 }
805
806 if (up_flag) {
807 /*
808 * handle the case that two shift or control
809 * keys are depressed simultaneously
810 */
811 if (kbd->shift_down[value]) {
812 kbd->shift_down[value]--;
813 }
814 } else {
815 kbd->shift_down[value]++;
816 }
817
818 if (kbd->shift_down[value]) {
819 kbd->shift_state |= (1 << value);
820 } else {
821 kbd->shift_state &= ~(1 << value);
822 }
823
824 // kludge
825 if (up_flag && kbd->shift_state != old_state && kbd->npadch != -1) {
826 put_utf8(kbd, kbd->npadch);
827 kbd->npadch = -1;
828 }
829}
830
831static void k_meta(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
832{
833}
834
835static void k_ascii(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
836{
837 int base;
838
839 if (up_flag) {
840 return;
841 }
842
843 if (value < 10) {
844 // decimal input of code, while Alt depressed
845 base = 10;
846 } else {
847 // hexadecimal input of code, while AltGr depressed
848 value -= 10;
849 base = 16;
850 }
851
852 if (kbd->npadch == -1) {
853 kbd->npadch = value;
854 } else {
855 kbd->npadch = kbd->npadch * base + value;
856 }
857}
858
859static void k_lock(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
860{
861 if (up_flag || kbd->rep) {
862 return;
863 }
864
865 chg_vc_kbd_lock(kbd, value);
866}
867
868static void k_slock(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
869{
870 k_shift(kbd, value, up_flag);
871 if (up_flag || kbd->rep) {
872 return;
873 }
874
875 chg_vc_kbd_slock(kbd, value);
876 // try to make Alt, oops, AltGr and such work
877 if (!kbd->key_maps[kbd->lockstate ^ kbd->slockstate]) {
878 kbd->slockstate = 0;
879 chg_vc_kbd_slock(kbd, value);
880 }
881}
882
883static void k_brl(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
884{
885}
886
887void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down)
888{
889 unsigned char shift_final;
890 unsigned char type;
891 unsigned short *key_map;
892 unsigned short keysym;
893
894 if (!state) {
895 return;
896 }
897
898 state->rep = (down == 2);
899
900 shift_final = (state->shift_state | state->slockstate) ^ state->lockstate;
901 key_map = state->key_maps[shift_final];
902 if (!key_map) {
903 // Unsupported shift state (e.g. ctrl = 4, alt = 8), just reset to the default state
904 state->shift_state = 0;
905 state->slockstate = 0;
906 state->lockstate = 0;
907 return;
908 }
909
910 if (keycode < NR_KEYS) {
911 if (state->console_fd < 0) {
912 keysym = key_map[keycode];
913 } else {
914 struct kbentry kbe;
915 kbe.kb_table = shift_final;
916 kbe.kb_index = keycode;
917 if (ioctl(state->console_fd, KDGKBENT, &kbe) == 0)
918 keysym = (kbe.kb_value ^ 0xf000);
919 else
920 return;
921 }
922 } else {
923 return;
924 }
925
926 type = KTYP(keysym);
927
928 if (type < 0xf0) {
929 if (down) {
930 put_utf8(state, keysym);
931 }
932 } else {
933 type -= 0xf0;
934
935 // if type is KT_LETTER then it can be affected by Caps Lock
936 if (type == KT_LETTER) {
937 type = KT_LATIN;
938
939 if (vc_kbd_led(state, K_CAPSLOCK)) {
940 shift_final = shift_final ^ (1 << KG_SHIFT);
941 key_map = state->key_maps[shift_final];
942 if (key_map) {
943 if (state->console_fd < 0) {
944 keysym = key_map[keycode];
945 } else {
946 struct kbentry kbe;
947 kbe.kb_table = shift_final;
948 kbe.kb_index = keycode;
949 if (ioctl(state->console_fd, KDGKBENT, &kbe) == 0)
950 keysym = (kbe.kb_value ^ 0xf000);
951 }
952 }
953 }
954 }
955
956 (*k_handler[type])(state, keysym & 0xff, !down);
957
958 if (type != KT_SLOCK) {
959 state->slockstate = 0;
960 }
961 }
962
963 if (state->text_len > 0) {
964 state->text[state->text_len] = '\0';
965 SDL_SendKeyboardText(state->text);
966 state->text_len = 0;
967 }
968}
969
970#elif !defined(SDL_INPUT_FBSDKBIO) // !SDL_INPUT_LINUXKD
971
972SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
973{
974 return NULL;
975}
976
977void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted)
978{
979}
980
981void 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)
982{
983}
984
985void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state)
986{
987}
988
989void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down)
990{
991}
992
993void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
994{
995}
996
997#endif // SDL_INPUT_LINUXKD
998