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
  31struct keysym2code {
  32    uint32_t count;
  33    uint16_t keycodes[4];
  34};
  35
  36struct kbd_layout_t {
  37    GHashTable *hash;
  38};
  39
  40static int get_keysym(const name2keysym_t *table,
  41                      const char *name)
  42{
  43    const name2keysym_t *p;
  44    for(p = table; p->name != NULL; p++) {
  45        if (!strcmp(p->name, name)) {
  46            return p->keysym;
  47        }
  48    }
  49    if (name[0] == 'U' && strlen(name) == 5) { /* try unicode Uxxxx */
  50        char *end;
  51        int ret = (int)strtoul(name + 1, &end, 16);
  52        if (*end == '\0' && ret > 0) {
  53            return ret;
  54        }
  55    }
  56    return 0;
  57}
  58
  59
  60static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k)
  61{
  62    struct keysym2code *keysym2code;
  63
  64    keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
  65    if (keysym2code) {
  66        if (keysym2code->count < ARRAY_SIZE(keysym2code->keycodes)) {
  67            keysym2code->keycodes[keysym2code->count++] = keycode;
  68        } else {
  69            warn_report("more than %zd keycodes for keysym %d",
  70                        ARRAY_SIZE(keysym2code->keycodes), keysym);
  71        }
  72        return;
  73    }
  74
  75    keysym2code = g_new0(struct keysym2code, 1);
  76    keysym2code->keycodes[0] = keycode;
  77    keysym2code->count = 1;
  78    g_hash_table_replace(k->hash, GINT_TO_POINTER(keysym), keysym2code);
  79    trace_keymap_add(keysym, keycode, line);
  80}
  81
  82static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table,
  83                                           const char *language,
  84                                           kbd_layout_t *k)
  85{
  86    FILE *f;
  87    char * filename;
  88    char line[1024];
  89    char keyname[64];
  90    int len;
  91
  92    filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language);
  93    trace_keymap_parse(filename);
  94    f = filename ? fopen(filename, "r") : NULL;
  95    g_free(filename);
  96    if (!f) {
  97        fprintf(stderr, "Could not read keymap file: '%s'\n", language);
  98        return NULL;
  99    }
 100
 101    if (!k) {
 102        k = g_new0(kbd_layout_t, 1);
 103        k->hash = g_hash_table_new(NULL, NULL);
 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            parse_keyboard_layout(table, line + 8, k);
 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    fclose(f);
 168    return k;
 169}
 170
 171
 172kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
 173                                   const char *language)
 174{
 175    return parse_keyboard_layout(table, language, NULL);
 176}
 177
 178
 179int keysym2scancode(kbd_layout_t *k, int keysym,
 180                    bool shift, bool altgr, bool ctrl)
 181{
 182    static const uint32_t mask =
 183        SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL;
 184    uint32_t mods, i;
 185    struct keysym2code *keysym2code;
 186
 187#ifdef XK_ISO_Left_Tab
 188    if (keysym == XK_ISO_Left_Tab) {
 189        keysym = XK_Tab;
 190    }
 191#endif
 192
 193    keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
 194    if (!keysym2code) {
 195        trace_keymap_unmapped(keysym);
 196        warn_report("no scancode found for keysym %d", keysym);
 197        return 0;
 198    }
 199
 200    if (keysym2code->count == 1) {
 201        return keysym2code->keycodes[0];
 202    }
 203
 204    /*
 205     * We have multiple keysym -> keycode mappings.
 206     *
 207     * Check whenever we find one mapping where the modifier state of
 208     * the mapping matches the current user interface modifier state.
 209     * If so, prefer that one.
 210     */
 211    mods = 0;
 212    if (shift) {
 213        mods |= SCANCODE_SHIFT;
 214    }
 215    if (altgr) {
 216        mods |= SCANCODE_ALTGR;
 217    }
 218    if (ctrl) {
 219        mods |= SCANCODE_CTRL;
 220    }
 221
 222    for (i = 0; i < keysym2code->count; i++) {
 223        if ((keysym2code->keycodes[i] & mask) == mods) {
 224            return keysym2code->keycodes[i];
 225        }
 226    }
 227    return keysym2code->keycodes[0];
 228}
 229
 230int keycode_is_keypad(kbd_layout_t *k, int keycode)
 231{
 232    if (keycode >= 0x47 && keycode <= 0x53) {
 233        return true;
 234    }
 235    return false;
 236}
 237
 238int keysym_is_numlock(kbd_layout_t *k, int keysym)
 239{
 240    switch (keysym) {
 241    case 0xffb0 ... 0xffb9:  /* KP_0 .. KP_9 */
 242    case 0xffac:             /* KP_Separator */
 243    case 0xffae:             /* KP_Decimal   */
 244        return true;
 245    }
 246    return false;
 247}
 248