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 | |
61 | typedef void(k_handler_fn)(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag); |
62 | static k_handler_fn K_HANDLERS; |
63 | static k_handler_fn *k_handler[16] = { K_HANDLERS }; |
64 | |
65 | typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd); |
66 | static void fn_enter(SDL_EVDEV_keyboard_state *kbd); |
67 | static void fn_caps_toggle(SDL_EVDEV_keyboard_state *kbd); |
68 | static void fn_caps_on(SDL_EVDEV_keyboard_state *kbd); |
69 | static void fn_num(SDL_EVDEV_keyboard_state *kbd); |
70 | static void fn_compose(SDL_EVDEV_keyboard_state *kbd); |
71 | |
72 | static 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 | |
84 | struct 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 |
109 | static 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 |
131 | static 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 | |
160 | static SDL_EVDEV_keyboard_state *kbd_cleanup_state = NULL; |
161 | static int kbd_cleanup_sigactions_installed = 0; |
162 | static int kbd_cleanup_atexit_installed = 0; |
163 | |
164 | static struct sigaction old_sigaction[NSIG]; |
165 | |
166 | static 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 | |
173 | static 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 | |
184 | static void SDL_EVDEV_kbd_reraise_signal(int sig) |
185 | { |
186 | (void)raise(sig); |
187 | } |
188 | |
189 | static siginfo_t *SDL_EVDEV_kdb_cleanup_siginfo = NULL; |
190 | static void *SDL_EVDEV_kdb_cleanup_ucontext = NULL; |
191 | |
192 | static 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 | |
216 | static 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 | |
248 | static 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 | |
257 | static 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 | |
303 | enum { |
304 | VT_SIGNAL_NONE, |
305 | VT_SIGNAL_RELEASE, |
306 | VT_SIGNAL_ACQUIRE, |
307 | }; |
308 | static int vt_release_signal; |
309 | static int vt_acquire_signal; |
310 | static SDL_AtomicInt vt_signal_pending; |
311 | |
312 | typedef void (*signal_handler)(int signum); |
313 | |
314 | static void kbd_vt_release_signal_action(int signum) |
315 | { |
316 | SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_RELEASE); |
317 | } |
318 | |
319 | static void kbd_vt_acquire_signal_action(int signum) |
320 | { |
321 | SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_ACQUIRE); |
322 | } |
323 | |
324 | static 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 | |
343 | static 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 | |
363 | static 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 | |
381 | static 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 | |
404 | static 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 | |
423 | SDL_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 | |
465 | void 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 | |
498 | void 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 | |
510 | void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state) |
511 | { |
512 | if (!state) { |
513 | return; |
514 | } |
515 | |
516 | kbd_vt_update(state); |
517 | } |
518 | |
519 | void 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 | */ |
550 | static 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 | |
558 | static 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 | */ |
593 | static 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 | |
621 | static bool vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag) |
622 | { |
623 | return (kbd->ledflagstate & flag) != 0; |
624 | } |
625 | |
626 | static 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 | |
632 | static 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 | |
638 | static void chg_vc_kbd_lock(SDL_EVDEV_keyboard_state *kbd, int flag) |
639 | { |
640 | kbd->lockstate ^= 1 << flag; |
641 | } |
642 | |
643 | static void chg_vc_kbd_slock(SDL_EVDEV_keyboard_state *kbd, int flag) |
644 | { |
645 | kbd->slockstate ^= 1 << flag; |
646 | } |
647 | |
648 | static 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 | |
658 | static 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 | |
666 | static 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 | |
675 | static 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 | |
684 | static void fn_num(SDL_EVDEV_keyboard_state *kbd) |
685 | { |
686 | if (!kbd->rep) { |
687 | chg_vc_kbd_led(kbd, K_NUMLOCK); |
688 | } |
689 | } |
690 | |
691 | static void fn_compose(SDL_EVDEV_keyboard_state *kbd) |
692 | { |
693 | kbd->dead_key_next = true; |
694 | } |
695 | |
696 | /* |
697 | * Special key handlers |
698 | */ |
699 | |
700 | static void k_ignore(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
701 | { |
702 | } |
703 | |
704 | static 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 | |
717 | static void k_lowercase(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
718 | { |
719 | } |
720 | |
721 | static 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 | |
739 | static 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 | |
748 | static 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 | |
755 | static void k_dead2(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
756 | { |
757 | k_deadunicode(kbd, value, up_flag); |
758 | } |
759 | |
760 | static void k_cons(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
761 | { |
762 | } |
763 | |
764 | static void k_fn(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
765 | { |
766 | } |
767 | |
768 | static void k_cur(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
769 | { |
770 | } |
771 | |
772 | static 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 | |
788 | static 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 | |
831 | static void k_meta(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
832 | { |
833 | } |
834 | |
835 | static 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 | |
859 | static 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 | |
868 | static 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 | |
883 | static void k_brl(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag) |
884 | { |
885 | } |
886 | |
887 | void 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 | |
972 | SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void) |
973 | { |
974 | return NULL; |
975 | } |
976 | |
977 | void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted) |
978 | { |
979 | } |
980 | |
981 | void 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 | |
985 | void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state) |
986 | { |
987 | } |
988 | |
989 | void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down) |
990 | { |
991 | } |
992 | |
993 | void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state) |
994 | { |
995 | } |
996 | |
997 | #endif // SDL_INPUT_LINUXKD |
998 | |