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 "keymaps.h"
  27#include "sysemu/sysemu.h"
  28#include "trace.h"
  29#include "qemu/error-report.h"
  30#include "qapi/error.h"
  31
  32struct keysym2code {
  33    uint32_t count;
  34    uint16_t keycodes[4];
  35};
  36
  37struct kbd_layout_t {
  38    GHashTable *hash;
  39};
  40
  41static int get_keysym(const name2keysym_t *table,
  42                      const char *name)
  43{
  44    const name2keysym_t *p;
  45    for(p = table; p->name != NULL; p++) {
  46        if (!strcmp(p->name, name)) {
  47            return p->keysym;
  48        }
  49    }
  50    if (name[0] == 'U' && strlen(name) == 5) { /* try unicode Uxxxx */
  51        char *end;
  52        int ret = (int)strtoul(name + 1, &end, 16);
  53        if (*end == '\0' && ret > 0) {
  54            return ret;
  55        }
  56    }
  57    return 0;
  58}
  59
  60
  61static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k)
  62{
  63    struct keysym2code *keysym2code;
  64
  65    keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
  66    if (keysym2code) {
  67        if (keysym2code->count < ARRAY_SIZE(keysym2code->keycodes)) {
  68            keysym2code->keycodes[keysym2code->count++] = keycode;
  69        } else {
  70            warn_report("more than %zd keycodes for keysym %d",
  71                        ARRAY_SIZE(keysym2code->keycodes), keysym);
  72        }
  73        return;
  74    }
  75
  76    keysym2code = g_new0(struct keysym2code, 1);
  77    keysym2code->keycodes[0] = keycode;
  78    keysym2code->count = 1;
  79    g_hash_table_replace(k->hash, GINT_TO_POINTER(keysym), keysym2code);
  80    trace_keymap_add(keysym, keycode, line);
  81}
  82
  83static int parse_keyboard_layout(kbd_layout_t *k,
  84                                 const name2keysym_t *table,
  85                                 const char *language, Error **errp)
  86{
  87    int ret;
  88    FILE *f;
  89    char * filename;
  90    char line[1024];
  91    char keyname[64];
  92    int len;
  93
  94    filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language);
  95    trace_keymap_parse(filename);
  96    f = filename ? fopen(filename, "r") : NULL;
  97    g_free(filename);
  98    if (!f) {
  99        error_setg(errp, "could not read keymap file: '%s'", language);
 100        return -1;
 101    }
 102
 103    for(;;) {
 104        if (fgets(line, 1024, f) == NULL) {
 105            break;
 106        }
 107        len = strlen(line);
 108        if (len > 0 && line[len - 1] == '\n') {
 109            line[len - 1] = '\0';
 110        }
 111        if (line[0] == '#') {
 112            continue;
 113        }
 114        if (!strncmp(line, "map ", 4)) {
 115            continue;
 116        }
 117        if (!strncmp(line, "include ", 8)) {
 118            if (parse_keyboard_layout(k, table, line + 8, errp) < 0) {
 119                ret = -1;
 120                goto out;
 121            }
 122        } else {
 123            int offset = 0;
 124            while (line[offset] != 0 &&
 125                   line[offset] != ' ' &&
 126                   offset < sizeof(keyname) - 1) {
 127                keyname[offset] = line[offset];
 128                offset++;
 129            }
 130            keyname[offset] = 0;
 131            if (strlen(keyname)) {
 132                int keysym;
 133                keysym = get_keysym(table, keyname);
 134                if (keysym == 0) {
 135                    /* warn_report("unknown keysym %s", line);*/
 136                } else {
 137                    const char *rest = line + offset + 1;
 138                    int keycode = strtol(rest, NULL, 0);
 139
 140                    if (strstr(rest, "shift")) {
 141                        keycode |= SCANCODE_SHIFT;
 142                    }
 143                    if (strstr(rest, "altgr")) {
 144                        keycode |= SCANCODE_ALTGR;
 145                    }
 146                    if (strstr(rest, "ctrl")) {
 147                        keycode |= SCANCODE_CTRL;
 148                    }
 149
 150                    add_keysym(line, keysym, keycode, k);
 151
 152                    if (strstr(rest, "addupper")) {
 153                        char *c;
 154                        for (c = keyname; *c; c++) {
 155                            *c = qemu_toupper(*c);
 156                        }
 157                        keysym = get_keysym(table, keyname);
 158                        if (keysym) {
 159                            add_keysym(line, keysym,
 160                                       keycode | SCANCODE_SHIFT, k);
 161                        }
 162                    }
 163                }
 164            }
 165        }
 166    }
 167
 168    ret = 0;
 169out:
 170    fclose(f);
 171    return ret;
 172}
 173
 174
 175kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
 176                                   const char *language, Error **errp)
 177{
 178    kbd_layout_t *k;
 179
 180    k = g_new0(kbd_layout_t, 1);
 181    k->hash = g_hash_table_new(NULL, NULL);
 182    if (parse_keyboard_layout(k, table, language, errp) < 0) {
 183        g_hash_table_unref(k->hash);
 184        g_free(k);
 185        return NULL;
 186    }
 187    return k;
 188}
 189
 190
 191int keysym2scancode(kbd_layout_t *k, int keysym,
 192                    bool shift, bool altgr, bool ctrl)
 193{
 194    static const uint32_t mask =
 195        SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL;
 196    uint32_t mods, i;
 197    struct keysym2code *keysym2code;
 198
 199#ifdef XK_ISO_Left_Tab
 200    if (keysym == XK_ISO_Left_Tab) {
 201        keysym = XK_Tab;
 202    }
 203#endif
 204
 205    keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
 206    if (!keysym2code) {
 207        trace_keymap_unmapped(keysym);
 208        warn_report("no scancode found for keysym %d", keysym);
 209        return 0;
 210    }
 211
 212    if (keysym2code->count == 1) {
 213        return keysym2code->keycodes[0];
 214    }
 215
 216    /*
 217     * We have multiple keysym -> keycode mappings.
 218     *
 219     * Check whenever we find one mapping where the modifier state of
 220     * the mapping matches the current user interface modifier state.
 221     * If so, prefer that one.
 222     */
 223    mods = 0;
 224    if (shift) {
 225        mods |= SCANCODE_SHIFT;
 226    }
 227    if (altgr) {
 228        mods |= SCANCODE_ALTGR;
 229    }
 230    if (ctrl) {
 231        mods |= SCANCODE_CTRL;
 232    }
 233
 234    for (i = 0; i < keysym2code->count; i++) {
 235        if ((keysym2code->keycodes[i] & mask) == mods) {
 236            return keysym2code->keycodes[i];
 237        }
 238    }
 239    return keysym2code->keycodes[0];
 240}
 241
 242int keycode_is_keypad(kbd_layout_t *k, int keycode)
 243{
 244    if (keycode >= 0x47 && keycode <= 0x53) {
 245        return true;
 246    }
 247    return false;
 248}
 249
 250int keysym_is_numlock(kbd_layout_t *k, int keysym)
 251{
 252    switch (keysym) {
 253    case 0xffb0 ... 0xffb9:  /* KP_0 .. KP_9 */
 254    case 0xffac:             /* KP_Separator */
 255    case 0xffae:             /* KP_Decimal   */
 256        return true;
 257    }
 258    return false;
 259}
 260