src/core/linux/SDL_evdev_kbd.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 31 Oct 2018 15:01:20 -0700
changeset 12375 d9a16c76d8d1
parent 12284 fe9bafcd47ba
child 12503 806492103856
permissions -rw-r--r--
Fixed bug 4347 - Keyboard LEDs don't work on linux console

Rainer Sabelka

When using SLD2 on a Linux console with the KMS/DRM video backend and Linux evdev keyboard support, the caps lock, scroll lock, and num lock leds do not work.

The attached patch adds ioctls for setting the LED state in SDL_evdev_kbd.c
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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     fprintf(stderr, "(SDL restoring keyboard) ");
   220     ioctl(kbd->console_fd, KDSKBMODE, kbd->old_kbd_mode);
   221 }
   222 
   223 void
   224 SDL_EVDEV_kbd_reraise_signal(int sig)
   225 {
   226     raise(sig);
   227 }
   228 
   229 siginfo_t* SDL_EVDEV_kdb_cleanup_siginfo = NULL;
   230 void*      SDL_EVDEV_kdb_cleanup_ucontext = NULL;
   231 
   232 static void kbd_cleanup_signal_action(int signum, siginfo_t* info, void* ucontext)
   233 {
   234     struct sigaction* old_action_p = &(old_sigaction[signum]);
   235     sigset_t sigset;
   236 
   237     /* Restore original signal handler before going any further. */
   238     sigaction(signum, old_action_p, NULL);
   239 
   240     /* Unmask current signal. */
   241     sigemptyset(&sigset);
   242     sigaddset(&sigset, signum);
   243     sigprocmask(SIG_UNBLOCK, &sigset, NULL);
   244 
   245     /* Save original signal info and context for archeologists. */
   246     SDL_EVDEV_kdb_cleanup_siginfo = info;
   247     SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
   248 
   249     /* Restore keyboard. */
   250     kbd_cleanup();
   251 
   252     /* Reraise signal. */
   253     SDL_EVDEV_kbd_reraise_signal(signum);
   254 }
   255 
   256 static void kbd_unregister_emerg_cleanup()
   257 {
   258     int tabidx, signum;
   259 
   260     kbd_cleanup_state = NULL;
   261 
   262     if (!kbd_cleanup_sigactions_installed) {
   263         return;
   264     }
   265     kbd_cleanup_sigactions_installed = 0;
   266 
   267     for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
   268         struct sigaction* old_action_p;
   269         struct sigaction cur_action;
   270         signum = fatal_signals[tabidx];
   271         old_action_p = &(old_sigaction[signum]);
   272 
   273         /* Examine current signal action */
   274         if (sigaction(signum, NULL, &cur_action))
   275             continue;
   276 
   277         /* Check if action installed and not modifed */
   278         if (!(cur_action.sa_flags & SA_SIGINFO)
   279                 || cur_action.sa_sigaction != &kbd_cleanup_signal_action)
   280             continue;
   281 
   282         /* Restore original action */
   283         sigaction(signum, old_action_p, NULL);
   284     }
   285 }
   286 
   287 static void kbd_cleanup_atexit(void)
   288 {
   289     /* Restore keyboard. */
   290     kbd_cleanup();
   291 
   292     /* Try to restore signal handlers in case shared library is being unloaded */
   293     kbd_unregister_emerg_cleanup();
   294 }
   295 
   296 static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state * kbd)
   297 {
   298     int tabidx, signum;
   299 
   300     if (kbd_cleanup_state != NULL) {
   301         return;
   302     }
   303     kbd_cleanup_state = kbd;
   304 
   305     if (!kbd_cleanup_atexit_installed) {
   306         /* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
   307          * functions that are called when the shared library is unloaded.
   308          * -- man atexit(3)
   309          */
   310         atexit(kbd_cleanup_atexit);
   311         kbd_cleanup_atexit_installed = 1;
   312     }
   313 
   314     if (kbd_cleanup_sigactions_installed) {
   315         return;
   316     }
   317     kbd_cleanup_sigactions_installed = 1;
   318 
   319     for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
   320         struct sigaction* old_action_p;
   321         struct sigaction new_action;
   322         signum = fatal_signals[tabidx];   
   323         old_action_p = &(old_sigaction[signum]);
   324         if (sigaction(signum, NULL, old_action_p))
   325             continue;
   326 
   327         /* Skip SIGHUP and SIGPIPE if handler is already installed
   328          * - assume the handler will do the cleanup
   329          */
   330         if ((signum == SIGHUP || signum == SIGPIPE)
   331                 && (old_action_p->sa_handler != SIG_DFL 
   332                     || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL))
   333             continue;
   334 
   335         new_action = *old_action_p;
   336         new_action.sa_flags |= SA_SIGINFO;
   337         new_action.sa_sigaction = &kbd_cleanup_signal_action;
   338         sigaction(signum, &new_action, NULL);
   339     }
   340 }
   341 
   342 SDL_EVDEV_keyboard_state *
   343 SDL_EVDEV_kbd_init(void)
   344 {
   345     SDL_EVDEV_keyboard_state *kbd;
   346     int i;
   347     char flag_state;
   348     char shift_state[2] = {TIOCL_GETSHIFTSTATE, 0};
   349 
   350     kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(*kbd));
   351     if (!kbd) {
   352         return NULL;
   353     }
   354 
   355     kbd->npadch = -1;
   356 
   357     /* This might fail if we're not connected to a tty (e.g. on the Steam Link) */
   358     kbd->console_fd = open("/dev/tty", O_RDONLY);
   359 
   360     if (ioctl(kbd->console_fd, TIOCLINUX, shift_state) == 0) {
   361         kbd->shift_state = *shift_state;
   362     }
   363 
   364     if (ioctl(kbd->console_fd, KDGKBLED, &flag_state) == 0) {
   365         kbd->ledflagstate = flag_state;
   366     }
   367 
   368     kbd->accents = &default_accents;
   369     if (ioctl(kbd->console_fd, KDGKBDIACR, kbd->accents) < 0) {
   370         /* No worries, we'll use the default accent table */
   371     }
   372 
   373     kbd->key_maps = default_key_maps;
   374     if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
   375         /* Set the keyboard in UNICODE mode and load the keymaps */
   376         ioctl(kbd->console_fd, KDSKBMODE, K_UNICODE);
   377 
   378         if (SDL_EVDEV_kbd_load_keymaps(kbd) < 0) {
   379             for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
   380                 if (kbd->key_maps[i]) {
   381                     SDL_free(kbd->key_maps[i]);
   382                 }
   383             }
   384             SDL_free(kbd->key_maps);
   385 
   386             kbd->key_maps = default_key_maps;
   387         }
   388 
   389         /* Allow inhibiting keyboard mute with env. variable for debugging etc. */
   390         if (getenv("SDL_INPUT_LINUX_KEEP_KBD") == NULL) {
   391             /* Mute the keyboard so keystrokes only generate evdev events
   392              * and do not leak through to the console
   393              */
   394             ioctl(kbd->console_fd, KDSKBMODE, K_OFF);
   395 
   396             /* Make sure to restore keyboard if application fails to call
   397              * SDL_Quit before exit or fatal signal is raised.
   398              */
   399             if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, SDL_FALSE)) {
   400                 kbd_register_emerg_cleanup(kbd);
   401             }
   402         }
   403     }
   404 
   405 #ifdef DUMP_ACCENTS
   406     SDL_EVDEV_dump_accents(kbd);
   407 #endif
   408 #ifdef DUMP_KEYMAP
   409     SDL_EVDEV_dump_keymap(kbd);
   410 #endif
   411     return kbd;
   412 }
   413 
   414 void
   415 SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
   416 {
   417     if (!kbd) {
   418         return;
   419     }
   420 
   421     kbd_unregister_emerg_cleanup();
   422 
   423     if (kbd->console_fd >= 0) {
   424         /* Restore the original keyboard mode */
   425         ioctl(kbd->console_fd, KDSKBMODE, kbd->old_kbd_mode);
   426 
   427         close(kbd->console_fd);
   428         kbd->console_fd = -1;
   429     }
   430 
   431     if (kbd->key_maps && kbd->key_maps != default_key_maps) {
   432         int i;
   433         for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
   434             if (kbd->key_maps[i]) {
   435                 SDL_free(kbd->key_maps[i]);
   436             }
   437         }
   438         SDL_free(kbd->key_maps);
   439     }
   440 
   441     SDL_free(kbd);
   442 }
   443 
   444 /*
   445  * Helper Functions.
   446  */
   447 static void put_queue(SDL_EVDEV_keyboard_state *kbd, uint c)
   448 {
   449     /* c is already part of a UTF-8 sequence and safe to add as a character */
   450     if (kbd->text_len < (sizeof(kbd->text)-1)) {
   451         kbd->text[kbd->text_len++] = (char)c;
   452     }
   453 }
   454 
   455 static void put_utf8(SDL_EVDEV_keyboard_state *kbd, uint c)
   456 {
   457     if (c < 0x80)
   458         /*  0******* */
   459         put_queue(kbd, c);
   460     else if (c < 0x800) {
   461         /* 110***** 10****** */
   462         put_queue(kbd, 0xc0 | (c >> 6));
   463         put_queue(kbd, 0x80 | (c & 0x3f));
   464     } else if (c < 0x10000) {
   465         if (c >= 0xD800 && c < 0xE000)
   466             return;
   467         if (c == 0xFFFF)
   468             return;
   469         /* 1110**** 10****** 10****** */
   470         put_queue(kbd, 0xe0 | (c >> 12));
   471         put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
   472         put_queue(kbd, 0x80 | (c & 0x3f));
   473     } else if (c < 0x110000) {
   474         /* 11110*** 10****** 10****** 10****** */
   475         put_queue(kbd, 0xf0 | (c >> 18));
   476         put_queue(kbd, 0x80 | ((c >> 12) & 0x3f));
   477         put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
   478         put_queue(kbd, 0x80 | (c & 0x3f));
   479     }
   480 }
   481 
   482 /*
   483  * We have a combining character DIACR here, followed by the character CH.
   484  * If the combination occurs in the table, return the corresponding value.
   485  * Otherwise, if CH is a space or equals DIACR, return DIACR.
   486  * Otherwise, conclude that DIACR was not combining after all,
   487  * queue it and return CH.
   488  */
   489 static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
   490 {
   491     unsigned int d = kbd->diacr;
   492     unsigned int i;
   493 
   494     kbd->diacr = 0;
   495 
   496     for (i = 0; i < kbd->accents->kb_cnt; i++) {
   497         if (kbd->accents->kbdiacr[i].diacr == d &&
   498             kbd->accents->kbdiacr[i].base == ch) {
   499             return kbd->accents->kbdiacr[i].result;
   500         }
   501     }
   502 
   503     if (ch == ' ' || ch == d)
   504         return d;
   505 
   506     put_utf8(kbd, d);
   507 
   508     return ch;
   509 }
   510 
   511 static int vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
   512 {
   513     return (kbd->ledflagstate & flag) != 0;
   514 }
   515 
   516 static void set_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
   517 {
   518     kbd->ledflagstate |= flag;
   519     ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
   520 }
   521 
   522 static void clr_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
   523 {
   524     kbd->ledflagstate &= ~flag;
   525     ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
   526 }
   527 
   528 static void chg_vc_kbd_lock(SDL_EVDEV_keyboard_state *kbd, int flag)
   529 {
   530     kbd->lockstate ^= 1 << flag;
   531 }
   532 
   533 static void chg_vc_kbd_slock(SDL_EVDEV_keyboard_state *kbd, int flag)
   534 {
   535     kbd->slockstate ^= 1 << flag;
   536 }
   537 
   538 static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
   539 {
   540     kbd->ledflagstate ^= flag;
   541     ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
   542 }
   543 
   544 /*
   545  * Special function handlers
   546  */
   547 
   548 static void fn_enter(SDL_EVDEV_keyboard_state *kbd)
   549 {
   550     if (kbd->diacr) {
   551         put_utf8(kbd, kbd->diacr);
   552         kbd->diacr = 0;
   553     }
   554 }
   555 
   556 static void fn_caps_toggle(SDL_EVDEV_keyboard_state *kbd)
   557 {
   558     if (kbd->rep)
   559         return;
   560 
   561     chg_vc_kbd_led(kbd, K_CAPSLOCK);
   562 }
   563 
   564 static void fn_caps_on(SDL_EVDEV_keyboard_state *kbd)
   565 {
   566     if (kbd->rep)
   567         return;
   568 
   569     set_vc_kbd_led(kbd, K_CAPSLOCK);
   570 }
   571 
   572 static void fn_num(SDL_EVDEV_keyboard_state *kbd)
   573 {
   574     if (!kbd->rep)
   575         chg_vc_kbd_led(kbd, K_NUMLOCK);
   576 }
   577 
   578 static void fn_compose(SDL_EVDEV_keyboard_state *kbd)
   579 {
   580     kbd->dead_key_next = SDL_TRUE;
   581 }
   582 
   583 /*
   584  * Special key handlers
   585  */
   586 
   587 static void k_ignore(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   588 {
   589 }
   590 
   591 static void k_spec(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   592 {
   593     if (up_flag)
   594         return;
   595     if (value >= SDL_arraysize(fn_handler))
   596         return;
   597     if (fn_handler[value])
   598         fn_handler[value](kbd);
   599 }
   600 
   601 static void k_lowercase(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   602 {
   603 }
   604 
   605 static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   606 {
   607     if (up_flag)
   608         return;        /* no action, if this is a key release */
   609 
   610     if (kbd->diacr)
   611         value = handle_diacr(kbd, value);
   612 
   613     if (kbd->dead_key_next) {
   614         kbd->dead_key_next = SDL_FALSE;
   615         kbd->diacr = value;
   616         return;
   617     }
   618     put_utf8(kbd, value);
   619 }
   620 
   621 static void k_deadunicode(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
   622 {
   623     if (up_flag)
   624         return;
   625 
   626     kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
   627 }
   628 
   629 static void k_dead(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   630 {
   631     const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' };
   632 
   633     k_deadunicode(kbd, ret_diacr[value], up_flag);
   634 }
   635 
   636 static void k_dead2(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   637 {
   638     k_deadunicode(kbd, value, up_flag);
   639 }
   640 
   641 static void k_cons(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   642 {
   643 }
   644 
   645 static void k_fn(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   646 {
   647 }
   648 
   649 static void k_cur(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   650 {
   651 }
   652 
   653 static void k_pad(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   654 {
   655     static const char pad_chars[] = "0123456789+-*/\015,.?()#";
   656 
   657     if (up_flag)
   658         return;        /* no action, if this is a key release */
   659 
   660     if (!vc_kbd_led(kbd, K_NUMLOCK)) {
   661         /* unprintable action */
   662         return;
   663     }
   664 
   665     put_queue(kbd, pad_chars[value]);
   666 }
   667 
   668 static void k_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   669 {
   670     int old_state = kbd->shift_state;
   671 
   672     if (kbd->rep)
   673         return;
   674     /*
   675      * Mimic typewriter:
   676      * a CapsShift key acts like Shift but undoes CapsLock
   677      */
   678     if (value == KVAL(K_CAPSSHIFT)) {
   679         value = KVAL(K_SHIFT);
   680         if (!up_flag)
   681             clr_vc_kbd_led(kbd, K_CAPSLOCK);
   682     }
   683 
   684     if (up_flag) {
   685         /*
   686          * handle the case that two shift or control
   687          * keys are depressed simultaneously
   688          */
   689         if (kbd->shift_down[value])
   690             kbd->shift_down[value]--;
   691     } else
   692         kbd->shift_down[value]++;
   693 
   694     if (kbd->shift_down[value])
   695         kbd->shift_state |= (1 << value);
   696     else
   697         kbd->shift_state &= ~(1 << value);
   698 
   699     /* kludge */
   700     if (up_flag && kbd->shift_state != old_state && kbd->npadch != -1) {
   701         put_utf8(kbd, kbd->npadch);
   702         kbd->npadch = -1;
   703     }
   704 }
   705 
   706 static void k_meta(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   707 {
   708 }
   709 
   710 static void k_ascii(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   711 {
   712     int base;
   713 
   714     if (up_flag)
   715         return;
   716 
   717     if (value < 10) {
   718         /* decimal input of code, while Alt depressed */
   719         base = 10;
   720     } else {
   721         /* hexadecimal input of code, while AltGr depressed */
   722         value -= 10;
   723         base = 16;
   724     }
   725 
   726     if (kbd->npadch == -1)
   727         kbd->npadch = value;
   728     else
   729         kbd->npadch = kbd->npadch * base + value;
   730 }
   731 
   732 static void k_lock(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   733 {
   734     if (up_flag || kbd->rep)
   735         return;
   736 
   737     chg_vc_kbd_lock(kbd, value);
   738 }
   739 
   740 static void k_slock(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   741 {
   742     k_shift(kbd, value, up_flag);
   743     if (up_flag || kbd->rep)
   744         return;
   745 
   746     chg_vc_kbd_slock(kbd, value);
   747     /* try to make Alt, oops, AltGr and such work */
   748     if (!kbd->key_maps[kbd->lockstate ^ kbd->slockstate]) {
   749         kbd->slockstate = 0;
   750         chg_vc_kbd_slock(kbd, value);
   751     }
   752 }
   753 
   754 static void k_brl(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
   755 {
   756 }
   757 
   758 void
   759 SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *kbd, unsigned int keycode, int down)
   760 {
   761     unsigned char shift_final;
   762     unsigned char type;
   763     unsigned short *key_map;
   764     unsigned short keysym;
   765 
   766     if (!kbd) {
   767         return;
   768     }
   769 
   770     kbd->rep = (down == 2);
   771 
   772     shift_final = (kbd->shift_state | kbd->slockstate) ^ kbd->lockstate;
   773     key_map = kbd->key_maps[shift_final];
   774     if (!key_map) {
   775         /* Unsupported shift state (e.g. ctrl = 4, alt = 8), just reset to the default state */
   776         kbd->shift_state = 0;
   777         kbd->slockstate = 0;
   778         kbd->lockstate = 0;
   779         return;
   780     }
   781 
   782     if (keycode < NR_KEYS) {
   783         keysym = key_map[keycode];
   784     } else {
   785         return;
   786     }
   787 
   788     type = KTYP(keysym);
   789 
   790     if (type < 0xf0) {
   791         if (down) {
   792             put_utf8(kbd, keysym);
   793         }
   794     } else {
   795         type -= 0xf0;
   796 
   797         /* if type is KT_LETTER then it can be affected by Caps Lock */
   798         if (type == KT_LETTER) {
   799             type = KT_LATIN;
   800 
   801             if (vc_kbd_led(kbd, K_CAPSLOCK)) {
   802                 key_map = kbd->key_maps[shift_final ^ (1 << KG_SHIFT)];
   803                 if (key_map) {
   804                     keysym = key_map[keycode];
   805                 }
   806             }
   807         }
   808 
   809         (*k_handler[type])(kbd, keysym & 0xff, !down);
   810 
   811         if (type != KT_SLOCK) {
   812             kbd->slockstate = 0;
   813         }
   814     }
   815 
   816     if (kbd->text_len > 0) {
   817         kbd->text[kbd->text_len] = '\0';
   818         SDL_SendKeyboardText(kbd->text);
   819         kbd->text_len = 0;
   820     }
   821 }
   822 
   823 #else /* !SDL_INPUT_LINUXKD */
   824 
   825 SDL_EVDEV_keyboard_state *
   826 SDL_EVDEV_kbd_init(void)
   827 {
   828     return NULL;
   829 }
   830 
   831 void
   832 SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down)
   833 {
   834 }
   835 
   836 void
   837 SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
   838 {
   839 }
   840 
   841 #endif /* SDL_INPUT_LINUXKD */
   842 
   843 /* vi: set ts=4 sw=4 expandtab: */