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