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