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#include "ui/input.h"
  32
  33struct keysym2code {
  34    uint32_t count;
  35    uint16_t keycodes[4];
  36};
  37
  38struct kbd_layout_t {
  39    GHashTable *hash;
  40};
  41
  42static int get_keysym(const name2keysym_t *table,
  43                      const char *name)
  44{
  45    const name2keysym_t *p;
  46    for(p = table; p->name != NULL; p++) {
  47        if (!strcmp(p->name, name)) {
  48            return p->keysym;
  49        }
  50    }
  51    if (name[0] == 'U' && strlen(name) == 5) { /* try unicode Uxxxx */
  52        char *end;
  53        int ret = (int)strtoul(name + 1, &end, 16);
  54        if (*end == '\0' && ret > 0) {
  55            return ret;
  56        }
  57    }
  58    return 0;
  59}
  60
  61
  62static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k)
  63{
  64    struct keysym2code *keysym2code;
  65
  66    keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
  67    if (keysym2code) {
  68        if (keysym2code->count < ARRAY_SIZE(keysym2code->keycodes)) {
  69            keysym2code->keycodes[keysym2code->count++] = keycode;
  70        } else {
  71            warn_report("more than %zd keycodes for keysym %d",
  72                        ARRAY_SIZE(keysym2code->keycodes), keysym);
  73        }
  74        return;
  75    }
  76
  77    keysym2code = g_new0(struct keysym2code, 1);
  78    keysym2code->keycodes[0] = keycode;
  79    keysym2code->count = 1;
  80    g_hash_table_replace(k->hash, GINT_TO_POINTER(keysym), keysym2code);
  81    trace_keymap_add(keysym, keycode, line);
  82}
  83
  84static int parse_keyboard_layout(kbd_layout_t *k,
  85                                 const name2keysym_t *table,
  86                                 const char *language, Error **errp)
  87{
  88    int ret;
  89    FILE *f;
  90    char * filename;
  91    char line[1024];
  92    char keyname[64];
  93    int len;
  94
  95    filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language);
  96    trace_keymap_parse(filename);
  97    f = filename ? fopen(filename, "r") : NULL;
  98    g_free(filename);
  99    if (!f) {
 100        error_setg(errp, "could not read keymap file: '%s'", language);
 101        return -1;
 102    }
 103
 104    for(;;) {
 105        if (fgets(line, 1024, f) == NULL) {
 106            break;
 107        }
 108        len = strlen(line);
 109        if (len > 0 && line[len - 1] == '\n') {
 110            line[len - 1] = '\0';
 111        }
 112        if (line[0] == '#') {
 113            continue;
 114        }
 115        if (!strncmp(line, "map ", 4)) {
 116            continue;
 117        }
 118        if (!strncmp(line, "include ", 8)) {
 119            error_setg(errp, "keymap include files are not supported any more");
 120            ret = -1;
 121            goto out;
 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                    QKbdState *kbd, bool down)
 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    /* We have multiple keysym -> keycode mappings. */
 217    if (down) {
 218        /*
 219         * On keydown: Check whenever we find one mapping where the
 220         * modifier state of the mapping matches the current user
 221         * interface modifier state.  If so, prefer that one.
 222         */
 223        mods = 0;
 224        if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_SHIFT)) {
 225            mods |= SCANCODE_SHIFT;
 226        }
 227        if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_ALTGR)) {
 228            mods |= SCANCODE_ALTGR;
 229        }
 230        if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_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    } else {
 240        /*
 241         * On keyup: Try find a key which is actually down.
 242         */
 243        for (i = 0; i < keysym2code->count; i++) {
 244            QKeyCode qcode = qemu_input_key_number_to_qcode
 245                (keysym2code->keycodes[i]);
 246            if (kbd && qkbd_state_key_get(kbd, qcode)) {
 247                return keysym2code->keycodes[i];
 248            }
 249        }
 250    }
 251    return keysym2code->keycodes[0];
 252}
 253
 254int keycode_is_keypad(kbd_layout_t *k, int keycode)
 255{
 256    if (keycode >= 0x47 && keycode <= 0x53) {
 257        return true;
 258    }
 259    return false;
 260}
 261
 262int keysym_is_numlock(kbd_layout_t *k, int keysym)
 263{
 264    switch (keysym) {
 265    case 0xffb0 ... 0xffb9:  /* KP_0 .. KP_9 */
 266    case 0xffac:             /* KP_Separator */
 267    case 0xffae:             /* KP_Decimal   */
 268        return true;
 269    }
 270    return false;
 271}
 272