summary refs log tree commit diff stats
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/Makefile.objs2
-rw-r--r--ui/cocoa.m28
-rw-r--r--ui/curses.c2
-rw-r--r--ui/egl-headless.c3
-rw-r--r--ui/egl-helpers.c9
-rw-r--r--ui/gtk-egl.c3
-rw-r--r--ui/gtk.c43
-rw-r--r--ui/kbd-state.c130
-rw-r--r--ui/keymaps.c55
-rw-r--r--ui/keymaps.h3
-rw-r--r--ui/sdl2-input.c50
-rw-r--r--ui/sdl2.c12
-rw-r--r--ui/sdl_keysym.h278
-rw-r--r--ui/spice-display.c2
-rw-r--r--ui/vnc.c119
-rw-r--r--ui/vnc.h5
16 files changed, 251 insertions, 493 deletions
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 9b6f0c6b67..7f8b3da791 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -8,7 +8,7 @@ vnc-obj-y += vnc-ws.o
 vnc-obj-y += vnc-jobs.o
 
 common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
-common-obj-y += input.o input-keymap.o input-legacy.o
+common-obj-y += input.o input-keymap.o input-legacy.o kbd-state.o
 common-obj-$(CONFIG_LINUX) += input-linux.o
 common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
 common-obj-$(CONFIG_COCOA) += cocoa.o
diff --git a/ui/cocoa.m b/ui/cocoa.m
index ddc058e76e..e2567d6946 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -54,6 +54,9 @@
 #ifndef MAC_OS_X_VERSION_10_12
 #define MAC_OS_X_VERSION_10_12 101200
 #endif
+#ifndef MAC_OS_X_VERSION_10_13
+#define MAC_OS_X_VERSION_10_13 101300
+#endif
 
 /* macOS 10.12 deprecated many constants, #define the new names for older SDKs */
 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
@@ -90,6 +93,14 @@
 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
 #define NSModalResponseOK NSFileHandlingPanelOKButton
 #endif
+/* 10.14 deprecates NSOnState and NSOffState in favor of
+ * NSControlStateValueOn/Off, which were introduced in 10.13.
+ * Define for older versions
+ */
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
+#define NSControlStateValueOn NSOnState
+#define NSControlStateValueOff NSOffState
+#endif
 
 //#define DEBUG
 
@@ -377,7 +388,12 @@ QemuCocoaView *cocoaView;
     COCOA_DEBUG("QemuCocoaView: drawRect\n");
 
     // get CoreGraphic context
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10
     CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort];
+#else
+    CGContextRef viewContextRef = [[NSGraphicsContext currentContext] CGContext];
+#endif
+
     CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
     CGContextSetShouldAntialias (viewContextRef, NO);
 
@@ -1147,9 +1163,9 @@ QemuCocoaView *cocoaView;
 {
     stretch_video = !stretch_video;
     if (stretch_video == true) {
-        [sender setState: NSOnState];
+        [sender setState: NSControlStateValueOn];
     } else {
-        [sender setState: NSOffState];
+        [sender setState: NSControlStateValueOff];
     }
 }
 
@@ -1390,15 +1406,15 @@ QemuCocoaView *cocoaView;
     {
         /* Unselect the currently selected item */
         for (NSMenuItem *item in [menu itemArray]) {
-            if (item.state == NSOnState) {
-                [item setState: NSOffState];
+            if (item.state == NSControlStateValueOn) {
+                [item setState: NSControlStateValueOff];
                 break;
             }
         }
     }
 
     // check the menu item
-    [sender setState: NSOnState];
+    [sender setState: NSControlStateValueOn];
 
     // get the throttle percentage
     throttle_pct = [sender tag];
@@ -1502,7 +1518,7 @@ int main (int argc, const char * argv[]) {
                    initWithTitle: [NSString stringWithFormat: @"%d%%", percentage] action:@selector(adjustSpeed:) keyEquivalent:@""] autorelease];
 
         if (percentage == 100) {
-            [menuItem setState: NSOnState];
+            [menuItem setState: NSControlStateValueOn];
         }
 
         /* Calculate the throttle percentage */
diff --git a/ui/curses.c b/ui/curses.c
index f4e7a12f74..6e0091c3b2 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -273,7 +273,7 @@ static void curses_refresh(DisplayChangeListener *dcl)
             }
 
             keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
-                                      false, false, false);
+                                      NULL, false);
             if (keycode == 0)
                 continue;
 
diff --git a/ui/egl-headless.c b/ui/egl-headless.c
index 519e7bad32..e67b47aeff 100644
--- a/ui/egl-headless.c
+++ b/ui/egl-headless.c
@@ -142,7 +142,8 @@ static void egl_scanout_flush(DisplayChangeListener *dcl,
         egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb,
                          !edpy->y_0_top);
         egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb,
-                          !edpy->y_0_top, edpy->pos_x, edpy->pos_y);
+                          !edpy->y_0_top, edpy->pos_x, edpy->pos_y,
+                          1.0, 1.0);
     } else {
         /* no cursor -> use simple framebuffer blit */
         egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top);
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 5e115b3fb4..e90eef8c9c 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -120,14 +120,15 @@ void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip)
 }
 
 void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip,
-                       int x, int y)
+                       int x, int y, double scale_x, double scale_y)
 {
     glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
+    int w = scale_x * src->width;
+    int h = scale_y * src->height;
     if (flip) {
-        glViewport(x, y, src->width, src->height);
+        glViewport(x, y, w, h);
     } else {
-        glViewport(x, dst->height - src->height - y,
-                   src->width, src->height);
+        glViewport(x, dst->height - h - y, w, h);
     }
     glEnable(GL_TEXTURE_2D);
     glBindTexture(GL_TEXTURE_2D, src->texture);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index afd17148c0..42801b688b 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -278,7 +278,8 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
                          vc->gfx.y0_top);
         egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb,
                           vc->gfx.y0_top,
-                          vc->gfx.cursor_x, vc->gfx.cursor_y);
+                          vc->gfx.cursor_x, vc->gfx.cursor_y,
+                          vc->gfx.scale_x, vc->gfx.scale_y);
     } else {
         egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
     }
diff --git a/ui/gtk.c b/ui/gtk.c
index 87c0e33d2a..949b143e4e 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -122,17 +122,6 @@
 
 #define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
 
-static const int modifier_keycode[] = {
-    Q_KEY_CODE_SHIFT,
-    Q_KEY_CODE_SHIFT_R,
-    Q_KEY_CODE_CTRL,
-    Q_KEY_CODE_CTRL_R,
-    Q_KEY_CODE_ALT,
-    Q_KEY_CODE_ALT_R,
-    Q_KEY_CODE_META_L,
-    Q_KEY_CODE_META_R,
-};
-
 static const guint16 *keycode_map;
 static size_t keycode_maplen;
 
@@ -187,7 +176,6 @@ struct GtkDisplayState {
 
     bool external_pause_update;
 
-    bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
     bool ignore_keys;
 
     DisplayOptions *opts;
@@ -426,20 +414,12 @@ static void gd_update_full_redraw(VirtualConsole *vc)
 static void gtk_release_modifiers(GtkDisplayState *s)
 {
     VirtualConsole *vc = gd_vc_find_current(s);
-    int i, qcode;
 
     if (vc->type != GD_VC_GFX ||
         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
         return;
     }
-    for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
-        qcode = modifier_keycode[i];
-        if (!s->modifier_pressed[i]) {
-            continue;
-        }
-        qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode, false);
-        s->modifier_pressed[i] = false;
-    }
+    qkbd_state_lift_all_keys(vc->gfx.kbd);
 }
 
 static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
@@ -1004,7 +984,9 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
                                          &delta_x, &delta_y)) {
             return TRUE;
         }
-        if (delta_y > 0) {
+        if (delta_y == 0) {
+            return TRUE;
+        } else if (delta_y > 0) {
             btn = INPUT_BUTTON_WHEEL_DOWN;
         } else {
             btn = INPUT_BUTTON_WHEEL_UP;
@@ -1113,7 +1095,6 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
     VirtualConsole *vc = opaque;
     GtkDisplayState *s = vc->s;
     int qcode;
-    int i;
 
     if (s->ignore_keys) {
         s->ignore_keys = (key->type == GDK_KEY_PRESS);
@@ -1134,8 +1115,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
         || key->hardware_keycode == VK_PAUSE
 #endif
         ) {
-        qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
-                                        key->type == GDK_KEY_PRESS);
+        qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE,
+                             key->type == GDK_KEY_PRESS);
         return TRUE;
     }
 
@@ -1144,14 +1125,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
     trace_gd_key_event(vc->label, key->hardware_keycode, qcode,
                        (key->type == GDK_KEY_PRESS) ? "down" : "up");
 
-    for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
-        if (qcode == modifier_keycode[i]) {
-            s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
-        }
-    }
-
-    qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode,
-                                    key->type == GDK_KEY_PRESS);
+    qkbd_state_key_event(vc->gfx.kbd, qcode,
+                         key->type == GDK_KEY_PRESS);
 
     return TRUE;
 }
@@ -2043,6 +2018,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
                           GDK_ENTER_NOTIFY_MASK |
                           GDK_LEAVE_NOTIFY_MASK |
                           GDK_SCROLL_MASK |
+                          GDK_SMOOTH_SCROLL_MASK |
                           GDK_KEY_PRESS_MASK);
     gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
 
@@ -2052,6 +2028,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
                              vc->tab_item, gtk_label_new(vc->label));
 
+    vc->gfx.kbd = qkbd_state_init(con);
     vc->gfx.dcl.con = con;
     register_displaychangelistener(&vc->gfx.dcl);
 
diff --git a/ui/kbd-state.c b/ui/kbd-state.c
new file mode 100644
index 0000000000..ac14add70e
--- /dev/null
+++ b/ui/kbd-state.c
@@ -0,0 +1,130 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/bitmap.h"
+#include "qemu/queue.h"
+#include "ui/console.h"
+#include "ui/input.h"
+#include "ui/kbd-state.h"
+
+struct QKbdState {
+    QemuConsole *con;
+    int key_delay_ms;
+    DECLARE_BITMAP(keys, Q_KEY_CODE__MAX);
+    DECLARE_BITMAP(mods, QKBD_MOD__MAX);
+};
+
+static void qkbd_state_modifier_update(QKbdState *kbd,
+                                      QKeyCode qcode1, QKeyCode qcode2,
+                                      QKbdModifier mod)
+{
+    if (test_bit(qcode1, kbd->keys) || test_bit(qcode2, kbd->keys)) {
+        set_bit(mod, kbd->mods);
+    } else {
+        clear_bit(mod, kbd->mods);
+    }
+}
+
+bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod)
+{
+    return test_bit(mod, kbd->mods);
+}
+
+bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode)
+{
+    return test_bit(qcode, kbd->keys);
+}
+
+void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down)
+{
+    bool state = test_bit(qcode, kbd->keys);
+
+    if (state == down) {
+        /*
+         * Filter out events which don't change the keyboard state.
+         *
+         * Most notably this allows to simply send along all key-up
+         * events, and this function will filter out everything where
+         * the corresponding key-down event wasn't send to the guest,
+         * for example due to being a host hotkey.
+         */
+        return;
+    }
+
+    /* update key and modifier state */
+    change_bit(qcode, kbd->keys);
+    switch (qcode) {
+    case Q_KEY_CODE_SHIFT:
+    case Q_KEY_CODE_SHIFT_R:
+        qkbd_state_modifier_update(kbd, Q_KEY_CODE_SHIFT, Q_KEY_CODE_SHIFT_R,
+                                   QKBD_MOD_SHIFT);
+        break;
+    case Q_KEY_CODE_CTRL:
+    case Q_KEY_CODE_CTRL_R:
+        qkbd_state_modifier_update(kbd, Q_KEY_CODE_CTRL, Q_KEY_CODE_CTRL_R,
+                                   QKBD_MOD_CTRL);
+        break;
+    case Q_KEY_CODE_ALT:
+        qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT, Q_KEY_CODE_ALT,
+                                   QKBD_MOD_ALT);
+        break;
+    case Q_KEY_CODE_ALT_R:
+        qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT_R, Q_KEY_CODE_ALT_R,
+                                   QKBD_MOD_ALTGR);
+        break;
+    case Q_KEY_CODE_CAPS_LOCK:
+        if (down) {
+            change_bit(QKBD_MOD_CAPSLOCK, kbd->mods);
+        }
+        break;
+    case Q_KEY_CODE_NUM_LOCK:
+        if (down) {
+            change_bit(QKBD_MOD_NUMLOCK, kbd->mods);
+        }
+        break;
+    default:
+        /* keep gcc happy */
+        break;
+    }
+
+    /* send to guest */
+    if (qemu_console_is_graphic(kbd->con)) {
+        qemu_input_event_send_key_qcode(kbd->con, qcode, down);
+        if (kbd->key_delay_ms) {
+            qemu_input_event_send_key_delay(kbd->key_delay_ms);
+        }
+    }
+}
+
+void qkbd_state_lift_all_keys(QKbdState *kbd)
+{
+    int qcode;
+
+    for (qcode = 0; qcode < Q_KEY_CODE__MAX; qcode++) {
+        if (test_bit(qcode, kbd->keys)) {
+            qkbd_state_key_event(kbd, qcode, false);
+        }
+    }
+}
+
+void qkbd_state_set_delay(QKbdState *kbd, int delay_ms)
+{
+    kbd->key_delay_ms = delay_ms;
+}
+
+void qkbd_state_free(QKbdState *kbd)
+{
+    g_free(kbd);
+}
+
+QKbdState *qkbd_state_init(QemuConsole *con)
+{
+    QKbdState *kbd = g_new0(QKbdState, 1);
+
+    kbd->con = con;
+
+    return kbd;
+}
diff --git a/ui/keymaps.c b/ui/keymaps.c
index 6e44f738ed..544b55c27b 100644
--- a/ui/keymaps.c
+++ b/ui/keymaps.c
@@ -28,6 +28,7 @@
 #include "trace.h"
 #include "qemu/error-report.h"
 #include "qapi/error.h"
+#include "ui/input.h"
 
 struct keysym2code {
     uint32_t count;
@@ -188,7 +189,7 @@ kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
 
 
 int keysym2scancode(kbd_layout_t *k, int keysym,
-                    bool shift, bool altgr, bool ctrl)
+                    QKbdState *kbd, bool down)
 {
     static const uint32_t mask =
         SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL;
@@ -212,27 +213,39 @@ int keysym2scancode(kbd_layout_t *k, int keysym,
         return keysym2code->keycodes[0];
     }
 
-    /*
-     * We have multiple keysym -> keycode mappings.
-     *
-     * 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 (shift) {
-        mods |= SCANCODE_SHIFT;
-    }
-    if (altgr) {
-        mods |= SCANCODE_ALTGR;
-    }
-    if (ctrl) {
-        mods |= SCANCODE_CTRL;
-    }
+    /* 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];
+        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 keysym2code->keycodes[0];
diff --git a/ui/keymaps.h b/ui/keymaps.h
index 4e9c87fb8f..b6d48aac40 100644
--- a/ui/keymaps.h
+++ b/ui/keymaps.h
@@ -26,6 +26,7 @@
 #define QEMU_KEYMAPS_H
 
 #include "qemu-common.h"
+#include "ui/kbd-state.h"
 
 typedef struct {
     const char* name;
@@ -55,7 +56,7 @@ typedef struct kbd_layout_t kbd_layout_t;
 kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
                                    const char *language, Error **errp);
 int keysym2scancode(kbd_layout_t *k, int keysym,
-                    bool shift, bool altgr, bool ctrl);
+                    QKbdState *kbd, bool down);
 int keycode_is_keypad(kbd_layout_t *k, int keycode);
 int keysym_is_numlock(kbd_layout_t *k, int keysym);
 
diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c
index 1378b63dd9..664364a5e5 100644
--- a/ui/sdl2-input.c
+++ b/ui/sdl2-input.c
@@ -30,63 +30,23 @@
 #include "ui/sdl2.h"
 #include "sysemu/sysemu.h"
 
-static uint8_t modifiers_state[SDL_NUM_SCANCODES];
-
-void sdl2_reset_keys(struct sdl2_console *scon)
-{
-    QemuConsole *con = scon ? scon->dcl.con : NULL;
-    int i;
-
-    for (i = 0 ;
-         i < SDL_NUM_SCANCODES && i < qemu_input_map_usb_to_qcode_len ;
-         i++) {
-        if (modifiers_state[i]) {
-            int qcode = qemu_input_map_usb_to_qcode[i];
-            qemu_input_event_send_key_qcode(con, qcode, false);
-            modifiers_state[i] = 0;
-        }
-    }
-}
-
 void sdl2_process_key(struct sdl2_console *scon,
                       SDL_KeyboardEvent *ev)
 {
     int qcode;
-    QemuConsole *con = scon ? scon->dcl.con : NULL;
+    QemuConsole *con = scon->dcl.con;
 
     if (ev->keysym.scancode >= qemu_input_map_usb_to_qcode_len) {
         return;
     }
-
     qcode = qemu_input_map_usb_to_qcode[ev->keysym.scancode];
-
-    /* modifier state tracking */
-    switch (ev->keysym.scancode) {
-    case SDL_SCANCODE_LCTRL:
-    case SDL_SCANCODE_LSHIFT:
-    case SDL_SCANCODE_LALT:
-    case SDL_SCANCODE_LGUI:
-    case SDL_SCANCODE_RCTRL:
-    case SDL_SCANCODE_RSHIFT:
-    case SDL_SCANCODE_RALT:
-    case SDL_SCANCODE_RGUI:
-        if (ev->type == SDL_KEYUP) {
-            modifiers_state[ev->keysym.scancode] = 0;
-        } else {
-            modifiers_state[ev->keysym.scancode] = 1;
-        }
-        break;
-    default:
-        /* nothing */
-        break;
-    }
+    qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN);
 
     if (!qemu_console_is_graphic(con)) {
-        bool ctrl = (modifiers_state[SDL_SCANCODE_LCTRL] ||
-                     modifiers_state[SDL_SCANCODE_RCTRL]);
+        bool ctrl = qkbd_state_modifier_get(scon->kbd, QKBD_MOD_CTRL);
         if (ev->type == SDL_KEYDOWN) {
-            switch (ev->keysym.scancode) {
-            case SDL_SCANCODE_RETURN:
+            switch (qcode) {
+            case Q_KEY_CODE_RET:
                 kbd_put_keysym_console(con, '\n');
                 break;
             default:
diff --git a/ui/sdl2.c b/ui/sdl2.c
index cde7feba91..1277cf28fb 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -38,7 +38,6 @@ static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
 
 static int gui_saved_grab;
 static int gui_fullscreen;
-static int gui_keysym;
 static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
 static SDL_Cursor *sdl_cursor_normal;
 static SDL_Cursor *sdl_cursor_hidden;
@@ -330,6 +329,7 @@ static void handle_keydown(SDL_Event *ev)
     int win;
     struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
     int gui_key_modifier_pressed = get_mod_state();
+    int gui_keysym = 0;
 
     if (!scon->ignore_hotkeys && gui_key_modifier_pressed && !ev->key.repeat) {
         switch (ev->key.keysym.scancode) {
@@ -410,16 +410,9 @@ static void handle_keydown(SDL_Event *ev)
 static void handle_keyup(SDL_Event *ev)
 {
     struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
-    int gui_key_modifier_pressed = get_mod_state();
 
     scon->ignore_hotkeys = false;
-
-    if (!gui_key_modifier_pressed) {
-        gui_keysym = 0;
-    }
-    if (!gui_keysym) {
-        sdl2_process_key(scon, &ev->key);
-    }
+    sdl2_process_key(scon, &ev->key);
 }
 
 static void handle_textinput(SDL_Event *ev)
@@ -823,6 +816,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
         sdl2_console[i].dcl.ops = &dcl_2d_ops;
 #endif
         sdl2_console[i].dcl.con = con;
+        sdl2_console[i].kbd = qkbd_state_init(con);
         register_displaychangelistener(&sdl2_console[i].dcl);
 
 #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)
diff --git a/ui/sdl_keysym.h b/ui/sdl_keysym.h
deleted file mode 100644
index 599d9fc64d..0000000000
--- a/ui/sdl_keysym.h
+++ /dev/null
@@ -1,278 +0,0 @@
-
-#include "keymaps.h"
-
-static const name2keysym_t name2keysym[]={
-/* ascii */
-    { "space",                0x020},
-    { "exclam",               0x021},
-    { "quotedbl",             0x022},
-    { "numbersign",           0x023},
-    { "dollar",               0x024},
-    { "percent",              0x025},
-    { "ampersand",            0x026},
-    { "apostrophe",           0x027},
-    { "parenleft",            0x028},
-    { "parenright",           0x029},
-    { "asterisk",             0x02a},
-    { "plus",                 0x02b},
-    { "comma",                0x02c},
-    { "minus",                0x02d},
-    { "period",               0x02e},
-    { "slash",                0x02f},
-    { "0",                    0x030},
-    { "1",                    0x031},
-    { "2",                    0x032},
-    { "3",                    0x033},
-    { "4",                    0x034},
-    { "5",                    0x035},
-    { "6",                    0x036},
-    { "7",                    0x037},
-    { "8",                    0x038},
-    { "9",                    0x039},
-    { "colon",                0x03a},
-    { "semicolon",            0x03b},
-    { "less",                 0x03c},
-    { "equal",                0x03d},
-    { "greater",              0x03e},
-    { "question",             0x03f},
-    { "at",                   0x040},
-    { "A",                    0x041},
-    { "B",                    0x042},
-    { "C",                    0x043},
-    { "D",                    0x044},
-    { "E",                    0x045},
-    { "F",                    0x046},
-    { "G",                    0x047},
-    { "H",                    0x048},
-    { "I",                    0x049},
-    { "J",                    0x04a},
-    { "K",                    0x04b},
-    { "L",                    0x04c},
-    { "M",                    0x04d},
-    { "N",                    0x04e},
-    { "O",                    0x04f},
-    { "P",                    0x050},
-    { "Q",                    0x051},
-    { "R",                    0x052},
-    { "S",                    0x053},
-    { "T",                    0x054},
-    { "U",                    0x055},
-    { "V",                    0x056},
-    { "W",                    0x057},
-    { "X",                    0x058},
-    { "Y",                    0x059},
-    { "Z",                    0x05a},
-    { "bracketleft",          0x05b},
-    { "backslash",            0x05c},
-    { "bracketright",         0x05d},
-    { "asciicircum",          0x05e},
-    { "underscore",           0x05f},
-    { "grave",                0x060},
-    { "a",                    0x061},
-    { "b",                    0x062},
-    { "c",                    0x063},
-    { "d",                    0x064},
-    { "e",                    0x065},
-    { "f",                    0x066},
-    { "g",                    0x067},
-    { "h",                    0x068},
-    { "i",                    0x069},
-    { "j",                    0x06a},
-    { "k",                    0x06b},
-    { "l",                    0x06c},
-    { "m",                    0x06d},
-    { "n",                    0x06e},
-    { "o",                    0x06f},
-    { "p",                    0x070},
-    { "q",                    0x071},
-    { "r",                    0x072},
-    { "s",                    0x073},
-    { "t",                    0x074},
-    { "u",                    0x075},
-    { "v",                    0x076},
-    { "w",                    0x077},
-    { "x",                    0x078},
-    { "y",                    0x079},
-    { "z",                    0x07a},
-    { "braceleft",            0x07b},
-    { "bar",                  0x07c},
-    { "braceright",           0x07d},
-    { "asciitilde",           0x07e},
-
-/* latin 1 extensions */
-{ "nobreakspace",         0x0a0},
-{ "exclamdown",           0x0a1},
-{ "cent",         	  0x0a2},
-{ "sterling",             0x0a3},
-{ "currency",             0x0a4},
-{ "yen",                  0x0a5},
-{ "brokenbar",            0x0a6},
-{ "section",              0x0a7},
-{ "diaeresis",            0x0a8},
-{ "copyright",            0x0a9},
-{ "ordfeminine",          0x0aa},
-{ "guillemotleft",        0x0ab},
-{ "notsign",              0x0ac},
-{ "hyphen",               0x0ad},
-{ "registered",           0x0ae},
-{ "macron",               0x0af},
-{ "degree",               0x0b0},
-{ "plusminus",            0x0b1},
-{ "twosuperior",          0x0b2},
-{ "threesuperior",        0x0b3},
-{ "acute",                0x0b4},
-{ "mu",                   0x0b5},
-{ "paragraph",            0x0b6},
-{ "periodcentered",       0x0b7},
-{ "cedilla",              0x0b8},
-{ "onesuperior",          0x0b9},
-{ "masculine",            0x0ba},
-{ "guillemotright",       0x0bb},
-{ "onequarter",           0x0bc},
-{ "onehalf",              0x0bd},
-{ "threequarters",        0x0be},
-{ "questiondown",         0x0bf},
-{ "Agrave",               0x0c0},
-{ "Aacute",               0x0c1},
-{ "Acircumflex",          0x0c2},
-{ "Atilde",               0x0c3},
-{ "Adiaeresis",           0x0c4},
-{ "Aring",                0x0c5},
-{ "AE",                   0x0c6},
-{ "Ccedilla",             0x0c7},
-{ "Egrave",               0x0c8},
-{ "Eacute",               0x0c9},
-{ "Ecircumflex",          0x0ca},
-{ "Ediaeresis",           0x0cb},
-{ "Igrave",               0x0cc},
-{ "Iacute",               0x0cd},
-{ "Icircumflex",          0x0ce},
-{ "Idiaeresis",           0x0cf},
-{ "ETH",                  0x0d0},
-{ "Eth",                  0x0d0},
-{ "Ntilde",               0x0d1},
-{ "Ograve",               0x0d2},
-{ "Oacute",               0x0d3},
-{ "Ocircumflex",          0x0d4},
-{ "Otilde",               0x0d5},
-{ "Odiaeresis",           0x0d6},
-{ "multiply",             0x0d7},
-{ "Ooblique",             0x0d8},
-{ "Oslash",               0x0d8},
-{ "Ugrave",               0x0d9},
-{ "Uacute",               0x0da},
-{ "Ucircumflex",          0x0db},
-{ "Udiaeresis",           0x0dc},
-{ "Yacute",               0x0dd},
-{ "THORN",                0x0de},
-{ "Thorn",                0x0de},
-{ "ssharp",               0x0df},
-{ "agrave",               0x0e0},
-{ "aacute",               0x0e1},
-{ "acircumflex",          0x0e2},
-{ "atilde",               0x0e3},
-{ "adiaeresis",           0x0e4},
-{ "aring",                0x0e5},
-{ "ae",                   0x0e6},
-{ "ccedilla",             0x0e7},
-{ "egrave",               0x0e8},
-{ "eacute",               0x0e9},
-{ "ecircumflex",          0x0ea},
-{ "ediaeresis",           0x0eb},
-{ "igrave",               0x0ec},
-{ "iacute",               0x0ed},
-{ "icircumflex",          0x0ee},
-{ "idiaeresis",           0x0ef},
-{ "eth",                  0x0f0},
-{ "ntilde",               0x0f1},
-{ "ograve",               0x0f2},
-{ "oacute",               0x0f3},
-{ "ocircumflex",          0x0f4},
-{ "otilde",               0x0f5},
-{ "odiaeresis",           0x0f6},
-{ "division",             0x0f7},
-{ "oslash",               0x0f8},
-{ "ooblique",             0x0f8},
-{ "ugrave",               0x0f9},
-{ "uacute",               0x0fa},
-{ "ucircumflex",          0x0fb},
-{ "udiaeresis",           0x0fc},
-{ "yacute",               0x0fd},
-{ "thorn",                0x0fe},
-{ "ydiaeresis",           0x0ff},
-#if SDL_MAJOR_VERSION == 1
-{"EuroSign", SDLK_EURO},
-
-    /* modifiers */
-{"Control_L", SDLK_LCTRL},
-{"Control_R", SDLK_RCTRL},
-{"Alt_L", SDLK_LALT},
-{"Alt_R", SDLK_RALT},
-{"Caps_Lock", SDLK_CAPSLOCK},
-{"Meta_L", SDLK_LMETA},
-{"Meta_R", SDLK_RMETA},
-{"Shift_L", SDLK_LSHIFT},
-{"Shift_R", SDLK_RSHIFT},
-{"Super_L", SDLK_LSUPER},
-{"Super_R", SDLK_RSUPER},
-
-    /* special keys */
-{"BackSpace", SDLK_BACKSPACE},
-{"Tab", SDLK_TAB},
-{"Return", SDLK_RETURN},
-{"Right", SDLK_RIGHT},
-{"Left", SDLK_LEFT},
-{"Up", SDLK_UP},
-{"Down", SDLK_DOWN},
-{"Page_Down", SDLK_PAGEDOWN},
-{"Page_Up", SDLK_PAGEUP},
-{"Insert", SDLK_INSERT},
-{"Delete", SDLK_DELETE},
-{"Home", SDLK_HOME},
-{"End", SDLK_END},
-{"Scroll_Lock", SDLK_SCROLLOCK},
-{"F1", SDLK_F1},
-{"F2", SDLK_F2},
-{"F3", SDLK_F3},
-{"F4", SDLK_F4},
-{"F5", SDLK_F5},
-{"F6", SDLK_F6},
-{"F7", SDLK_F7},
-{"F8", SDLK_F8},
-{"F9", SDLK_F9},
-{"F10", SDLK_F10},
-{"F11", SDLK_F11},
-{"F12", SDLK_F12},
-{"F13", SDLK_F13},
-{"F14", SDLK_F14},
-{"F15", SDLK_F15},
-{"Sys_Req", SDLK_SYSREQ},
-{"KP_0", SDLK_KP0},
-{"KP_1", SDLK_KP1},
-{"KP_2", SDLK_KP2},
-{"KP_3", SDLK_KP3},
-{"KP_4", SDLK_KP4},
-{"KP_5", SDLK_KP5},
-{"KP_6", SDLK_KP6},
-{"KP_7", SDLK_KP7},
-{"KP_8", SDLK_KP8},
-{"KP_9", SDLK_KP9},
-{"KP_Add", SDLK_KP_PLUS},
-{"KP_Decimal", SDLK_KP_PERIOD},
-{"KP_Divide", SDLK_KP_DIVIDE},
-{"KP_Enter", SDLK_KP_ENTER},
-{"KP_Equal", SDLK_KP_EQUALS},
-{"KP_Multiply", SDLK_KP_MULTIPLY},
-{"KP_Subtract", SDLK_KP_MINUS},
-{"help", SDLK_HELP},
-{"Menu", SDLK_MENU},
-{"Power", SDLK_POWER},
-{"Print", SDLK_PRINT},
-{"Mode_switch", SDLK_MODE},
-{"Multi_Key", SDLK_COMPOSE},
-{"Num_Lock", SDLK_NUMLOCK},
-{"Pause", SDLK_PAUSE},
-{"Escape", SDLK_ESCAPE},
-#endif
-{NULL, 0},
-};
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 52f8cb5ae1..aea6f6ebce 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -1090,7 +1090,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
         egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb,
                          !y_0_top);
         egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb,
-                          !y_0_top, x, y);
+                          !y_0_top, x, y, 1.0, 1.0);
         glFlush();
     }
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 6002d09407..0fef646fc4 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -59,7 +59,6 @@ static QTAILQ_HEAD(, VncDisplay) vnc_displays =
     QTAILQ_HEAD_INITIALIZER(vnc_displays);
 
 static int vnc_cursor_define(VncState *vs);
-static void vnc_release_modifiers(VncState *vs);
 static void vnc_update_throttle_offset(VncState *vs);
 
 static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
@@ -1267,7 +1266,7 @@ void vnc_disconnect_finish(VncState *vs)
     vnc_sasl_client_cleanup(vs);
 #endif /* CONFIG_VNC_SASL */
     audio_del(vs);
-    vnc_release_modifiers(vs);
+    qkbd_state_lift_all_keys(vs->vd->kbd);
 
     if (vs->mouse_mode_notifier.notify != NULL) {
         qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
@@ -1756,26 +1755,10 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y)
     qemu_input_event_sync();
 }
 
-static void reset_keys(VncState *vs)
+static void press_key(VncState *vs, QKeyCode qcode)
 {
-    int i;
-    for(i = 0; i < 256; i++) {
-        if (vs->modifiers_state[i]) {
-            qemu_input_event_send_key_number(vs->vd->dcl.con, i, false);
-            qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
-            vs->modifiers_state[i] = 0;
-        }
-    }
-}
-
-static void press_key(VncState *vs, int keysym)
-{
-    int keycode = keysym2scancode(vs->vd->kbd_layout, keysym,
-                                  false, false, false) & SCANCODE_KEYMASK;
-    qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true);
-    qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
-    qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
-    qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
+    qkbd_state_key_event(vs->vd->kbd, qcode, true);
+    qkbd_state_key_event(vs->vd->kbd, qcode, false);
 }
 
 static void vnc_led_state_change(VncState *vs)
@@ -1816,32 +1799,20 @@ static void kbd_leds(void *opaque, int ledstate)
 
 static void do_key_event(VncState *vs, int down, int keycode, int sym)
 {
+    QKeyCode qcode = qemu_input_key_number_to_qcode(keycode);
+
     /* QEMU console switch */
-    switch(keycode) {
-    case 0x2a:                          /* Left Shift */
-    case 0x36:                          /* Right Shift */
-    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;
-        break;
-    case 0x02 ... 0x0a: /* '1' to '9' keys */
-        if (vs->vd->dcl.con == NULL &&
-            down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) {
+    switch (qcode) {
+    case Q_KEY_CODE_1 ... Q_KEY_CODE_9: /* '1' to '9' keys */
+        if (vs->vd->dcl.con == NULL && down &&
+            qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL) &&
+            qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) {
             /* Reset the modifiers sent to the current console */
-            reset_keys(vs);
-            console_select(keycode - 0x02);
+            qkbd_state_lift_all_keys(vs->vd->kbd);
+            console_select(qcode - Q_KEY_CODE_1);
             return;
         }
-        break;
-    case 0x3a:                        /* CapsLock */
-    case 0x45:                        /* NumLock */
-        if (down)
-            vs->modifiers_state[keycode] ^= 1;
+    default:
         break;
     }
 
@@ -1856,16 +1827,14 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
            toggles numlock away from the VNC window.
         */
         if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
-            if (!vs->modifiers_state[0x45]) {
+            if (!qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) {
                 trace_vnc_key_sync_numlock(true);
-                vs->modifiers_state[0x45] = 1;
-                press_key(vs, 0xff7f);
+                press_key(vs, Q_KEY_CODE_NUM_LOCK);
             }
         } else {
-            if (vs->modifiers_state[0x45]) {
+            if (qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) {
                 trace_vnc_key_sync_numlock(false);
-                vs->modifiers_state[0x45] = 0;
-                press_key(vs, 0xff7f);
+                press_key(vs, Q_KEY_CODE_NUM_LOCK);
             }
         }
     }
@@ -1878,30 +1847,25 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
            toggles capslock away from the VNC window.
         */
         int uppercase = !!(sym >= 'A' && sym <= 'Z');
-        int shift = !!(vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]);
-        int capslock = !!(vs->modifiers_state[0x3a]);
+        bool shift = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_SHIFT);
+        bool capslock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CAPSLOCK);
         if (capslock) {
             if (uppercase == shift) {
                 trace_vnc_key_sync_capslock(false);
-                vs->modifiers_state[0x3a] = 0;
-                press_key(vs, 0xffe5);
+                press_key(vs, Q_KEY_CODE_CAPS_LOCK);
             }
         } else {
             if (uppercase != shift) {
                 trace_vnc_key_sync_capslock(true);
-                vs->modifiers_state[0x3a] = 1;
-                press_key(vs, 0xffe5);
+                press_key(vs, Q_KEY_CODE_CAPS_LOCK);
             }
         }
     }
 
-    if (qemu_console_is_graphic(NULL)) {
-        qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down);
-        qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
-    } else {
-        bool numlock = vs->modifiers_state[0x45];
-        bool control = (vs->modifiers_state[0x1d] ||
-                        vs->modifiers_state[0x9d]);
+    qkbd_state_key_event(vs->vd->kbd, qcode, down);
+    if (!qemu_console_is_graphic(NULL)) {
+        bool numlock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK);
+        bool control = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL);
         /* QEMU console emulation */
         if (down) {
             switch (keycode) {
@@ -2002,27 +1966,6 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
     }
 }
 
-static void vnc_release_modifiers(VncState *vs)
-{
-    static const int keycodes[] = {
-        /* shift, control, alt keys, both left & right */
-        0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8,
-    };
-    int i, keycode;
-
-    if (!qemu_console_is_graphic(NULL)) {
-        return;
-    }
-    for (i = 0; i < ARRAY_SIZE(keycodes); i++) {
-        keycode = keycodes[i];
-        if (!vs->modifiers_state[keycode]) {
-            continue;
-        }
-        qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
-        qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
-    }
-}
-
 static const char *code2name(int keycode)
 {
     return QKeyCode_str(qemu_input_key_number_to_qcode(keycode));
@@ -2030,9 +1973,6 @@ static const char *code2name(int keycode)
 
 static void key_event(VncState *vs, int down, uint32_t sym)
 {
-    bool shift = vs->modifiers_state[0x2a] || vs->modifiers_state[0x36];
-    bool altgr = vs->modifiers_state[0xb8];
-    bool ctrl  = vs->modifiers_state[0x1d] || vs->modifiers_state[0x9d];
     int keycode;
     int lsym = sym;
 
@@ -2041,7 +1981,7 @@ static void key_event(VncState *vs, int down, uint32_t sym)
     }
 
     keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF,
-                              shift, altgr, ctrl) & SCANCODE_KEYMASK;
+                              vs->vd->kbd, down) & SCANCODE_KEYMASK;
     trace_vnc_key_event_map(down, sym, keycode, code2name(keycode));
     do_key_event(vs, down, keycode, sym);
 }
@@ -3259,6 +3199,7 @@ void vnc_display_init(const char *id, Error **errp)
 
     vd->dcl.ops = &dcl_ops;
     register_displaychangelistener(&vd->dcl);
+    vd->kbd = qkbd_state_init(vd->dcl.con);
 }
 
 
@@ -3995,7 +3936,6 @@ void vnc_display_open(const char *id, Error **errp)
         vd->led = qemu_add_led_event_handler(kbd_leds, vd);
     }
     vd->ledstate = 0;
-    vd->key_delay_ms = key_delay_ms;
 
     device_id = qemu_opt_get(opts, "display");
     if (device_id) {
@@ -4012,10 +3952,13 @@ void vnc_display_open(const char *id, Error **errp)
     }
 
     if (con != vd->dcl.con) {
+        qkbd_state_free(vd->kbd);
         unregister_displaychangelistener(&vd->dcl);
         vd->dcl.con = con;
         register_displaychangelistener(&vd->dcl);
+        vd->kbd = qkbd_state_init(vd->dcl.con);
     }
+    qkbd_state_set_delay(vd->kbd, key_delay_ms);
 
     if (saddr == NULL) {
         goto cleanup;
diff --git a/ui/vnc.h b/ui/vnc.h
index a86e0610e8..81daa7a0eb 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -44,6 +44,7 @@
 #include "keymaps.h"
 #include "vnc-palette.h"
 #include "vnc-enc-zrle.h"
+#include "ui/kbd-state.h"
 
 // #define _VNC_DEBUG 1
 
@@ -155,7 +156,7 @@ struct VncDisplay
     int lock_key_sync;
     QEMUPutLEDEntry *led;
     int ledstate;
-    int key_delay_ms;
+    QKbdState *kbd;
     QemuMutex mutex;
 
     QEMUCursor *cursor;
@@ -326,8 +327,6 @@ struct VncState
 
     VncReadEvent *read_handler;
     size_t read_handler_expect;
-    /* input */
-    uint8_t modifiers_state[256];
 
     bool abort;
     QemuMutex output_mutex;