[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 1 of 3][IOEMU] Fix keymap handling for vnc console
Fix keymap handling for international keyboards Signed-off-by: John Haxby <john.haxby@xxxxxxxxxx> keymaps.c | 364 ++++++++++++++++++++++++++++++++++++-------------------------- vnc.c | 134 ++++++++-------------- 2 files changed, 264 insertions(+), 234 deletions(-) diff --git a/keymaps.c b/keymaps.c index 1984678..4029500 100644 --- a/keymaps.c +++ b/keymaps.c @@ -22,60 +22,62 @@ * 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) { - 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; - struct key_range *shift_range; - struct key_range *localstate_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) @@ -97,143 +99,209 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, "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] == '#') + 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 (!strncmp(line, "map ", 4)) + } + 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 (!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 (rest && strstr(rest, "shift")) { - add_to_key_range(&k->shift_range, keysym); - //fprintf(stderr, "shift keysym %04x keycode %d\n", keysym, keycode); - } - if (rest && strstr(rest, "localstate")) { - add_to_key_range(&k->localstate_range, keycode); - //fprintf(stderr, "localstate 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++; - } - } - } - } + } + 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(void *kbd_layout, int keysym) +static int keysym2scancode_map (keysym_map_t *map, 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 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 inline int keycode_is_keypad(void *kbd_layout, int keycode) +static int keysym2scancode(void *kbd_layout, int keysym) { kbd_layout_t *k = kbd_layout; - struct key_range *kr; + int scancode; - for (kr = k->keypad_range; kr; kr = kr->next) - if (keycode >= kr->start && keycode <= kr->end) - return 1; + 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 keysym_is_numlock(void *kbd_layout, int keysym) +static int keysym2scancode1(void *kbd_layout, int keysym, + int *shift, int *altgr) { kbd_layout_t *k = kbd_layout; - struct key_range *kr; + 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; - for (kr = k->numlock_range; kr; kr = kr->next) - if (keysym >= kr->start && keysym <= kr->end) - return 1; + /* 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; -}+} -static inline int keysym_is_shift(void *kbd_layout, int keysym) +static int keycode_is_keypad(void *kbd_layout, int keycode) { kbd_layout_t *k = kbd_layout; - struct key_range *kr; - - for (kr = k->shift_range; kr; kr = kr->next) - if (keysym >= kr->start && keysym <= kr->end) - return 1; + if (keycode >= 0 && keycode < MAX_SCANCODE) + return !!(k->flags[keycode] & KEY_KEYPAD); return 0; } -static inline int keycode_is_shiftable(void *kbd_layout, int keycode) +static int keysym_is_numlock(void *kbd_layout, int keysym) +{ + kbd_layout_t *k = kbd_layout; + return (keysym2scancode_map(&k->numlock, keysym) != 0); +} + +static int keysym_is_shift(void *kbd_layout, int keysym) { kbd_layout_t *k = kbd_layout; - struct key_range *kr; + return (keysym2scancode_map(&k->shift, keysym) != 0); +} - for (kr = k->localstate_range; kr; kr = kr->next) - if (keycode >= kr->start && keycode <= kr->end) - return 0; - return 1; +static 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 --git a/vnc.c b/vnc.c index 01e22e5..e19860c 100644 --- a/vnc.c +++ b/vnc.c @@ -1272,14 +1272,22 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y) 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; } } @@ -1287,69 +1295,34 @@ 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 press_key_shift_down(VncState *vs, int down, int keycode) +static void twiddle_modifiers(VncState *vs, int down, int shift, int altgr) { - if (down) - kbd_put_keycode(0x2a & 0x7f); - - if (keycode & 0x80) - kbd_put_keycode(0xe0); - if (down) - kbd_put_keycode(keycode & 0x7f); - else - kbd_put_keycode(keycode | 0x80); - - if (!down) - kbd_put_keycode(0x2a | 0x80); -} - -static void press_key_shift_up(VncState *vs, int down, int keycode) -{ - if (down) { - if (vs->modifiers_state[0x2a]) - kbd_put_keycode(0x2a | 0x80);- if (vs->modifiers_state[0x36]) - kbd_put_keycode(0x36 | 0x80); - } - - if (keycode & 0x80) - kbd_put_keycode(0xe0); - if (down) - kbd_put_keycode(keycode & 0x7f); - else - kbd_put_keycode(keycode | 0x80); - - if (!down) { - if (vs->modifiers_state[0x2a]) - kbd_put_keycode(0x2a & 0x7f);- if (vs->modifiers_state[0x36]) - kbd_put_keycode(0x36 & 0x7f); - } + 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, uint32_t sym) { int keycode; - int shift_keys = 0; + int altgr = 0; int shift = 0; int keypad = 0; - if (is_graphic_console()) { - if (sym >= 'A' && sym <= 'Z') { - sym = sym - 'A' + 'a'; - shift = 1; - } - else { - shift = keysym_is_shift(vs->kbd_layout, sym & 0xFFFF); - } - } - shift_keys = vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]; - - keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF); + 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; @@ -1362,17 +1335,9 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) case 0x1d: /* Left CTRL */ case 0x9d: /* Right CTRL */ case 0x38: /* Left ALT */ - case 0xb8: /* Right ALT */ - if (keycode & 0x80) - kbd_put_keycode(0xe0); - if (down) { - vs->modifiers_state[keycode] = 1; - kbd_put_keycode(keycode & 0x7f); - } - else { - vs->modifiers_state[keycode] = 0; - kbd_put_keycode(keycode | 0x80); - } + case 0xb8: /* Right ALT aka AltGr */ + vs->modifiers_state[keycode] = down; + put_keycode(keycode, down); return;case 0x02 ... 0x0a: /* '1' to '9' keys */ if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) { @@ -1383,13 +1348,15 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) } break; case 0x3a: /* CapsLock */ + if (is_graphic_console()) + return; case 0x45: /* NumLock */ if (down) { - kbd_put_keycode(keycode & 0x7f); + put_keycode(keycode, 1); } else { vs->modifiers_state[keycode] ^= 1; - kbd_put_keycode(keycode | 0x80); + put_keycode(keycode, 0); } return; } @@ -1398,7 +1365,10 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) 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 + pressing 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]) { @@ -1417,22 +1387,14 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) /* If the shift state needs to change then simulate an additional keypress before sending this one. Ignore for non shiftable keys. */ - if (shift && !shift_keys) { - press_key_shift_down(vs, down, keycode); - return; - } - else if (!shift && shift_keys && !keypad && - keycode_is_shiftable(vs->kbd_layout, keycode)) { - press_key_shift_up(vs, down, keycode); - return; - } - - 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) { @@ -2528,7 +2490,7 @@ void vnc_display_init(DisplayState *ds) vs->kbd_layout = init_keyboard_layout(keyboard_layout); if (!vs->kbd_layout) exit(1); - vs->modifiers_state[0x45] = 1; /* NumLock on - on boot */ + vs->modifiers_state[0x45] = 0; /* NumLock off - on boot */ vs->ds->data = NULL; vs->ds->dpy_update = vnc_dpy_update; _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |