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