sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_evdev_kbd_freebsd.c (17198B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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 
     22 #include "../../SDL_internal.h"
     23 
     24 #include "../linux/SDL_evdev_kbd.h"
     25 #include "SDL_hints.h"
     26 
     27 #ifdef SDL_INPUT_FBSDKBIO
     28 
     29 /* This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source, slightly modified to work with FreeBSD */
     30 
     31 #include <unistd.h>
     32 #include <fcntl.h>
     33 #include <sys/ioctl.h>
     34 #include <sys/kbio.h>
     35 #include <sys/consio.h>
     36 
     37 #include <signal.h>
     38 
     39 #include "../../events/SDL_events_c.h"
     40 #include "SDL_evdev_kbd_default_keyaccmap.h"
     41 
     42 typedef void (fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
     43 
     44 /*
     45  * Keyboard State
     46  */
     47 
     48 struct SDL_EVDEV_keyboard_state
     49 {
     50     int console_fd;
     51     int keyboard_fd;
     52     unsigned long old_kbd_mode;
     53     unsigned short **key_maps;
     54     keymap_t* key_map;
     55     keyboard_info_t* kbInfo;
     56     unsigned char shift_down[4];        /* shift state counters.. */
     57     SDL_bool dead_key_next;
     58     int npadch;                    /* -1 or number assembled on pad */
     59     accentmap_t *accents;
     60     unsigned int diacr;
     61     SDL_bool rep;                    /* flag telling character repeat */
     62     unsigned char lockstate;
     63     unsigned char ledflagstate;
     64     char shift_state;
     65     char text[128];
     66     unsigned int text_len;
     67 };
     68 
     69 static int SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd)
     70 {
     71     return (ioctl(kbd->keyboard_fd, GIO_KEYMAP, kbd->key_map) >= 0);
     72 }
     73 
     74 static SDL_EVDEV_keyboard_state * kbd_cleanup_state = NULL;
     75 static int kbd_cleanup_sigactions_installed = 0;
     76 static int kbd_cleanup_atexit_installed = 0;
     77 
     78 static struct sigaction old_sigaction[NSIG];
     79 
     80 static int fatal_signals[] =
     81 {
     82     /* Handlers for SIGTERM and SIGINT are installed in SDL_QuitInit. */
     83     SIGHUP,  SIGQUIT, SIGILL,  SIGABRT,
     84     SIGFPE,  SIGSEGV, SIGPIPE, SIGBUS,
     85     SIGSYS
     86 };
     87 
     88 static void kbd_cleanup(void)
     89 {
     90     SDL_EVDEV_keyboard_state* kbd = kbd_cleanup_state;
     91     if (kbd == NULL) {
     92         return;
     93     }
     94     kbd_cleanup_state = NULL;
     95     
     96     ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
     97     if (kbd->keyboard_fd != kbd->console_fd) close(kbd->keyboard_fd);
     98     ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
     99 }
    100 
    101 void
    102 SDL_EVDEV_kbd_reraise_signal(int sig)
    103 {
    104     raise(sig);
    105 }
    106 
    107 siginfo_t* SDL_EVDEV_kdb_cleanup_siginfo = NULL;
    108 void*      SDL_EVDEV_kdb_cleanup_ucontext = NULL;
    109 
    110 static void kbd_cleanup_signal_action(int signum, siginfo_t* info, void* ucontext)
    111 {
    112     struct sigaction* old_action_p = &(old_sigaction[signum]);
    113     sigset_t sigset;
    114 
    115     /* Restore original signal handler before going any further. */
    116     sigaction(signum, old_action_p, NULL);
    117 
    118     /* Unmask current signal. */
    119     sigemptyset(&sigset);
    120     sigaddset(&sigset, signum);
    121     sigprocmask(SIG_UNBLOCK, &sigset, NULL);
    122 
    123     /* Save original signal info and context for archeologists. */
    124     SDL_EVDEV_kdb_cleanup_siginfo = info;
    125     SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
    126 
    127     /* Restore keyboard. */
    128     kbd_cleanup();
    129 
    130     /* Reraise signal. */
    131     SDL_EVDEV_kbd_reraise_signal(signum);
    132 }
    133 
    134 static void kbd_unregister_emerg_cleanup()
    135 {
    136     int tabidx, signum;
    137 
    138     kbd_cleanup_state = NULL;
    139 
    140     if (!kbd_cleanup_sigactions_installed) {
    141         return;
    142     }
    143     kbd_cleanup_sigactions_installed = 0;
    144 
    145     for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
    146         struct sigaction* old_action_p;
    147         struct sigaction cur_action;
    148         signum = fatal_signals[tabidx];
    149         old_action_p = &(old_sigaction[signum]);
    150 
    151         /* Examine current signal action */
    152         if (sigaction(signum, NULL, &cur_action))
    153             continue;
    154 
    155         /* Check if action installed and not modifed */
    156         if (!(cur_action.sa_flags & SA_SIGINFO)
    157                 || cur_action.sa_sigaction != &kbd_cleanup_signal_action)
    158             continue;
    159 
    160         /* Restore original action */
    161         sigaction(signum, old_action_p, NULL);
    162     }
    163 }
    164 
    165 static void kbd_cleanup_atexit(void)
    166 {
    167     /* Restore keyboard. */
    168     kbd_cleanup();
    169 
    170     /* Try to restore signal handlers in case shared library is being unloaded */
    171     kbd_unregister_emerg_cleanup();
    172 }
    173 
    174 static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state * kbd)
    175 {
    176     int tabidx, signum;
    177 
    178     if (kbd_cleanup_state != NULL) {
    179         return;
    180     }
    181     kbd_cleanup_state = kbd;
    182 
    183     if (!kbd_cleanup_atexit_installed) {
    184         /* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
    185          * functions that are called when the shared library is unloaded.
    186          * -- man atexit(3)
    187          */
    188         atexit(kbd_cleanup_atexit);
    189         kbd_cleanup_atexit_installed = 1;
    190     }
    191 
    192     if (kbd_cleanup_sigactions_installed) {
    193         return;
    194     }
    195     kbd_cleanup_sigactions_installed = 1;
    196 
    197     for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
    198         struct sigaction* old_action_p;
    199         struct sigaction new_action;
    200         signum = fatal_signals[tabidx];   
    201         old_action_p = &(old_sigaction[signum]);
    202         if (sigaction(signum, NULL, old_action_p))
    203             continue;
    204 
    205         /* Skip SIGHUP and SIGPIPE if handler is already installed
    206          * - assume the handler will do the cleanup
    207          */
    208         if ((signum == SIGHUP || signum == SIGPIPE)
    209                 && (old_action_p->sa_handler != SIG_DFL 
    210                     || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL))
    211             continue;
    212 
    213         new_action = *old_action_p;
    214         new_action.sa_flags |= SA_SIGINFO;
    215         new_action.sa_sigaction = &kbd_cleanup_signal_action;
    216         sigaction(signum, &new_action, NULL);
    217     }
    218 }
    219 
    220 SDL_EVDEV_keyboard_state *
    221 SDL_EVDEV_kbd_init(void)
    222 {
    223     SDL_EVDEV_keyboard_state *kbd;
    224     char flag_state;
    225     char* devicePath;
    226 
    227     kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(SDL_EVDEV_keyboard_state));
    228     if (!kbd) {
    229         return NULL;
    230     }
    231 
    232     kbd->npadch = -1;
    233 
    234     /* This might fail if we're not connected to a tty (e.g. on the Steam Link) */
    235     kbd->keyboard_fd = kbd->console_fd = open("/dev/tty", O_RDONLY);
    236 
    237     kbd->shift_state = 0;
    238 
    239     kbd->accents = SDL_calloc(sizeof(accentmap_t), 1);
    240     kbd->key_map = SDL_calloc(sizeof(keymap_t), 1);
    241     kbd->kbInfo = SDL_calloc(sizeof(keyboard_info_t), 1);    
    242 
    243     ioctl(kbd->console_fd, KDGKBINFO, kbd->kbInfo);
    244 
    245     if (ioctl(kbd->console_fd, KDGKBSTATE, &flag_state) == 0) {
    246         kbd->ledflagstate = flag_state;
    247     }
    248     
    249     if (ioctl(kbd->console_fd, GIO_DEADKEYMAP, kbd->accents) < 0)
    250     {
    251         SDL_free(kbd->accents);
    252         kbd->accents = &accentmap_default_us_acc;
    253     }
    254 
    255     if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
    256         /* Set the keyboard in XLATE mode and load the keymaps */
    257         ioctl(kbd->console_fd, KDSKBMODE, (unsigned long)(K_XLATE));
    258         if(!SDL_EVDEV_kbd_load_keymaps(kbd))
    259         {
    260             SDL_free(kbd->key_map);
    261             kbd->key_map = &keymap_default_us_acc;
    262         }
    263         /* Allow inhibiting keyboard mute with env. variable for debugging etc. */
    264         if (getenv("SDL_INPUT_FREEBSD_KEEP_KBD") == NULL) {
    265             /* Take keyboard from console and open the actual keyboard device.
    266              * Ensures that the keystrokes do not leak through to the console.
    267              */
    268             ioctl(kbd->console_fd, CONS_RELKBD, 1ul);
    269             asprintf(&devicePath, "/dev/kbd%d", kbd->kbInfo->kb_index);         
    270             kbd->keyboard_fd = open(devicePath, O_WRONLY);
    271             if (kbd->keyboard_fd == -1)
    272             {
    273                 // Give keyboard back.
    274                 ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
    275                 kbd->keyboard_fd = kbd->console_fd;
    276             }
    277 
    278             /* Make sure to restore keyboard if application fails to call
    279              * SDL_Quit before exit or fatal signal is raised.
    280              */
    281             if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, SDL_FALSE)) {
    282                 kbd_register_emerg_cleanup(kbd);
    283             }
    284             free(devicePath);
    285         }
    286         else kbd->keyboard_fd = kbd->console_fd;
    287     }
    288 
    289     return kbd;
    290 }
    291 
    292 void
    293 SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
    294 {
    295     if (!kbd) {
    296         return;
    297     }
    298 
    299     kbd_unregister_emerg_cleanup();
    300 
    301     if (kbd->keyboard_fd >= 0) {
    302         /* Restore the original keyboard mode */
    303         ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
    304 
    305         close(kbd->keyboard_fd);
    306         if (kbd->console_fd != kbd->keyboard_fd && kbd->console_fd >= 0)
    307         {
    308             // Give back keyboard.
    309             ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
    310         }
    311         kbd->console_fd = kbd->keyboard_fd = -1;
    312     }
    313 
    314     SDL_free(kbd);
    315 }
    316 
    317 /*
    318  * Helper Functions.
    319  */
    320 static void put_queue(SDL_EVDEV_keyboard_state *kbd, uint c)
    321 {
    322     /* c is already part of a UTF-8 sequence and safe to add as a character */
    323     if (kbd->text_len < (sizeof(kbd->text)-1)) {
    324         kbd->text[kbd->text_len++] = (char)c;
    325     }
    326 }
    327 
    328 static void put_utf8(SDL_EVDEV_keyboard_state *kbd, uint c)
    329 {
    330     if (c < 0x80)
    331         /*  0******* */
    332         put_queue(kbd, c);
    333     else if (c < 0x800) {
    334         /* 110***** 10****** */
    335         put_queue(kbd, 0xc0 | (c >> 6));
    336         put_queue(kbd, 0x80 | (c & 0x3f));
    337     } else if (c < 0x10000) {
    338         if (c >= 0xD800 && c < 0xE000)
    339             return;
    340         if (c == 0xFFFF)
    341             return;
    342         /* 1110**** 10****** 10****** */
    343         put_queue(kbd, 0xe0 | (c >> 12));
    344         put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
    345         put_queue(kbd, 0x80 | (c & 0x3f));
    346     } else if (c < 0x110000) {
    347         /* 11110*** 10****** 10****** 10****** */
    348         put_queue(kbd, 0xf0 | (c >> 18));
    349         put_queue(kbd, 0x80 | ((c >> 12) & 0x3f));
    350         put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
    351         put_queue(kbd, 0x80 | (c & 0x3f));
    352     }
    353 }
    354 
    355 /*
    356  * We have a combining character DIACR here, followed by the character CH.
    357  * If the combination occurs in the table, return the corresponding value.
    358  * Otherwise, if CH is a space or equals DIACR, return DIACR.
    359  * Otherwise, conclude that DIACR was not combining after all,
    360  * queue it and return CH.
    361  */
    362 static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
    363 {
    364     unsigned int d = kbd->diacr;
    365     unsigned int i, j;
    366 
    367     kbd->diacr = 0;
    368 
    369     for (i = 0; i < kbd->accents->n_accs; i++) {
    370         if (kbd->accents->acc[i].accchar == d)
    371         {
    372             for (j = 0; j < NUM_ACCENTCHARS; ++j) {
    373                     if (kbd->accents->acc[i].map[j][0] == 0)        /* end of table */
    374                             break;
    375                     if (kbd->accents->acc[i].map[j][0] == ch)
    376                             return kbd->accents->acc[i].map[j][1];
    377             }
    378         }
    379     }
    380 
    381     if (ch == ' ' || ch == d) {
    382         put_utf8(kbd, d);
    383         return 0;
    384     }
    385     put_utf8(kbd, d);
    386 
    387     return ch;
    388 }
    389 
    390 static int vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
    391 {
    392     return (kbd->ledflagstate & flag) != 0;
    393 }
    394 
    395 static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
    396 {
    397     kbd->ledflagstate ^= flag;
    398     ioctl(kbd->keyboard_fd, KDSKBSTATE, (unsigned long)(kbd->ledflagstate));
    399 }
    400 
    401 /*
    402  * Special function handlers
    403  */
    404 
    405 static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
    406 {
    407     if (up_flag)
    408         return;        /* no action, if this is a key release */
    409 
    410     if (kbd->diacr)
    411         value = handle_diacr(kbd, value);
    412 
    413     if (kbd->dead_key_next) {
    414         kbd->dead_key_next = SDL_FALSE;
    415         kbd->diacr = value;
    416         return;
    417     }
    418     put_utf8(kbd, value);
    419 }
    420 
    421 static void k_deadunicode(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
    422 {
    423     if (up_flag)
    424         return;
    425 
    426     kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
    427 }
    428 
    429 static void k_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
    430 {
    431     int old_state = kbd->shift_state;
    432 
    433     if (kbd->rep)
    434         return;
    435 
    436     if (up_flag) {
    437         /*
    438          * handle the case that two shift or control
    439          * keys are depressed simultaneously
    440          */
    441         if (kbd->shift_down[value])
    442             kbd->shift_down[value]--;
    443     } else
    444         kbd->shift_down[value]++;
    445 
    446     if (kbd->shift_down[value])
    447         kbd->shift_state |= (1 << value);
    448     else
    449         kbd->shift_state &= ~(1 << value);
    450 
    451     /* kludge */
    452     if (up_flag && kbd->shift_state != old_state && kbd->npadch != -1) {
    453         put_utf8(kbd, kbd->npadch);
    454         kbd->npadch = -1;
    455     }
    456 }
    457 
    458 void
    459 SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *kbd, unsigned int keycode, int down)
    460 {
    461     keymap_t key_map;
    462     struct keyent_t keysym;
    463     unsigned int final_key_state;
    464     unsigned int map_from_key_sym;
    465 
    466     key_map = *kbd->key_map;
    467 
    468     if (!kbd) {
    469         return;
    470     }
    471 
    472     kbd->rep = (down == 2);
    473 
    474     if (keycode < NUM_KEYS) {
    475         if (keycode >= 89 && keycode <= 95) {
    476             /* These constitute unprintable language-related keys, so ignore them. */
    477             return;
    478         }
    479         if (keycode > 95)
    480             keycode -= 7;
    481         if (vc_kbd_led(kbd, ALKED) || (kbd->shift_state & 0x8))
    482         {
    483             keycode += ALTGR_OFFSET;
    484         }
    485         keysym = key_map.key[keycode];
    486     } else {
    487         return;
    488     }
    489 
    490     final_key_state = kbd->shift_state & 0x7;
    491     if ((keysym.flgs & FLAG_LOCK_C) && vc_kbd_led(kbd, LED_CAP))
    492         final_key_state ^= 0x1;
    493     if ((keysym.flgs & FLAG_LOCK_N) && vc_kbd_led(kbd, LED_NUM))
    494         final_key_state ^= 0x1;
    495 
    496     map_from_key_sym = keysym.map[final_key_state];
    497     if ((keysym.spcl & (0x80 >> final_key_state)) || (map_from_key_sym & SPCLKEY)) {
    498         /* Special function.*/
    499         if (map_from_key_sym == 0)
    500             return; /* Nothing to do. */
    501         if (map_from_key_sym & SPCLKEY)
    502             map_from_key_sym &= ~SPCLKEY;
    503         if (map_from_key_sym >= F_ACC && map_from_key_sym <= L_ACC) {
    504             /* Accent function.*/
    505             unsigned int accent_index = map_from_key_sym - F_ACC;
    506             if (kbd->accents->acc[accent_index].accchar != 0) {
    507                 k_deadunicode(kbd, kbd->accents->acc[accent_index].accchar, !down);
    508             }
    509         } else {
    510             switch(map_from_key_sym) {
    511             case ASH: /* alt/meta shift */
    512                 k_shift(kbd, 3, down == 0);
    513                 break;
    514             case LSHA: /* left shift + alt lock */
    515             case RSHA: /* right shift + alt lock */
    516                 if (down == 0) chg_vc_kbd_led(kbd, ALKED);
    517             case LSH: /* left shift */
    518             case RSH: /* right shift */
    519                 k_shift(kbd, 0, down == 0);
    520                 break;
    521             case LCTRA: /* left ctrl + alt lock */
    522             case RCTRA: /* right ctrl + alt lock */
    523                 if (down == 0) chg_vc_kbd_led(kbd, ALKED);
    524             case LCTR: /* left ctrl */
    525             case RCTR: /* right ctrl */
    526                 k_shift(kbd, 1, down == 0);
    527                 break;
    528             case LALTA: /* left alt + alt lock */
    529             case RALTA: /* right alt + alt lock */
    530                 if (down == 0) chg_vc_kbd_led(kbd, ALKED);
    531             case LALT: /* left alt */
    532             case RALT: /* right alt */
    533                 k_shift(kbd, 2, down == 0);
    534                 break;
    535             case ALK: /* alt lock */
    536                 if (down == 1) chg_vc_kbd_led(kbd, ALKED);
    537                 break;
    538             case CLK: /* caps lock*/
    539                 if (down == 1) chg_vc_kbd_led(kbd, CLKED);
    540                 break;
    541             case NLK: /* num lock */
    542                 if (down == 1) chg_vc_kbd_led(kbd, NLKED);
    543                 break;
    544             case SLK: /* scroll lock */
    545                 if (down == 1) chg_vc_kbd_led(kbd, SLKED);
    546                 break;
    547             default:
    548                 return;
    549             }
    550         }
    551     } else {
    552         if (map_from_key_sym == '\n' || map_from_key_sym == '\r') {
    553             if (kbd->diacr) {
    554                 kbd->diacr = 0;
    555                 return;
    556             }
    557         }
    558         if (map_from_key_sym >= ' ' && map_from_key_sym != 127) {
    559             k_self(kbd, map_from_key_sym, !down);
    560         }
    561     }
    562 
    563     if (kbd->text_len > 0) {
    564         kbd->text[kbd->text_len] = '\0';
    565         SDL_SendKeyboardText(kbd->text);
    566         kbd->text_len = 0;
    567     }
    568 }
    569 
    570 #endif /* SDL_INPUT_FBSDKBIO */
    571 
    572 /* vi: set ts=4 sw=4 expandtab: */