# HG changeset patch # User Ewan Mellor # Node ID c7f4a89eb054a1ad411da1e4cdc8aeda1a98c4fa # Parent 565cd8f32c70da8ae7dbaaeb9dff28aa8b6307e1 Fix numpad handling in QEMU's VNC server. The keymaps that we have include information on which keys change depending upon the numlock setting, but this isn't being used. By forcing numlock on and off as necessary, when receiving these keysyms through the VNC connection, we ensure that the server's numlock status is the same as the client's. Signed-off-by: Ewan Mellor Index: ioemu/keymaps.c =================================================================== --- ioemu.orig/keymaps.c 2006-12-06 23:41:30.000000000 +0000 +++ ioemu/keymaps.c 2006-12-08 18:20:27.000000000 +0000 @@ -36,8 +36,10 @@ #define MAX_EXTRA_COUNT 256 typedef struct { uint16_t keysym2keycode[MAX_NORMAL_KEYCODE]; + int keysym2numlock[MAX_NORMAL_KEYCODE]; struct { int keysym; + int numlock; uint16_t keycode; } keysym2keycode_extra[MAX_EXTRA_COUNT]; int extra_count; @@ -50,6 +52,8 @@ char file_name[1024]; char line[1024]; int len; + int *keycode2numlock; + int i; snprintf(file_name, sizeof(file_name), "%s/keymaps/%s", bios_dir, language); @@ -63,6 +67,15 @@ "Could not read keymap file: '%s'\n", file_name); return 0; } + + /* Allocate a temporary map tracking which keycodes change when numlock is + set. Keycodes are 16 bit, so 65536 is safe. */ + keycode2numlock = malloc(65536 * sizeof(int)); + if (!keycode2numlock) { + perror("Could not read keymap file"); + return 0; + } + for(;;) { if (fgets(line, 1024, f) == NULL) break; @@ -86,13 +99,19 @@ if (keysym == 0) { // fprintf(stderr, "Warning: unknown keysym %s\n", line); } else { - const char *rest = end_of_keysym + 1; - int keycode = strtol(rest, NULL, 0); + char *rest = end_of_keysym + 1; + int keycode = strtol(rest, &rest, 0); + int numlock = (rest != NULL && + strstr(rest, "numlock") != NULL); + + keycode2numlock[keycode] = numlock; + /* 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; + k->keysym2numlock[keysym] = numlock; } else { if (k->extra_count >= MAX_EXTRA_COUNT) { fprintf(stderr, @@ -107,6 +126,8 @@ keysym = keysym; k->keysym2keycode_extra[k->extra_count]. keycode = keycode; + k->keysym2keycode_extra[k->extra_count]. + numlock = numlock; k->extra_count++; } } @@ -115,6 +136,22 @@ } } fclose(f); + + for (i = 0; i < MAX_NORMAL_KEYCODE; i++) { + if (k->keysym2numlock[i] != 1) { + k->keysym2numlock[i] = -keycode2numlock[k->keysym2keycode[i]]; + } + } + + for (i = 0; i < k->extra_count; i++) { + if (k->keysym2keycode_extra[i].numlock != 1) { + k->keysym2keycode_extra[i].numlock = + -keycode2numlock[k->keysym2keycode_extra[i].keycode]; + } + } + + free(keycode2numlock); + return k; } @@ -143,3 +180,25 @@ } return 0; } + +/** + * Returns 1 if the given keysym requires numlock to be pressed, -1 if it + * requires it to be cleared, and 0 otherwise. + */ +static int keysym2numlock(void *kbd_layout, int keysym) +{ + kbd_layout_t *k = kbd_layout; + if (keysym < MAX_NORMAL_KEYCODE) { + return k->keysym2numlock[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].numlock; + } + return 0; +} Index: ioemu/vnc.c =================================================================== --- ioemu.orig/vnc.c 2006-12-08 18:18:26.000000000 +0000 +++ ioemu/vnc.c 2006-12-08 18:19:43.000000000 +0000 @@ -115,6 +115,7 @@ int ctl_keys; /* Ctrl+Alt starts calibration */ int shift_keys; /* Shift / CapsLock keys */ + int numlock; }; #define DIRTY_PIXEL_BITS 64 @@ -854,14 +855,40 @@ } } +static void press_key(VncState *vs, int keycode) +{ + kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) & 0x7f); + kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) | 0x80); +} + static void do_key_event(VncState *vs, int down, uint32_t sym) { sym &= 0xFFFF; if (is_graphic_console()) { int keycode; + int numlock; keycode = keysym2scancode(vs->kbd_layout, sym); + numlock = keysym2numlock(vs->kbd_layout, sym); + + /* 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. + */ + if (numlock == 1) { + if (!vs->numlock) { + vs->numlock = 1; + press_key(vs, XK_Num_Lock); + } + } + else if (numlock == -1) { + if (vs->numlock) { + vs->numlock = 0; + press_key(vs, XK_Num_Lock); + } + } + if (keycode & 0x80) kbd_put_keycode(0xe0); if (down) @@ -932,6 +959,10 @@ vs->shift_keys ^= 2; break; + case XK_Num_Lock: + vs->numlock = !vs->numlock; + break; + case XK_1 ... XK_9: if ((vs->ctl_keys & 3) != 3) break; @@ -1355,6 +1386,7 @@ vs->lsock = -1; vs->csock = -1; vs->depth = 4; + vs->numlock = 0; vs->ds = ds; Index: ioemu/vnc_keysym.h =================================================================== --- ioemu.orig/vnc_keysym.h 2006-12-08 18:17:01.000000000 +0000 +++ ioemu/vnc_keysym.h 2006-12-08 18:19:43.000000000 +0000 @@ -231,6 +231,19 @@ {"Home", 0xff50}, /* XK_Home */ {"End", 0xff57}, /* XK_End */ {"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */ +{"KP_Home", 0xff95}, +{"KP_Left", 0xff96}, +{"KP_Up", 0xff97}, +{"KP_Right", 0xff98}, +{"KP_Down", 0xff99}, +{"KP_Prior", 0xff9a}, +{"KP_Page_Up", 0xff9a}, +{"KP_Next", 0xff9b}, +{"KP_Page_Down", 0xff9b}, +{"KP_End", 0xff9c}, +{"KP_Begin", 0xff9d}, +{"KP_Insert", 0xff9e}, +{"KP_Delete", 0xff9f}, {"F1", 0xffbe}, /* XK_F1 */ {"F2", 0xffbf}, /* XK_F2 */ {"F3", 0xffc0}, /* XK_F3 */ @@ -258,6 +271,7 @@ {"KP_8", 0xffb8}, /* XK_KP_8 */ {"KP_9", 0xffb9}, /* XK_KP_9 */ {"KP_Add", 0xffab}, /* XK_KP_Add */ +{"KP_Separator", 0xffac},/* XK_KP_Separator */ {"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ {"KP_Divide", 0xffaf}, /* XK_KP_Divide */ {"KP_Enter", 0xff8d}, /* XK_KP_Enter */