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 | |
62 | typedef void (k_handler_fn)(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag); |
63 | static k_handler_fn K_HANDLERS; |
64 | static k_handler_fn *k_handler[16] = { K_HANDLERS }; |
65 | |
66 | typedef void (fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd); |
67 | static void fn_enter(SDL_EVDEV_keyboard_state *kbd); |
68 | static void fn_caps_toggle(SDL_EVDEV_keyboard_state *kbd); |
69 | static void fn_caps_on(SDL_EVDEV_keyboard_state *kbd); |
70 | static void fn_num(SDL_EVDEV_keyboard_state *kbd); |
71 | static void fn_compose(SDL_EVDEV_keyboard_state *kbd); |
72 | |
73 | static 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 | |
87 | struct 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 |
107 | static 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 |
129 | static 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 | |
158 | static 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 | |
197 | static SDL_EVDEV_keyboard_state * kbd_cleanup_state = NULL; |
198 | static int kbd_cleanup_sigactions_installed = 0; |
199 | static int kbd_cleanup_atexit_installed = 0; |
200 | |
201 | static struct sigaction old_sigaction[NSIG]; |
202 | |
203 | static 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 | |
211 | static 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 | |
222 | void |
223 | SDL_EVDEV_kbd_reraise_signal(int sig) |
224 | { |
225 | raise(sig); |
226 | } |
227 | |
228 | siginfo_t* SDL_EVDEV_kdb_cleanup_siginfo = NULL; |
229 | void* SDL_EVDEV_kdb_cleanup_ucontext = NULL; |
230 | |
231 | static 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 | |
255 | static 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 | |
286 | static 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 | |
295 | static 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 | |
341 | SDL_EVDEV_keyboard_state * |
342 | SDL_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 | |
413 | void |
414 | SDL_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 | */ |
446 | static 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 | |
454 | static 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 | */ |
488 | static 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 | |
510 | static int vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag) |
511 | { |
512 | return (kbd->ledflagstate & flag) != 0; |
513 | } |
514 | |
515 | static 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 | |
521 | static 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 | |
527 | static void chg_vc_kbd_lock(SDL_EVDEV_keyboard_state *kbd, int flag) |
528 | { |
529 | kbd->lockstate ^= 1 << flag; |
530 | } |
531 | |
532 | static void chg_vc_kbd_slock(SDL_EVDEV_keyboard_state *kbd, int flag) |
533 | { |
534 | kbd->slockstate ^= 1 << flag; |
535 | } |
536 | |
537 | static 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 | |
547 | static 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 | |
555 | static 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 | |
563 | static 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 | |
571 | static void fn_num(SDL_EVDEV_keyboard_state *kbd) |
572 | { |
573 | if (!kbd->rep) |
574 | chg_vc_kbd_led(kbd, K_NUMLOCK); |
575 | } |
576 | |
577 | static 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 | |
586 | static void k_ignore(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
587 | { |
588 | } |
589 | |
590 | static 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 | |
600 | static void k_lowercase(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
601 | { |
602 | } |
603 | |
604 | static 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 | |
620 | static 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 | |
628 | static 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 | |
635 | static void k_dead2(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
636 | { |
637 | k_deadunicode(kbd, value, up_flag); |
638 | } |
639 | |
640 | static void k_cons(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
641 | { |
642 | } |
643 | |
644 | static void k_fn(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
645 | { |
646 | } |
647 | |
648 | static void k_cur(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
649 | { |
650 | } |
651 | |
652 | static 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 | |
667 | static 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 | |
705 | static void k_meta(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
706 | { |
707 | } |
708 | |
709 | static 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 | |
731 | static 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 | |
739 | static 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 | |
753 | static void k_brl(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
754 | { |
755 | } |
756 | |
757 | void |
758 | SDL_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 | |
824 | SDL_EVDEV_keyboard_state * |
825 | SDL_EVDEV_kbd_init(void) |
826 | { |
827 | return NULL; |
828 | } |
829 | |
830 | void |
831 | SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down) |
832 | { |
833 | } |
834 | |
835 | void |
836 | SDL_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 | |