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