[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 1 of 3] Fix keymap handling for vnc console
From: |
John Haxby |
Subject: |
[Qemu-devel] [PATCH 1 of 3] Fix keymap handling for vnc console |
Date: |
Tue, 16 Dec 2008 15:46:24 +0000 |
User-agent: |
Thunderbird 2.0.0.18 (X11/20081119) |
Fix keymap handling for international keyboards
Signed-off-by: John Haxby <address@hidden>
curses_keys.h | 2
keymaps.c | 347 ++++++++++++++++++++++++++++++++++++----------------------
sdl_keysym.h | 2
vnc.c | 87 ++++++++++----
vnc_keysym.h | 2
5 files changed, 286 insertions(+), 154 deletions(-)
diff -up qemu-0.9.1.6042/curses_keys.h.keymap01 qemu-0.9.1.6042/curses_keys.h
--- qemu-0.9.1.6042/curses_keys.h.keymap01 2008-10-28 00:11:06.000000000
+0000
+++ qemu-0.9.1.6042/curses_keys.h 2008-12-16 10:47:36.000000000 +0000
@@ -244,7 +244,7 @@ typedef struct {
int keysym;
} name2keysym_t;
-static const name2keysym_t name2keysym[] = {
+static name2keysym_t name2keysym[] = {
/* Plain ASCII */
{ "space", 0x020 },
{ "exclam", 0x021 },
diff -up qemu-0.9.1.6042/keymaps.c.keymap01 qemu-0.9.1.6042/keymaps.c
--- qemu-0.9.1.6042/keymaps.c.keymap01 2008-10-02 19:26:42.000000000 +0100
+++ qemu-0.9.1.6042/keymaps.c 2008-12-16 10:47:36.000000000 +0000
@@ -22,58 +22,64 @@
* THE SOFTWARE.
*/
+static int cmp_keysym(const void *a, const void *b) {
+ return strcmp(((name2keysym_t *) a)->name, ((name2keysym_t *) b)->name);
+}
+
+
static int get_keysym(const char *name)
{
- const name2keysym_t *p;
- for(p = name2keysym; p->name != NULL; p++) {
- if (!strcmp(p->name, name))
- return p->keysym;
+ static int n = -1;
+ int l, r, m;
+
+ if (n < 0) {
+ for (n = 0; name2keysym[n].name; n++);
+ qsort(name2keysym, n, sizeof(name2keysym_t), cmp_keysym);
+ }
+ l = 0;
+ r = n-1;
+ while (l <= r) {
+ m = (l + r) / 2;
+ int cmp = strcmp(name2keysym[m].name, name);
+ if (cmp < 0)
+ l = m + 1;
+ else if (cmp > 0)
+ r = m - 1;
+ else
+ return name2keysym[m].keysym;
+ }
+ if (name[0] == 'U') {
+ /* unicode symbol key */
+ char *ptr;
+ int k = strtol(name+1, &ptr, 16);
+ return *ptr ? 0 : k;
}
- return 0;
}
-struct key_range {
- int start;
- int end;
- struct key_range *next;
-};
+#define MAX_SCANCODE 256
+#define KEY_LOCALSTATE 0x1
+#define KEY_KEYPAD 0x2
#define MAX_NORMAL_KEYCODE 512
#define MAX_EXTRA_COUNT 256
typedef struct {
- uint16_t keysym2keycode[MAX_NORMAL_KEYCODE];
- struct {
- int keysym;
- uint16_t keycode;
- } keysym2keycode_extra[MAX_EXTRA_COUNT];
- int extra_count;
- struct key_range *keypad_range;
- struct key_range *numlock_range;
-} kbd_layout_t;
+ int keysym;
+ int keycode;
+} keysym2keycode_t;
-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 = qemu_mallocz(sizeof(*kr));
- if (kr) {
- kr->start = kr->end = code;
- kr->next = *krp;
- *krp = kr;
- }
- }
-}
+typedef struct {
+ int n;
+ keysym2keycode_t k[MAX_SCANCODE];
+} keysym_map_t;
+
+typedef struct {
+ keysym_map_t plain;
+ keysym_map_t shift;
+ keysym_map_t altgr;
+ keysym_map_t shift_altgr;
+ keysym_map_t numlock;
+ uint32_t flags [MAX_SCANCODE];
+} kbd_layout_t;
static kbd_layout_t *parse_keyboard_layout(const char *language,
kbd_layout_t * k)
@@ -81,105 +87,194 @@ static kbd_layout_t *parse_keyboard_layo
FILE *f;
char file_name[1024];
char line[1024];
- int len;
snprintf(file_name, sizeof(file_name),
- "%s/keymaps/%s", bios_dir, language);
+ "%s/keymaps/%s", bios_dir, language);
if (!k)
k = qemu_mallocz(sizeof(kbd_layout_t));
if (!k)
- return 0;
+ return 0;
if (!(f = fopen(file_name, "r"))) {
fprintf(stderr,
"Could not read keymap file: '%s'\n", file_name);
return 0;
}
- for(;;) {
- if (fgets(line, 1024, f) == NULL)
- break;
- len = strlen(line);
- if (len > 0 && line[len - 1] == '\n')
- line[len - 1] = '\0';
- if (line[0] == '#')
- continue;
- if (!strncmp(line, "map ", 4))
- continue;
- if (!strncmp(line, "include ", 8)) {
- parse_keyboard_layout(line + 8, k);
- } else {
- char *end_of_keysym = line;
- while (*end_of_keysym != 0 && *end_of_keysym != ' ')
- end_of_keysym++;
- if (*end_of_keysym) {
- int keysym;
- *end_of_keysym = 0;
- keysym = get_keysym(line);
- if (keysym == 0) {
- // fprintf(stderr, "Warning: unknown keysym
%s\n", line);
- } else {
- const char *rest = end_of_keysym + 1;
- char *rest2;
- int keycode = strtol(rest, &rest2, 0);
-
- if (rest && 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(keycode&0x80)
- keycode=(keycode<<8)^0x80e0; */
- 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);
- } 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++;
- }
- }
- }
- }
+ while (fgets(line, 1024, f)) {
+ char *ptr = strchr(line, '#');
+ char keyname[1024], p1[1024], p2[1024];
+ int keysym, keycode;
+ int shift = 0;
+ int altgr = 0;
+ int addupper = 0;
+ int numlock = 0;
+ int inhibit = 0;
+
+ if (ptr)
+ *ptr-- = '\0';
+ else
+ ptr = &line[strlen(line)-1];
+ while (isspace(*ptr))
+ *ptr-- = '\0';
+ if (!*line)
+ continue;
+ if (strncmp(line, "map ", 4) == 0)
+ continue;
+ if (sscanf(line, "include %s", p1) == 1) {
+ parse_keyboard_layout(p1, k);
+ continue;
+ }
+ if (sscanf(line, "%s %i %s %s", keyname, &keycode, p1, p2) == 4) {
+ shift = (strcmp(p1, "shift") == 0 || strcmp(p2, "shift") == 0);
+ altgr = (strcmp(p1, "altgr") == 0 || strcmp(p2, "altgr") == 0);
+ } else if (sscanf(line, "%s %i %s", keyname, &keycode, p1) == 3) {
+ shift = (strcmp(p1, "shift") == 0);
+ altgr = (strcmp(p1, "altgr") == 0);
+ addupper = (strcmp(p1, "addupper") == 0);
+ numlock = (strcmp(p1, "numlock") == 0);
+ inhibit = (strcmp(p1, "inhibit") == 0);
+ } else if (sscanf(line, "%s %i", keyname, &keycode) != 2)
+ /* silently ignore spurious lines */
+ continue;
+
+ if (inhibit)
+ continue;
+ if ((keysym = get_keysym(keyname)) == 0) {
+ fprintf(stderr, "%s: warning: unknown keysym %s\n",
+ file_name, keyname);
+ continue;
+ }
+ if (keycode <= 0 || keycode >= MAX_SCANCODE) {
+ fprintf(stderr, "%s: warning: keycode %#x for %s out of range\n",
+ file_name, keycode, keyname);
+ continue;
+ }
+ if (numlock)
+ k->numlock.k[keycode].keysym = keysym;
+ else if (shift && altgr)
+ k->shift_altgr.k[keycode].keysym = keysym;
+ else if (altgr)
+ k->altgr.k[keycode].keysym = keysym;
+ else if (shift)
+ k->shift.k[keycode].keysym = keysym;
+ else {
+ k->plain.k[keycode].keysym = keysym;
+ if (addupper)
+ k->shift.k[keycode].keysym = keysym + 'A' - 'a';
}
}
fclose(f);
return k;
}
+static int cmp_map (const void *a, const void *b) {
+ return ((keysym2keycode_t *) b)->keysym - ((keysym2keycode_t *) a)->keysym;
+}
+
+static void sort_map (keysym_map_t *map) {
+ int i;
+ for (i = 0; i < MAX_SCANCODE; i++)
+ map->k[i].keycode = i;
+ /* sort undefined scancodes to the end */
+ qsort(map->k, MAX_SCANCODE, sizeof(keysym2keycode_t), cmp_map);
+ for (map->n = 0; map->n < MAX_SCANCODE; map->n++)
+ if (!map->k[map->n].keysym)
+ break;
+}
+
static void *init_keyboard_layout(const char *language)
{
- return parse_keyboard_layout(language, 0);
+ kbd_layout_t *k = parse_keyboard_layout(language, NULL);
+ if (k) {
+ int i;
+ for (i = 0; i < MAX_SCANCODE; i++) {
+ if (!(k->shift.k[i].keysym
+ || k->altgr.k[i].keysym
+ || k->shift_altgr.k[i].keysym))
+ k->flags[i] |= KEY_LOCALSTATE;
+ if (k->numlock.k[i].keysym)
+ k->flags[i] |= KEY_KEYPAD;
+ }
+ sort_map(&k->plain);
+ sort_map(&k->shift);
+ sort_map(&k->altgr);
+ sort_map(&k->shift_altgr);
+ sort_map(&k->numlock);
+ }
+ return k;
+}
+
+static int keysym2scancode_map (keysym_map_t *map, int keysym)
+{
+ int l = 0, r = map->n - 1, m;
+ while (l <= r) {
+ m = (l + r) / 2;
+ if (map->k[m].keysym == keysym)
+ return map->k[m].keycode;
+ else if (map->k[m].keysym < keysym)
+ r = m - 1;
+ else
+ l = m + 1;
+ }
+ return 0;
}
static int keysym2scancode(void *kbd_layout, int keysym)
{
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;
-#ifdef XK_ISO_Left_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;
+ int scancode;
+
+ if ((scancode = keysym2scancode_map(&k->plain, keysym)))
+ return scancode;
+ if ((scancode = keysym2scancode_map(&k->numlock, keysym)))
+ return scancode;
+ if ((scancode = keysym2scancode_map(&k->shift, keysym)))
+ return scancode;
+ if ((scancode = keysym2scancode_map(&k->altgr, keysym)))
+ return scancode;
+ if ((scancode = keysym2scancode_map(&k->shift_altgr, keysym)))
+ return scancode;
+ return 0;
+}
+
+static inline int keysym2scancode1(void *kbd_layout, int keysym,
+ int *shift, int *altgr)
+{
+ kbd_layout_t *k = kbd_layout;
+ int s;
+
+ /* normal case: keysym can be found the expected modifier's map */
+ if (*shift && *altgr && (s = keysym2scancode_map(&k->shift_altgr, keysym)))
+ return s;
+ if (*altgr && (s = keysym2scancode_map(&k->altgr, keysym)))
+ return s;
+ if (*shift && (s = keysym2scancode_map(&k->shift, keysym)))
+ return s;
+ if ((s = keysym2scancode_map(&k->plain, keysym)))
+ return s;
+ if ((s = keysym2scancode_map(&k->numlock, keysym)))
+ return s;
+
+ /* fallback for when there is some keyboard/state mismatch */
+ if ((s = keysym2scancode_map(&k->plain, keysym))) {
+ *shift = 0;
+ *altgr = 0;
+ return s;
+ }
+ if ((s = keysym2scancode_map(&k->shift, keysym))) {
+ *shift = 1;
+ *altgr = 0;
+ return s;
+ }
+ if ((s = keysym2scancode_map(&k->altgr, keysym))) {
+ *shift = 0;
+ *altgr = 1;
+ return s;
+ }
+ if ((s = keysym2scancode_map(&k->shift_altgr, keysym))) {
+ *shift = 1;
+ *altgr = 1;
+ return s;
}
return 0;
}
@@ -187,21 +282,21 @@ static int keysym2scancode(void *kbd_lay
static inline int keycode_is_keypad(void *kbd_layout, 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;
- return 0;
+ if (keycode >= 0 && keycode < MAX_SCANCODE)
+ return !!(k->flags[keycode] & KEY_KEYPAD);
}
static inline int keysym_is_numlock(void *kbd_layout, int keysym)
{
kbd_layout_t *k = kbd_layout;
- struct key_range *kr;
+ return (keysym2scancode_map(&k->numlock, keysym) != 0);
+}
- for (kr = k->numlock_range; kr; kr = kr->next)
- if (keysym >= kr->start && keysym <= kr->end)
- return 1;
+static inline int keycode_is_shiftable(void *kbd_layout, int keycode)
+{
+ kbd_layout_t *k = kbd_layout;
+ if (keycode >= 0 || keycode < MAX_SCANCODE)
+ return !(k->flags[keycode] & KEY_LOCALSTATE);
return 0;
}
diff -up qemu-0.9.1.6042/sdl_keysym.h.keymap01 qemu-0.9.1.6042/sdl_keysym.h
--- qemu-0.9.1.6042/sdl_keysym.h.keymap01 2008-10-02 19:26:42.000000000
+0100
+++ qemu-0.9.1.6042/sdl_keysym.h 2008-12-16 10:47:36.000000000 +0000
@@ -2,7 +2,7 @@ typedef struct {
const char* name;
int keysym;
} name2keysym_t;
-static const name2keysym_t name2keysym[]={
+static name2keysym_t name2keysym[]={
/* ascii */
{ "space", 0x020},
{ "exclam", 0x021},
diff -up qemu-0.9.1.6042/vnc.c.keymap01 qemu-0.9.1.6042/vnc.c
--- qemu-0.9.1.6042/vnc.c.keymap01 2008-12-13 18:57:12.000000000 +0000
+++ qemu-0.9.1.6042/vnc.c 2008-12-16 10:52:09.000000000 +0000
@@ -1031,14 +1031,22 @@ static void pointer_event(VncState *vs,
check_pointer_type_change(vs, kbd_mouse_is_absolute());
}
+static void put_keycode(int keycode, int down)
+{
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ if (down)
+ kbd_put_keycode(keycode & 0x7f);
+ else
+ kbd_put_keycode(keycode | 0x80);
+}
+
static void reset_keys(VncState *vs)
{
int i;
for(i = 0; i < 256; i++) {
if (vs->modifiers_state[i]) {
- if (i & 0x80)
- kbd_put_keycode(0xe0);
- kbd_put_keycode(i | 0x80);
+ put_keycode(i, 0);
vs->modifiers_state[i] = 0;
}
}
@@ -1046,12 +1054,29 @@ static void reset_keys(VncState *vs)
static void press_key(VncState *vs, int keysym)
{
- kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) & 0x7f);
- kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) | 0x80);
+ put_keycode(keysym2scancode(vs->kbd_layout, keysym), 1);
+ put_keycode(keysym2scancode(vs->kbd_layout, keysym), 0);
+}
+
+static void twiddle_modifiers(VncState *vs, int down, int shift, int altgr)
+{
+ if (shift && !(vs->modifiers_state[0x2a] || vs->modifiers_state[0x36]))
+ put_keycode(0x2a, down);
+ if (!shift && vs->modifiers_state[0x2a])
+ put_keycode(0x2a, !down);
+ if (!shift && vs->modifiers_state[0x36])
+ put_keycode(0x36, !down);
+ if (altgr && !vs->modifiers_state[0xb8])
+ put_keycode(0xb8, down);
+ if (!altgr && vs->modifiers_state[0xb8])
+ put_keycode(0xb8, !down);
}
-static void do_key_event(VncState *vs, int down, int keycode, int sym)
+static void do_key_event(VncState *vs, int down, int keycode, int sym,
+ int shift, int altgr)
{
+ int keypad = 0;
+
/* QEMU console switch */
switch(keycode) {
case 0x2a: /* Left Shift */
@@ -1059,11 +1084,8 @@ static void do_key_event(VncState *vs, i
case 0x1d: /* Left CTRL */
case 0x9d: /* Right CTRL */
case 0x38: /* Left ALT */
- case 0xb8: /* Right ALT */
- if (down)
- vs->modifiers_state[keycode] = 1;
- else
- vs->modifiers_state[keycode] = 0;
+ case 0xb8: /* Right ALT aka AltGr*/
+ vs->modifiers_state[keycode] = down;
break;
case 0x02 ... 0x0a: /* '1' to '9' keys */
if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) {
@@ -1074,16 +1096,22 @@ static void do_key_event(VncState *vs, i
}
break;
case 0x3a: /* CapsLock */
+ if (is_graphic_console())
+ return;
case 0x45: /* NumLock */
if (!down)
vs->modifiers_state[keycode] ^= 1;
break;
}
- if (keycode_is_keypad(vs->kbd_layout, keycode)) {
+ keypad = keycode_is_keypad(vs->kbd_layout, keycode);
+ if (keypad) {
/* If the numlock state needs to change then simulate an additional
keypress before sending this one. This will happen if the user
- toggles numlock away from the VNC window.
+ toggles numlock away from the VNC window. This isn't perfect as
+ pressig the shift key will typically and temporarily have the
+ effect of inverting the numlock setting: the shift key will be
+ effectively cancelled out.
*/
if (keysym_is_numlock(vs->kbd_layout, sym & 0xFFFF)) {
if (!vs->modifiers_state[0x45]) {
@@ -1099,12 +1127,14 @@ static void do_key_event(VncState *vs, i
}
if (is_graphic_console()) {
- if (keycode & 0x80)
- kbd_put_keycode(0xe0);
- if (down)
- kbd_put_keycode(keycode & 0x7f);
- else
- kbd_put_keycode(keycode | 0x80);
+ if (keycode_is_shiftable(vs->kbd_layout, keycode) && !keypad) {
+ if (down)
+ twiddle_modifiers(vs, down, shift, altgr);
+ put_keycode(keycode, down);
+ if (!down)
+ twiddle_modifiers(vs, down, shift, altgr);
+ } else
+ put_keycode(keycode, down);
} else {
/* QEMU console emulation */
if (down) {
@@ -1153,13 +1183,18 @@ static void do_key_event(VncState *vs, i
static void key_event(VncState *vs, int down, uint32_t sym)
{
+ int shift;
+ int altgr;
int keycode;
- if (sym >= 'A' && sym <= 'Z' && is_graphic_console())
- sym = sym - 'A' + 'a';
-
- keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
- do_key_event(vs, down, keycode, sym);
+ shift = (vs->modifiers_state[0x2a] || vs->modifiers_state[0x36]);
+ altgr = vs->modifiers_state[0xb8];
+ keycode = keysym2scancode1(vs->kbd_layout, sym & 0xFFFF, &shift, &altgr);
+ if (keycode == 0) {
+ fprintf(stderr, "Key lost : keysym 0x%x(%d)\n", sym, sym);
+ return;
+ }
+ do_key_event(vs, down, keycode, sym, shift, altgr);
}
static void ext_key_event(VncState *vs, int down,
@@ -1169,7 +1204,9 @@ static void ext_key_event(VncState *vs,
if (keyboard_layout)
key_event(vs, down, sym);
else
- do_key_event(vs, down, keycode, sym);
+ do_key_event(vs, down, keycode, sym,
+ vs->modifiers_state[0x2a] || vs->modifiers_state[0x36],
+ vs->modifiers_state[0xb8]);
}
static void framebuffer_update_request(VncState *vs, int incremental,
diff -up qemu-0.9.1.6042/vnc_keysym.h.keymap01 qemu-0.9.1.6042/vnc_keysym.h
--- qemu-0.9.1.6042/vnc_keysym.h.keymap01 2008-10-02 19:26:42.000000000
+0100
+++ qemu-0.9.1.6042/vnc_keysym.h 2008-12-16 10:47:36.000000000 +0000
@@ -2,7 +2,7 @@ typedef struct {
const char* name;
int keysym;
} name2keysym_t;
-static const name2keysym_t name2keysym[]={
+static name2keysym_t name2keysym[]={
/* ascii */
{ "space", 0x020},
{ "exclam", 0x021},