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