* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qemu/datadir.h"
#include "keymaps.h"
-#include "sysemu/sysemu.h"
+#include "trace.h"
+#include "qemu/ctype.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "ui/input.h"
+
+struct keysym2code {
+ uint32_t count;
+ uint16_t keycodes[4];
+};
+
+struct kbd_layout_t {
+ GHashTable *hash;
+};
static int get_keysym(const name2keysym_t *table,
const char *name)
}
-static void add_to_key_range(struct key_range **krp, int code) {
- struct key_range *kr;
- for (kr = *krp; kr; kr = kr->next) {
- if (code >= kr->start && code <= kr->end) {
- break;
- }
- if (code == kr->start - 1) {
- kr->start--;
- break;
- }
- if (code == kr->end + 1) {
- kr->end++;
- break;
- }
- }
- if (kr == NULL) {
- kr = g_malloc0(sizeof(*kr));
- kr->start = kr->end = code;
- kr->next = *krp;
- *krp = kr;
- }
-}
+static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k)
+{
+ struct keysym2code *keysym2code;
-static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k) {
- if (keysym < MAX_NORMAL_KEYCODE) {
- /* fprintf(stderr,"Setting keysym %s (%d) to %d\n",
- line, keysym, keycode); */
- k->keysym2keycode[keysym] = keycode;
- } else {
- if (k->extra_count >= MAX_EXTRA_COUNT) {
- fprintf(stderr, "Warning: Could not assign keysym %s (0x%x)"
- " because of memory constraints.\n", line, keysym);
+ keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
+ if (keysym2code) {
+ if (keysym2code->count < ARRAY_SIZE(keysym2code->keycodes)) {
+ keysym2code->keycodes[keysym2code->count++] = keycode;
} else {
-#if 0
- fprintf(stderr, "Setting %d: %d,%d\n",
- k->extra_count, keysym, keycode);
-#endif
- k->keysym2keycode_extra[k->extra_count].
- keysym = keysym;
- k->keysym2keycode_extra[k->extra_count].
- keycode = keycode;
- k->extra_count++;
+ warn_report("more than %zd keycodes for keysym %d",
+ ARRAY_SIZE(keysym2code->keycodes), keysym);
}
+ return;
}
+
+ keysym2code = g_new0(struct keysym2code, 1);
+ keysym2code->keycodes[0] = keycode;
+ keysym2code->count = 1;
+ g_hash_table_replace(k->hash, GINT_TO_POINTER(keysym), keysym2code);
+ trace_keymap_add(keysym, keycode, line);
}
-static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table,
- const char *language,
- kbd_layout_t *k)
+static int parse_keyboard_layout(kbd_layout_t *k,
+ const name2keysym_t *table,
+ const char *language, Error **errp)
{
+ int ret;
FILE *f;
char * filename;
char line[1024];
+ char keyname[64];
int len;
filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language);
+ trace_keymap_parse(filename);
f = filename ? fopen(filename, "r") : NULL;
g_free(filename);
if (!f) {
- fprintf(stderr, "Could not read keymap file: '%s'\n", language);
- return NULL;
- }
-
- if (!k) {
- k = g_malloc0(sizeof(kbd_layout_t));
+ error_setg(errp, "could not read keymap file: '%s'", language);
+ return -1;
}
for(;;) {
continue;
}
if (!strncmp(line, "include ", 8)) {
- parse_keyboard_layout(table, line + 8, k);
+ error_setg(errp, "keymap include files are not supported any more");
+ ret = -1;
+ goto out;
} else {
- char *end_of_keysym = line;
- while (*end_of_keysym != 0 && *end_of_keysym != ' ') {
- end_of_keysym++;
+ int offset = 0;
+ while (line[offset] != 0 &&
+ line[offset] != ' ' &&
+ offset < sizeof(keyname) - 1) {
+ keyname[offset] = line[offset];
+ offset++;
}
- if (*end_of_keysym) {
+ keyname[offset] = 0;
+ if (strlen(keyname)) {
int keysym;
- *end_of_keysym = 0;
- keysym = get_keysym(table, line);
+ keysym = get_keysym(table, keyname);
if (keysym == 0) {
- /* fprintf(stderr, "Warning: unknown keysym %s\n", line);*/
+ /* warn_report("unknown keysym %s", line);*/
} else {
- const char *rest = end_of_keysym + 1;
+ const char *rest = line + offset + 1;
int keycode = strtol(rest, NULL, 0);
- if (strstr(rest, "numlock")) {
- add_to_key_range(&k->keypad_range, keycode);
- add_to_key_range(&k->numlock_range, keysym);
- /* fprintf(stderr, "keypad keysym %04x keycode %d\n",
- keysym, keycode); */
- }
-
if (strstr(rest, "shift")) {
keycode |= SCANCODE_SHIFT;
}
if (strstr(rest, "addupper")) {
char *c;
- for (c = line; *c; c++) {
+ for (c = keyname; *c; c++) {
*c = qemu_toupper(*c);
}
- keysym = get_keysym(table, line);
+ keysym = get_keysym(table, keyname);
if (keysym) {
add_keysym(line, keysym,
keycode | SCANCODE_SHIFT, k);
}
}
}
+
+ ret = 0;
+out:
fclose(f);
- return k;
+ return ret;
}
-void *init_keyboard_layout(const name2keysym_t *table, const char *language)
+kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
+ const char *language, Error **errp)
{
- return parse_keyboard_layout(table, language, NULL);
+ kbd_layout_t *k;
+
+ k = g_new0(kbd_layout_t, 1);
+ k->hash = g_hash_table_new(NULL, NULL);
+ if (parse_keyboard_layout(k, table, language, errp) < 0) {
+ g_hash_table_unref(k->hash);
+ g_free(k);
+ return NULL;
+ }
+ return k;
}
-int keysym2scancode(void *kbd_layout, int keysym)
+int keysym2scancode(kbd_layout_t *k, int keysym,
+ QKbdState *kbd, bool down)
{
- kbd_layout_t *k = kbd_layout;
- if (keysym < MAX_NORMAL_KEYCODE) {
- if (k->keysym2keycode[keysym] == 0) {
- fprintf(stderr, "Warning: no scancode found for keysym %d\n",
- keysym);
- }
- return k->keysym2keycode[keysym];
- } else {
- int i;
+ static const uint32_t mask =
+ SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL;
+ uint32_t mods, i;
+ struct keysym2code *keysym2code;
+
#ifdef XK_ISO_Left_Tab
- if (keysym == XK_ISO_Left_Tab) {
- keysym = XK_Tab;
- }
+ if (keysym == XK_ISO_Left_Tab) {
+ keysym = XK_Tab;
+ }
#endif
- for (i = 0; i < k->extra_count; i++) {
- if (k->keysym2keycode_extra[i].keysym == keysym) {
- return k->keysym2keycode_extra[i].keycode;
+
+ keysym2code = g_hash_table_lookup(k->hash, GINT_TO_POINTER(keysym));
+ if (!keysym2code) {
+ trace_keymap_unmapped(keysym);
+ warn_report("no scancode found for keysym %d", keysym);
+ return 0;
+ }
+
+ if (keysym2code->count == 1) {
+ return keysym2code->keycodes[0];
+ }
+
+ /* We have multiple keysym -> keycode mappings. */
+ if (down) {
+ /*
+ * On keydown: Check whenever we find one mapping where the
+ * modifier state of the mapping matches the current user
+ * interface modifier state. If so, prefer that one.
+ */
+ mods = 0;
+ if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_SHIFT)) {
+ mods |= SCANCODE_SHIFT;
+ }
+ if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_ALTGR)) {
+ mods |= SCANCODE_ALTGR;
+ }
+ if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_CTRL)) {
+ mods |= SCANCODE_CTRL;
+ }
+
+ for (i = 0; i < keysym2code->count; i++) {
+ if ((keysym2code->keycodes[i] & mask) == mods) {
+ return keysym2code->keycodes[i];
+ }
+ }
+ } else {
+ /*
+ * On keyup: Try find a key which is actually down.
+ */
+ for (i = 0; i < keysym2code->count; i++) {
+ QKeyCode qcode = qemu_input_key_number_to_qcode
+ (keysym2code->keycodes[i]);
+ if (kbd && qkbd_state_key_get(kbd, qcode)) {
+ return keysym2code->keycodes[i];
}
}
}
- return 0;
+ return keysym2code->keycodes[0];
}
-int keycode_is_keypad(void *kbd_layout, int keycode)
+int keycode_is_keypad(kbd_layout_t *k, int keycode)
{
- kbd_layout_t *k = kbd_layout;
- struct key_range *kr;
-
- for (kr = k->keypad_range; kr; kr = kr->next) {
- if (keycode >= kr->start && keycode <= kr->end) {
- return 1;
- }
+ if (keycode >= 0x47 && keycode <= 0x53) {
+ return true;
}
- return 0;
+ return false;
}
-int keysym_is_numlock(void *kbd_layout, int keysym)
+int keysym_is_numlock(kbd_layout_t *k, int keysym)
{
- kbd_layout_t *k = kbd_layout;
- struct key_range *kr;
-
- for (kr = k->numlock_range; kr; kr = kr->next) {
- if (keysym >= kr->start && keysym <= kr->end) {
- return 1;
- }
+ switch (keysym) {
+ case 0xffb0 ... 0xffb9: /* KP_0 .. KP_9 */
+ case 0xffac: /* KP_Separator */
+ case 0xffae: /* KP_Decimal */
+ return true;
}
- return 0;
+ return false;
}