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