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