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
slouken@10797
     1
/*
slouken@10797
     2
  Simple DirectMedia Layer
slouken@11811
     3
  Copyright (C) 1997-2018 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: */