xref: /openbmc/qemu/ui/keymaps.c (revision 54aa3de72ea2aaa2e903e7e879a4f3dda515a00e)
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