[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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.