|  | /* | 
|  | * 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/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; | 
|  | } |