xref: /openbmc/qemu/ui/keymaps.c (revision 7cc25f6c)
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 
35 struct keysym2code {
36     uint32_t count;
37     uint16_t keycodes[4];
38 };
39 
40 struct kbd_layout_t {
41     GHashTable *hash;
42 };
43 
44 static 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 
64 static 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 
86 static 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;
171 out:
172     fclose(f);
173     return ret;
174 }
175 
176 
177 kbd_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 
193 int 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 
256 int 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 
264 int 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 }
274