/* * QEMU keysym to keycode conversion using rdesktop keymaps * * Copyright (c) 2004 Johannes Schindelin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/datadir.h" #include "keymaps.h" #include "trace.h" #include "qemu/ctype.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "ui/input.h" struct keysym2code { uint32_t count; uint16_t keycodes[4]; }; struct kbd_layout_t { GHashTable *hash; }; static int get_keysym(const name2keysym_t *table, const char *name) { const name2keysym_t *p; for(p = table; p->name != NULL; p++) { if (!strcmp(p->name, name)) { return p->keysym; } } if (name[0] == 'U' && strlen(name) == 5) { /* try unicode Uxxxx */ char *end; int ret = (int)strtoul(name + 1, &end, 16); if (*end == '\0' && ret > 0) { return ret; } } return 0; } static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k) { struct keysym2code *keysym2code; keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym)); if (keysym2code) { if (keysym2code->count < ARRAY_SIZE(keysym2code->keycodes)) { keysym2code->keycodes[keysym2code->count++] = keycode; } else { warn_report("more than %zd keycodes for keysym %d", ARRAY_SIZE(keysym2code->keycodes), keysym); } return; } keysym2code = g_new0(struct keysym2code, 1); keysym2code->keycodes[0] = keycode; keysym2code->count = 1; g_hash_table_replace(k->hash, GINT_TO_POINTER(keysym), keysym2code); trace_keymap_add(keysym, keycode, line); } static int parse_keyboard_layout(kbd_layout_t *k, const name2keysym_t *table, const char *language, Error **errp) { int ret; FILE *f; char * filename; char line[1024]; char keyname[64]; int len; filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language); trace_keymap_parse(filename); f = filename ? fopen(filename, "r") : NULL; g_free(filename); if (!f) { error_setg(errp, "could not read keymap file: '%s'", language); return -1; } for(;;) { if (fgets(line, 1024, f) == NULL) { break; } len = strlen(line); if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; } if (line[0] == '#') { continue; } if (!strncmp(line, "map ", 4)) { continue; } if (!strncmp(line, "include ", 8)) { error_setg(errp, "keymap include files are not supported any more"); ret = -1; goto out; } else { int offset = 0; while (line[offset] != 0 && line[offset] != ' ' && offset < sizeof(keyname) - 1) { keyname[offset] = line[offset]; offset++; } keyname[offset] = 0; if (strlen(keyname)) { int keysym; keysym = get_keysym(table, keyname); if (keysym == 0) { /* warn_report("unknown keysym %s", line);*/ } else { const char *rest = line + offset + 1; int keycode = strtol(rest, NULL, 0); if (strstr(rest, "shift")) { keycode |= SCANCODE_SHIFT; } if (strstr(rest, "altgr")) { keycode |= SCANCODE_ALTGR; } if (strstr(rest, "ctrl")) { keycode |= SCANCODE_CTRL; } add_keysym(line, keysym, keycode, k); if (strstr(rest, "addupper")) { char *c; for (c = keyname; *c; c++) { *c = qemu_toupper(*c); } keysym = get_keysym(table, keyname); if (keysym) { add_keysym(line, keysym, keycode | SCANCODE_SHIFT, k); } } } } } } ret = 0; out: fclose(f); return ret; } kbd_layout_t *init_keyboard_layout(const name2keysym_t *table, const char *language, Error **errp) { kbd_layout_t *k; k = g_new0(kbd_layout_t, 1); k->hash = g_hash_table_new(NULL, NULL); if (parse_keyboard_layout(k, table, language, errp) < 0) { g_hash_table_unref(k->hash); g_free(k); return NULL; } return k; } int keysym2scancode(kbd_layout_t *k, int keysym, QKbdState *kbd, bool down) { static const uint32_t mask = SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL; uint32_t mods, i; struct keysym2code *keysym2code; #ifdef XK_ISO_Left_Tab if (keysym == XK_ISO_Left_Tab) { keysym = XK_Tab; } #endif keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym)); if (!keysym2code) { trace_keymap_unmapped(keysym); warn_report("no scancode found for keysym %d", keysym); return 0; } if (keysym2code->count == 1) { return keysym2code->keycodes[0]; } /* We have multiple keysym -> keycode mappings. */ if (down) { /* * On keydown: Check whenever we find one mapping where the * modifier state of the mapping matches the current user * interface modifier state. If so, prefer that one. */ mods = 0; if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_SHIFT)) { mods |= SCANCODE_SHIFT; } if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_ALTGR)) { mods |= SCANCODE_ALTGR; } if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_CTRL)) { mods |= SCANCODE_CTRL; } for (i = 0; i < keysym2code->count; i++) { if ((keysym2code->keycodes[i] & mask) == mods) { return keysym2code->keycodes[i]; } } } else { /* * On keyup: Try find a key which is actually down. */ for (i = 0; i < keysym2code->count; i++) { QKeyCode qcode = qemu_input_key_number_to_qcode (keysym2code->keycodes[i]); if (kbd && qkbd_state_key_get(kbd, qcode)) { return keysym2code->keycodes[i]; } } } return keysym2code->keycodes[0]; } int keycode_is_keypad(kbd_layout_t *k, int keycode) { if (keycode >= 0x47 && keycode <= 0x53) { return true; } return false; } int keysym_is_numlock(kbd_layout_t *k, int keysym) { switch (keysym) { case 0xffb0 ... 0xffb9: /* KP_0 .. KP_9 */ case 0xffac: /* KP_Separator */ case 0xffae: /* KP_Decimal */ return true; } return false; }