summary refs log tree commit diff stats
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/Makefile.objs6
-rw-r--r--ui/cocoa.m81
-rw-r--r--ui/console.c69
-rw-r--r--ui/curses.c47
-rw-r--r--ui/gtk.c77
-rw-r--r--ui/input-legacy.c453
-rw-r--r--ui/input.c682
-rw-r--r--ui/sdl.c114
-rw-r--r--ui/sdl2-keymap.h266
-rw-r--r--ui/sdl2.c829
-rw-r--r--ui/sdl_keysym.h3
-rw-r--r--ui/spice-input.c84
-rw-r--r--ui/vnc.c71
-rw-r--r--ui/vnc.h1
14 files changed, 2092 insertions, 691 deletions
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index f33be47576..6f2294efda 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -7,14 +7,14 @@ vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
 vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o
 vnc-obj-y += vnc-jobs.o
 
-common-obj-y += keymaps.o console.o cursor.o input.o qemu-pixman.o
+common-obj-y += keymaps.o console.o cursor.o input.o input-legacy.o qemu-pixman.o
 common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
-common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
+common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o sdl2.o
 common-obj-$(CONFIG_COCOA) += cocoa.o
 common-obj-$(CONFIG_CURSES) += curses.o
 common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
 common-obj-$(CONFIG_GTK) += gtk.o x_keymap.o
 
-$(obj)/sdl.o $(obj)/sdl_zoom.o: QEMU_CFLAGS += $(SDL_CFLAGS) 
+$(obj)/sdl.o $(obj)/sdl_zoom.o $(obj)/sdl2.o: QEMU_CFLAGS += $(SDL_CFLAGS)
 
 $(obj)/gtk.o: QEMU_CFLAGS += $(GTK_CFLAGS) $(VTE_CFLAGS)
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 866177770a..f20fd1ffa2 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -27,6 +27,7 @@
 
 #include "qemu-common.h"
 #include "ui/console.h"
+#include "ui/input.h"
 #include "sysemu/sysemu.h"
 
 #ifndef MAC_OS_X_VERSION_10_4
@@ -49,14 +50,6 @@
 #endif
 
 #define cgrect(nsrect) (*(CGRect *)&(nsrect))
-#define COCOA_MOUSE_EVENT \
-        if (isTabletEnabled) { \
-            kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \
-        } else if (isMouseGrabbed) { \
-            kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \
-        } else { \
-            [NSApp sendEvent:event]; \
-        }
 
 typedef struct {
     int width;
@@ -67,6 +60,7 @@ typedef struct {
 
 NSWindow *normalWindow;
 static DisplayChangeListener *dcl;
+static int last_buttons;
 
 int gArgc;
 char **gArgv;
@@ -501,6 +495,7 @@ QemuCocoaView *cocoaView;
 
     int buttons = 0;
     int keycode;
+    bool mouse_event = false;
     NSPoint p = [event locationInWindow];
 
     switch ([event type]) {
@@ -514,16 +509,14 @@ QemuCocoaView *cocoaView;
 
             if (keycode) {
                 if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
-                    kbd_put_keycode(keycode);
-                    kbd_put_keycode(keycode | 0x80);
+                    qemu_input_event_send_key_number(dcl->con, keycode, true);
+                    qemu_input_event_send_key_number(dcl->con, keycode, false);
                 } else if (qemu_console_is_graphic(NULL)) {
-                    if (keycode & 0x80)
-                        kbd_put_keycode(0xe0);
                     if (modifiers_state[keycode] == 0) { // keydown
-                        kbd_put_keycode(keycode & 0x7f);
+                        qemu_input_event_send_key_number(dcl->con, keycode, true);
                         modifiers_state[keycode] = 1;
                     } else { // keyup
-                        kbd_put_keycode(keycode | 0x80);
+                        qemu_input_event_send_key_number(dcl->con, keycode, false);
                         modifiers_state[keycode] = 0;
                     }
                 }
@@ -557,9 +550,7 @@ QemuCocoaView *cocoaView;
 
             // handle keys for graphic console
             } else if (qemu_console_is_graphic(NULL)) {
-                if (keycode & 0x80) //check bit for e0 in front
-                    kbd_put_keycode(0xe0);
-                kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
+                qemu_input_event_send_key_number(dcl->con, keycode, true);
 
             // handlekeys for Monitor
             } else {
@@ -607,9 +598,7 @@ QemuCocoaView *cocoaView;
             }
 
             if (qemu_console_is_graphic(NULL)) {
-                if (keycode & 0x80)
-                    kbd_put_keycode(0xe0);
-                kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
+                qemu_input_event_send_key_number(dcl->con, keycode, false);
             }
             break;
         case NSMouseMoved:
@@ -626,7 +615,7 @@ QemuCocoaView *cocoaView;
                     }
                 }
             }
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSLeftMouseDown:
             if ([event modifierFlags] & NSCommandKeyMask) {
@@ -634,15 +623,15 @@ QemuCocoaView *cocoaView;
             } else {
                 buttons |= MOUSE_EVENT_LBUTTON;
             }
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSRightMouseDown:
             buttons |= MOUSE_EVENT_RBUTTON;
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSOtherMouseDown:
             buttons |= MOUSE_EVENT_MBUTTON;
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSLeftMouseDragged:
             if ([event modifierFlags] & NSCommandKeyMask) {
@@ -650,19 +639,19 @@ QemuCocoaView *cocoaView;
             } else {
                 buttons |= MOUSE_EVENT_LBUTTON;
             }
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSRightMouseDragged:
             buttons |= MOUSE_EVENT_RBUTTON;
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSOtherMouseDragged:
             buttons |= MOUSE_EVENT_MBUTTON;
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSLeftMouseUp:
             if (isTabletEnabled) {
-                    COCOA_MOUSE_EVENT
+                    mouse_event = true;
             } else if (!isMouseGrabbed) {
                 if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) {
                     [self grabMouse];
@@ -670,18 +659,20 @@ QemuCocoaView *cocoaView;
                     [NSApp sendEvent:event];
                 }
             } else {
-                COCOA_MOUSE_EVENT
+                mouse_event = true;
             }
             break;
         case NSRightMouseUp:
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSOtherMouseUp:
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSScrollWheel:
             if (isTabletEnabled || isMouseGrabbed) {
-                kbd_mouse_event(0, 0, -[event deltaY], 0);
+                buttons |= ([event deltaY] < 0) ?
+                    MOUSE_EVENT_WHEELUP : MOUSE_EVENT_WHEELDN;
+                mouse_event = true;
             } else {
                 [NSApp sendEvent:event];
             }
@@ -689,6 +680,30 @@ QemuCocoaView *cocoaView;
         default:
             [NSApp sendEvent:event];
     }
+
+    if (mouse_event) {
+        if (last_buttons != buttons) {
+            static uint32_t bmap[INPUT_BUTTON_MAX] = {
+                [INPUT_BUTTON_LEFT]       = MOUSE_EVENT_LBUTTON,
+                [INPUT_BUTTON_MIDDLE]     = MOUSE_EVENT_MBUTTON,
+                [INPUT_BUTTON_RIGHT]      = MOUSE_EVENT_RBUTTON,
+                [INPUT_BUTTON_WHEEL_UP]   = MOUSE_EVENT_WHEELUP,
+                [INPUT_BUTTON_WHEEL_DOWN] = MOUSE_EVENT_WHEELDN,
+            };
+            qemu_input_update_buttons(dcl->con, bmap, last_buttons, buttons);
+            last_buttons = buttons;
+        }
+        if (isTabletEnabled) {
+            qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, screen.width);
+            qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, p.y, screen.height);
+        } else if (isMouseGrabbed) {
+            qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]);
+            qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]);
+        } else {
+            [NSApp sendEvent:event];
+        }
+        qemu_input_event_sync();
+    }
 }
 
 - (void) grabMouse
@@ -1023,7 +1038,7 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
 
     COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
 
-    if (kbd_mouse_is_absolute()) {
+    if (qemu_input_is_absolute()) {
         if (![cocoaView isAbsoluteEnabled]) {
             if ([cocoaView isMouseGrabbed]) {
                 [cocoaView ungrabMouse];
diff --git a/ui/console.c b/ui/console.c
index 502e1600ab..4df251d579 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -124,6 +124,8 @@ struct QemuConsole {
 
     /* Graphic console state.  */
     Object *device;
+    uint32_t head;
+    QemuUIInfo ui_info;
     const GraphicHwOps *hw_ops;
     void *hw;
 
@@ -1179,6 +1181,8 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
     s = QEMU_CONSOLE(obj);
     object_property_add_link(obj, "device", TYPE_DEVICE,
                              (Object **)&s->device, &local_err);
+    object_property_add_uint32_ptr(obj, "head",
+                                   &s->head, &local_err);
 
     if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
         (console_type == GRAPHIC_CONSOLE))) {
@@ -1344,6 +1348,16 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl)
     gui_setup_refresh(ds);
 }
 
+int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info)
+{
+    assert(con != NULL);
+    con->ui_info = *info;
+    if (con->hw_ops->ui_info) {
+        return con->hw_ops->ui_info(con->hw, con->head, info);
+    }
+    return -1;
+}
+
 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
 {
     DisplayState *s = con->ds;
@@ -1569,7 +1583,7 @@ DisplayState *init_displaystate(void)
     return display_state;
 }
 
-QemuConsole *graphic_console_init(DeviceState *dev,
+QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
                                   const GraphicHwOps *hw_ops,
                                   void *opaque)
 {
@@ -1587,6 +1601,8 @@ QemuConsole *graphic_console_init(DeviceState *dev,
     if (dev) {
         object_property_set_link(OBJECT(s), OBJECT(dev),
                                  "device", &local_err);
+        object_property_set_int(OBJECT(s), head,
+                                "head", &local_err);
     }
 
     s->surface = qemu_create_displaysurface(width, height);
@@ -1601,10 +1617,11 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index)
     return consoles[index];
 }
 
-QemuConsole *qemu_console_lookup_by_device(DeviceState *dev)
+QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
 {
     Error *local_err = NULL;
     Object *obj;
+    uint32_t h;
     int i;
 
     for (i = 0; i < nb_consoles; i++) {
@@ -1613,9 +1630,15 @@ QemuConsole *qemu_console_lookup_by_device(DeviceState *dev)
         }
         obj = object_property_get_link(OBJECT(consoles[i]),
                                        "device", &local_err);
-        if (DEVICE(obj) == dev) {
-            return consoles[i];
+        if (DEVICE(obj) != dev) {
+            continue;
         }
+        h = object_property_get_int(OBJECT(consoles[i]),
+                                    "head", &local_err);
+        if (h != head) {
+            continue;
+        }
+        return consoles[i];
     }
     return NULL;
 }
@@ -1641,6 +1664,44 @@ bool qemu_console_is_fixedsize(QemuConsole *con)
     return con && (con->console_type != TEXT_CONSOLE);
 }
 
+int qemu_console_get_index(QemuConsole *con)
+{
+    if (con == NULL) {
+        con = active_console;
+    }
+    return con ? con->index : -1;
+}
+
+uint32_t qemu_console_get_head(QemuConsole *con)
+{
+    if (con == NULL) {
+        con = active_console;
+    }
+    return con ? con->head : -1;
+}
+
+QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con)
+{
+    assert(con != NULL);
+    return &con->ui_info;
+}
+
+int qemu_console_get_width(QemuConsole *con, int fallback)
+{
+    if (con == NULL) {
+        con = active_console;
+    }
+    return con ? surface_width(con->surface) : fallback;
+}
+
+int qemu_console_get_height(QemuConsole *con, int fallback)
+{
+    if (con == NULL) {
+        con = active_console;
+    }
+    return con ? surface_height(con->surface) : fallback;
+}
+
 static void text_console_set_echo(CharDriverState *chr, bool echo)
 {
     QemuConsole *s = chr->opaque;
diff --git a/ui/curses.c b/ui/curses.c
index dbc3d5ec73..b044790e43 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -30,6 +30,7 @@
 
 #include "qemu-common.h"
 #include "ui/console.h"
+#include "ui/input.h"
 #include "sysemu/sysemu.h"
 
 #define FONT_HEIGHT 16
@@ -274,32 +275,34 @@ static void curses_refresh(DisplayChangeListener *dcl)
         if (qemu_console_is_graphic(NULL)) {
             /* since terminals don't know about key press and release
              * events, we need to emit both for each key received */
-            if (keycode & SHIFT)
-                kbd_put_keycode(SHIFT_CODE);
-            if (keycode & CNTRL)
-                kbd_put_keycode(CNTRL_CODE);
-            if (keycode & ALT)
-                kbd_put_keycode(ALT_CODE);
+            if (keycode & SHIFT) {
+                qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
+            }
+            if (keycode & CNTRL) {
+                qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
+            }
+            if (keycode & ALT) {
+                qemu_input_event_send_key_number(NULL, ALT_CODE, true);
+            }
             if (keycode & ALTGR) {
-                kbd_put_keycode(SCANCODE_EMUL0);
-                kbd_put_keycode(ALT_CODE);
+                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
             }
-            if (keycode & GREY)
-                kbd_put_keycode(GREY_CODE);
-            kbd_put_keycode(keycode & KEY_MASK);
-            if (keycode & GREY)
-                kbd_put_keycode(GREY_CODE);
-            kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE);
+
+            qemu_input_event_send_key_number(NULL, keycode, true);
+            qemu_input_event_send_key_number(NULL, keycode, false);
+
             if (keycode & ALTGR) {
-                kbd_put_keycode(SCANCODE_EMUL0);
-                kbd_put_keycode(ALT_CODE | KEY_RELEASE);
+                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
+            }
+            if (keycode & ALT) {
+                qemu_input_event_send_key_number(NULL, ALT_CODE, false);
+            }
+            if (keycode & CNTRL) {
+                qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
+            }
+            if (keycode & SHIFT) {
+                qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
             }
-            if (keycode & ALT)
-                kbd_put_keycode(ALT_CODE | KEY_RELEASE);
-            if (keycode & CNTRL)
-                kbd_put_keycode(CNTRL_CODE | KEY_RELEASE);
-            if (keycode & SHIFT)
-                kbd_put_keycode(SHIFT_CODE | KEY_RELEASE);
         } else {
             keysym = curses2qemu[chr];
             if (keysym == -1)
diff --git a/ui/gtk.c b/ui/gtk.c
index a633d89346..185149571e 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -59,6 +59,7 @@
 
 #include "trace.h"
 #include "ui/console.h"
+#include "ui/input.h"
 #include "sysemu/sysemu.h"
 #include "qmp-commands.h"
 #include "x_keymap.h"
@@ -193,7 +194,7 @@ static void gd_update_cursor(GtkDisplayState *s, gboolean override)
     on_vga = gd_on_vga(s);
 
     if ((override || on_vga) &&
-        (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) {
+        (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s))) {
         gdk_window_set_cursor(window, s->null_cursor);
     } else {
         gdk_window_set_cursor(window, NULL);
@@ -280,10 +281,7 @@ static void gtk_release_modifiers(GtkDisplayState *s)
         if (!s->modifier_pressed[i]) {
             continue;
         }
-        if (keycode & SCANCODE_GREY) {
-            kbd_put_keycode(SCANCODE_EMUL0);
-        }
-        kbd_put_keycode(keycode | SCANCODE_UP);
+        qemu_input_event_send_key_number(s->dcl.con, keycode, false);
         s->modifier_pressed[i] = false;
     }
 }
@@ -582,7 +580,6 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
                                 void *opaque)
 {
     GtkDisplayState *s = opaque;
-    int dx, dy;
     int x, y;
     int mx, my;
     int fbh, fbw;
@@ -610,25 +607,21 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
         return TRUE;
     }
 
-    if (kbd_mouse_is_absolute()) {
-        dx = x * 0x7FFF / (surface_width(s->ds) - 1);
-        dy = y * 0x7FFF / (surface_height(s->ds) - 1);
-    } else if (s->last_x == -1 || s->last_y == -1) {
-        dx = 0;
-        dy = 0;
-    } else {
-        dx = x - s->last_x;
-        dy = y - s->last_y;
+    if (qemu_input_is_absolute()) {
+        qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_X, x,
+                             surface_width(s->ds));
+        qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_Y, y,
+                             surface_height(s->ds));
+        qemu_input_event_sync();
+    } else if (s->last_x != -1 && s->last_y != -1 && gd_is_grab_active(s)) {
+        qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_X, x - s->last_x);
+        qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_Y, y - s->last_y);
+        qemu_input_event_sync();
     }
-
     s->last_x = x;
     s->last_y = y;
 
-    if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) {
-        kbd_mouse_event(dx, dy, 0, s->button_mask);
-    }
-
-    if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) {
+    if (!qemu_input_is_absolute() && gd_is_grab_active(s)) {
         GdkScreen *screen = gtk_widget_get_screen(s->drawing_area);
         int x = (int)motion->x_root;
         int y = (int)motion->y_root;
@@ -673,35 +666,20 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
                                 void *opaque)
 {
     GtkDisplayState *s = opaque;
-    int dx, dy;
-    int n;
+    InputButton btn;
 
     if (button->button == 1) {
-        n = 0x01;
+        btn = INPUT_BUTTON_LEFT;
     } else if (button->button == 2) {
-        n = 0x04;
+        btn = INPUT_BUTTON_MIDDLE;
     } else if (button->button == 3) {
-        n = 0x02;
+        btn = INPUT_BUTTON_RIGHT;
     } else {
-        n = 0x00;
-    }
-
-    if (button->type == GDK_BUTTON_PRESS) {
-        s->button_mask |= n;
-    } else if (button->type == GDK_BUTTON_RELEASE) {
-        s->button_mask &= ~n;
-    }
-
-    if (kbd_mouse_is_absolute()) {
-        dx = s->last_x * 0x7FFF / (surface_width(s->ds) - 1);
-        dy = s->last_y * 0x7FFF / (surface_height(s->ds) - 1);
-    } else {
-        dx = 0;
-        dy = 0;
+        return TRUE;
     }
 
-    kbd_mouse_event(dx, dy, 0, s->button_mask);
-        
+    qemu_input_queue_btn(s->dcl.con, btn, button->type == GDK_BUTTON_PRESS);
+    qemu_input_event_sync();
     return TRUE;
 }
 
@@ -745,17 +723,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
         }
     }
 
-    if (qemu_keycode & SCANCODE_GREY) {
-        kbd_put_keycode(SCANCODE_EMUL0);
-    }
-
-    if (key->type == GDK_KEY_PRESS) {
-        kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK);
-    } else if (key->type == GDK_KEY_RELEASE) {
-        kbd_put_keycode(qemu_keycode | SCANCODE_UP);
-    } else {
-        g_assert_not_reached();
-    }
+    qemu_input_event_send_key_number(s->dcl.con, qemu_keycode,
+                                     key->type == GDK_KEY_PRESS);
 
     return TRUE;
 }
diff --git a/ui/input-legacy.c b/ui/input-legacy.c
new file mode 100644
index 0000000000..f38984b192
--- /dev/null
+++ b/ui/input-legacy.c
@@ -0,0 +1,453 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysemu/sysemu.h"
+#include "monitor/monitor.h"
+#include "ui/console.h"
+#include "qapi/error.h"
+#include "qmp-commands.h"
+#include "qapi-types.h"
+#include "ui/keymaps.h"
+#include "ui/input.h"
+
+struct QEMUPutMouseEntry {
+    QEMUPutMouseEvent *qemu_put_mouse_event;
+    void *qemu_put_mouse_event_opaque;
+    int qemu_put_mouse_event_absolute;
+
+    /* new input core */
+    QemuInputHandler h;
+    QemuInputHandlerState *s;
+    int axis[INPUT_AXIS_MAX];
+    int buttons;
+};
+
+struct QEMUPutKbdEntry {
+    QEMUPutKBDEvent *put_kbd;
+    void *opaque;
+    QemuInputHandlerState *s;
+};
+
+struct QEMUPutLEDEntry {
+    QEMUPutLEDEvent *put_led;
+    void *opaque;
+    QTAILQ_ENTRY(QEMUPutLEDEntry) next;
+};
+
+static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers =
+    QTAILQ_HEAD_INITIALIZER(led_handlers);
+static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers =
+    QTAILQ_HEAD_INITIALIZER(mouse_handlers);
+
+static const int key_defs[] = {
+    [Q_KEY_CODE_SHIFT] = 0x2a,
+    [Q_KEY_CODE_SHIFT_R] = 0x36,
+
+    [Q_KEY_CODE_ALT] = 0x38,
+    [Q_KEY_CODE_ALT_R] = 0xb8,
+    [Q_KEY_CODE_ALTGR] = 0x64,
+    [Q_KEY_CODE_ALTGR_R] = 0xe4,
+    [Q_KEY_CODE_CTRL] = 0x1d,
+    [Q_KEY_CODE_CTRL_R] = 0x9d,
+
+    [Q_KEY_CODE_MENU] = 0xdd,
+
+    [Q_KEY_CODE_ESC] = 0x01,
+
+    [Q_KEY_CODE_1] = 0x02,
+    [Q_KEY_CODE_2] = 0x03,
+    [Q_KEY_CODE_3] = 0x04,
+    [Q_KEY_CODE_4] = 0x05,
+    [Q_KEY_CODE_5] = 0x06,
+    [Q_KEY_CODE_6] = 0x07,
+    [Q_KEY_CODE_7] = 0x08,
+    [Q_KEY_CODE_8] = 0x09,
+    [Q_KEY_CODE_9] = 0x0a,
+    [Q_KEY_CODE_0] = 0x0b,
+    [Q_KEY_CODE_MINUS] = 0x0c,
+    [Q_KEY_CODE_EQUAL] = 0x0d,
+    [Q_KEY_CODE_BACKSPACE] = 0x0e,
+
+    [Q_KEY_CODE_TAB] = 0x0f,
+    [Q_KEY_CODE_Q] = 0x10,
+    [Q_KEY_CODE_W] = 0x11,
+    [Q_KEY_CODE_E] = 0x12,
+    [Q_KEY_CODE_R] = 0x13,
+    [Q_KEY_CODE_T] = 0x14,
+    [Q_KEY_CODE_Y] = 0x15,
+    [Q_KEY_CODE_U] = 0x16,
+    [Q_KEY_CODE_I] = 0x17,
+    [Q_KEY_CODE_O] = 0x18,
+    [Q_KEY_CODE_P] = 0x19,
+    [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
+    [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
+    [Q_KEY_CODE_RET] = 0x1c,
+
+    [Q_KEY_CODE_A] = 0x1e,
+    [Q_KEY_CODE_S] = 0x1f,
+    [Q_KEY_CODE_D] = 0x20,
+    [Q_KEY_CODE_F] = 0x21,
+    [Q_KEY_CODE_G] = 0x22,
+    [Q_KEY_CODE_H] = 0x23,
+    [Q_KEY_CODE_J] = 0x24,
+    [Q_KEY_CODE_K] = 0x25,
+    [Q_KEY_CODE_L] = 0x26,
+    [Q_KEY_CODE_SEMICOLON] = 0x27,
+    [Q_KEY_CODE_APOSTROPHE] = 0x28,
+    [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
+
+    [Q_KEY_CODE_BACKSLASH] = 0x2b,
+    [Q_KEY_CODE_Z] = 0x2c,
+    [Q_KEY_CODE_X] = 0x2d,
+    [Q_KEY_CODE_C] = 0x2e,
+    [Q_KEY_CODE_V] = 0x2f,
+    [Q_KEY_CODE_B] = 0x30,
+    [Q_KEY_CODE_N] = 0x31,
+    [Q_KEY_CODE_M] = 0x32,
+    [Q_KEY_CODE_COMMA] = 0x33,
+    [Q_KEY_CODE_DOT] = 0x34,
+    [Q_KEY_CODE_SLASH] = 0x35,
+
+    [Q_KEY_CODE_ASTERISK] = 0x37,
+
+    [Q_KEY_CODE_SPC] = 0x39,
+    [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
+    [Q_KEY_CODE_F1] = 0x3b,
+    [Q_KEY_CODE_F2] = 0x3c,
+    [Q_KEY_CODE_F3] = 0x3d,
+    [Q_KEY_CODE_F4] = 0x3e,
+    [Q_KEY_CODE_F5] = 0x3f,
+    [Q_KEY_CODE_F6] = 0x40,
+    [Q_KEY_CODE_F7] = 0x41,
+    [Q_KEY_CODE_F8] = 0x42,
+    [Q_KEY_CODE_F9] = 0x43,
+    [Q_KEY_CODE_F10] = 0x44,
+    [Q_KEY_CODE_NUM_LOCK] = 0x45,
+    [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
+
+    [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
+    [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
+    [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
+    [Q_KEY_CODE_KP_ADD] = 0x4e,
+    [Q_KEY_CODE_KP_ENTER] = 0x9c,
+    [Q_KEY_CODE_KP_DECIMAL] = 0x53,
+    [Q_KEY_CODE_SYSRQ] = 0x54,
+
+    [Q_KEY_CODE_KP_0] = 0x52,
+    [Q_KEY_CODE_KP_1] = 0x4f,
+    [Q_KEY_CODE_KP_2] = 0x50,
+    [Q_KEY_CODE_KP_3] = 0x51,
+    [Q_KEY_CODE_KP_4] = 0x4b,
+    [Q_KEY_CODE_KP_5] = 0x4c,
+    [Q_KEY_CODE_KP_6] = 0x4d,
+    [Q_KEY_CODE_KP_7] = 0x47,
+    [Q_KEY_CODE_KP_8] = 0x48,
+    [Q_KEY_CODE_KP_9] = 0x49,
+
+    [Q_KEY_CODE_LESS] = 0x56,
+
+    [Q_KEY_CODE_F11] = 0x57,
+    [Q_KEY_CODE_F12] = 0x58,
+
+    [Q_KEY_CODE_PRINT] = 0xb7,
+
+    [Q_KEY_CODE_HOME] = 0xc7,
+    [Q_KEY_CODE_PGUP] = 0xc9,
+    [Q_KEY_CODE_PGDN] = 0xd1,
+    [Q_KEY_CODE_END] = 0xcf,
+
+    [Q_KEY_CODE_LEFT] = 0xcb,
+    [Q_KEY_CODE_UP] = 0xc8,
+    [Q_KEY_CODE_DOWN] = 0xd0,
+    [Q_KEY_CODE_RIGHT] = 0xcd,
+
+    [Q_KEY_CODE_INSERT] = 0xd2,
+    [Q_KEY_CODE_DELETE] = 0xd3,
+#ifdef NEED_CPU_H
+#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
+    [Q_KEY_CODE_STOP] = 0xf0,
+    [Q_KEY_CODE_AGAIN] = 0xf1,
+    [Q_KEY_CODE_PROPS] = 0xf2,
+    [Q_KEY_CODE_UNDO] = 0xf3,
+    [Q_KEY_CODE_FRONT] = 0xf4,
+    [Q_KEY_CODE_COPY] = 0xf5,
+    [Q_KEY_CODE_OPEN] = 0xf6,
+    [Q_KEY_CODE_PASTE] = 0xf7,
+    [Q_KEY_CODE_FIND] = 0xf8,
+    [Q_KEY_CODE_CUT] = 0xf9,
+    [Q_KEY_CODE_LF] = 0xfa,
+    [Q_KEY_CODE_HELP] = 0xfb,
+    [Q_KEY_CODE_META_L] = 0xfc,
+    [Q_KEY_CODE_META_R] = 0xfd,
+    [Q_KEY_CODE_COMPOSE] = 0xfe,
+#endif
+#endif
+    [Q_KEY_CODE_MAX] = 0,
+};
+
+int index_from_key(const char *key)
+{
+    int i;
+
+    for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
+        if (!strcmp(key, QKeyCode_lookup[i])) {
+            break;
+        }
+    }
+
+    /* Return Q_KEY_CODE_MAX if the key is invalid */
+    return i;
+}
+
+static int *keycodes;
+static int keycodes_size;
+static QEMUTimer *key_timer;
+
+static int keycode_from_keyvalue(const KeyValue *value)
+{
+    if (value->kind == KEY_VALUE_KIND_QCODE) {
+        return key_defs[value->qcode];
+    } else {
+        assert(value->kind == KEY_VALUE_KIND_NUMBER);
+        return value->number;
+    }
+}
+
+static void free_keycodes(void)
+{
+    g_free(keycodes);
+    keycodes = NULL;
+    keycodes_size = 0;
+}
+
+static void release_keys(void *opaque)
+{
+    while (keycodes_size > 0) {
+        qemu_input_event_send_key_number(NULL, keycodes[--keycodes_size],
+                                         false);
+    }
+
+    free_keycodes();
+}
+
+void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time,
+                  Error **errp)
+{
+    int keycode;
+    KeyValueList *p;
+
+    if (!key_timer) {
+        key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL);
+    }
+
+    if (keycodes != NULL) {
+        timer_del(key_timer);
+        release_keys(NULL);
+    }
+
+    if (!has_hold_time) {
+        hold_time = 100;
+    }
+
+    for (p = keys; p != NULL; p = p->next) {
+        /* key down events */
+        keycode = keycode_from_keyvalue(p->value);
+        if (keycode < 0x01 || keycode > 0xff) {
+            error_setg(errp, "invalid hex keycode 0x%x", keycode);
+            free_keycodes();
+            return;
+        }
+
+        qemu_input_event_send_key_number(NULL, keycode, true);
+
+        keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1));
+        keycodes[keycodes_size++] = keycode;
+    }
+
+    /* delayed key up events */
+    timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+                   muldiv64(get_ticks_per_sec(), hold_time, 1000));
+}
+
+static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
+                             InputEvent *evt)
+{
+    QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev;
+    int keycode = keycode_from_keyvalue(evt->key->key);
+
+    if (!entry || !entry->put_kbd) {
+        return;
+    }
+    if (evt->key->key->kind == KEY_VALUE_KIND_QCODE &&
+        evt->key->key->qcode == Q_KEY_CODE_PAUSE) {
+        /* specific case */
+        int v = evt->key->down ? 0 : 0x80;
+        entry->put_kbd(entry->opaque, 0xe1);
+        entry->put_kbd(entry->opaque, 0x1d | v);
+        entry->put_kbd(entry->opaque, 0x45 | v);
+        return;
+    }
+    if (keycode & SCANCODE_GREY) {
+        entry->put_kbd(entry->opaque, SCANCODE_EMUL0);
+        keycode &= ~SCANCODE_GREY;
+    }
+    if (!evt->key->down) {
+        keycode |= SCANCODE_UP;
+    }
+    entry->put_kbd(entry->opaque, keycode);
+}
+
+static QemuInputHandler legacy_kbd_handler = {
+    .name  = "legacy-kbd",
+    .mask  = INPUT_EVENT_MASK_KEY,
+    .event = legacy_kbd_event,
+};
+
+QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
+{
+    QEMUPutKbdEntry *entry;
+
+    entry = g_new0(QEMUPutKbdEntry, 1);
+    entry->put_kbd = func;
+    entry->opaque = opaque;
+    entry->s = qemu_input_handler_register((DeviceState *)entry,
+                                           &legacy_kbd_handler);
+    return entry;
+}
+
+void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry)
+{
+    qemu_input_handler_unregister(entry->s);
+    g_free(entry);
+}
+
+static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
+                               InputEvent *evt)
+{
+    static const int bmap[INPUT_BUTTON_MAX] = {
+        [INPUT_BUTTON_LEFT]   = MOUSE_EVENT_LBUTTON,
+        [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
+        [INPUT_BUTTON_RIGHT]  = MOUSE_EVENT_RBUTTON,
+    };
+    QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev;
+
+    switch (evt->kind) {
+    case INPUT_EVENT_KIND_BTN:
+        if (evt->btn->down) {
+            s->buttons |= bmap[evt->btn->button];
+        } else {
+            s->buttons &= ~bmap[evt->btn->button];
+        }
+        break;
+    case INPUT_EVENT_KIND_ABS:
+        s->axis[evt->abs->axis] = evt->abs->value;
+        break;
+    case INPUT_EVENT_KIND_REL:
+        s->axis[evt->rel->axis] += evt->rel->value;
+        break;
+    default:
+        break;
+    }
+}
+
+static void legacy_mouse_sync(DeviceState *dev)
+{
+    QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev;
+
+    s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
+                            s->axis[INPUT_AXIS_X],
+                            s->axis[INPUT_AXIS_Y],
+                            0,
+                            s->buttons);
+
+    if (!s->qemu_put_mouse_event_absolute) {
+        s->axis[INPUT_AXIS_X] = 0;
+        s->axis[INPUT_AXIS_Y] = 0;
+    }
+}
+
+QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
+                                                void *opaque, int absolute,
+                                                const char *name)
+{
+    QEMUPutMouseEntry *s;
+
+    s = g_malloc0(sizeof(QEMUPutMouseEntry));
+
+    s->qemu_put_mouse_event = func;
+    s->qemu_put_mouse_event_opaque = opaque;
+    s->qemu_put_mouse_event_absolute = absolute;
+
+    s->h.name = name;
+    s->h.mask = INPUT_EVENT_MASK_BTN |
+        (absolute ? INPUT_EVENT_MASK_ABS : INPUT_EVENT_MASK_REL);
+    s->h.event = legacy_mouse_event;
+    s->h.sync = legacy_mouse_sync;
+    s->s = qemu_input_handler_register((DeviceState *)s,
+                                       &s->h);
+
+    return s;
+}
+
+void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry)
+{
+    qemu_input_handler_activate(entry->s);
+}
+
+void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry)
+{
+    qemu_input_handler_unregister(entry->s);
+
+    g_free(entry);
+}
+
+QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func,
+                                            void *opaque)
+{
+    QEMUPutLEDEntry *s;
+
+    s = g_malloc0(sizeof(QEMUPutLEDEntry));
+
+    s->put_led = func;
+    s->opaque = opaque;
+    QTAILQ_INSERT_TAIL(&led_handlers, s, next);
+    return s;
+}
+
+void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry)
+{
+    if (entry == NULL)
+        return;
+    QTAILQ_REMOVE(&led_handlers, entry, next);
+    g_free(entry);
+}
+
+void kbd_put_ledstate(int ledstate)
+{
+    QEMUPutLEDEntry *cursor;
+
+    QTAILQ_FOREACH(cursor, &led_handlers, next) {
+        cursor->put_led(cursor->opaque, ledstate);
+    }
+}
diff --git a/ui/input.c b/ui/input.c
index 1c70f60e0d..2761911f3c 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -1,520 +1,333 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
 #include "sysemu/sysemu.h"
-#include "monitor/monitor.h"
-#include "ui/console.h"
-#include "qapi/error.h"
-#include "qmp-commands.h"
 #include "qapi-types.h"
-#include "ui/keymaps.h"
-
-struct QEMUPutMouseEntry {
-    QEMUPutMouseEvent *qemu_put_mouse_event;
-    void *qemu_put_mouse_event_opaque;
-    int qemu_put_mouse_event_absolute;
-    char *qemu_put_mouse_event_name;
-
-    int index;
-
-    /* used internally by qemu for handling mice */
-    QTAILQ_ENTRY(QEMUPutMouseEntry) node;
-};
-
-struct QEMUPutKbdEntry {
-    QEMUPutKBDEvent *put_kbd;
-    void *opaque;
-    QTAILQ_ENTRY(QEMUPutKbdEntry) next;
-};
+#include "qmp-commands.h"
+#include "trace.h"
+#include "ui/input.h"
+#include "ui/console.h"
 
-struct QEMUPutLEDEntry {
-    QEMUPutLEDEvent *put_led;
-    void *opaque;
-    QTAILQ_ENTRY(QEMUPutLEDEntry) next;
+struct QemuInputHandlerState {
+    DeviceState       *dev;
+    QemuInputHandler  *handler;
+    int               id;
+    int               events;
+    QTAILQ_ENTRY(QemuInputHandlerState) node;
 };
-
-static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers =
-    QTAILQ_HEAD_INITIALIZER(led_handlers);
-static QTAILQ_HEAD(, QEMUPutKbdEntry) kbd_handlers =
-    QTAILQ_HEAD_INITIALIZER(kbd_handlers);
-static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers =
-    QTAILQ_HEAD_INITIALIZER(mouse_handlers);
+static QTAILQ_HEAD(, QemuInputHandlerState) handlers =
+    QTAILQ_HEAD_INITIALIZER(handlers);
 static NotifierList mouse_mode_notifiers =
     NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
 
-static const int key_defs[] = {
-    [Q_KEY_CODE_SHIFT] = 0x2a,
-    [Q_KEY_CODE_SHIFT_R] = 0x36,
-
-    [Q_KEY_CODE_ALT] = 0x38,
-    [Q_KEY_CODE_ALT_R] = 0xb8,
-    [Q_KEY_CODE_ALTGR] = 0x64,
-    [Q_KEY_CODE_ALTGR_R] = 0xe4,
-    [Q_KEY_CODE_CTRL] = 0x1d,
-    [Q_KEY_CODE_CTRL_R] = 0x9d,
-
-    [Q_KEY_CODE_MENU] = 0xdd,
-
-    [Q_KEY_CODE_ESC] = 0x01,
-
-    [Q_KEY_CODE_1] = 0x02,
-    [Q_KEY_CODE_2] = 0x03,
-    [Q_KEY_CODE_3] = 0x04,
-    [Q_KEY_CODE_4] = 0x05,
-    [Q_KEY_CODE_5] = 0x06,
-    [Q_KEY_CODE_6] = 0x07,
-    [Q_KEY_CODE_7] = 0x08,
-    [Q_KEY_CODE_8] = 0x09,
-    [Q_KEY_CODE_9] = 0x0a,
-    [Q_KEY_CODE_0] = 0x0b,
-    [Q_KEY_CODE_MINUS] = 0x0c,
-    [Q_KEY_CODE_EQUAL] = 0x0d,
-    [Q_KEY_CODE_BACKSPACE] = 0x0e,
-
-    [Q_KEY_CODE_TAB] = 0x0f,
-    [Q_KEY_CODE_Q] = 0x10,
-    [Q_KEY_CODE_W] = 0x11,
-    [Q_KEY_CODE_E] = 0x12,
-    [Q_KEY_CODE_R] = 0x13,
-    [Q_KEY_CODE_T] = 0x14,
-    [Q_KEY_CODE_Y] = 0x15,
-    [Q_KEY_CODE_U] = 0x16,
-    [Q_KEY_CODE_I] = 0x17,
-    [Q_KEY_CODE_O] = 0x18,
-    [Q_KEY_CODE_P] = 0x19,
-    [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
-    [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
-    [Q_KEY_CODE_RET] = 0x1c,
-
-    [Q_KEY_CODE_A] = 0x1e,
-    [Q_KEY_CODE_S] = 0x1f,
-    [Q_KEY_CODE_D] = 0x20,
-    [Q_KEY_CODE_F] = 0x21,
-    [Q_KEY_CODE_G] = 0x22,
-    [Q_KEY_CODE_H] = 0x23,
-    [Q_KEY_CODE_J] = 0x24,
-    [Q_KEY_CODE_K] = 0x25,
-    [Q_KEY_CODE_L] = 0x26,
-    [Q_KEY_CODE_SEMICOLON] = 0x27,
-    [Q_KEY_CODE_APOSTROPHE] = 0x28,
-    [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
-
-    [Q_KEY_CODE_BACKSLASH] = 0x2b,
-    [Q_KEY_CODE_Z] = 0x2c,
-    [Q_KEY_CODE_X] = 0x2d,
-    [Q_KEY_CODE_C] = 0x2e,
-    [Q_KEY_CODE_V] = 0x2f,
-    [Q_KEY_CODE_B] = 0x30,
-    [Q_KEY_CODE_N] = 0x31,
-    [Q_KEY_CODE_M] = 0x32,
-    [Q_KEY_CODE_COMMA] = 0x33,
-    [Q_KEY_CODE_DOT] = 0x34,
-    [Q_KEY_CODE_SLASH] = 0x35,
-
-    [Q_KEY_CODE_ASTERISK] = 0x37,
-
-    [Q_KEY_CODE_SPC] = 0x39,
-    [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
-    [Q_KEY_CODE_F1] = 0x3b,
-    [Q_KEY_CODE_F2] = 0x3c,
-    [Q_KEY_CODE_F3] = 0x3d,
-    [Q_KEY_CODE_F4] = 0x3e,
-    [Q_KEY_CODE_F5] = 0x3f,
-    [Q_KEY_CODE_F6] = 0x40,
-    [Q_KEY_CODE_F7] = 0x41,
-    [Q_KEY_CODE_F8] = 0x42,
-    [Q_KEY_CODE_F9] = 0x43,
-    [Q_KEY_CODE_F10] = 0x44,
-    [Q_KEY_CODE_NUM_LOCK] = 0x45,
-    [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
-
-    [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
-    [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
-    [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
-    [Q_KEY_CODE_KP_ADD] = 0x4e,
-    [Q_KEY_CODE_KP_ENTER] = 0x9c,
-    [Q_KEY_CODE_KP_DECIMAL] = 0x53,
-    [Q_KEY_CODE_SYSRQ] = 0x54,
-
-    [Q_KEY_CODE_KP_0] = 0x52,
-    [Q_KEY_CODE_KP_1] = 0x4f,
-    [Q_KEY_CODE_KP_2] = 0x50,
-    [Q_KEY_CODE_KP_3] = 0x51,
-    [Q_KEY_CODE_KP_4] = 0x4b,
-    [Q_KEY_CODE_KP_5] = 0x4c,
-    [Q_KEY_CODE_KP_6] = 0x4d,
-    [Q_KEY_CODE_KP_7] = 0x47,
-    [Q_KEY_CODE_KP_8] = 0x48,
-    [Q_KEY_CODE_KP_9] = 0x49,
-
-    [Q_KEY_CODE_LESS] = 0x56,
-
-    [Q_KEY_CODE_F11] = 0x57,
-    [Q_KEY_CODE_F12] = 0x58,
-
-    [Q_KEY_CODE_PRINT] = 0xb7,
-
-    [Q_KEY_CODE_HOME] = 0xc7,
-    [Q_KEY_CODE_PGUP] = 0xc9,
-    [Q_KEY_CODE_PGDN] = 0xd1,
-    [Q_KEY_CODE_END] = 0xcf,
-
-    [Q_KEY_CODE_LEFT] = 0xcb,
-    [Q_KEY_CODE_UP] = 0xc8,
-    [Q_KEY_CODE_DOWN] = 0xd0,
-    [Q_KEY_CODE_RIGHT] = 0xcd,
-
-    [Q_KEY_CODE_INSERT] = 0xd2,
-    [Q_KEY_CODE_DELETE] = 0xd3,
-#ifdef NEED_CPU_H
-#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
-    [Q_KEY_CODE_STOP] = 0xf0,
-    [Q_KEY_CODE_AGAIN] = 0xf1,
-    [Q_KEY_CODE_PROPS] = 0xf2,
-    [Q_KEY_CODE_UNDO] = 0xf3,
-    [Q_KEY_CODE_FRONT] = 0xf4,
-    [Q_KEY_CODE_COPY] = 0xf5,
-    [Q_KEY_CODE_OPEN] = 0xf6,
-    [Q_KEY_CODE_PASTE] = 0xf7,
-    [Q_KEY_CODE_FIND] = 0xf8,
-    [Q_KEY_CODE_CUT] = 0xf9,
-    [Q_KEY_CODE_LF] = 0xfa,
-    [Q_KEY_CODE_HELP] = 0xfb,
-    [Q_KEY_CODE_META_L] = 0xfc,
-    [Q_KEY_CODE_META_R] = 0xfd,
-    [Q_KEY_CODE_COMPOSE] = 0xfe,
-#endif
-#endif
-    [Q_KEY_CODE_MAX] = 0,
-};
-
-int index_from_key(const char *key)
+QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev,
+                                                   QemuInputHandler *handler)
 {
-    int i;
+    QemuInputHandlerState *s = g_new0(QemuInputHandlerState, 1);
+    static int id = 1;
 
-    for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
-        if (!strcmp(key, QKeyCode_lookup[i])) {
-            break;
-        }
-    }
+    s->dev = dev;
+    s->handler = handler;
+    s->id = id++;
+    QTAILQ_INSERT_TAIL(&handlers, s, node);
 
-    /* Return Q_KEY_CODE_MAX if the key is invalid */
-    return i;
+    qemu_input_check_mode_change();
+    return s;
 }
 
-int index_from_keycode(int code)
+void qemu_input_handler_activate(QemuInputHandlerState *s)
 {
-    int i;
-
-    for (i = 0; i < Q_KEY_CODE_MAX; i++) {
-        if (key_defs[i] == code) {
-            break;
-        }
-    }
-
-    /* Return Q_KEY_CODE_MAX if the code is invalid */
-    return i;
+    QTAILQ_REMOVE(&handlers, s, node);
+    QTAILQ_INSERT_HEAD(&handlers, s, node);
+    qemu_input_check_mode_change();
 }
 
-static int *keycodes;
-static int keycodes_size;
-static QEMUTimer *key_timer;
-
-static int keycode_from_keyvalue(const KeyValue *value)
+void qemu_input_handler_unregister(QemuInputHandlerState *s)
 {
-    if (value->kind == KEY_VALUE_KIND_QCODE) {
-        return key_defs[value->qcode];
-    } else {
-        assert(value->kind == KEY_VALUE_KIND_NUMBER);
-        return value->number;
-    }
+    QTAILQ_REMOVE(&handlers, s, node);
+    g_free(s);
+    qemu_input_check_mode_change();
 }
 
-static void free_keycodes(void)
+static QemuInputHandlerState*
+qemu_input_find_handler(uint32_t mask)
 {
-    g_free(keycodes);
-    keycodes = NULL;
-    keycodes_size = 0;
-}
+    QemuInputHandlerState *s;
 
-static void release_keys(void *opaque)
-{
-    while (keycodes_size > 0) {
-        if (keycodes[--keycodes_size] & SCANCODE_GREY) {
-            kbd_put_keycode(SCANCODE_EMUL0);
+    QTAILQ_FOREACH(s, &handlers, node) {
+        if (mask & s->handler->mask) {
+            return s;
         }
-        kbd_put_keycode(keycodes[keycodes_size] | SCANCODE_UP);
     }
-
-    free_keycodes();
+    return NULL;
 }
 
-void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time,
-                  Error **errp)
+static void qemu_input_transform_abs_rotate(InputEvent *evt)
 {
-    int keycode;
-    KeyValueList *p;
-
-    if (!key_timer) {
-        key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL);
-    }
-
-    if (keycodes != NULL) {
-        timer_del(key_timer);
-        release_keys(NULL);
-    }
-
-    if (!has_hold_time) {
-        hold_time = 100;
-    }
-
-    for (p = keys; p != NULL; p = p->next) {
-        /* key down events */
-        keycode = keycode_from_keyvalue(p->value);
-        if (keycode < 0x01 || keycode > 0xff) {
-            error_setg(errp, "invalid hex keycode 0x%x", keycode);
-            free_keycodes();
-            return;
+    switch (graphic_rotate) {
+    case 90:
+        if (evt->abs->axis == INPUT_AXIS_X) {
+            evt->abs->axis = INPUT_AXIS_Y;
+        } else if (evt->abs->axis == INPUT_AXIS_Y) {
+            evt->abs->axis = INPUT_AXIS_X;
+            evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
         }
-
-        if (keycode & SCANCODE_GREY) {
-            kbd_put_keycode(SCANCODE_EMUL0);
+        break;
+    case 180:
+        evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
+        break;
+    case 270:
+        if (evt->abs->axis == INPUT_AXIS_X) {
+            evt->abs->axis = INPUT_AXIS_Y;
+            evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
+        } else if (evt->abs->axis == INPUT_AXIS_Y) {
+            evt->abs->axis = INPUT_AXIS_X;
         }
-        kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
-
-        keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1));
-        keycodes[keycodes_size++] = keycode;
+        break;
     }
-
-    /* delayed key up events */
-    timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
-                   muldiv64(get_ticks_per_sec(), hold_time, 1000));
 }
 
-QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
+static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
 {
-    QEMUPutKbdEntry *entry;
+    const char *name;
+    int idx = -1;
 
-    entry = g_malloc0(sizeof(QEMUPutKbdEntry));
-    entry->put_kbd = func;
-    entry->opaque = opaque;
-    QTAILQ_INSERT_HEAD(&kbd_handlers, entry, next);
-    return entry;
+    if (src) {
+        idx = qemu_console_get_index(src);
+    }
+    switch (evt->kind) {
+    case INPUT_EVENT_KIND_KEY:
+        switch (evt->key->key->kind) {
+        case KEY_VALUE_KIND_NUMBER:
+            trace_input_event_key_number(idx, evt->key->key->number,
+                                         evt->key->down);
+            break;
+        case KEY_VALUE_KIND_QCODE:
+            name = QKeyCode_lookup[evt->key->key->qcode];
+            trace_input_event_key_qcode(idx, name, evt->key->down);
+            break;
+        case KEY_VALUE_KIND_MAX:
+            /* keep gcc happy */
+            break;
+        }
+        break;
+    case INPUT_EVENT_KIND_BTN:
+        name = InputButton_lookup[evt->btn->button];
+        trace_input_event_btn(idx, name, evt->btn->down);
+        break;
+    case INPUT_EVENT_KIND_REL:
+        name = InputAxis_lookup[evt->rel->axis];
+        trace_input_event_rel(idx, name, evt->rel->value);
+        break;
+    case INPUT_EVENT_KIND_ABS:
+        name = InputAxis_lookup[evt->abs->axis];
+        trace_input_event_abs(idx, name, evt->abs->value);
+        break;
+    case INPUT_EVENT_KIND_MAX:
+        /* keep gcc happy */
+        break;
+    }
 }
 
-void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry)
+void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
 {
-    QTAILQ_REMOVE(&kbd_handlers, entry, next);
-}
+    QemuInputHandlerState *s;
 
-static void check_mode_change(void)
-{
-    static int current_is_absolute, current_has_absolute;
-    int is_absolute;
-    int has_absolute;
+    if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
+        return;
+    }
 
-    is_absolute = kbd_mouse_is_absolute();
-    has_absolute = kbd_mouse_has_absolute();
+    qemu_input_event_trace(src, evt);
 
-    if (is_absolute != current_is_absolute ||
-        has_absolute != current_has_absolute) {
-        notifier_list_notify(&mouse_mode_notifiers, NULL);
+    /* pre processing */
+    if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) {
+            qemu_input_transform_abs_rotate(evt);
     }
 
-    current_is_absolute = is_absolute;
-    current_has_absolute = has_absolute;
+    /* send event */
+    s = qemu_input_find_handler(1 << evt->kind);
+    s->handler->event(s->dev, src, evt);
+    s->events++;
 }
 
-QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
-                                                void *opaque, int absolute,
-                                                const char *name)
+void qemu_input_event_sync(void)
 {
-    QEMUPutMouseEntry *s;
-    static int mouse_index = 0;
-
-    s = g_malloc0(sizeof(QEMUPutMouseEntry));
+    QemuInputHandlerState *s;
 
-    s->qemu_put_mouse_event = func;
-    s->qemu_put_mouse_event_opaque = opaque;
-    s->qemu_put_mouse_event_absolute = absolute;
-    s->qemu_put_mouse_event_name = g_strdup(name);
-    s->index = mouse_index++;
-
-    QTAILQ_INSERT_TAIL(&mouse_handlers, s, node);
+    if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
+        return;
+    }
 
-    check_mode_change();
+    trace_input_event_sync();
 
-    return s;
+    QTAILQ_FOREACH(s, &handlers, node) {
+        if (!s->events) {
+            continue;
+        }
+        if (s->handler->sync) {
+            s->handler->sync(s->dev);
+        }
+        s->events = 0;
+    }
 }
 
-void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry)
+InputEvent *qemu_input_event_new_key(KeyValue *key, bool down)
 {
-    QTAILQ_REMOVE(&mouse_handlers, entry, node);
-    QTAILQ_INSERT_HEAD(&mouse_handlers, entry, node);
-
-    check_mode_change();
+    InputEvent *evt = g_new0(InputEvent, 1);
+    evt->key = g_new0(InputKeyEvent, 1);
+    evt->kind = INPUT_EVENT_KIND_KEY;
+    evt->key->key = key;
+    evt->key->down = down;
+    return evt;
 }
 
-void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry)
+void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down)
 {
-    QTAILQ_REMOVE(&mouse_handlers, entry, node);
-
-    g_free(entry->qemu_put_mouse_event_name);
-    g_free(entry);
-
-    check_mode_change();
+    InputEvent *evt;
+    evt = qemu_input_event_new_key(key, down);
+    qemu_input_event_send(src, evt);
+    qemu_input_event_sync();
+    qapi_free_InputEvent(evt);
 }
 
-QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func,
-                                            void *opaque)
+void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down)
 {
-    QEMUPutLEDEntry *s;
+    KeyValue *key = g_new0(KeyValue, 1);
+    key->kind = KEY_VALUE_KIND_NUMBER;
+    key->number = num;
+    qemu_input_event_send_key(src, key, down);
+}
 
-    s = g_malloc0(sizeof(QEMUPutLEDEntry));
+void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down)
+{
+    KeyValue *key = g_new0(KeyValue, 1);
+    key->kind = KEY_VALUE_KIND_QCODE;
+    key->qcode = q;
+    qemu_input_event_send_key(src, key, down);
+}
 
-    s->put_led = func;
-    s->opaque = opaque;
-    QTAILQ_INSERT_TAIL(&led_handlers, s, next);
-    return s;
+InputEvent *qemu_input_event_new_btn(InputButton btn, bool down)
+{
+    InputEvent *evt = g_new0(InputEvent, 1);
+    evt->btn = g_new0(InputBtnEvent, 1);
+    evt->kind = INPUT_EVENT_KIND_BTN;
+    evt->btn->button = btn;
+    evt->btn->down = down;
+    return evt;
 }
 
-void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry)
+void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down)
 {
-    if (entry == NULL)
-        return;
-    QTAILQ_REMOVE(&led_handlers, entry, next);
-    g_free(entry);
+    InputEvent *evt;
+    evt = qemu_input_event_new_btn(btn, down);
+    qemu_input_event_send(src, evt);
+    qapi_free_InputEvent(evt);
 }
 
-void kbd_put_keycode(int keycode)
+void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map,
+                               uint32_t button_old, uint32_t button_new)
 {
-    QEMUPutKbdEntry *entry = QTAILQ_FIRST(&kbd_handlers);
+    InputButton btn;
+    uint32_t mask;
 
-    if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
-        return;
-    }
-    if (entry && entry->put_kbd) {
-        entry->put_kbd(entry->opaque, keycode);
+    for (btn = 0; btn < INPUT_BUTTON_MAX; btn++) {
+        mask = button_map[btn];
+        if ((button_old & mask) == (button_new & mask)) {
+            continue;
+        }
+        qemu_input_queue_btn(src, btn, button_new & mask);
     }
 }
 
-void kbd_put_ledstate(int ledstate)
+bool qemu_input_is_absolute(void)
 {
-    QEMUPutLEDEntry *cursor;
+    QemuInputHandlerState *s;
 
-    QTAILQ_FOREACH(cursor, &led_handlers, next) {
-        cursor->put_led(cursor->opaque, ledstate);
-    }
+    s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS);
+    return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS);
 }
 
-void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
+int qemu_input_scale_axis(int value, int size_in, int size_out)
 {
-    QEMUPutMouseEntry *entry;
-    QEMUPutMouseEvent *mouse_event;
-    void *mouse_event_opaque;
-    int width, height;
-
-    if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
-        return;
+    if (size_in < 2) {
+        return size_out / 2;
     }
-    if (QTAILQ_EMPTY(&mouse_handlers)) {
-        return;
-    }
-
-    entry = QTAILQ_FIRST(&mouse_handlers);
+    return (int64_t)value * (size_out - 1) / (size_in - 1);
+}
 
-    mouse_event = entry->qemu_put_mouse_event;
-    mouse_event_opaque = entry->qemu_put_mouse_event_opaque;
+InputEvent *qemu_input_event_new_move(InputEventKind kind,
+                                      InputAxis axis, int value)
+{
+    InputEvent *evt = g_new0(InputEvent, 1);
+    InputMoveEvent *move = g_new0(InputMoveEvent, 1);
+
+    evt->kind = kind;
+    evt->data = move;
+    move->axis = axis;
+    move->value = value;
+    return evt;
+}
 
-    if (mouse_event) {
-        if (entry->qemu_put_mouse_event_absolute) {
-            width = 0x7fff;
-            height = 0x7fff;
-        } else {
-            width = graphic_width - 1;
-            height = graphic_height - 1;
-        }
+void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value)
+{
+    InputEvent *evt;
+    evt = qemu_input_event_new_move(INPUT_EVENT_KIND_REL, axis, value);
+    qemu_input_event_send(src, evt);
+    qapi_free_InputEvent(evt);
+}
 
-        switch (graphic_rotate) {
-        case 0:
-            mouse_event(mouse_event_opaque,
-                        dx, dy, dz, buttons_state);
-            break;
-        case 90:
-            mouse_event(mouse_event_opaque,
-                        width - dy, dx, dz, buttons_state);
-            break;
-        case 180:
-            mouse_event(mouse_event_opaque,
-                        width - dx, height - dy, dz, buttons_state);
-            break;
-        case 270:
-            mouse_event(mouse_event_opaque,
-                        dy, height - dx, dz, buttons_state);
-            break;
-        }
-    }
+void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int size)
+{
+    InputEvent *evt;
+    int scaled = qemu_input_scale_axis(value, size, INPUT_EVENT_ABS_SIZE);
+    evt = qemu_input_event_new_move(INPUT_EVENT_KIND_ABS, axis, scaled);
+    qemu_input_event_send(src, evt);
+    qapi_free_InputEvent(evt);
 }
 
-int kbd_mouse_is_absolute(void)
+void qemu_input_check_mode_change(void)
 {
-    if (QTAILQ_EMPTY(&mouse_handlers)) {
-        return 0;
+    static int current_is_absolute;
+    int is_absolute;
+
+    is_absolute = qemu_input_is_absolute();
+
+    if (is_absolute != current_is_absolute) {
+        trace_input_mouse_mode(is_absolute);
+        notifier_list_notify(&mouse_mode_notifiers, NULL);
     }
 
-    return QTAILQ_FIRST(&mouse_handlers)->qemu_put_mouse_event_absolute;
+    current_is_absolute = is_absolute;
 }
 
-int kbd_mouse_has_absolute(void)
+void qemu_add_mouse_mode_change_notifier(Notifier *notify)
 {
-    QEMUPutMouseEntry *entry;
-
-    QTAILQ_FOREACH(entry, &mouse_handlers, node) {
-        if (entry->qemu_put_mouse_event_absolute) {
-            return 1;
-        }
-    }
+    notifier_list_add(&mouse_mode_notifiers, notify);
+}
 
-    return 0;
+void qemu_remove_mouse_mode_change_notifier(Notifier *notify)
+{
+    notifier_remove(notify);
 }
 
 MouseInfoList *qmp_query_mice(Error **errp)
 {
     MouseInfoList *mice_list = NULL;
-    QEMUPutMouseEntry *cursor;
+    MouseInfoList *info;
+    QemuInputHandlerState *s;
     bool current = true;
 
-    QTAILQ_FOREACH(cursor, &mouse_handlers, node) {
-        MouseInfoList *info = g_malloc0(sizeof(*info));
-        info->value = g_malloc0(sizeof(*info->value));
-        info->value->name = g_strdup(cursor->qemu_put_mouse_event_name);
-        info->value->index = cursor->index;
-        info->value->absolute = !!cursor->qemu_put_mouse_event_absolute;
+    QTAILQ_FOREACH(s, &handlers, node) {
+        if (!(s->handler->mask &
+              (INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS))) {
+            continue;
+        }
+
+        info = g_new0(MouseInfoList, 1);
+        info->value = g_new0(MouseInfo, 1);
+        info->value->index = s->id;
+        info->value->name = g_strdup(s->handler->name);
+        info->value->absolute = s->handler->mask & INPUT_EVENT_MASK_ABS;
         info->value->current = current;
 
         current = false;
-
         info->next = mice_list;
         mice_list = info;
     }
@@ -524,19 +337,14 @@ MouseInfoList *qmp_query_mice(Error **errp)
 
 void do_mouse_set(Monitor *mon, const QDict *qdict)
 {
-    QEMUPutMouseEntry *cursor;
+    QemuInputHandlerState *s;
     int index = qdict_get_int(qdict, "index");
     int found = 0;
 
-    if (QTAILQ_EMPTY(&mouse_handlers)) {
-        monitor_printf(mon, "No mouse devices connected\n");
-        return;
-    }
-
-    QTAILQ_FOREACH(cursor, &mouse_handlers, node) {
-        if (cursor->index == index) {
+    QTAILQ_FOREACH(s, &handlers, node) {
+        if (s->id == index) {
             found = 1;
-            qemu_activate_mouse_event_handler(cursor);
+            qemu_input_handler_activate(s);
             break;
         }
     }
@@ -545,15 +353,5 @@ void do_mouse_set(Monitor *mon, const QDict *qdict)
         monitor_printf(mon, "Mouse at given index not found\n");
     }
 
-    check_mode_change();
-}
-
-void qemu_add_mouse_mode_change_notifier(Notifier *notify)
-{
-    notifier_list_add(&mouse_mode_notifiers, notify);
-}
-
-void qemu_remove_mouse_mode_change_notifier(Notifier *notify)
-{
-    notifier_remove(notify);
+    qemu_input_check_mode_change();
 }
diff --git a/ui/sdl.c b/ui/sdl.c
index 9d8583c4e6..c1a16bebdc 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -26,10 +26,13 @@
 #undef WIN32_LEAN_AND_MEAN
 
 #include <SDL.h>
+
+#if SDL_MAJOR_VERSION == 1
 #include <SDL_syswm.h>
 
 #include "qemu-common.h"
 #include "ui/console.h"
+#include "ui/input.h"
 #include "sysemu/sysemu.h"
 #include "x_keymap.h"
 #include "sdl_zoom.h"
@@ -261,9 +264,7 @@ static void reset_keys(void)
     int i;
     for(i = 0; i < 256; i++) {
         if (modifiers_state[i]) {
-            if (i & SCANCODE_GREY)
-                kbd_put_keycode(SCANCODE_EMUL0);
-            kbd_put_keycode(i | SCANCODE_UP);
+            qemu_input_event_send_key_number(dcl->con, i, false);
             modifiers_state[i] = 0;
         }
     }
@@ -271,16 +272,12 @@ static void reset_keys(void)
 
 static void sdl_process_key(SDL_KeyboardEvent *ev)
 {
-    int keycode, v;
+    int keycode;
 
     if (ev->keysym.sym == SDLK_PAUSE) {
         /* specific case */
-        v = 0;
-        if (ev->type == SDL_KEYUP)
-            v |= SCANCODE_UP;
-        kbd_put_keycode(0xe1);
-        kbd_put_keycode(0x1d | v);
-        kbd_put_keycode(0x45 | v);
+        qemu_input_event_send_key_qcode(dcl->con, Q_KEY_CODE_PAUSE,
+                                        ev->type == SDL_KEYDOWN);
         return;
     }
 
@@ -312,19 +309,15 @@ static void sdl_process_key(SDL_KeyboardEvent *ev)
     case 0x45: /* num lock */
     case 0x3a: /* caps lock */
         /* SDL does not send the key up event, so we generate it */
-        kbd_put_keycode(keycode);
-        kbd_put_keycode(keycode | SCANCODE_UP);
+        qemu_input_event_send_key_number(dcl->con, keycode, true);
+        qemu_input_event_send_key_number(dcl->con, keycode, false);
         return;
 #endif
     }
 
     /* now send the key code */
-    if (keycode & SCANCODE_GREY)
-        kbd_put_keycode(SCANCODE_EMUL0);
-    if (ev->type == SDL_KEYUP)
-        kbd_put_keycode(keycode | SCANCODE_UP);
-    else
-        kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
+    qemu_input_event_send_key_number(dcl->con, keycode,
+                                     ev->type == SDL_KEYDOWN);
 }
 
 static void sdl_update_caption(void)
@@ -360,7 +353,7 @@ static void sdl_hide_cursor(void)
     if (!cursor_hide)
         return;
 
-    if (kbd_mouse_is_absolute()) {
+    if (qemu_input_is_absolute()) {
         SDL_ShowCursor(1);
         SDL_SetCursor(sdl_cursor_hidden);
     } else {
@@ -373,10 +366,10 @@ static void sdl_show_cursor(void)
     if (!cursor_hide)
         return;
 
-    if (!kbd_mouse_is_absolute() || !qemu_console_is_graphic(NULL)) {
+    if (!qemu_input_is_absolute() || !qemu_console_is_graphic(NULL)) {
         SDL_ShowCursor(1);
         if (guest_cursor &&
-                (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+                (gui_grab || qemu_input_is_absolute() || absolute_enabled))
             SDL_SetCursor(guest_sprite);
         else
             SDL_SetCursor(sdl_cursor_normal);
@@ -395,8 +388,9 @@ static void sdl_grab_start(void)
     }
     if (guest_cursor) {
         SDL_SetCursor(guest_sprite);
-        if (!kbd_mouse_is_absolute() && !absolute_enabled)
+        if (!qemu_input_is_absolute() && !absolute_enabled) {
             SDL_WarpMouse(guest_x, guest_y);
+        }
     } else
         sdl_hide_cursor();
     SDL_WM_GrabInput(SDL_GRAB_ON);
@@ -425,7 +419,7 @@ static void absolute_mouse_grab(void)
 
 static void sdl_mouse_mode_change(Notifier *notify, void *data)
 {
-    if (kbd_mouse_is_absolute()) {
+    if (qemu_input_is_absolute()) {
         if (!absolute_enabled) {
             absolute_enabled = 1;
             if (qemu_console_is_graphic(NULL)) {
@@ -440,33 +434,36 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data)
     }
 }
 
-static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
+static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state)
 {
-    int buttons = 0;
-
-    if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) {
-        buttons |= MOUSE_EVENT_LBUTTON;
-    }
-    if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) {
-        buttons |= MOUSE_EVENT_RBUTTON;
-    }
-    if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) {
-        buttons |= MOUSE_EVENT_MBUTTON;
-    }
-
-    if (kbd_mouse_is_absolute()) {
-        dx = x * 0x7FFF / (real_screen->w - 1);
-        dy = y * 0x7FFF / (real_screen->h - 1);
+    static uint32_t bmap[INPUT_BUTTON_MAX] = {
+        [INPUT_BUTTON_LEFT]       = SDL_BUTTON(SDL_BUTTON_LEFT),
+        [INPUT_BUTTON_MIDDLE]     = SDL_BUTTON(SDL_BUTTON_MIDDLE),
+        [INPUT_BUTTON_RIGHT]      = SDL_BUTTON(SDL_BUTTON_RIGHT),
+        [INPUT_BUTTON_WHEEL_UP]   = SDL_BUTTON(SDL_BUTTON_WHEELUP),
+        [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
+    };
+    static uint32_t prev_state;
+
+    if (prev_state != state) {
+        qemu_input_update_buttons(dcl->con, bmap, prev_state, state);
+        prev_state = state;
+    }
+
+    if (qemu_input_is_absolute()) {
+        qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x,
+                             real_screen->w);
+        qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y,
+                             real_screen->h);
     } else if (guest_cursor) {
         x -= guest_x;
         y -= guest_y;
         guest_x += x;
         guest_y += y;
-        dx = x;
-        dy = y;
+        qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, x);
+        qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, y);
     }
-
-    kbd_mouse_event(dx, dy, dz, buttons);
+    qemu_input_event_sync();
 }
 
 static void sdl_scale(int width, int height)
@@ -694,7 +691,7 @@ static void handle_mousemotion(SDL_Event *ev)
     int max_x, max_y;
 
     if (qemu_console_is_graphic(NULL) &&
-        (kbd_mouse_is_absolute() || absolute_enabled)) {
+        (qemu_input_is_absolute() || absolute_enabled)) {
         max_x = real_screen->w - 1;
         max_y = real_screen->h - 1;
         if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
@@ -707,8 +704,8 @@ static void handle_mousemotion(SDL_Event *ev)
             sdl_grab_start();
         }
     }
-    if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
-        sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0,
+    if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
+        sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel,
                              ev->motion.x, ev->motion.y, ev->motion.state);
     }
 }
@@ -717,35 +714,24 @@ static void handle_mousebutton(SDL_Event *ev)
 {
     int buttonstate = SDL_GetMouseState(NULL, NULL);
     SDL_MouseButtonEvent *bev;
-    int dz;
 
     if (!qemu_console_is_graphic(NULL)) {
         return;
     }
 
     bev = &ev->button;
-    if (!gui_grab && !kbd_mouse_is_absolute()) {
+    if (!gui_grab && !qemu_input_is_absolute()) {
         if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
             /* start grabbing all events */
             sdl_grab_start();
         }
     } else {
-        dz = 0;
         if (ev->type == SDL_MOUSEBUTTONDOWN) {
             buttonstate |= SDL_BUTTON(bev->button);
         } else {
             buttonstate &= ~SDL_BUTTON(bev->button);
         }
-#ifdef SDL_BUTTON_WHEELUP
-        if (bev->button == SDL_BUTTON_WHEELUP &&
-            ev->type == SDL_MOUSEBUTTONDOWN) {
-            dz = -1;
-        } else if (bev->button == SDL_BUTTON_WHEELDOWN &&
-                   ev->type == SDL_MOUSEBUTTONDOWN) {
-            dz = 1;
-        }
-#endif
-        sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate);
+        sdl_send_mouse_event(0, 0, bev->x, bev->y, buttonstate);
     }
 }
 
@@ -760,7 +746,7 @@ static void handle_activation(SDL_Event *ev)
     }
 #endif
     if (!gui_grab && ev->active.gain && qemu_console_is_graphic(NULL) &&
-        (kbd_mouse_is_absolute() || absolute_enabled)) {
+        (qemu_input_is_absolute() || absolute_enabled)) {
         absolute_mouse_grab();
     }
     if (ev->active.state & SDL_APPACTIVE) {
@@ -832,10 +818,11 @@ static void sdl_mouse_warp(DisplayChangeListener *dcl,
     if (on) {
         if (!guest_cursor)
             sdl_show_cursor();
-        if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
+        if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
             SDL_SetCursor(guest_sprite);
-            if (!kbd_mouse_is_absolute() && !absolute_enabled)
+            if (!qemu_input_is_absolute() && !absolute_enabled) {
                 SDL_WarpMouse(x, y);
+            }
         }
     } else if (gui_grab)
         sdl_hide_cursor();
@@ -863,7 +850,7 @@ static void sdl_mouse_define(DisplayChangeListener *dcl,
     g_free(mask);
 
     if (guest_cursor &&
-            (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+            (gui_grab || qemu_input_is_absolute() || absolute_enabled))
         SDL_SetCursor(guest_sprite);
 }
 
@@ -966,3 +953,4 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
 
     atexit(sdl_cleanup);
 }
+#endif
diff --git a/ui/sdl2-keymap.h b/ui/sdl2-keymap.h
new file mode 100644
index 0000000000..5a12f4543a
--- /dev/null
+++ b/ui/sdl2-keymap.h
@@ -0,0 +1,266 @@
+
+/* map SDL2 scancodes to QKeyCode */
+
+static const int sdl2_scancode_to_qcode[SDL_NUM_SCANCODES] = {
+    [SDL_SCANCODE_A]                 = Q_KEY_CODE_A,
+    [SDL_SCANCODE_B]                 = Q_KEY_CODE_B,
+    [SDL_SCANCODE_C]                 = Q_KEY_CODE_C,
+    [SDL_SCANCODE_D]                 = Q_KEY_CODE_D,
+    [SDL_SCANCODE_E]                 = Q_KEY_CODE_E,
+    [SDL_SCANCODE_F]                 = Q_KEY_CODE_F,
+    [SDL_SCANCODE_G]                 = Q_KEY_CODE_G,
+    [SDL_SCANCODE_H]                 = Q_KEY_CODE_H,
+    [SDL_SCANCODE_I]                 = Q_KEY_CODE_I,
+    [SDL_SCANCODE_J]                 = Q_KEY_CODE_J,
+    [SDL_SCANCODE_K]                 = Q_KEY_CODE_K,
+    [SDL_SCANCODE_L]                 = Q_KEY_CODE_L,
+    [SDL_SCANCODE_M]                 = Q_KEY_CODE_M,
+    [SDL_SCANCODE_N]                 = Q_KEY_CODE_N,
+    [SDL_SCANCODE_O]                 = Q_KEY_CODE_O,
+    [SDL_SCANCODE_P]                 = Q_KEY_CODE_P,
+    [SDL_SCANCODE_Q]                 = Q_KEY_CODE_Q,
+    [SDL_SCANCODE_R]                 = Q_KEY_CODE_R,
+    [SDL_SCANCODE_S]                 = Q_KEY_CODE_S,
+    [SDL_SCANCODE_T]                 = Q_KEY_CODE_T,
+    [SDL_SCANCODE_U]                 = Q_KEY_CODE_U,
+    [SDL_SCANCODE_V]                 = Q_KEY_CODE_V,
+    [SDL_SCANCODE_W]                 = Q_KEY_CODE_W,
+    [SDL_SCANCODE_X]                 = Q_KEY_CODE_X,
+    [SDL_SCANCODE_Y]                 = Q_KEY_CODE_Y,
+    [SDL_SCANCODE_Z]                 = Q_KEY_CODE_Z,
+
+    [SDL_SCANCODE_1]                 = Q_KEY_CODE_1,
+    [SDL_SCANCODE_2]                 = Q_KEY_CODE_2,
+    [SDL_SCANCODE_3]                 = Q_KEY_CODE_3,
+    [SDL_SCANCODE_4]                 = Q_KEY_CODE_4,
+    [SDL_SCANCODE_5]                 = Q_KEY_CODE_5,
+    [SDL_SCANCODE_6]                 = Q_KEY_CODE_6,
+    [SDL_SCANCODE_7]                 = Q_KEY_CODE_7,
+    [SDL_SCANCODE_8]                 = Q_KEY_CODE_8,
+    [SDL_SCANCODE_9]                 = Q_KEY_CODE_9,
+    [SDL_SCANCODE_0]                 = Q_KEY_CODE_0,
+
+    [SDL_SCANCODE_RETURN]            = Q_KEY_CODE_RET,
+    [SDL_SCANCODE_ESCAPE]            = Q_KEY_CODE_ESC,
+    [SDL_SCANCODE_BACKSPACE]         = Q_KEY_CODE_BACKSPACE,
+    [SDL_SCANCODE_TAB]               = Q_KEY_CODE_TAB,
+    [SDL_SCANCODE_SPACE]             = Q_KEY_CODE_SPC,
+    [SDL_SCANCODE_MINUS]             = Q_KEY_CODE_MINUS,
+    [SDL_SCANCODE_EQUALS]            = Q_KEY_CODE_EQUAL,
+    [SDL_SCANCODE_LEFTBRACKET]       = Q_KEY_CODE_BRACKET_LEFT,
+    [SDL_SCANCODE_RIGHTBRACKET]      = Q_KEY_CODE_BRACKET_RIGHT,
+    [SDL_SCANCODE_BACKSLASH]         = Q_KEY_CODE_BACKSLASH,
+#if 0
+    [SDL_SCANCODE_NONUSHASH]         = Q_KEY_CODE_NONUSHASH,
+#endif
+    [SDL_SCANCODE_SEMICOLON]         = Q_KEY_CODE_SEMICOLON,
+    [SDL_SCANCODE_APOSTROPHE]        = Q_KEY_CODE_APOSTROPHE,
+    [SDL_SCANCODE_GRAVE]             = Q_KEY_CODE_GRAVE_ACCENT,
+    [SDL_SCANCODE_COMMA]             = Q_KEY_CODE_COMMA,
+    [SDL_SCANCODE_PERIOD]            = Q_KEY_CODE_DOT,
+    [SDL_SCANCODE_SLASH]             = Q_KEY_CODE_SLASH,
+    [SDL_SCANCODE_CAPSLOCK]          = Q_KEY_CODE_CAPS_LOCK,
+
+    [SDL_SCANCODE_F1]                = Q_KEY_CODE_F1,
+    [SDL_SCANCODE_F2]                = Q_KEY_CODE_F2,
+    [SDL_SCANCODE_F3]                = Q_KEY_CODE_F3,
+    [SDL_SCANCODE_F4]                = Q_KEY_CODE_F4,
+    [SDL_SCANCODE_F5]                = Q_KEY_CODE_F5,
+    [SDL_SCANCODE_F6]                = Q_KEY_CODE_F6,
+    [SDL_SCANCODE_F7]                = Q_KEY_CODE_F7,
+    [SDL_SCANCODE_F8]                = Q_KEY_CODE_F8,
+    [SDL_SCANCODE_F9]                = Q_KEY_CODE_F9,
+    [SDL_SCANCODE_F10]               = Q_KEY_CODE_F10,
+    [SDL_SCANCODE_F11]               = Q_KEY_CODE_F11,
+    [SDL_SCANCODE_F12]               = Q_KEY_CODE_F12,
+
+    [SDL_SCANCODE_PRINTSCREEN]       = Q_KEY_CODE_PRINT,
+    [SDL_SCANCODE_SCROLLLOCK]        = Q_KEY_CODE_SCROLL_LOCK,
+    [SDL_SCANCODE_PAUSE]             = Q_KEY_CODE_PAUSE,
+    [SDL_SCANCODE_INSERT]            = Q_KEY_CODE_INSERT,
+    [SDL_SCANCODE_HOME]              = Q_KEY_CODE_HOME,
+    [SDL_SCANCODE_PAGEUP]            = Q_KEY_CODE_PGUP,
+    [SDL_SCANCODE_DELETE]            = Q_KEY_CODE_DELETE,
+    [SDL_SCANCODE_END]               = Q_KEY_CODE_END,
+    [SDL_SCANCODE_PAGEDOWN]          = Q_KEY_CODE_PGDN,
+    [SDL_SCANCODE_RIGHT]             = Q_KEY_CODE_RIGHT,
+    [SDL_SCANCODE_LEFT]              = Q_KEY_CODE_LEFT,
+    [SDL_SCANCODE_DOWN]              = Q_KEY_CODE_DOWN,
+    [SDL_SCANCODE_UP]                = Q_KEY_CODE_UP,
+    [SDL_SCANCODE_NUMLOCKCLEAR]      = Q_KEY_CODE_NUM_LOCK,
+
+    [SDL_SCANCODE_KP_DIVIDE]         = Q_KEY_CODE_KP_DIVIDE,
+    [SDL_SCANCODE_KP_MULTIPLY]       = Q_KEY_CODE_KP_MULTIPLY,
+    [SDL_SCANCODE_KP_MINUS]          = Q_KEY_CODE_KP_SUBTRACT,
+    [SDL_SCANCODE_KP_PLUS]           = Q_KEY_CODE_KP_ADD,
+    [SDL_SCANCODE_KP_ENTER]          = Q_KEY_CODE_KP_ENTER,
+    [SDL_SCANCODE_KP_1]              = Q_KEY_CODE_KP_1,
+    [SDL_SCANCODE_KP_2]              = Q_KEY_CODE_KP_2,
+    [SDL_SCANCODE_KP_3]              = Q_KEY_CODE_KP_3,
+    [SDL_SCANCODE_KP_4]              = Q_KEY_CODE_KP_4,
+    [SDL_SCANCODE_KP_5]              = Q_KEY_CODE_KP_5,
+    [SDL_SCANCODE_KP_6]              = Q_KEY_CODE_KP_6,
+    [SDL_SCANCODE_KP_7]              = Q_KEY_CODE_KP_7,
+    [SDL_SCANCODE_KP_8]              = Q_KEY_CODE_KP_8,
+    [SDL_SCANCODE_KP_9]              = Q_KEY_CODE_KP_9,
+    [SDL_SCANCODE_KP_0]              = Q_KEY_CODE_KP_0,
+    [SDL_SCANCODE_KP_PERIOD]         = Q_KEY_CODE_KP_DECIMAL,
+#if 0
+    [SDL_SCANCODE_NONUSBACKSLASH]    = Q_KEY_CODE_NONUSBACKSLASH,
+    [SDL_SCANCODE_APPLICATION]       = Q_KEY_CODE_APPLICATION,
+    [SDL_SCANCODE_POWER]             = Q_KEY_CODE_POWER,
+    [SDL_SCANCODE_KP_EQUALS]         = Q_KEY_CODE_KP_EQUALS,
+
+    [SDL_SCANCODE_F13]               = Q_KEY_CODE_F13,
+    [SDL_SCANCODE_F14]               = Q_KEY_CODE_F14,
+    [SDL_SCANCODE_F15]               = Q_KEY_CODE_F15,
+    [SDL_SCANCODE_F16]               = Q_KEY_CODE_F16,
+    [SDL_SCANCODE_F17]               = Q_KEY_CODE_F17,
+    [SDL_SCANCODE_F18]               = Q_KEY_CODE_F18,
+    [SDL_SCANCODE_F19]               = Q_KEY_CODE_F19,
+    [SDL_SCANCODE_F20]               = Q_KEY_CODE_F20,
+    [SDL_SCANCODE_F21]               = Q_KEY_CODE_F21,
+    [SDL_SCANCODE_F22]               = Q_KEY_CODE_F22,
+    [SDL_SCANCODE_F23]               = Q_KEY_CODE_F23,
+    [SDL_SCANCODE_F24]               = Q_KEY_CODE_F24,
+
+    [SDL_SCANCODE_EXECUTE]           = Q_KEY_CODE_EXECUTE,
+#endif
+    [SDL_SCANCODE_HELP]              = Q_KEY_CODE_HELP,
+    [SDL_SCANCODE_MENU]              = Q_KEY_CODE_MENU,
+#if 0
+    [SDL_SCANCODE_SELECT]            = Q_KEY_CODE_SELECT,
+#endif
+    [SDL_SCANCODE_STOP]              = Q_KEY_CODE_STOP,
+    [SDL_SCANCODE_AGAIN]             = Q_KEY_CODE_AGAIN,
+    [SDL_SCANCODE_UNDO]              = Q_KEY_CODE_UNDO,
+    [SDL_SCANCODE_CUT]               = Q_KEY_CODE_CUT,
+    [SDL_SCANCODE_COPY]              = Q_KEY_CODE_COPY,
+    [SDL_SCANCODE_PASTE]             = Q_KEY_CODE_PASTE,
+    [SDL_SCANCODE_FIND]              = Q_KEY_CODE_FIND,
+#if 0
+    [SDL_SCANCODE_MUTE]              = Q_KEY_CODE_MUTE,
+    [SDL_SCANCODE_VOLUMEUP]          = Q_KEY_CODE_VOLUMEUP,
+    [SDL_SCANCODE_VOLUMEDOWN]        = Q_KEY_CODE_VOLUMEDOWN,
+
+    [SDL_SCANCODE_KP_COMMA]          = Q_KEY_CODE_KP_COMMA,
+    [SDL_SCANCODE_KP_EQUALSAS400]    = Q_KEY_CODE_KP_EQUALSAS400,
+
+    [SDL_SCANCODE_INTERNATIONAL1]    = Q_KEY_CODE_INTERNATIONAL1,
+    [SDL_SCANCODE_INTERNATIONAL2]    = Q_KEY_CODE_INTERNATIONAL2,
+    [SDL_SCANCODE_INTERNATIONAL3]    = Q_KEY_CODE_INTERNATIONAL3,
+    [SDL_SCANCODE_INTERNATIONAL4]    = Q_KEY_CODE_INTERNATIONAL4,
+    [SDL_SCANCODE_INTERNATIONAL5]    = Q_KEY_CODE_INTERNATIONAL5,
+    [SDL_SCANCODE_INTERNATIONAL6]    = Q_KEY_CODE_INTERNATIONAL6,
+    [SDL_SCANCODE_INTERNATIONAL7]    = Q_KEY_CODE_INTERNATIONAL7,
+    [SDL_SCANCODE_INTERNATIONAL8]    = Q_KEY_CODE_INTERNATIONAL8,
+    [SDL_SCANCODE_INTERNATIONAL9]    = Q_KEY_CODE_INTERNATIONAL9,
+    [SDL_SCANCODE_LANG1]             = Q_KEY_CODE_LANG1,
+    [SDL_SCANCODE_LANG2]             = Q_KEY_CODE_LANG2,
+    [SDL_SCANCODE_LANG3]             = Q_KEY_CODE_LANG3,
+    [SDL_SCANCODE_LANG4]             = Q_KEY_CODE_LANG4,
+    [SDL_SCANCODE_LANG5]             = Q_KEY_CODE_LANG5,
+    [SDL_SCANCODE_LANG6]             = Q_KEY_CODE_LANG6,
+    [SDL_SCANCODE_LANG7]             = Q_KEY_CODE_LANG7,
+    [SDL_SCANCODE_LANG8]             = Q_KEY_CODE_LANG8,
+    [SDL_SCANCODE_LANG9]             = Q_KEY_CODE_LANG9,
+    [SDL_SCANCODE_ALTERASE]          = Q_KEY_CODE_ALTERASE,
+#endif
+    [SDL_SCANCODE_SYSREQ]            = Q_KEY_CODE_SYSRQ,
+#if 0
+    [SDL_SCANCODE_CANCEL]            = Q_KEY_CODE_CANCEL,
+    [SDL_SCANCODE_CLEAR]             = Q_KEY_CODE_CLEAR,
+    [SDL_SCANCODE_PRIOR]             = Q_KEY_CODE_PRIOR,
+    [SDL_SCANCODE_RETURN2]           = Q_KEY_CODE_RETURN2,
+    [SDL_SCANCODE_SEPARATOR]         = Q_KEY_CODE_SEPARATOR,
+    [SDL_SCANCODE_OUT]               = Q_KEY_CODE_OUT,
+    [SDL_SCANCODE_OPER]              = Q_KEY_CODE_OPER,
+    [SDL_SCANCODE_CLEARAGAIN]        = Q_KEY_CODE_CLEARAGAIN,
+    [SDL_SCANCODE_CRSEL]             = Q_KEY_CODE_CRSEL,
+    [SDL_SCANCODE_EXSEL]             = Q_KEY_CODE_EXSEL,
+    [SDL_SCANCODE_KP_00]             = Q_KEY_CODE_KP_00,
+    [SDL_SCANCODE_KP_000]            = Q_KEY_CODE_KP_000,
+    [SDL_SCANCODE_THOUSANDSSEPARATOR] = Q_KEY_CODE_THOUSANDSSEPARATOR,
+    [SDL_SCANCODE_DECIMALSEPARATOR]  = Q_KEY_CODE_DECIMALSEPARATOR,
+    [SDL_SCANCODE_CURRENCYUNIT]      = Q_KEY_CODE_CURRENCYUNIT,
+    [SDL_SCANCODE_CURRENCYSUBUNIT]   = Q_KEY_CODE_CURRENCYSUBUNIT,
+    [SDL_SCANCODE_KP_LEFTPAREN]      = Q_KEY_CODE_KP_LEFTPAREN,
+    [SDL_SCANCODE_KP_RIGHTPAREN]     = Q_KEY_CODE_KP_RIGHTPAREN,
+    [SDL_SCANCODE_KP_LEFTBRACE]      = Q_KEY_CODE_KP_LEFTBRACE,
+    [SDL_SCANCODE_KP_RIGHTBRACE]     = Q_KEY_CODE_KP_RIGHTBRACE,
+    [SDL_SCANCODE_KP_TAB]            = Q_KEY_CODE_KP_TAB,
+    [SDL_SCANCODE_KP_BACKSPACE]      = Q_KEY_CODE_KP_BACKSPACE,
+    [SDL_SCANCODE_KP_A]              = Q_KEY_CODE_KP_A,
+    [SDL_SCANCODE_KP_B]              = Q_KEY_CODE_KP_B,
+    [SDL_SCANCODE_KP_C]              = Q_KEY_CODE_KP_C,
+    [SDL_SCANCODE_KP_D]              = Q_KEY_CODE_KP_D,
+    [SDL_SCANCODE_KP_E]              = Q_KEY_CODE_KP_E,
+    [SDL_SCANCODE_KP_F]              = Q_KEY_CODE_KP_F,
+    [SDL_SCANCODE_KP_XOR]            = Q_KEY_CODE_KP_XOR,
+    [SDL_SCANCODE_KP_POWER]          = Q_KEY_CODE_KP_POWER,
+    [SDL_SCANCODE_KP_PERCENT]        = Q_KEY_CODE_KP_PERCENT,
+    [SDL_SCANCODE_KP_LESS]           = Q_KEY_CODE_KP_LESS,
+    [SDL_SCANCODE_KP_GREATER]        = Q_KEY_CODE_KP_GREATER,
+    [SDL_SCANCODE_KP_AMPERSAND]      = Q_KEY_CODE_KP_AMPERSAND,
+    [SDL_SCANCODE_KP_DBLAMPERSAND]   = Q_KEY_CODE_KP_DBLAMPERSAND,
+    [SDL_SCANCODE_KP_VERTICALBAR]    = Q_KEY_CODE_KP_VERTICALBAR,
+    [SDL_SCANCODE_KP_DBLVERTICALBAR] = Q_KEY_CODE_KP_DBLVERTICALBAR,
+    [SDL_SCANCODE_KP_COLON]          = Q_KEY_CODE_KP_COLON,
+    [SDL_SCANCODE_KP_HASH]           = Q_KEY_CODE_KP_HASH,
+    [SDL_SCANCODE_KP_SPACE]          = Q_KEY_CODE_KP_SPACE,
+    [SDL_SCANCODE_KP_AT]             = Q_KEY_CODE_KP_AT,
+    [SDL_SCANCODE_KP_EXCLAM]         = Q_KEY_CODE_KP_EXCLAM,
+    [SDL_SCANCODE_KP_MEMSTORE]       = Q_KEY_CODE_KP_MEMSTORE,
+    [SDL_SCANCODE_KP_MEMRECALL]      = Q_KEY_CODE_KP_MEMRECALL,
+    [SDL_SCANCODE_KP_MEMCLEAR]       = Q_KEY_CODE_KP_MEMCLEAR,
+    [SDL_SCANCODE_KP_MEMADD]         = Q_KEY_CODE_KP_MEMADD,
+    [SDL_SCANCODE_KP_MEMSUBTRACT]    = Q_KEY_CODE_KP_MEMSUBTRACT,
+    [SDL_SCANCODE_KP_MEMMULTIPLY]    = Q_KEY_CODE_KP_MEMMULTIPLY,
+    [SDL_SCANCODE_KP_MEMDIVIDE]      = Q_KEY_CODE_KP_MEMDIVIDE,
+    [SDL_SCANCODE_KP_PLUSMINUS]      = Q_KEY_CODE_KP_PLUSMINUS,
+    [SDL_SCANCODE_KP_CLEAR]          = Q_KEY_CODE_KP_CLEAR,
+    [SDL_SCANCODE_KP_CLEARENTRY]     = Q_KEY_CODE_KP_CLEARENTRY,
+    [SDL_SCANCODE_KP_BINARY]         = Q_KEY_CODE_KP_BINARY,
+    [SDL_SCANCODE_KP_OCTAL]          = Q_KEY_CODE_KP_OCTAL,
+    [SDL_SCANCODE_KP_DECIMAL]        = Q_KEY_CODE_KP_DECIMAL,
+    [SDL_SCANCODE_KP_HEXADECIMAL]    = Q_KEY_CODE_KP_HEXADECIMAL,
+#endif
+    [SDL_SCANCODE_LCTRL]             = Q_KEY_CODE_CTRL,
+    [SDL_SCANCODE_LSHIFT]            = Q_KEY_CODE_SHIFT,
+    [SDL_SCANCODE_LALT]              = Q_KEY_CODE_ALT,
+    [SDL_SCANCODE_LGUI]              = Q_KEY_CODE_META_L,
+    [SDL_SCANCODE_RCTRL]             = Q_KEY_CODE_CTRL_R,
+    [SDL_SCANCODE_RSHIFT]            = Q_KEY_CODE_SHIFT_R,
+    [SDL_SCANCODE_RALT]              = Q_KEY_CODE_ALTGR,
+    [SDL_SCANCODE_RGUI]              = Q_KEY_CODE_META_R,
+#if 0
+    [SDL_SCANCODE_MODE]              = Q_KEY_CODE_MODE,
+    [SDL_SCANCODE_AUDIONEXT]         = Q_KEY_CODE_AUDIONEXT,
+    [SDL_SCANCODE_AUDIOPREV]         = Q_KEY_CODE_AUDIOPREV,
+    [SDL_SCANCODE_AUDIOSTOP]         = Q_KEY_CODE_AUDIOSTOP,
+    [SDL_SCANCODE_AUDIOPLAY]         = Q_KEY_CODE_AUDIOPLAY,
+    [SDL_SCANCODE_AUDIOMUTE]         = Q_KEY_CODE_AUDIOMUTE,
+    [SDL_SCANCODE_MEDIASELECT]       = Q_KEY_CODE_MEDIASELECT,
+    [SDL_SCANCODE_WWW]               = Q_KEY_CODE_WWW,
+    [SDL_SCANCODE_MAIL]              = Q_KEY_CODE_MAIL,
+    [SDL_SCANCODE_CALCULATOR]        = Q_KEY_CODE_CALCULATOR,
+    [SDL_SCANCODE_COMPUTER]          = Q_KEY_CODE_COMPUTER,
+    [SDL_SCANCODE_AC_SEARCH]         = Q_KEY_CODE_AC_SEARCH,
+    [SDL_SCANCODE_AC_HOME]           = Q_KEY_CODE_AC_HOME,
+    [SDL_SCANCODE_AC_BACK]           = Q_KEY_CODE_AC_BACK,
+    [SDL_SCANCODE_AC_FORWARD]        = Q_KEY_CODE_AC_FORWARD,
+    [SDL_SCANCODE_AC_STOP]           = Q_KEY_CODE_AC_STOP,
+    [SDL_SCANCODE_AC_REFRESH]        = Q_KEY_CODE_AC_REFRESH,
+    [SDL_SCANCODE_AC_BOOKMARKS]      = Q_KEY_CODE_AC_BOOKMARKS,
+    [SDL_SCANCODE_BRIGHTNESSDOWN]    = Q_KEY_CODE_BRIGHTNESSDOWN,
+    [SDL_SCANCODE_BRIGHTNESSUP]      = Q_KEY_CODE_BRIGHTNESSUP,
+    [SDL_SCANCODE_DISPLAYSWITCH]     = Q_KEY_CODE_DISPLAYSWITCH,
+    [SDL_SCANCODE_KBDILLUMTOGGLE]    = Q_KEY_CODE_KBDILLUMTOGGLE,
+    [SDL_SCANCODE_KBDILLUMDOWN]      = Q_KEY_CODE_KBDILLUMDOWN,
+    [SDL_SCANCODE_KBDILLUMUP]        = Q_KEY_CODE_KBDILLUMUP,
+    [SDL_SCANCODE_EJECT]             = Q_KEY_CODE_EJECT,
+    [SDL_SCANCODE_SLEEP]             = Q_KEY_CODE_SLEEP,
+    [SDL_SCANCODE_APP1]              = Q_KEY_CODE_APP1,
+    [SDL_SCANCODE_APP2]              = Q_KEY_CODE_APP2,
+#endif
+};
diff --git a/ui/sdl2.c b/ui/sdl2.c
new file mode 100644
index 0000000000..f1532e9d2c
--- /dev/null
+++ b/ui/sdl2.c
@@ -0,0 +1,829 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
+
+/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
+#undef WIN32_LEAN_AND_MEAN
+
+#include <SDL.h>
+
+#if SDL_MAJOR_VERSION == 2
+#include <SDL_syswm.h>
+
+#include "qemu-common.h"
+#include "ui/console.h"
+#include "ui/input.h"
+#include "sysemu/sysemu.h"
+#include "sdl_zoom.h"
+
+#include "sdl2-keymap.h"
+
+static int sdl2_num_outputs;
+static struct sdl2_state {
+    DisplayChangeListener dcl;
+    DisplaySurface *surface;
+    SDL_Texture *texture;
+    SDL_Window *real_window;
+    SDL_Renderer *real_renderer;
+    int idx;
+    int last_vm_running; /* per console for caption reasons */
+    int x, y;
+} *sdl2_console;
+
+static SDL_Surface *guest_sprite_surface;
+static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
+
+static bool gui_saved_scaling;
+static int gui_saved_width;
+static int gui_saved_height;
+static int gui_saved_grab;
+static int gui_fullscreen;
+static int gui_noframe;
+static int gui_key_modifier_pressed;
+static int gui_keysym;
+static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
+static uint8_t modifiers_state[SDL_NUM_SCANCODES];
+static SDL_Cursor *sdl_cursor_normal;
+static SDL_Cursor *sdl_cursor_hidden;
+static int absolute_enabled;
+static int guest_cursor;
+static int guest_x, guest_y;
+static SDL_Cursor *guest_sprite;
+static int scaling_active;
+static Notifier mouse_mode_notifier;
+
+static void sdl_update_caption(struct sdl2_state *scon);
+
+static struct sdl2_state *get_scon_from_window(uint32_t window_id)
+{
+    int i;
+    for (i = 0; i < sdl2_num_outputs; i++) {
+        if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
+            return &sdl2_console[i];
+        }
+    }
+    return NULL;
+}
+
+static void sdl_update(DisplayChangeListener *dcl,
+                       int x, int y, int w, int h)
+{
+    struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
+    SDL_Rect rect;
+    DisplaySurface *surf = qemu_console_surface(dcl->con);
+
+    if (!surf) {
+        return;
+    }
+    if (!scon->texture) {
+        return;
+    }
+
+    rect.x = x;
+    rect.y = y;
+    rect.w = w;
+    rect.h = h;
+
+    SDL_UpdateTexture(scon->texture, NULL, surface_data(surf),
+                      surface_stride(surf));
+    SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect);
+    SDL_RenderPresent(scon->real_renderer);
+}
+
+static void do_sdl_resize(struct sdl2_state *scon, int width, int height,
+                          int bpp)
+{
+    int flags;
+
+    if (scon->real_window && scon->real_renderer) {
+        if (width && height) {
+            SDL_RenderSetLogicalSize(scon->real_renderer, width, height);
+            SDL_SetWindowSize(scon->real_window, width, height);
+        } else {
+            SDL_DestroyRenderer(scon->real_renderer);
+            SDL_DestroyWindow(scon->real_window);
+            scon->real_renderer = NULL;
+            scon->real_window = NULL;
+        }
+    } else {
+        if (!width || !height) {
+            return;
+        }
+        flags = 0;
+        if (gui_fullscreen) {
+            flags |= SDL_WINDOW_FULLSCREEN;
+        } else {
+            flags |= SDL_WINDOW_RESIZABLE;
+        }
+
+        scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
+                                             SDL_WINDOWPOS_UNDEFINED,
+                                             width, height, flags);
+        scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
+        sdl_update_caption(scon);
+    }
+}
+
+static void sdl_switch(DisplayChangeListener *dcl,
+                       DisplaySurface *new_surface)
+{
+    struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
+    int format = 0;
+    int idx = scon->idx;
+    DisplaySurface *old_surface = scon->surface;
+
+    /* temporary hack: allows to call sdl_switch to handle scaling changes */
+    if (new_surface) {
+        scon->surface = new_surface;
+    }
+
+    if (!new_surface && idx > 0) {
+        scon->surface = NULL;
+    }
+
+    if (new_surface == NULL) {
+        do_sdl_resize(scon, 0, 0, 0);
+    } else {
+        do_sdl_resize(scon, surface_width(scon->surface),
+                      surface_height(scon->surface), 0);
+    }
+
+    if (old_surface && scon->texture) {
+        SDL_DestroyTexture(scon->texture);
+        scon->texture = NULL;
+    }
+
+    if (new_surface) {
+        if (!scon->texture) {
+            if (surface_bits_per_pixel(scon->surface) == 16) {
+                format = SDL_PIXELFORMAT_RGB565;
+            } else if (surface_bits_per_pixel(scon->surface) == 32) {
+                format = SDL_PIXELFORMAT_ARGB8888;
+            }
+
+            scon->texture = SDL_CreateTexture(scon->real_renderer, format,
+                                              SDL_TEXTUREACCESS_STREAMING,
+                                              surface_width(new_surface),
+                                              surface_height(new_surface));
+        }
+    }
+}
+
+static void reset_keys(void)
+{
+    int i;
+
+    for (i = 0; i < 256; i++) {
+        if (modifiers_state[i]) {
+            int qcode = sdl2_scancode_to_qcode[i];
+            qemu_input_event_send_key_qcode(NULL, qcode, false);
+            modifiers_state[i] = 0;
+        }
+    }
+}
+
+static void sdl_process_key(SDL_KeyboardEvent *ev)
+{
+    int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
+
+    switch (ev->keysym.scancode) {
+#if 0
+    case SDL_SCANCODE_NUMLOCKCLEAR:
+    case SDL_SCANCODE_CAPSLOCK:
+        /* SDL does not send the key up event, so we generate it */
+        qemu_input_event_send_key_qcode(NULL, qcode, true);
+        qemu_input_event_send_key_qcode(NULL, qcode, false);
+        return;
+#endif
+    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;
+        }
+        /* fall though */
+    default:
+        qemu_input_event_send_key_qcode(NULL, qcode,
+                                        ev->type == SDL_KEYDOWN);
+    }
+}
+
+static void sdl_update_caption(struct sdl2_state *scon)
+{
+    char win_title[1024];
+    char icon_title[1024];
+    const char *status = "";
+
+    if (!runstate_is_running()) {
+        status = " [Stopped]";
+    } else if (gui_grab) {
+        if (alt_grab) {
+            status = " - Press Ctrl-Alt-Shift to exit mouse grab";
+        } else if (ctrl_grab) {
+            status = " - Press Right-Ctrl to exit mouse grab";
+        } else {
+            status = " - Press Ctrl-Alt to exit mouse grab";
+        }
+    }
+
+    if (qemu_name) {
+        snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
+                 scon->idx, status);
+        snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
+    } else {
+        snprintf(win_title, sizeof(win_title), "QEMU%s", status);
+        snprintf(icon_title, sizeof(icon_title), "QEMU");
+    }
+
+    if (scon->real_window) {
+        SDL_SetWindowTitle(scon->real_window, win_title);
+    }
+}
+
+static void sdl_hide_cursor(void)
+{
+    if (!cursor_hide) {
+        return;
+    }
+
+    if (qemu_input_is_absolute()) {
+        SDL_ShowCursor(1);
+        SDL_SetCursor(sdl_cursor_hidden);
+    } else {
+        SDL_ShowCursor(0);
+    }
+}
+
+static void sdl_show_cursor(void)
+{
+    if (!cursor_hide) {
+        return;
+    }
+
+    if (!qemu_input_is_absolute()) {
+        SDL_ShowCursor(1);
+        if (guest_cursor &&
+            (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
+            SDL_SetCursor(guest_sprite);
+        } else {
+            SDL_SetCursor(sdl_cursor_normal);
+        }
+    }
+}
+
+static void sdl_grab_start(struct sdl2_state *scon)
+{
+    /*
+     * If the application is not active, do not try to enter grab state. This
+     * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
+     * application (SDL bug).
+     */
+    if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
+        return;
+    }
+    if (guest_cursor) {
+        SDL_SetCursor(guest_sprite);
+        if (!qemu_input_is_absolute() && !absolute_enabled) {
+            SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
+        }
+    } else {
+        sdl_hide_cursor();
+    }
+    SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
+    gui_grab = 1;
+    sdl_update_caption(scon);
+}
+
+static void sdl_grab_end(struct sdl2_state *scon)
+{
+    SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
+    gui_grab = 0;
+    sdl_show_cursor();
+    sdl_update_caption(scon);
+}
+
+static void absolute_mouse_grab(struct sdl2_state *scon)
+{
+    int mouse_x, mouse_y;
+    int scr_w, scr_h;
+    SDL_GetMouseState(&mouse_x, &mouse_y);
+    SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
+    if (mouse_x > 0 && mouse_x < scr_w - 1 &&
+        mouse_y > 0 && mouse_y < scr_h - 1) {
+        sdl_grab_start(scon);
+    }
+}
+
+static void sdl_mouse_mode_change(Notifier *notify, void *data)
+{
+    if (qemu_input_is_absolute()) {
+        if (!absolute_enabled) {
+            absolute_enabled = 1;
+            absolute_mouse_grab(&sdl2_console[0]);
+        }
+    } else if (absolute_enabled) {
+        if (!gui_fullscreen) {
+            sdl_grab_end(&sdl2_console[0]);
+        }
+        absolute_enabled = 0;
+    }
+}
+
+static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy,
+                                 int dz, int x, int y, int state)
+{
+    static uint32_t bmap[INPUT_BUTTON_MAX] = {
+        [INPUT_BUTTON_LEFT]       = SDL_BUTTON(SDL_BUTTON_LEFT),
+        [INPUT_BUTTON_MIDDLE]     = SDL_BUTTON(SDL_BUTTON_MIDDLE),
+        [INPUT_BUTTON_RIGHT]      = SDL_BUTTON(SDL_BUTTON_RIGHT),
+#if 0
+        [INPUT_BUTTON_WHEEL_UP]   = SDL_BUTTON(SDL_BUTTON_WHEELUP),
+        [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
+#endif
+    };
+    static uint32_t prev_state;
+
+    if (prev_state != state) {
+        qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
+        prev_state = state;
+    }
+
+    if (qemu_input_is_absolute()) {
+        int scr_w, scr_h;
+        int max_w = 0, max_h = 0;
+        int off_x = 0, off_y = 0;
+        int cur_off_x = 0, cur_off_y = 0;
+        int i;
+
+        for (i = 0; i < sdl2_num_outputs; i++) {
+            struct sdl2_state *thiscon = &sdl2_console[i];
+            if (thiscon->real_window && thiscon->surface) {
+                SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
+                cur_off_x = thiscon->x;
+                cur_off_y = thiscon->y;
+                if (scr_w + cur_off_x > max_w) {
+                    max_w = scr_w + cur_off_x;
+                }
+                if (scr_h + cur_off_y > max_h) {
+                    max_h = scr_h + cur_off_y;
+                }
+                if (i == scon->idx) {
+                    off_x = cur_off_x;
+                    off_y = cur_off_y;
+                }
+            }
+        }
+        qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
+        qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
+    } else if (guest_cursor) {
+        x -= guest_x;
+        y -= guest_y;
+        guest_x += x;
+        guest_y += y;
+        qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, x);
+        qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, y);
+    }
+    qemu_input_event_sync();
+}
+
+static void sdl_scale(struct sdl2_state *scon, int width, int height)
+{
+    int bpp = 0;
+    do_sdl_resize(scon, width, height, bpp);
+    scaling_active = 1;
+}
+
+static void toggle_full_screen(struct sdl2_state *scon)
+{
+    int width = surface_width(scon->surface);
+    int height = surface_height(scon->surface);
+    int bpp = surface_bits_per_pixel(scon->surface);
+
+    gui_fullscreen = !gui_fullscreen;
+    if (gui_fullscreen) {
+        SDL_GetWindowSize(scon->real_window,
+                          &gui_saved_width, &gui_saved_height);
+        gui_saved_scaling = scaling_active;
+
+        do_sdl_resize(scon, width, height, bpp);
+        scaling_active = 0;
+
+        gui_saved_grab = gui_grab;
+        sdl_grab_start(scon);
+    } else {
+        if (gui_saved_scaling) {
+            sdl_scale(scon, gui_saved_width, gui_saved_height);
+        } else {
+            do_sdl_resize(scon, width, height, 0);
+        }
+        if (!gui_saved_grab) {
+            sdl_grab_end(scon);
+        }
+    }
+    graphic_hw_invalidate(scon->dcl.con);
+    graphic_hw_update(scon->dcl.con);
+}
+
+static void handle_keydown(SDL_Event *ev)
+{
+    int mod_state;
+    struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+
+    if (alt_grab) {
+        mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
+            (gui_grab_code | KMOD_LSHIFT);
+    } else if (ctrl_grab) {
+        mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
+    } else {
+        mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
+    }
+    gui_key_modifier_pressed = mod_state;
+
+    if (gui_key_modifier_pressed) {
+        switch (ev->key.keysym.scancode) {
+        case SDL_SCANCODE_F:
+            toggle_full_screen(scon);
+            gui_keysym = 1;
+            break;
+        case SDL_SCANCODE_U:
+            if (scaling_active) {
+                scaling_active = 0;
+                sdl_switch(&scon->dcl, NULL);
+                graphic_hw_invalidate(scon->dcl.con);
+                graphic_hw_update(scon->dcl.con);
+            }
+            gui_keysym = 1;
+            break;
+        case SDL_SCANCODE_KP_PLUS:
+        case SDL_SCANCODE_KP_MINUS:
+            if (!gui_fullscreen) {
+                int scr_w, scr_h;
+                int width, height;
+                SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
+
+                width = MAX(scr_w + (ev->key.keysym.scancode ==
+                                     SDL_SCANCODE_KP_PLUS ? 50 : -50),
+                            160);
+                height = (surface_height(scon->surface) * width) /
+                    surface_width(scon->surface);
+
+                sdl_scale(scon, width, height);
+                graphic_hw_invalidate(NULL);
+                graphic_hw_update(NULL);
+                gui_keysym = 1;
+            }
+        default:
+            break;
+        }
+    }
+    if (!gui_keysym) {
+        sdl_process_key(&ev->key);
+    }
+}
+
+static void handle_keyup(SDL_Event *ev)
+{
+    int mod_state;
+    struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+
+    if (!alt_grab) {
+        mod_state = (ev->key.keysym.mod & gui_grab_code);
+    } else {
+        mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
+    }
+    if (!mod_state && gui_key_modifier_pressed) {
+        gui_key_modifier_pressed = 0;
+        if (gui_keysym == 0) {
+            /* exit/enter grab if pressing Ctrl-Alt */
+            if (!gui_grab) {
+                sdl_grab_start(scon);
+            } else if (!gui_fullscreen) {
+                sdl_grab_end(scon);
+            }
+            /* SDL does not send back all the modifiers key, so we must
+             * correct it. */
+            reset_keys();
+            return;
+        }
+        gui_keysym = 0;
+    }
+    if (!gui_keysym) {
+        sdl_process_key(&ev->key);
+    }
+}
+
+static void handle_mousemotion(SDL_Event *ev)
+{
+    int max_x, max_y;
+    struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+
+    if (qemu_input_is_absolute() || absolute_enabled) {
+        int scr_w, scr_h;
+        SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
+        max_x = scr_w - 1;
+        max_y = scr_h - 1;
+        if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
+                         ev->motion.x == max_x || ev->motion.y == max_y)) {
+            sdl_grab_end(scon);
+        }
+        if (!gui_grab &&
+            (ev->motion.x > 0 && ev->motion.x < max_x &&
+             ev->motion.y > 0 && ev->motion.y < max_y)) {
+            sdl_grab_start(scon);
+        }
+    }
+    if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
+        sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, 0,
+                             ev->motion.x, ev->motion.y, ev->motion.state);
+    }
+}
+
+static void handle_mousebutton(SDL_Event *ev)
+{
+    int buttonstate = SDL_GetMouseState(NULL, NULL);
+    SDL_MouseButtonEvent *bev;
+    struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+    int dz;
+
+    bev = &ev->button;
+    if (!gui_grab && !qemu_input_is_absolute()) {
+        if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
+            /* start grabbing all events */
+            sdl_grab_start(scon);
+        }
+    } else {
+        dz = 0;
+        if (ev->type == SDL_MOUSEBUTTONDOWN) {
+            buttonstate |= SDL_BUTTON(bev->button);
+        } else {
+            buttonstate &= ~SDL_BUTTON(bev->button);
+        }
+#ifdef SDL_BUTTON_WHEELUP
+        if (bev->button == SDL_BUTTON_WHEELUP &&
+            ev->type == SDL_MOUSEBUTTONDOWN) {
+            dz = -1;
+        } else if (bev->button == SDL_BUTTON_WHEELDOWN &&
+                   ev->type == SDL_MOUSEBUTTONDOWN) {
+            dz = 1;
+        }
+#endif
+        sdl_send_mouse_event(scon, 0, 0, dz, bev->x, bev->y, buttonstate);
+    }
+}
+
+static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
+{
+    int w, h;
+    struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+
+    switch (ev->window.event) {
+    case SDL_WINDOWEVENT_RESIZED:
+        sdl_scale(scon, ev->window.data1, ev->window.data2);
+        graphic_hw_invalidate(scon->dcl.con);
+        graphic_hw_update(scon->dcl.con);
+        break;
+    case SDL_WINDOWEVENT_EXPOSED:
+        SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
+        sdl_update(dcl, 0, 0, w, h);
+        break;
+    case SDL_WINDOWEVENT_FOCUS_GAINED:
+    case SDL_WINDOWEVENT_ENTER:
+        if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
+            absolute_mouse_grab(scon);
+        }
+        break;
+    case SDL_WINDOWEVENT_FOCUS_LOST:
+        if (gui_grab && !gui_fullscreen) {
+            sdl_grab_end(scon);
+        }
+        break;
+    case SDL_WINDOWEVENT_RESTORED:
+        update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
+        break;
+    case SDL_WINDOWEVENT_MINIMIZED:
+        update_displaychangelistener(dcl, 500);
+        break;
+    case SDL_WINDOWEVENT_CLOSE:
+        if (!no_quit) {
+            no_shutdown = 0;
+            qemu_system_shutdown_request();
+        }
+        break;
+    }
+}
+
+static void sdl_refresh(DisplayChangeListener *dcl)
+{
+    struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
+    SDL_Event ev1, *ev = &ev1;
+
+    if (scon->last_vm_running != runstate_is_running()) {
+        scon->last_vm_running = runstate_is_running();
+        sdl_update_caption(scon);
+    }
+
+    graphic_hw_update(dcl->con);
+
+    while (SDL_PollEvent(ev)) {
+        switch (ev->type) {
+        case SDL_KEYDOWN:
+            handle_keydown(ev);
+            break;
+        case SDL_KEYUP:
+            handle_keyup(ev);
+            break;
+        case SDL_QUIT:
+            if (!no_quit) {
+                no_shutdown = 0;
+                qemu_system_shutdown_request();
+            }
+            break;
+        case SDL_MOUSEMOTION:
+            handle_mousemotion(ev);
+            break;
+        case SDL_MOUSEBUTTONDOWN:
+        case SDL_MOUSEBUTTONUP:
+            handle_mousebutton(ev);
+            break;
+        case SDL_WINDOWEVENT:
+            handle_windowevent(dcl, ev);
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+static void sdl_mouse_warp(DisplayChangeListener *dcl,
+                           int x, int y, int on)
+{
+    struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
+    if (on) {
+        if (!guest_cursor) {
+            sdl_show_cursor();
+        }
+        if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
+            SDL_SetCursor(guest_sprite);
+            if (!qemu_input_is_absolute() && !absolute_enabled) {
+                SDL_WarpMouseInWindow(scon->real_window, x, y);
+            }
+        }
+    } else if (gui_grab) {
+        sdl_hide_cursor();
+    }
+    guest_cursor = on;
+    guest_x = x, guest_y = y;
+}
+
+static void sdl_mouse_define(DisplayChangeListener *dcl,
+                             QEMUCursor *c)
+{
+
+    if (guest_sprite) {
+        SDL_FreeCursor(guest_sprite);
+    }
+
+    if (guest_sprite_surface) {
+        SDL_FreeSurface(guest_sprite_surface);
+    }
+
+    guest_sprite_surface =
+        SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
+                                 0xff0000, 0x00ff00, 0xff, 0xff000000);
+
+    if (!guest_sprite_surface) {
+        fprintf(stderr, "Failed to make rgb surface from %p\n", c);
+        return;
+    }
+    guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
+                                         c->hot_x, c->hot_y);
+    if (!guest_sprite) {
+        fprintf(stderr, "Failed to make color cursor from %p\n", c);
+        return;
+    }
+    if (guest_cursor &&
+        (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
+        SDL_SetCursor(guest_sprite);
+    }
+}
+
+static void sdl_cleanup(void)
+{
+    if (guest_sprite) {
+        SDL_FreeCursor(guest_sprite);
+    }
+    SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+static const DisplayChangeListenerOps dcl_ops = {
+    .dpy_name          = "sdl",
+    .dpy_gfx_update    = sdl_update,
+    .dpy_gfx_switch    = sdl_switch,
+    .dpy_refresh       = sdl_refresh,
+    .dpy_mouse_set     = sdl_mouse_warp,
+    .dpy_cursor_define = sdl_mouse_define,
+};
+
+void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
+{
+    int flags;
+    uint8_t data = 0;
+    char *filename;
+    int i;
+
+    if (no_frame) {
+        gui_noframe = 1;
+    }
+
+#ifdef __linux__
+    /* on Linux, SDL may use fbcon|directfb|svgalib when run without
+     * accessible $DISPLAY to open X11 window.  This is often the case
+     * when qemu is run using sudo.  But in this case, and when actually
+     * run in X11 environment, SDL fights with X11 for the video card,
+     * making current display unavailable, often until reboot.
+     * So make x11 the default SDL video driver if this variable is unset.
+     * This is a bit hackish but saves us from bigger problem.
+     * Maybe it's a good idea to fix this in SDL instead.
+     */
+    setenv("SDL_VIDEODRIVER", "x11", 0);
+#endif
+
+    flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
+    if (SDL_Init(flags)) {
+        fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
+                SDL_GetError());
+        exit(1);
+    }
+
+    for (i = 0;; i++) {
+        QemuConsole *con = qemu_console_lookup_by_index(i);
+        if (!con || !qemu_console_is_graphic(con)) {
+            break;
+        }
+    }
+    sdl2_num_outputs = i;
+    sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs);
+    for (i = 0; i < sdl2_num_outputs; i++) {
+        QemuConsole *con = qemu_console_lookup_by_index(i);
+        sdl2_console[i].dcl.ops = &dcl_ops;
+        sdl2_console[i].dcl.con = con;
+        register_displaychangelistener(&sdl2_console[i].dcl);
+        sdl2_console[i].idx = i;
+    }
+
+    /* Load a 32x32x4 image. White pixels are transparent. */
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
+    if (filename) {
+        SDL_Surface *image = SDL_LoadBMP(filename);
+        if (image) {
+            uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
+            SDL_SetColorKey(image, SDL_TRUE, colorkey);
+            SDL_SetWindowIcon(sdl2_console[0].real_window, image);
+        }
+        g_free(filename);
+    }
+
+    if (full_screen) {
+        gui_fullscreen = 1;
+        sdl_grab_start(0);
+    }
+
+    mouse_mode_notifier.notify = sdl_mouse_mode_change;
+    qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
+
+    gui_grab = 0;
+
+    sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
+    sdl_cursor_normal = SDL_GetCursor();
+
+    atexit(sdl_cleanup);
+}
+#endif
diff --git a/ui/sdl_keysym.h b/ui/sdl_keysym.h
index ee904805da..599d9fc64d 100644
--- a/ui/sdl_keysym.h
+++ b/ui/sdl_keysym.h
@@ -200,6 +200,7 @@ static const name2keysym_t name2keysym[]={
 { "yacute",               0x0fd},
 { "thorn",                0x0fe},
 { "ydiaeresis",           0x0ff},
+#if SDL_MAJOR_VERSION == 1
 {"EuroSign", SDLK_EURO},
 
     /* modifiers */
@@ -272,6 +273,6 @@ static const name2keysym_t name2keysym[]={
 {"Num_Lock", SDLK_NUMLOCK},
 {"Pause", SDLK_PAUSE},
 {"Escape", SDLK_ESCAPE},
-
+#endif
 {NULL, 0},
 };
diff --git a/ui/spice-input.c b/ui/spice-input.c
index 3beb8deadb..6dab23b75c 100644
--- a/ui/spice-input.c
+++ b/ui/spice-input.c
@@ -26,12 +26,15 @@
 #include "qemu-common.h"
 #include "ui/qemu-spice.h"
 #include "ui/console.h"
+#include "ui/keymaps.h"
+#include "ui/input.h"
 
 /* keyboard bits */
 
 typedef struct QemuSpiceKbd {
     SpiceKbdInstance sin;
     int ledstate;
+    bool emul0;
 } QemuSpiceKbd;
 
 static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
@@ -47,9 +50,24 @@ static const SpiceKbdInterface kbd_interface = {
     .get_leds           = kbd_get_leds,
 };
 
-static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t scancode)
 {
-    kbd_put_keycode(frag);
+    QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin);
+    int keycode;
+    bool up;
+
+    if (scancode == SCANCODE_EMUL0) {
+        kbd->emul0 = true;
+        return;
+    }
+    keycode = scancode & ~SCANCODE_UP;
+    up = scancode & SCANCODE_UP;
+    if (kbd->emul0) {
+        kbd->emul0 = false;
+        keycode |= SCANCODE_GREY;
+    }
+
+    qemu_input_event_send_key_number(NULL, keycode, !up);
 }
 
 static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
@@ -80,41 +98,52 @@ static void kbd_leds(void *opaque, int ledstate)
 typedef struct QemuSpicePointer {
     SpiceMouseInstance  mouse;
     SpiceTabletInstance tablet;
-    int width, height, x, y;
+    int width, height;
+    uint32_t last_bmask;
     Notifier mouse_mode;
     bool absolute;
 } QemuSpicePointer;
 
-static int map_buttons(int spice_buttons)
+static void spice_update_buttons(QemuSpicePointer *pointer,
+                                 int wheel, uint32_t button_mask)
 {
-    int qemu_buttons = 0;
-
-    /*
-     * Note: SPICE_MOUSE_BUTTON_* specifies the wire protocol but this
-     * isn't what we get passed in via interface callbacks for the
-     * middle and right button ...
-     */
-    if (spice_buttons & SPICE_MOUSE_BUTTON_MASK_LEFT) {
-        qemu_buttons |= MOUSE_EVENT_LBUTTON;
+    static uint32_t bmap[INPUT_BUTTON_MAX] = {
+        [INPUT_BUTTON_LEFT]        = 0x01,
+        [INPUT_BUTTON_MIDDLE]      = 0x04,
+        [INPUT_BUTTON_RIGHT]       = 0x02,
+        [INPUT_BUTTON_WHEEL_UP]    = 0x10,
+        [INPUT_BUTTON_WHEEL_DOWN]  = 0x20,
+    };
+
+    if (wheel < 0) {
+        button_mask |= 0x10;
     }
-    if (spice_buttons & 0x04 /* SPICE_MOUSE_BUTTON_MASK_MIDDLE */) {
-        qemu_buttons |= MOUSE_EVENT_MBUTTON;
+    if (wheel > 0) {
+        button_mask |= 0x20;
     }
-    if (spice_buttons & 0x02 /* SPICE_MOUSE_BUTTON_MASK_RIGHT */) {
-        qemu_buttons |= MOUSE_EVENT_RBUTTON;
+
+    if (pointer->last_bmask == button_mask) {
+        return;
     }
-    return qemu_buttons;
+    qemu_input_update_buttons(NULL, bmap, pointer->last_bmask, button_mask);
+    pointer->last_bmask = button_mask;
 }
 
 static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
                          uint32_t buttons_state)
 {
-    kbd_mouse_event(dx, dy, dz, map_buttons(buttons_state));
+    QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse);
+    spice_update_buttons(pointer, dz, buttons_state);
+    qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx);
+    qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy);
+    qemu_input_event_sync();
 }
 
 static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state)
 {
-    kbd_mouse_event(0, 0, 0, map_buttons(buttons_state));
+    QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse);
+    spice_update_buttons(pointer, 0, buttons_state);
+    qemu_input_event_sync();
 }
 
 static const SpiceMouseInterface mouse_interface = {
@@ -145,9 +174,10 @@ static void tablet_position(SpiceTabletInstance* sin, int x, int y,
 {
     QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
 
-    pointer->x = x * 0x7FFF / (pointer->width - 1);
-    pointer->y = y * 0x7FFF / (pointer->height - 1);
-    kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
+    spice_update_buttons(pointer, 0, buttons_state);
+    qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, pointer->width);
+    qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, pointer->width);
+    qemu_input_event_sync();
 }
 
 
@@ -156,7 +186,8 @@ static void tablet_wheel(SpiceTabletInstance* sin, int wheel,
 {
     QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
 
-    kbd_mouse_event(pointer->x, pointer->y, wheel, map_buttons(buttons_state));
+    spice_update_buttons(pointer, wheel, buttons_state);
+    qemu_input_event_sync();
 }
 
 static void tablet_buttons(SpiceTabletInstance *sin,
@@ -164,7 +195,8 @@ static void tablet_buttons(SpiceTabletInstance *sin,
 {
     QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
 
-    kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
+    spice_update_buttons(pointer, 0, buttons_state);
+    qemu_input_event_sync();
 }
 
 static const SpiceTabletInterface tablet_interface = {
@@ -181,7 +213,7 @@ static const SpiceTabletInterface tablet_interface = {
 static void mouse_mode_notifier(Notifier *notifier, void *data)
 {
     QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode);
-    bool is_absolute  = kbd_mouse_is_absolute();
+    bool is_absolute  = qemu_input_is_absolute();
 
     if (pointer->absolute == is_absolute) {
         return;
diff --git a/ui/vnc.c b/ui/vnc.c
index 5601cc34ef..7dfc94a358 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -33,6 +33,7 @@
 #include "qapi/qmp/types.h"
 #include "qmp-commands.h"
 #include "qemu/osdep.h"
+#include "ui/input.h"
 
 #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
 #define VNC_REFRESH_INTERVAL_INC  50
@@ -1483,7 +1484,7 @@ static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
 static void check_pointer_type_change(Notifier *notifier, void *data)
 {
     VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
-    int absolute = kbd_mouse_is_absolute();
+    int absolute = qemu_input_is_absolute();
 
     if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
         vnc_lock_output(vs);
@@ -1502,39 +1503,37 @@ static void check_pointer_type_change(Notifier *notifier, void *data)
 
 static void pointer_event(VncState *vs, int button_mask, int x, int y)
 {
-    int buttons = 0;
-    int dz = 0;
+    static uint32_t bmap[INPUT_BUTTON_MAX] = {
+        [INPUT_BUTTON_LEFT]       = 0x01,
+        [INPUT_BUTTON_MIDDLE]     = 0x02,
+        [INPUT_BUTTON_RIGHT]      = 0x04,
+        [INPUT_BUTTON_WHEEL_UP]   = 0x08,
+        [INPUT_BUTTON_WHEEL_DOWN] = 0x10,
+    };
+    QemuConsole *con = vs->vd->dcl.con;
     int width = surface_width(vs->vd->ds);
     int height = surface_height(vs->vd->ds);
 
-    if (button_mask & 0x01)
-        buttons |= MOUSE_EVENT_LBUTTON;
-    if (button_mask & 0x02)
-        buttons |= MOUSE_EVENT_MBUTTON;
-    if (button_mask & 0x04)
-        buttons |= MOUSE_EVENT_RBUTTON;
-    if (button_mask & 0x08)
-        dz = -1;
-    if (button_mask & 0x10)
-        dz = 1;
+    if (vs->last_bmask != button_mask) {
+        qemu_input_update_buttons(con, bmap, vs->last_bmask, button_mask);
+        vs->last_bmask = button_mask;
+    }
 
     if (vs->absolute) {
-        kbd_mouse_event(width  > 1 ? x * 0x7FFF / (width  - 1) : 0x4000,
-                        height > 1 ? y * 0x7FFF / (height - 1) : 0x4000,
-                        dz, buttons);
+        qemu_input_queue_abs(con, INPUT_AXIS_X, x, width);
+        qemu_input_queue_abs(con, INPUT_AXIS_Y, y, height);
     } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
-        x -= 0x7FFF;
-        y -= 0x7FFF;
-
-        kbd_mouse_event(x, y, dz, buttons);
+        qemu_input_queue_rel(con, INPUT_AXIS_X, x - 0x7FFF);
+        qemu_input_queue_rel(con, INPUT_AXIS_Y, y - 0x7FFF);
     } else {
-        if (vs->last_x != -1)
-            kbd_mouse_event(x - vs->last_x,
-                            y - vs->last_y,
-                            dz, buttons);
+        if (vs->last_x != -1) {
+            qemu_input_queue_rel(con, INPUT_AXIS_X, x - vs->last_x);
+            qemu_input_queue_rel(con, INPUT_AXIS_Y, y - vs->last_y);
+        }
         vs->last_x = x;
         vs->last_y = y;
     }
+    qemu_input_event_sync();
 }
 
 static void reset_keys(VncState *vs)
@@ -1542,9 +1541,7 @@ static void reset_keys(VncState *vs)
     int i;
     for(i = 0; i < 256; i++) {
         if (vs->modifiers_state[i]) {
-            if (i & SCANCODE_GREY)
-                kbd_put_keycode(SCANCODE_EMUL0);
-            kbd_put_keycode(i | SCANCODE_UP);
+            qemu_input_event_send_key_number(vs->vd->dcl.con, i, false);
             vs->modifiers_state[i] = 0;
         }
     }
@@ -1553,12 +1550,8 @@ static void reset_keys(VncState *vs)
 static void press_key(VncState *vs, int keysym)
 {
     int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK;
-    if (keycode & SCANCODE_GREY)
-        kbd_put_keycode(SCANCODE_EMUL0);
-    kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
-    if (keycode & SCANCODE_GREY)
-        kbd_put_keycode(SCANCODE_EMUL0);
-    kbd_put_keycode(keycode | SCANCODE_UP);
+    qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true);
+    qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
 }
 
 static int current_led_state(VncState *vs)
@@ -1700,12 +1693,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
     }
 
     if (qemu_console_is_graphic(NULL)) {
-        if (keycode & SCANCODE_GREY)
-            kbd_put_keycode(SCANCODE_EMUL0);
-        if (down)
-            kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
-        else
-            kbd_put_keycode(keycode | SCANCODE_UP);
+        qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down);
     } else {
         bool numlock = vs->modifiers_state[0x45];
         bool control = (vs->modifiers_state[0x1d] ||
@@ -1826,10 +1814,7 @@ static void vnc_release_modifiers(VncState *vs)
         if (!vs->modifiers_state[keycode]) {
             continue;
         }
-        if (keycode & SCANCODE_GREY) {
-            kbd_put_keycode(SCANCODE_EMUL0);
-        }
-        kbd_put_keycode(keycode | SCANCODE_UP);
+        qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
     }
 }
 
diff --git a/ui/vnc.h b/ui/vnc.h
index 6e9921387f..e63c14284b 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -257,6 +257,7 @@ struct VncState
     int absolute;
     int last_x;
     int last_y;
+    uint32_t last_bmask;
     int client_width;
     int client_height;
     VncShareMode share_mode;