qemu/ui/keymaps.c
<<
>>
Prefs
   1/*
   2 * QEMU keysym to keycode conversion using rdesktop keymaps
   3 *
   4 * Copyright (c) 2004 Johannes Schindelin
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "qemu-common.h"
  27#include "keymaps.h"
  28#include "sysemu/sysemu.h"
  29#include "trace.h"
  30#include "qemu/ctype.h"
  31#include "qemu/error-report.h"
  32#include "qapi/error.h"
  33#include "ui/input.h"
  34
  35struct keysym2code {
  36    uint32_t count;
  37    uint16_t keycodes[4];
  38};
  39
  40struct kbd_layout_t {
  41    GHashTable *hash;
  42};
  43
  44static int get_keysym(const name2keysym_t *table,
  45                      const char *name)
  46{
  47    const name2keysym_t *p;
  48    for(p = table; p->name != NULL; p++) {
  49        if (!strcmp(p->name, name)) {
  50            return p->keysym;
  51        }
  52    }
  53    if (name[0] == 'U' && strlen(name) == 5) { /* try unicode Uxxxx */
  54        char *end;
  55        int ret = (int)strtoul(name + 1, &end, 16);
  56        if (*end == '\0' && ret > 0) {
  57            return ret;
  58        }
  59    }
  60    return 0;
  61}
  62
  63
  64static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k)
  65{
  66    struct keysym2code *keysym2code;
  67
  68    keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
  69    if (keysym2code) {
  70        if (keysym2code->count < ARRAY_SIZE(keysym2code->keycodes)) {
  71            keysym2code->keycodes[keysym2code->count++] = keycode;
  72        } else {
  73            warn_report("more than %zd keycodes for keysym %d",
  74                        ARRAY_SIZE(keysym2code->keycodes), keysym);
  75        }
  76        return;
  77    }
  78
  79    keysym2code = g_new0(struct keysym2code, 1);
  80    keysym2code->keycodes[0] = keycode;
  81    keysym2code->count = 1;
  82    g_hash_table_replace(k->hash, GINT_TO_POINTER(keysym), keysym2code);
  83    trace_keymap_add(keysym, keycode, line);
  84}
  85
  86static int parse_keyboard_layout(kbd_layout_t *k,
  87                                 const name2keysym_t *table,
  88                                 const char *language, Error **errp)
  89{
  90    int ret;
  91    FILE *f;
  92    char * filename;
  93    char line[1024];
  94    char keyname[64];
  95    int len;
  96
  97    filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language);
  98    trace_keymap_parse(filename);
  99    f = filename ? fopen(filename, "r") : NULL;
 100    g_free(filename);
 101    if (!f) {
 102        error_setg(errp, "could not read keymap file: '%s'", language);
 103        return -1;
 104    }
 105
 106    for(;;) {
 107        if (fgets(line, 1024, f) == NULL) {
 108            break;
 109        }
 110        len = strlen(line);
 111        if (len > 0 && line[len - 1] == '\n') {
 112            line[len - 1] = '\0';
 113        }
 114        if (line[0] == '#') {
 115            continue;
 116        }
 117        if (!strncmp(line, "map ", 4)) {
 118            continue;
 119        }
 120        if (!strncmp(line, "include ", 8)) {
 121            error_setg(errp, "keymap include files are not supported any more");
 122            ret = -1;
 123            goto out;
 124        } else {
 125            int offset = 0;
 126            while (line[offset] != 0 &&
 127                   line[offset] != ' ' &&
 128                   offset < sizeof(keyname) - 1) {
 129                keyname[offset] = line[offset];
 130                offset++;
 131            }
 132            keyname[offset] = 0;
 133            if (strlen(keyname)) {
 134                int keysym;
 135                keysym = get_keysym(table, keyname);
 136                if (keysym == 0) {
 137                    /* warn_report("unknown keysym %s", line);*/
 138                } else {
 139                    const char *rest = line + offset + 1;
 140                    int keycode = strtol(rest, NULL, 0);
 141
 142                    if (strstr(rest, "shift")) {
 143                        keycode |= SCANCODE_SHIFT;
 144                    }
 145                    if (strstr(rest, "altgr")) {
 146                        keycode |= SCANCODE_ALTGR;
 147                    }
 148                    if (strstr(rest, "ctrl")) {
 149                        keycode |= SCANCODE_CTRL;
 150                    }
 151
 152                    add_keysym(line, keysym, keycode, k);
 153
 154                    if (strstr(rest, "addupper")) {
 155                        char *c;
 156                        for (c = keyname; *c; c++) {
 157                            *c = qemu_toupper(*c);
 158                        }
 159                        keysym = get_keysym(table, keyname);
 160                        if (keysym) {
 161                            add_keysym(line, keysym,
 162                                       keycode | SCANCODE_SHIFT, k);
 163                        }
 164                    }
 165                }
 166            }
 167        }
 168    }
 169
 170    ret = 0;
 171out:
 172    fclose(f);
 173    return ret;
 174}
 175
 176
 177kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
 178                                   const char *language, Error **errp)
 179{
 180    kbd_layout_t *k;
 181
 182    k = g_new0(kbd_layout_t, 1);
 183    k->hash = g_hash_table_new(NULL, NULL);
 184    if (parse_keyboard_layout(k, table, language, errp) < 0) {
 185        g_hash_table_unref(k->hash);
 186        g_free(k);
 187        return NULL;
 188    }
 189    return k;
 190}
 191
 192
 193int keysym2scancode(kbd_layout_t *k, int keysym,
 194                    QKbdState *kbd, bool down)
 195{
 196    static const uint32_t mask =
 197        SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL;
 198    uint32_t mods, i;
 199    struct keysym2code *keysym2code;
 200
 201#ifdef XK_ISO_Left_Tab
 202    if (keysym == XK_ISO_Left_Tab) {
 203        keysym = XK_Tab;
 204    }
 205#endif
 206
 207    keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
 208    if (!keysym2code) {
 209        trace_keymap_unmapped(keysym);
 210        warn_report("no scancode found for keysym %d", keysym);
 211        return 0;
 212    }
 213
 214    if (keysym2code->count == 1) {
 215        return keysym2code->keycodes[0];
 216    }
 217
 218    /* We have multiple keysym -> keycode mappings. */
 219    if (down) {
 220        /*
 221         * On keydown: Check whenever we find one mapping where the
 222         * modifier state of the mapping matches the current user
 223         * interface modifier state.  If so, prefer that one.
 224         */
 225        mods = 0;
 226        if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_SHIFT)) {
 227            mods |= SCANCODE_SHIFT;
 228        }
 229        if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_ALTGR)) {
 230            mods |= SCANCODE_ALTGR;
 231        }
 232        if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_CTRL)) {
 233            mods |= SCANCODE_CTRL;
 234        }
 235
 236        for (i = 0; i < keysym2code->count; i++) {
 237            if ((keysym2code->keycodes[i] & mask) == mods) {
 238                return keysym2code->keycodes[i];
 239            }
 240        }
 241    } else {
 242        /*
 243         * On keyup: Try find a key which is actually down.
 244         */
 245        for (i = 0; i < keysym2code->count; i++) {
 246            QKeyCode qcode = qemu_input_key_number_to_qcode
 247                (keysym2code->keycodes[i]);
 248            if (kbd && qkbd_state_key_get(kbd, qcode)) {
 249                return keysym2code->keycodes[i];
 250            }
 251        }
 252    }
 253    return keysym2code->keycodes[0];
 254}
 255
 256int keycode_is_keypad(kbd_layout_t *k, int keycode)
 257{
 258    if (keycode >= 0x47 && keycode <= 0x53) {
 259        return true;
 260    }
 261    return false;
 262}
 263
 264int keysym_is_numlock(kbd_layout_t *k, int keysym)
 265{
 266    switch (keysym) {
 267    case 0xffb0 ... 0xffb9:  /* KP_0 .. KP_9 */
 268    case 0xffac:             /* KP_Separator */
 269    case 0xffae:             /* KP_Decimal   */
 270        return true;
 271    }
 272    return false;
 273}
 274