xref: /qemu/ui/keymaps.c (revision 0f9668e0)
13e230dd2SCorentin Chary /*
23e230dd2SCorentin Chary  * QEMU keysym to keycode conversion using rdesktop keymaps
33e230dd2SCorentin Chary  *
43e230dd2SCorentin Chary  * Copyright (c) 2004 Johannes Schindelin
53e230dd2SCorentin Chary  *
63e230dd2SCorentin Chary  * Permission is hereby granted, free of charge, to any person obtaining a copy
73e230dd2SCorentin Chary  * of this software and associated documentation files (the "Software"), to deal
83e230dd2SCorentin Chary  * in the Software without restriction, including without limitation the rights
93e230dd2SCorentin Chary  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
103e230dd2SCorentin Chary  * copies of the Software, and to permit persons to whom the Software is
113e230dd2SCorentin Chary  * furnished to do so, subject to the following conditions:
123e230dd2SCorentin Chary  *
133e230dd2SCorentin Chary  * The above copyright notice and this permission notice shall be included in
143e230dd2SCorentin Chary  * all copies or substantial portions of the Software.
153e230dd2SCorentin Chary  *
163e230dd2SCorentin Chary  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
173e230dd2SCorentin Chary  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
183e230dd2SCorentin Chary  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
193e230dd2SCorentin Chary  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
203e230dd2SCorentin Chary  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
213e230dd2SCorentin Chary  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
223e230dd2SCorentin Chary  * THE SOFTWARE.
233e230dd2SCorentin Chary  */
243e230dd2SCorentin Chary 
25e16f4c87SPeter Maydell #include "qemu/osdep.h"
26*2c65db5eSPaolo Bonzini #include "qemu/datadir.h"
273e230dd2SCorentin Chary #include "keymaps.h"
28d3b787faSGerd Hoffmann #include "trace.h"
29856dfd8aSMarkus Armbruster #include "qemu/ctype.h"
308297be80SAlistair Francis #include "qemu/error-report.h"
31ab4f931eSFei Li #include "qapi/error.h"
3219c1b9fdSGerd Hoffmann #include "ui/input.h"
333e230dd2SCorentin Chary 
34d713e3fdSGerd Hoffmann struct keysym2code {
3523ad24e4SGerd Hoffmann     uint32_t count;
3623ad24e4SGerd Hoffmann     uint16_t keycodes[4];
37d713e3fdSGerd Hoffmann };
38d713e3fdSGerd Hoffmann 
39d713e3fdSGerd Hoffmann struct kbd_layout_t {
40d713e3fdSGerd Hoffmann     GHashTable *hash;
41fe5fca9aSGerd Hoffmann };
42fe5fca9aSGerd Hoffmann 
get_keysym(const name2keysym_t * table,const char * name)433e230dd2SCorentin Chary static int get_keysym(const name2keysym_t *table,
443e230dd2SCorentin Chary                       const char *name)
453e230dd2SCorentin Chary {
463e230dd2SCorentin Chary     const name2keysym_t *p;
473e230dd2SCorentin Chary     for(p = table; p->name != NULL; p++) {
4843948386SGonglei         if (!strcmp(p->name, name)) {
493e230dd2SCorentin Chary             return p->keysym;
503e230dd2SCorentin Chary         }
5143948386SGonglei     }
5282807159SJan Krupa     if (name[0] == 'U' && strlen(name) == 5) { /* try unicode Uxxxx */
5382807159SJan Krupa         char *end;
5482807159SJan Krupa         int ret = (int)strtoul(name + 1, &end, 16);
5543948386SGonglei         if (*end == '\0' && ret > 0) {
5682807159SJan Krupa             return ret;
5782807159SJan Krupa         }
5843948386SGonglei     }
593e230dd2SCorentin Chary     return 0;
603e230dd2SCorentin Chary }
613e230dd2SCorentin Chary 
623e230dd2SCorentin Chary 
add_keysym(char * line,int keysym,int keycode,kbd_layout_t * k)63d713e3fdSGerd Hoffmann static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k)
64d713e3fdSGerd Hoffmann {
65d713e3fdSGerd Hoffmann     struct keysym2code *keysym2code;
66d713e3fdSGerd Hoffmann 
67d713e3fdSGerd Hoffmann     keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
68d713e3fdSGerd Hoffmann     if (keysym2code) {
6923ad24e4SGerd Hoffmann         if (keysym2code->count < ARRAY_SIZE(keysym2code->keycodes)) {
7023ad24e4SGerd Hoffmann             keysym2code->keycodes[keysym2code->count++] = keycode;
7123ad24e4SGerd Hoffmann         } else {
7223ad24e4SGerd Hoffmann             warn_report("more than %zd keycodes for keysym %d",
7323ad24e4SGerd Hoffmann                         ARRAY_SIZE(keysym2code->keycodes), keysym);
7423ad24e4SGerd Hoffmann         }
75d713e3fdSGerd Hoffmann         return;
763e230dd2SCorentin Chary     }
77d713e3fdSGerd Hoffmann 
78d713e3fdSGerd Hoffmann     keysym2code = g_new0(struct keysym2code, 1);
7923ad24e4SGerd Hoffmann     keysym2code->keycodes[0] = keycode;
8023ad24e4SGerd Hoffmann     keysym2code->count = 1;
81d713e3fdSGerd Hoffmann     g_hash_table_replace(k->hash, GINT_TO_POINTER(keysym), keysym2code);
82d713e3fdSGerd Hoffmann     trace_keymap_add(keysym, keycode, line);
833e230dd2SCorentin Chary }
843e230dd2SCorentin Chary 
parse_keyboard_layout(kbd_layout_t * k,const name2keysym_t * table,const char * language,Error ** errp)85f7b9e299SMarkus Armbruster static int parse_keyboard_layout(kbd_layout_t *k,
86f7b9e299SMarkus Armbruster                                  const name2keysym_t *table,
87ab4f931eSFei Li                                  const char *language, Error **errp)
883e230dd2SCorentin Chary {
89f7b9e299SMarkus Armbruster     int ret;
903e230dd2SCorentin Chary     FILE *f;
913e230dd2SCorentin Chary     char * filename;
923e230dd2SCorentin Chary     char line[1024];
93d3b787faSGerd Hoffmann     char keyname[64];
943e230dd2SCorentin Chary     int len;
953e230dd2SCorentin Chary 
963e230dd2SCorentin Chary     filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language);
97d3b787faSGerd Hoffmann     trace_keymap_parse(filename);
98f2d3476eSMarkus Armbruster     f = filename ? fopen(filename, "r") : NULL;
99f2d3476eSMarkus Armbruster     g_free(filename);
100f2d3476eSMarkus Armbruster     if (!f) {
101ab4f931eSFei Li         error_setg(errp, "could not read keymap file: '%s'", language);
102f7b9e299SMarkus Armbruster         return -1;
10343948386SGonglei     }
104f2d3476eSMarkus Armbruster 
1053e230dd2SCorentin Chary     for(;;) {
10643948386SGonglei         if (fgets(line, 1024, f) == NULL) {
1073e230dd2SCorentin Chary             break;
10843948386SGonglei         }
1093e230dd2SCorentin Chary         len = strlen(line);
11043948386SGonglei         if (len > 0 && line[len - 1] == '\n') {
1113e230dd2SCorentin Chary             line[len - 1] = '\0';
11243948386SGonglei         }
11343948386SGonglei         if (line[0] == '#') {
1143e230dd2SCorentin Chary             continue;
11543948386SGonglei         }
11643948386SGonglei         if (!strncmp(line, "map ", 4)) {
1173e230dd2SCorentin Chary             continue;
11843948386SGonglei         }
1193e230dd2SCorentin Chary         if (!strncmp(line, "include ", 8)) {
1202a7bece6SGerd Hoffmann             error_setg(errp, "keymap include files are not supported any more");
121f7b9e299SMarkus Armbruster             ret = -1;
122f7b9e299SMarkus Armbruster             goto out;
1233e230dd2SCorentin Chary         } else {
124d3b787faSGerd Hoffmann             int offset = 0;
125d3b787faSGerd Hoffmann             while (line[offset] != 0 &&
126d3b787faSGerd Hoffmann                    line[offset] != ' ' &&
127d3b787faSGerd Hoffmann                    offset < sizeof(keyname) - 1) {
128d3b787faSGerd Hoffmann                 keyname[offset] = line[offset];
129d3b787faSGerd Hoffmann                 offset++;
13043948386SGonglei             }
131d3b787faSGerd Hoffmann             keyname[offset] = 0;
132d3b787faSGerd Hoffmann             if (strlen(keyname)) {
1333e230dd2SCorentin Chary                 int keysym;
134d3b787faSGerd Hoffmann                 keysym = get_keysym(table, keyname);
1353e230dd2SCorentin Chary                 if (keysym == 0) {
1362ab4b135SAlistair Francis                     /* warn_report("unknown keysym %s", line);*/
1373e230dd2SCorentin Chary                 } else {
138d3b787faSGerd Hoffmann                     const char *rest = line + offset + 1;
139955d7b26SMarkus Armbruster                     int keycode = strtol(rest, NULL, 0);
1403e230dd2SCorentin Chary 
141955d7b26SMarkus Armbruster                     if (strstr(rest, "shift")) {
1423e230dd2SCorentin Chary                         keycode |= SCANCODE_SHIFT;
143955d7b26SMarkus Armbruster                     }
144955d7b26SMarkus Armbruster                     if (strstr(rest, "altgr")) {
1453e230dd2SCorentin Chary                         keycode |= SCANCODE_ALTGR;
146955d7b26SMarkus Armbruster                     }
147955d7b26SMarkus Armbruster                     if (strstr(rest, "ctrl")) {
1483e230dd2SCorentin Chary                         keycode |= SCANCODE_CTRL;
149955d7b26SMarkus Armbruster                     }
1503e230dd2SCorentin Chary 
1513e230dd2SCorentin Chary                     add_keysym(line, keysym, keycode, k);
1523e230dd2SCorentin Chary 
153955d7b26SMarkus Armbruster                     if (strstr(rest, "addupper")) {
1543e230dd2SCorentin Chary                         char *c;
155d3b787faSGerd Hoffmann                         for (c = keyname; *c; c++) {
1567b0a03a1SChristoph Egger                             *c = qemu_toupper(*c);
15743948386SGonglei                         }
158d3b787faSGerd Hoffmann                         keysym = get_keysym(table, keyname);
15943948386SGonglei                         if (keysym) {
16043948386SGonglei                             add_keysym(line, keysym,
16143948386SGonglei                                        keycode | SCANCODE_SHIFT, k);
16243948386SGonglei                         }
1633e230dd2SCorentin Chary                     }
1643e230dd2SCorentin Chary                 }
1653e230dd2SCorentin Chary             }
1663e230dd2SCorentin Chary         }
1673e230dd2SCorentin Chary     }
168f7b9e299SMarkus Armbruster 
169f7b9e299SMarkus Armbruster     ret = 0;
170f7b9e299SMarkus Armbruster out:
1713e230dd2SCorentin Chary     fclose(f);
172f7b9e299SMarkus Armbruster     return ret;
1733e230dd2SCorentin Chary }
1743e230dd2SCorentin Chary 
1753e230dd2SCorentin Chary 
init_keyboard_layout(const name2keysym_t * table,const char * language,Error ** errp)176fe5fca9aSGerd Hoffmann kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
177ab4f931eSFei Li                                    const char *language, Error **errp)
1783e230dd2SCorentin Chary {
179f7b9e299SMarkus Armbruster     kbd_layout_t *k;
180f7b9e299SMarkus Armbruster 
181f7b9e299SMarkus Armbruster     k = g_new0(kbd_layout_t, 1);
182f7b9e299SMarkus Armbruster     k->hash = g_hash_table_new(NULL, NULL);
183ab4f931eSFei Li     if (parse_keyboard_layout(k, table, language, errp) < 0) {
184f7b9e299SMarkus Armbruster         g_hash_table_unref(k->hash);
185f7b9e299SMarkus Armbruster         g_free(k);
186f7b9e299SMarkus Armbruster         return NULL;
187f7b9e299SMarkus Armbruster     }
188f7b9e299SMarkus Armbruster     return k;
1893e230dd2SCorentin Chary }
1903e230dd2SCorentin Chary 
1913e230dd2SCorentin Chary 
keysym2scancode(kbd_layout_t * k,int keysym,QKbdState * kbd,bool down)192abb4f2c9SGerd Hoffmann int keysym2scancode(kbd_layout_t *k, int keysym,
19319c1b9fdSGerd Hoffmann                     QKbdState *kbd, bool down)
1943e230dd2SCorentin Chary {
195abb4f2c9SGerd Hoffmann     static const uint32_t mask =
196abb4f2c9SGerd Hoffmann         SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL;
197abb4f2c9SGerd Hoffmann     uint32_t mods, i;
198d713e3fdSGerd Hoffmann     struct keysym2code *keysym2code;
199d713e3fdSGerd Hoffmann 
2003e230dd2SCorentin Chary #ifdef XK_ISO_Left_Tab
20143948386SGonglei     if (keysym == XK_ISO_Left_Tab) {
2023e230dd2SCorentin Chary         keysym = XK_Tab;
20343948386SGonglei     }
2043e230dd2SCorentin Chary #endif
205d713e3fdSGerd Hoffmann 
206d713e3fdSGerd Hoffmann     keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
207d713e3fdSGerd Hoffmann     if (!keysym2code) {
208d713e3fdSGerd Hoffmann         trace_keymap_unmapped(keysym);
209d713e3fdSGerd Hoffmann         warn_report("no scancode found for keysym %d", keysym);
2103e230dd2SCorentin Chary         return 0;
2113e230dd2SCorentin Chary     }
2123e230dd2SCorentin Chary 
213abb4f2c9SGerd Hoffmann     if (keysym2code->count == 1) {
214abb4f2c9SGerd Hoffmann         return keysym2code->keycodes[0];
215abb4f2c9SGerd Hoffmann     }
216abb4f2c9SGerd Hoffmann 
21719c1b9fdSGerd Hoffmann     /* We have multiple keysym -> keycode mappings. */
21819c1b9fdSGerd Hoffmann     if (down) {
219abb4f2c9SGerd Hoffmann         /*
22019c1b9fdSGerd Hoffmann          * On keydown: Check whenever we find one mapping where the
22119c1b9fdSGerd Hoffmann          * modifier state of the mapping matches the current user
22219c1b9fdSGerd Hoffmann          * interface modifier state.  If so, prefer that one.
223abb4f2c9SGerd Hoffmann          */
224abb4f2c9SGerd Hoffmann         mods = 0;
2254ed26e19SGerd Hoffmann         if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_SHIFT)) {
226abb4f2c9SGerd Hoffmann             mods |= SCANCODE_SHIFT;
227abb4f2c9SGerd Hoffmann         }
2284ed26e19SGerd Hoffmann         if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_ALTGR)) {
229abb4f2c9SGerd Hoffmann             mods |= SCANCODE_ALTGR;
230abb4f2c9SGerd Hoffmann         }
2314ed26e19SGerd Hoffmann         if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_CTRL)) {
232abb4f2c9SGerd Hoffmann             mods |= SCANCODE_CTRL;
233abb4f2c9SGerd Hoffmann         }
234abb4f2c9SGerd Hoffmann 
235abb4f2c9SGerd Hoffmann         for (i = 0; i < keysym2code->count; i++) {
236abb4f2c9SGerd Hoffmann             if ((keysym2code->keycodes[i] & mask) == mods) {
237abb4f2c9SGerd Hoffmann                 return keysym2code->keycodes[i];
238abb4f2c9SGerd Hoffmann             }
239abb4f2c9SGerd Hoffmann         }
24019c1b9fdSGerd Hoffmann     } else {
24119c1b9fdSGerd Hoffmann         /*
24219c1b9fdSGerd Hoffmann          * On keyup: Try find a key which is actually down.
24319c1b9fdSGerd Hoffmann          */
24419c1b9fdSGerd Hoffmann         for (i = 0; i < keysym2code->count; i++) {
24519c1b9fdSGerd Hoffmann             QKeyCode qcode = qemu_input_key_number_to_qcode
24619c1b9fdSGerd Hoffmann                 (keysym2code->keycodes[i]);
24719c1b9fdSGerd Hoffmann             if (kbd && qkbd_state_key_get(kbd, qcode)) {
24819c1b9fdSGerd Hoffmann                 return keysym2code->keycodes[i];
24919c1b9fdSGerd Hoffmann             }
25019c1b9fdSGerd Hoffmann         }
25119c1b9fdSGerd Hoffmann     }
25223ad24e4SGerd Hoffmann     return keysym2code->keycodes[0];
253d713e3fdSGerd Hoffmann }
254d713e3fdSGerd Hoffmann 
keycode_is_keypad(kbd_layout_t * k,int keycode)255fe5fca9aSGerd Hoffmann int keycode_is_keypad(kbd_layout_t *k, int keycode)
2563e230dd2SCorentin Chary {
2576b71ea11SGerd Hoffmann     if (keycode >= 0x47 && keycode <= 0x53) {
2586b71ea11SGerd Hoffmann         return true;
25943948386SGonglei     }
2606b71ea11SGerd Hoffmann     return false;
2613e230dd2SCorentin Chary }
2623e230dd2SCorentin Chary 
keysym_is_numlock(kbd_layout_t * k,int keysym)263fe5fca9aSGerd Hoffmann int keysym_is_numlock(kbd_layout_t *k, int keysym)
2643e230dd2SCorentin Chary {
2656b71ea11SGerd Hoffmann     switch (keysym) {
2666b71ea11SGerd Hoffmann     case 0xffb0 ... 0xffb9:  /* KP_0 .. KP_9 */
2676b71ea11SGerd Hoffmann     case 0xffac:             /* KP_Separator */
2686b71ea11SGerd Hoffmann     case 0xffae:             /* KP_Decimal   */
2696b71ea11SGerd Hoffmann         return true;
27043948386SGonglei     }
2716b71ea11SGerd Hoffmann     return false;
2723e230dd2SCorentin Chary }
273