qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/4] linux framebuffer display driver
@ 2012-09-10 14:20 Gerd Hoffmann
  2012-09-10 14:20 ` [Qemu-devel] [PATCH 1/4] QLIST-ify display change listeners Gerd Hoffmann
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-09-10 14:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

  Hi,

Undusting an pretty old patch series, rebasing it, testing it, fixing
a bug or two, fixing codestyle issues:  Qemu can run on the linux
framebuffer console as graphical display now.

enjoy,
  Gerd

Gerd Hoffmann (4):
  QLIST-ify display change listeners.
  add unregister_displaychangelistener
  fbdev: add linux framebuffer display driver.
  fbdev: add monitor command to enable/disable

 console.h           |   86 +++--
 hmp-commands.hx     |   15 +
 hmp.c               |    9 +
 hmp.h               |    1 +
 hw/xenfb.c          |    2 +-
 qapi-schema.json    |   15 +
 qemu-options.hx     |    8 +
 qmp-commands.hx     |    6 +
 qmp.c               |   17 +
 sysemu.h            |    1 +
 trace-events        |   14 +
 ui/Makefile.objs    |    1 +
 ui/fbdev.c          |  953 +++++++++++++++++++++++++++++++++++++++++++++++++++
 ui/linux-keynames.h |  388 +++++++++++++++++++++
 vl.c                |   50 ++-
 15 files changed, 1518 insertions(+), 48 deletions(-)
 create mode 100644 ui/fbdev.c
 create mode 100644 ui/linux-keynames.h

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 1/4] QLIST-ify display change listeners.
  2012-09-10 14:20 [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Gerd Hoffmann
@ 2012-09-10 14:20 ` Gerd Hoffmann
  2012-09-10 14:20 ` [Qemu-devel] [PATCH 2/4] add unregister_displaychangelistener Gerd Hoffmann
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-09-10 14:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

---
 console.h  |   72 +++++++++++++++++++++++++++++++----------------------------
 hw/xenfb.c |    2 +-
 vl.c       |    9 ++-----
 3 files changed, 42 insertions(+), 41 deletions(-)

diff --git a/console.h b/console.h
index 4334db5..5a1abe9 100644
--- a/console.h
+++ b/console.h
@@ -162,7 +162,7 @@ struct DisplayChangeListener {
                      int w, int h, uint32_t c);
     void (*dpy_text_cursor)(struct DisplayState *s, int x, int y);
 
-    struct DisplayChangeListener *next;
+    QLIST_ENTRY(DisplayChangeListener) next;
 };
 
 struct DisplayAllocator {
@@ -177,7 +177,7 @@ struct DisplayState {
     struct QEMUTimer *gui_timer;
 
     struct DisplayAllocator* allocator;
-    struct DisplayChangeListener* listeners;
+    QLIST_HEAD(, DisplayChangeListener) listeners;
 
     void (*mouse_set)(int x, int y, int on);
     void (*cursor_define)(QEMUCursor *cursor);
@@ -229,72 +229,76 @@ static inline int is_buffer_shared(DisplaySurface *surface)
 
 static inline void register_displaychangelistener(DisplayState *ds, DisplayChangeListener *dcl)
 {
-    dcl->next = ds->listeners;
-    ds->listeners = dcl;
+    QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
 }
 
 static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
 {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
         dcl->dpy_update(s, x, y, w, h);
-        dcl = dcl->next;
     }
 }
 
 static inline void dpy_resize(DisplayState *s)
 {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
         dcl->dpy_resize(s);
-        dcl = dcl->next;
     }
 }
 
 static inline void dpy_setdata(DisplayState *s)
 {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_setdata) dcl->dpy_setdata(s);
-        dcl = dcl->next;
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_setdata) {
+            dcl->dpy_setdata(s);
+        }
     }
 }
 
 static inline void dpy_refresh(DisplayState *s)
 {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_refresh) dcl->dpy_refresh(s);
-        dcl = dcl->next;
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_refresh) {
+            dcl->dpy_refresh(s);
+        }
     }
 }
 
 static inline void dpy_copy(struct DisplayState *s, int src_x, int src_y,
-                             int dst_x, int dst_y, int w, int h) {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_copy)
+                             int dst_x, int dst_y, int w, int h)
+{
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_copy) {
             dcl->dpy_copy(s, src_x, src_y, dst_x, dst_y, w, h);
-        else /* TODO */
+        } else { /* TODO */
             dcl->dpy_update(s, dst_x, dst_y, w, h);
-        dcl = dcl->next;
+        }
     }
 }
 
 static inline void dpy_fill(struct DisplayState *s, int x, int y,
-                             int w, int h, uint32_t c) {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_fill) dcl->dpy_fill(s, x, y, w, h, c);
-        dcl = dcl->next;
+                             int w, int h, uint32_t c)
+{
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_fill) {
+            dcl->dpy_fill(s, x, y, w, h, c);
+        }
     }
 }
 
-static inline void dpy_cursor(struct DisplayState *s, int x, int y) {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_text_cursor) dcl->dpy_text_cursor(s, x, y);
-        dcl = dcl->next;
+static inline void dpy_cursor(struct DisplayState *s, int x, int y)
+{
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_text_cursor) {
+            dcl->dpy_text_cursor(s, x, y);
+        }
     }
 }
 
diff --git a/hw/xenfb.c b/hw/xenfb.c
index 338800a..ef24c33 100644
--- a/hw/xenfb.c
+++ b/hw/xenfb.c
@@ -717,7 +717,7 @@ static void xenfb_update(void *opaque)
 	if (xenfb_queue_full(xenfb))
 	    return;
 
-        for (l = xenfb->c.ds->listeners; l != NULL; l = l->next) {
+        QLIST_FOREACH(l, &xenfb->c.ds->listeners, next) {
             if (l->idle)
                 continue;
             idle = 0;
diff --git a/vl.c b/vl.c
index 7c577fa..2a7c92a 100644
--- a/vl.c
+++ b/vl.c
@@ -1276,15 +1276,14 @@ static void gui_update(void *opaque)
 {
     uint64_t interval = GUI_REFRESH_INTERVAL;
     DisplayState *ds = opaque;
-    DisplayChangeListener *dcl = ds->listeners;
+    DisplayChangeListener *dcl;
 
     dpy_refresh(ds);
 
-    while (dcl != NULL) {
+    QLIST_FOREACH(dcl, &ds->listeners, next) {
         if (dcl->gui_timer_interval &&
             dcl->gui_timer_interval < interval)
             interval = dcl->gui_timer_interval;
-        dcl = dcl->next;
     }
     qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock));
 }
@@ -3699,14 +3698,12 @@ int main(int argc, char **argv, char **envp)
 
     /* display setup */
     dpy_resize(ds);
-    dcl = ds->listeners;
-    while (dcl != NULL) {
+    QLIST_FOREACH(dcl, &ds->listeners, next) {
         if (dcl->dpy_refresh != NULL) {
             ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
             qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
             break;
         }
-        dcl = dcl->next;
     }
     text_consoles_set_display(ds);
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 2/4] add unregister_displaychangelistener
  2012-09-10 14:20 [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Gerd Hoffmann
  2012-09-10 14:20 ` [Qemu-devel] [PATCH 1/4] QLIST-ify display change listeners Gerd Hoffmann
@ 2012-09-10 14:20 ` Gerd Hoffmann
  2012-09-10 14:20 ` [Qemu-devel] [PATCH 3/4] fbdev: add linux framebuffer display driver Gerd Hoffmann
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-09-10 14:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Also change the way the gui_timer is initialized: each time a
displaychangelistener is registered or unregistered we'll check
whenever we need a timer (due to dpy_refresh callback being present)
and if so setup a timer, otherwise zap it.  This way the gui timer works
correctly with displaychangelisteners coming and going.
---
 console.h |   10 ++++++++++
 vl.c      |   31 +++++++++++++++++++++++--------
 2 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/console.h b/console.h
index 5a1abe9..ddf3a3b 100644
--- a/console.h
+++ b/console.h
@@ -227,9 +227,19 @@ static inline int is_buffer_shared(DisplaySurface *surface)
             !(surface->flags & QEMU_REALPIXELS_FLAG));
 }
 
+void gui_setup_refresh(DisplayState *ds);
+
 static inline void register_displaychangelistener(DisplayState *ds, DisplayChangeListener *dcl)
 {
     QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
+    gui_setup_refresh(ds);
+}
+
+static inline void unregister_displaychangelistener(DisplayState *ds,
+                                                    DisplayChangeListener *dcl)
+{
+    QLIST_REMOVE(dcl, next);
+    gui_setup_refresh(ds);
 }
 
 static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
diff --git a/vl.c b/vl.c
index 2a7c92a..fbb77fe 100644
--- a/vl.c
+++ b/vl.c
@@ -1288,6 +1288,29 @@ static void gui_update(void *opaque)
     qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock));
 }
 
+void gui_setup_refresh(DisplayState *ds)
+{
+    DisplayChangeListener *dcl;
+    bool need_timer = false;
+
+    QLIST_FOREACH(dcl, &ds->listeners, next) {
+        if (dcl->dpy_refresh != NULL) {
+            need_timer = true;
+            break;
+        }
+    }
+
+    if (need_timer && ds->gui_timer == NULL) {
+        ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
+        qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
+    }
+    if (!need_timer && ds->gui_timer != NULL) {
+        qemu_del_timer(ds->gui_timer);
+        qemu_free_timer(ds->gui_timer);
+        ds->gui_timer = NULL;
+    }
+}
+
 struct vm_change_state_entry {
     VMChangeStateHandler *cb;
     void *opaque;
@@ -2350,7 +2373,6 @@ int main(int argc, char **argv, char **envp)
     const char *kernel_filename, *kernel_cmdline;
     char boot_devices[33] = "cad"; /* default to HD->floppy->CD-ROM */
     DisplayState *ds;
-    DisplayChangeListener *dcl;
     int cyls, heads, secs, translation;
     QemuOpts *hda_opts = NULL, *opts, *machine_opts;
     QemuOptsList *olist;
@@ -3698,13 +3720,6 @@ int main(int argc, char **argv, char **envp)
 
     /* display setup */
     dpy_resize(ds);
-    QLIST_FOREACH(dcl, &ds->listeners, next) {
-        if (dcl->dpy_refresh != NULL) {
-            ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
-            qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
-            break;
-        }
-    }
     text_consoles_set_display(ds);
 
     if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 3/4] fbdev: add linux framebuffer display driver.
  2012-09-10 14:20 [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Gerd Hoffmann
  2012-09-10 14:20 ` [Qemu-devel] [PATCH 1/4] QLIST-ify display change listeners Gerd Hoffmann
  2012-09-10 14:20 ` [Qemu-devel] [PATCH 2/4] add unregister_displaychangelistener Gerd Hoffmann
@ 2012-09-10 14:20 ` Gerd Hoffmann
  2012-09-11 11:10   ` Markus Armbruster
  2012-09-11 20:37   ` Blue Swirl
  2012-09-10 14:21 ` [Qemu-devel] [PATCH 4/4] fbdev: add monitor command to enable/disable Gerd Hoffmann
  2012-09-11  8:09 ` [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Alon Levy
  4 siblings, 2 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-09-10 14:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Display works, requires truecolor framebuffer with 16 or 32 bpp on the
host.  32bpp is recommended.  The framebuffer is used as-is, qemu
doesn't try to switch modes.  With LCD displays mode switching is pretty
pointless IMHO, also it wouldn't work anyway with the most common
fbdev drivers (vesafb, KMS).  Guest display is centered on the host
screen.

Mouse works, uses /dev/input/mice.

Keyboard works.  Guest screen has whatever keymap you load inside
the guest.  Text windows (monitor, serial, ...) have a simple en-us
keymap.  Good enougth to type monitor commands.  Not goot enougth to
work seriously on a serial terminal.  But the qemu terminal emulation
isn't good enougth for that anyway ;)

Hot keys:
  Ctrl-Alt-F<nr>  -> host console switching.
  Ctrl-Alt-<nr>   -> qemu console switching.
  Ctrl-Alt-ESC    -> exit qemu.

Special feature:  Sane console switching.  Switching away stops screen
updates.  Switching back redraws the screen.  When started from the
linux console qemu uses the vt you've started it from (requires just
read/write access to /dev/fb0).  When starting from somewhere else qemu
tries to open a unused virtual terminal and switch to it (usually
requires root privileges to open /dev/tty<nr>).

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 console.h           |    4 +
 qemu-options.hx     |    8 +
 sysemu.h            |    1 +
 trace-events        |   14 +
 ui/Makefile.objs    |    1 +
 ui/fbdev.c          |  953 +++++++++++++++++++++++++++++++++++++++++++++++++++
 ui/linux-keynames.h |  388 +++++++++++++++++++++
 vl.c                |   12 +
 8 files changed, 1381 insertions(+), 0 deletions(-)
 create mode 100644 ui/fbdev.c
 create mode 100644 ui/linux-keynames.h

diff --git a/console.h b/console.h
index ddf3a3b..9d1fe72 100644
--- a/console.h
+++ b/console.h
@@ -384,6 +384,10 @@ void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
 /* sdl.c */
 void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
 
+/* fbdev.c */
+int fbdev_display_init(DisplayState *ds, const char *device);
+void fbdev_display_uninit(DisplayState *ds);
+
 /* cocoa.m */
 void cocoa_display_init(DisplayState *ds, int full_screen);
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 3c411c4..b02dcbb 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -923,6 +923,14 @@ Enable/disable audio stream compression (using celt 0.5.1).  Default is on.
 @end table
 ETEXI
 
+DEF("fbdev", 0, QEMU_OPTION_fbdev,
+    "-fbdev          enable fbdev\n", QEMU_ARCH_ALL)
+STEXI
+@item -fbdev
+@findex -fbdev
+Enable fbdev (linux framebuffer).
+ETEXI
+
 DEF("portrait", 0, QEMU_OPTION_portrait,
     "-portrait       rotate graphical output 90 deg left (only PXA LCD)\n",
     QEMU_ARCH_ALL)
diff --git a/sysemu.h b/sysemu.h
index 65552ac..34e6bfa 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -93,6 +93,7 @@ typedef enum DisplayType
     DT_DEFAULT,
     DT_CURSES,
     DT_SDL,
+    DT_FBDEV,
     DT_NOGRAPHIC,
     DT_NONE,
 } DisplayType;
diff --git a/trace-events b/trace-events
index 04b0723..3745db3 100644
--- a/trace-events
+++ b/trace-events
@@ -978,3 +978,17 @@ spapr_pci_rtas_ibm_change_msi(unsigned func, unsigned req) "func %u, requested %
 spapr_pci_rtas_ibm_query_interrupt_source_number(unsigned ioa, unsigned intr) "queries for #%u, IRQ%u"
 spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq) "@%"PRIx64"<=%"PRIx64" IRQ %u"
 spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ %u"
+
+# ui/fbdev.c
+fbdev_enabled(void) ""
+fbdev_cleanup(void) ""
+fbdev_vt_activate(int vtno, int wait) "vtno %d, wait %d"
+fbdev_vt_activated(void) ""
+fbdev_vt_release_request(void) ""
+fbdev_vt_released(void) ""
+fbdev_vt_aquire_request(void) ""
+fbdev_vt_aquired(void) ""
+fbdev_kbd_raw(int enable) "enable %d"
+fbdev_kbd_event(int keycode, const char *kname, int up) "keycode 0x%x [%s], down %d"
+fbdev_dpy_resize(int w, int h) "%dx%d"
+fbdev_dpy_redraw(void)
\ No newline at end of file
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index adc07be..55ddcf2 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -12,3 +12,4 @@ common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
 common-obj-$(CONFIG_COCOA) += cocoa.o
 common-obj-$(CONFIG_CURSES) += curses.o
 common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
+common-obj-$(CONFIG_LINUX) += fbdev.o
diff --git a/ui/fbdev.c b/ui/fbdev.c
new file mode 100644
index 0000000..32c0a3b
--- /dev/null
+++ b/ui/fbdev.c
@@ -0,0 +1,953 @@
+/*
+ * linux fbdev output driver.
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <linux/fb.h>
+
+#include "qemu-common.h"
+#include "console.h"
+#include "keymaps.h"
+#include "sysemu.h"
+#include "pflib.h"
+
+/*
+ * must be last so we get the linux input layer
+ * KEY_* defines, not the ncurses ones.
+ */
+#include <linux/input.h>
+
+/* -------------------------------------------------------------------- */
+
+/* file handles */
+static int                        tty = -1, fb = -1, mice = -1;
+
+/* saved state, for restore on exit */
+static int                        orig_vtno;
+static int                        kd_omode;
+static struct vt_mode             vt_omode;
+static struct fb_var_screeninfo   fb_ovar;
+
+/* framebuffer */
+static struct fb_fix_screeninfo   fb_fix;
+static struct fb_var_screeninfo   fb_var;
+static uint8_t                   *fb_mem;
+static int                        fb_mem_offset;
+
+/* linux console */
+static int                        vtno;
+static struct vt_mode             vt_mode;
+static struct termios             tty_attributes;
+static unsigned long              tty_mode;
+static unsigned int               tty_flags;
+static bool                       tty_mediumraw;
+static bool                       key_down[KEY_CNT];
+
+/* console switching */
+#define SIG_ACQ      (SIGRTMIN+6)
+#define SIG_REL      (SIGRTMIN+7)
+#define FB_ACTIVE    0
+#define FB_REL_REQ   1
+#define FB_INACTIVE  2
+#define FB_ACQ_REQ   3
+static int fb_switch_state;
+
+/* qdev windup */
+static DisplayChangeListener      *dcl;
+static QemuPfConv                 *conv;
+static PixelFormat                fbpf;
+static int                        resize_screen;
+static int                        redraw_screen;
+static int                        cx, cy, cw, ch;
+static Notifier                   exit_notifier;
+
+/* fwd decls */
+static int fbdev_activate_vt(int tty, int vtno, bool wait);
+
+/* -------------------------------------------------------------------- */
+/* mouse                                                                */
+
+static void read_mouse(void *opaque)
+{
+    char buf[3];
+    int rc, x, y, b;
+
+    rc = read(mice, buf, sizeof(buf));
+    if (rc != sizeof(buf)) {
+        return;
+    }
+
+    if (fb_switch_state != FB_ACTIVE) {
+        return;
+    }
+
+    x = buf[1];
+    y = -buf[2];
+    b = buf[0] & 0x7;
+
+    if (kbd_mouse_is_absolute()) {
+        static int ax, ay;
+        ax += x; ay += y;
+        if (ax < 0) {
+            ax = 0;
+        }
+        if (ay < 0) {
+            ay = 0;
+        }
+        if (ax >= cw) {
+            ax = cw-1;
+        }
+        if (ay >= ch) {
+            ay = ch-1;
+        }
+        kbd_mouse_event(ax * 0x7FFF / cw, ay * 0x7FFF / ch, 0, b);
+    } else {
+        kbd_mouse_event(x, y, 0, b);
+    }
+}
+
+static int init_mouse(void)
+{
+    mice = open("/dev/input/mice", O_RDONLY);
+    if (-1 == mice) {
+        return -1;
+    }
+    qemu_set_fd_handler(mice, read_mouse, NULL, NULL);
+    return 0;
+}
+
+static void uninit_mouse(void)
+{
+    if (-1 == mice) {
+        return;
+    }
+    qemu_set_fd_handler(mice, NULL, NULL, NULL);
+    close(mice);
+    mice = -1;
+}
+
+/* -------------------------------------------------------------------- */
+/* keyboard                                                             */
+
+static const char *keynames[] = {
+#include "linux-keynames.h"
+};
+
+static int scancode_map[KEY_CNT] = {
+    [KEY_ESC]           = 0x01,
+    [KEY_1]             = 0x02,
+    [KEY_2]             = 0x03,
+    [KEY_3]             = 0x04,
+    [KEY_4]             = 0x05,
+    [KEY_5]             = 0x06,
+    [KEY_6]             = 0x07,
+    [KEY_7]             = 0x08,
+    [KEY_8]             = 0x09,
+    [KEY_9]             = 0x0a,
+    [KEY_0]             = 0x0b,
+    [KEY_MINUS]         = 0x0c,
+    [KEY_EQUAL]         = 0x0d,
+    [KEY_BACKSPACE]     = 0x0e,
+
+    [KEY_TAB]           = 0x0f,
+    [KEY_Q]             = 0x10,
+    [KEY_W]             = 0x11,
+    [KEY_E]             = 0x12,
+    [KEY_R]             = 0x13,
+    [KEY_T]             = 0x14,
+    [KEY_Y]             = 0x15,
+    [KEY_U]             = 0x16,
+    [KEY_I]             = 0x17,
+    [KEY_O]             = 0x18,
+    [KEY_P]             = 0x19,
+    [KEY_LEFTBRACE]     = 0x1a,
+    [KEY_RIGHTBRACE]    = 0x1b,
+    [KEY_ENTER]         = 0x1c,
+
+    [KEY_A]             = 0x1e,
+    [KEY_S]             = 0x1f,
+    [KEY_D]             = 0x20,
+    [KEY_F]             = 0x21,
+    [KEY_G]             = 0x22,
+    [KEY_H]             = 0x23,
+    [KEY_J]             = 0x24,
+    [KEY_K]             = 0x25,
+    [KEY_L]             = 0x26,
+    [KEY_SEMICOLON]     = 0x27,
+    [KEY_APOSTROPHE]    = 0x28,
+    [KEY_GRAVE]         = 0x29,
+    [KEY_LEFTSHIFT]     = 0x2a,
+    [KEY_BACKSLASH]     = 0x2b,
+
+    [KEY_Z]             = 0x2c,
+    [KEY_X]             = 0x2d,
+    [KEY_C]             = 0x2e,
+    [KEY_V]             = 0x2f,
+    [KEY_B]             = 0x30,
+    [KEY_N]             = 0x31,
+    [KEY_M]             = 0x32,
+    [KEY_COMMA]         = 0x33,
+    [KEY_DOT]           = 0x34,
+    [KEY_SLASH]         = 0x35,
+    [KEY_RIGHTSHIFT]    = 0x36,
+    [KEY_SPACE]         = 0x39,
+
+    [KEY_F1]            = 0x3b,
+    [KEY_F2]            = 0x3c,
+    [KEY_F3]            = 0x3d,
+    [KEY_F4]            = 0x3e,
+    [KEY_F5]            = 0x3f,
+    [KEY_F6]            = 0x40,
+    [KEY_F7]            = 0x41,
+    [KEY_F8]            = 0x42,
+    [KEY_F9]            = 0x43,
+    [KEY_F10]           = 0x44,
+    [KEY_F11]           = 0x57,
+    [KEY_F12]           = 0x58,
+
+    [KEY_SYSRQ]         = 0xb7,
+    [KEY_SCROLLLOCK]    = 0x46,
+#if 0
+    [KEY_PAUSE]         = FIXME,
+#endif
+    [KEY_CAPSLOCK]      = 0x3a,
+    [KEY_102ND]         = 0x56,
+
+    [KEY_LEFTCTRL]      = 0x1d,
+    [KEY_LEFTMETA]      = 0xdb,
+    [KEY_LEFTALT]       = 0x38,
+    [KEY_RIGHTALT]      = 0xb8,
+    [KEY_RIGHTMETA]     = 0xdc,
+    [KEY_RIGHTCTRL]     = 0x9d,
+    [KEY_COMPOSE]       = 0xdd,
+
+    [KEY_INSERT]        = 0xd2,
+    [KEY_DELETE]        = 0xd3,
+    [KEY_HOME]          = 0xc7,
+    [KEY_END]           = 0xcf,
+    [KEY_PAGEUP]        = 0xc9,
+    [KEY_PAGEDOWN]      = 0xd1,
+
+    [KEY_UP]            = 0xc8,
+    [KEY_LEFT]          = 0xcb,
+    [KEY_RIGHT]         = 0xcd,
+    [KEY_DOWN]          = 0xd0,
+
+    [KEY_NUMLOCK]       = 0x45,
+    [KEY_KPSLASH]       = 0xb5,
+    [KEY_KPASTERISK]    = 0x37,
+    [KEY_KP7]           = 0x47,
+    [KEY_KP8]           = 0x48,
+    [KEY_KP9]           = 0x49,
+    [KEY_KPMINUS]       = 0x4a,
+    [KEY_KP4]           = 0x4b,
+    [KEY_KP5]           = 0x4c,
+    [KEY_KP6]           = 0x4d,
+    [KEY_KPPLUS]        = 0x4e,
+    [KEY_KP1]           = 0x4f,
+    [KEY_KP2]           = 0x50,
+    [KEY_KP3]           = 0x51,
+    [KEY_KP0]           = 0x52,
+    [KEY_KPDOT]         = 0x53,
+    [KEY_KPENTER]       = 0x9c,
+};
+
+static struct keysym_map {
+    int  normal, shifted;
+} keysym_map_en_us[KEY_CNT] = {
+    [KEY_A] = { .normal = 'a', .shifted = 'A' },
+    [KEY_B] = { .normal = 'b', .shifted = 'B' },
+    [KEY_C] = { .normal = 'c', .shifted = 'C' },
+    [KEY_D] = { .normal = 'd', .shifted = 'D' },
+    [KEY_E] = { .normal = 'e', .shifted = 'E' },
+    [KEY_F] = { .normal = 'f', .shifted = 'F' },
+    [KEY_G] = { .normal = 'g', .shifted = 'G' },
+    [KEY_H] = { .normal = 'h', .shifted = 'H' },
+    [KEY_I] = { .normal = 'i', .shifted = 'I' },
+    [KEY_J] = { .normal = 'j', .shifted = 'J' },
+    [KEY_K] = { .normal = 'k', .shifted = 'K' },
+    [KEY_L] = { .normal = 'l', .shifted = 'L' },
+    [KEY_M] = { .normal = 'm', .shifted = 'M' },
+    [KEY_N] = { .normal = 'n', .shifted = 'N' },
+    [KEY_O] = { .normal = 'o', .shifted = 'O' },
+    [KEY_P] = { .normal = 'p', .shifted = 'P' },
+    [KEY_Q] = { .normal = 'q', .shifted = 'Q' },
+    [KEY_R] = { .normal = 'r', .shifted = 'R' },
+    [KEY_S] = { .normal = 's', .shifted = 'S' },
+    [KEY_T] = { .normal = 't', .shifted = 'T' },
+    [KEY_U] = { .normal = 'u', .shifted = 'U' },
+    [KEY_V] = { .normal = 'v', .shifted = 'V' },
+    [KEY_W] = { .normal = 'w', .shifted = 'W' },
+    [KEY_X] = { .normal = 'x', .shifted = 'X' },
+    [KEY_Y] = { .normal = 'y', .shifted = 'Y' },
+    [KEY_Z] = { .normal = 'z', .shifted = 'Z' },
+
+    [KEY_1] = { .normal = '1', .shifted = '!' },
+    [KEY_2] = { .normal = '2', .shifted = '@' },
+    [KEY_3] = { .normal = '3', .shifted = '#' },
+    [KEY_4] = { .normal = '4', .shifted = '$' },
+    [KEY_5] = { .normal = '5', .shifted = '%' },
+    [KEY_6] = { .normal = '6', .shifted = '^' },
+    [KEY_7] = { .normal = '7', .shifted = '&' },
+    [KEY_8] = { .normal = '8', .shifted = '*' },
+    [KEY_9] = { .normal = '9', .shifted = '(' },
+    [KEY_0] = { .normal = '0', .shifted = ')' },
+
+    [KEY_MINUS]       = { .normal = '-',  .shifted = '_'  },
+    [KEY_EQUAL]       = { .normal = '=',  .shifted = '+'  },
+    [KEY_TAB]         = { .normal = '\t'  },
+    [KEY_LEFTBRACE]   = { .normal = '[',  .shifted = '{'  },
+    [KEY_RIGHTBRACE]  = { .normal = ']',  .shifted = '}'  },
+    [KEY_ENTER]       = { .normal = '\n', },
+    [KEY_SEMICOLON]   = { .normal = ';',  .shifted = ':'  },
+    [KEY_APOSTROPHE]  = { .normal = '"',  .shifted = '\'' },
+    [KEY_BACKSLASH]   = { .normal = '\\', .shifted = '|'  },
+    [KEY_COMMA]       = { .normal = ',',  .shifted = '<'  },
+    [KEY_DOT]         = { .normal = '.',  .shifted = '>'  },
+    [KEY_SLASH]       = { .normal = '/',  .shifted = '?'  },
+    [KEY_SPACE]       = { .normal = ' '   },
+
+    [KEY_BACKSPACE]   = { .normal = QEMU_KEY_BACKSPACE  },
+    [KEY_UP]          = { .normal = QEMU_KEY_UP         },
+    [KEY_DOWN]        = { .normal = QEMU_KEY_DOWN       },
+    [KEY_LEFT]        = { .normal = QEMU_KEY_LEFT       },
+    [KEY_RIGHT]       = { .normal = QEMU_KEY_RIGHT      },
+};
+
+static void start_mediumraw(int tty)
+{
+    struct termios tattr;
+
+    if (tty_mediumraw) {
+        return;
+    }
+    trace_fbdev_kbd_raw(1);
+
+    /* save state */
+    tcgetattr(tty, &tty_attributes);
+    ioctl(tty, KDGKBMODE, &tty_mode);
+    tty_flags = fcntl(tty, F_GETFL, NULL);
+
+    /* setup */
+    tattr = tty_attributes;
+    tattr.c_cflag &= ~(IXON|IXOFF);
+    tattr.c_lflag &= ~(ICANON|ECHO|ISIG);
+    tattr.c_iflag = 0;
+    tattr.c_cc[VMIN] = 1;
+    tattr.c_cc[VTIME] = 0;
+    tcsetattr(tty, TCSAFLUSH, &tattr);
+    ioctl(tty, KDSKBMODE, K_MEDIUMRAW);
+    fcntl(tty, F_SETFL, tty_flags | O_NONBLOCK);
+
+    tty_mediumraw = true;
+}
+
+static void stop_mediumraw(int tty)
+{
+    if (!tty_mediumraw) {
+        return;
+    }
+    trace_fbdev_kbd_raw(0);
+
+    /* restore state */
+    tcsetattr(tty, TCSANOW, &tty_attributes);
+    ioctl(tty, KDSKBMODE, tty_mode);
+    fcntl(tty, F_SETFL, tty_flags);
+
+    tty_mediumraw = false;
+}
+
+static void send_scancode(int keycode, int up)
+{
+    int scancode = scancode_map[keycode];
+
+    if (!scancode) {
+        fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
+                __func__, keycode, keynames[keycode]);
+        return;
+    }
+    if (scancode & SCANCODE_GREY) {
+        kbd_put_keycode(SCANCODE_EMUL0);
+    }
+    if (up) {
+        kbd_put_keycode(scancode | SCANCODE_UP);
+    } else {
+        kbd_put_keycode(scancode & SCANCODE_KEYCODEMASK);
+    }
+}
+
+static void send_keysym(int keycode, int shift)
+{
+    struct keysym_map *keysym_map = keysym_map_en_us;
+    int keysym;
+
+    if (shift && keysym_map[keycode].shifted) {
+        keysym = keysym_map[keycode].shifted;
+    } else if (keysym_map[keycode].normal) {
+        keysym = keysym_map[keycode].normal;
+    } else {
+        fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
+                __func__, keycode, keynames[keycode]);
+        return;
+    }
+    kbd_put_keysym(keysym);
+}
+
+static void reset_keys(void)
+{
+    int keycode;
+
+    for (keycode = 0; keycode < KEY_MAX; keycode++) {
+        if (key_down[keycode]) {
+            if (is_graphic_console()) {
+                send_scancode(keycode, 1);
+            }
+            key_down[keycode] = false;
+        }
+    }
+}
+
+static void read_mediumraw(void *opaque)
+{
+    uint8_t buf[32];
+    int i, rc, up, keycode;
+    bool ctrl, alt, shift;
+
+    rc = read(tty, buf, sizeof(buf));
+    switch (rc) {
+    case -1:
+        perror("read tty");
+        goto err;
+    case 0:
+        fprintf(stderr, "%s: eof\n", __func__);
+        goto err;
+    default:
+        for (i = 0; i < rc; i++) {
+            up      = buf[i] & 0x80;
+            keycode = buf[i] & 0x7f;
+            if (keycode == 0) {
+                keycode  = (buf[i+1] & 0x7f) << 7;
+                keycode |= buf[i+2] & 0x7f;
+                i += 2;
+            }
+            if (keycode > KEY_MAX) {
+                continue;
+            }
+
+            if (up) {
+                if (!key_down[keycode]) {
+                    continue;
+                }
+                key_down[keycode] = false;
+            } else {
+                key_down[keycode] = true;
+            }
+
+            trace_fbdev_kbd_event(keycode, keynames[keycode], !up);
+
+            alt   = key_down[KEY_LEFTALT]   || key_down[KEY_RIGHTALT];
+            ctrl  = key_down[KEY_LEFTCTRL]  || key_down[KEY_RIGHTCTRL];
+            shift = key_down[KEY_LEFTSHIFT] || key_down[KEY_RIGHTSHIFT];
+
+            if (ctrl && alt && !up) {
+                if (keycode == KEY_ESC) {
+                    fprintf(stderr, "=== fbdev emergency escape "
+                            "(ctrl-alt-esc) ===\n");
+                    exit(1);
+                }
+                if (keycode >= KEY_F1 && keycode <= KEY_F10) {
+                    fbdev_activate_vt(tty, keycode+1-KEY_F1, false);
+                    key_down[keycode] = false;
+                    continue;
+                }
+                if (keycode >= KEY_1 && keycode <= KEY_9) {
+                    console_select(keycode-KEY_1);
+                    reset_keys();
+                    continue;
+                }
+            }
+
+            if (is_graphic_console()) {
+                send_scancode(keycode, up);
+            } else if (!up) {
+                send_keysym(keycode, shift);
+            }
+        }
+    }
+    return;
+
+err:
+    exit(1);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void fbdev_cls(void)
+{
+    memset(fb_mem+fb_mem_offset, 0, fb_fix.line_length * fb_var.yres);
+}
+
+static int fbdev_activate_vt(int tty, int vtno, bool wait)
+{
+    trace_fbdev_vt_activate(vtno, wait);
+
+    if (ioctl(tty, VT_ACTIVATE, vtno) < 0) {
+        perror("ioctl VT_ACTIVATE");
+        return -1;
+    }
+
+    if (wait) {
+        if (ioctl(tty, VT_WAITACTIVE, vtno) < 0) {
+            perror("ioctl VT_WAITACTIVE");
+            return -1;
+        }
+        trace_fbdev_vt_activated();
+    }
+
+    return 0;
+}
+
+static void fbdev_cleanup(void)
+{
+    trace_fbdev_cleanup();
+
+    /* restore console */
+    if (fb_mem != NULL) {
+        munmap(fb_mem, fb_fix.smem_len+fb_mem_offset);
+        fb_mem = NULL;
+    }
+    if (fb != -1) {
+        if (ioctl(fb, FBIOPUT_VSCREENINFO, &fb_ovar) < 0) {
+            perror("ioctl FBIOPUT_VSCREENINFO");
+        }
+        close(fb);
+        fb = -1;
+    }
+
+    if (tty != -1) {
+        stop_mediumraw(tty);
+        if (ioctl(tty, KDSETMODE, kd_omode) < 0) {
+            perror("ioctl KDSETMODE");
+        }
+        if (ioctl(tty, VT_SETMODE, &vt_omode) < 0) {
+            perror("ioctl VT_SETMODE");
+        }
+        if (orig_vtno) {
+            fbdev_activate_vt(tty, orig_vtno, true);
+        }
+        qemu_set_fd_handler(tty, NULL, NULL, NULL);
+        close(tty);
+        tty = -1;
+    }
+}
+
+static int fbdev_init(const char *device)
+{
+    struct vt_stat vts;
+    unsigned long page_mask;
+    char ttyname[32];
+
+    /* open framebuffer */
+    if (device == NULL) {
+        device = getenv("FRAMEBUFFER");
+    }
+    if (device == NULL) {
+        device = "/dev/fb0";
+    }
+    fb = open(device, O_RDWR);
+    if (fb == -1) {
+        fprintf(stderr, "open %s: %s\n", device, strerror(errno));
+        return -1;
+    }
+
+    /* open virtual console */
+    tty = 0;
+    if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
+        fprintf(stderr, "Not started from virtual terminal, "
+                "trying to open one.\n");
+
+        snprintf(ttyname, sizeof(ttyname), "/dev/tty0");
+        tty = open(ttyname, O_RDWR);
+        if (tty == -1) {
+            fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno));
+            goto err_early;
+        }
+        if (ioctl(tty, VT_OPENQRY, &vtno) < 0) {
+            perror("ioctl VT_OPENQRY");
+            goto err_early;
+        }
+        if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
+            perror("ioctl VT_GETSTATE\n");
+            goto err_early;
+        }
+        close(tty);
+
+        snprintf(ttyname, sizeof(ttyname), "/dev/tty%d", vtno);
+        tty = open(ttyname, O_RDWR);
+        if (tty == -1) {
+            fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno));
+            goto err_early;
+        }
+        orig_vtno = vts.v_active;
+        fprintf(stderr, "Switching to vt %d (current %d).\n", vtno, orig_vtno);
+    } else {
+        orig_vtno = 0;
+        vtno = vts.v_active;
+        fprintf(stderr, "Started at vt %d, using it.\n", vtno);
+    }
+    fbdev_activate_vt(tty, vtno, true);
+
+    /* get current settings (which we have to restore) */
+    if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) < 0) {
+        perror("ioctl FBIOGET_VSCREENINFO");
+        goto err_early;
+    }
+    if (ioctl(tty, KDGETMODE, &kd_omode) < 0) {
+        perror("ioctl KDGETMODE");
+        goto err_early;
+    }
+    if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) {
+        perror("ioctl VT_GETMODE");
+        goto err_early;
+    }
+
+    /* checks & initialisation */
+    if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) < 0) {
+        perror("ioctl FBIOGET_FSCREENINFO");
+        goto err;
+    }
+    if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) < 0) {
+        perror("ioctl FBIOGET_VSCREENINFO");
+        goto err;
+    }
+    if (fb_fix.type != FB_TYPE_PACKED_PIXELS) {
+        fprintf(stderr, "can handle only packed pixel frame buffers\n");
+        goto err;
+    }
+    switch (fb_var.bits_per_pixel) {
+    case 32:
+        break;
+    default:
+        fprintf(stderr, "can't handle %d bpp frame buffers\n",
+                fb_var.bits_per_pixel);
+        goto err;
+    }
+
+    page_mask = getpagesize()-1;
+    fb_switch_state = FB_ACTIVE;
+    fb_mem_offset = (unsigned long)(fb_fix.smem_start) & page_mask;
+    fb_mem = mmap(NULL, fb_fix.smem_len+fb_mem_offset,
+                  PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);
+    if (fb_mem == MAP_FAILED) {
+        perror("mmap");
+        goto err;
+    }
+    /* move viewport to upper left corner */
+    if (fb_var.xoffset != 0 || fb_var.yoffset != 0) {
+        fb_var.xoffset = 0;
+        fb_var.yoffset = 0;
+        if (ioctl(fb, FBIOPAN_DISPLAY, &fb_var) < 0) {
+            perror("ioctl FBIOPAN_DISPLAY");
+            goto err;
+        }
+    }
+    if (ioctl(tty, KDSETMODE, KD_GRAPHICS) < 0) {
+        perror("ioctl KDSETMODE");
+        goto err;
+    }
+    /* some fb drivers need this again after switching to graphics ... */
+    fbdev_activate_vt(tty, vtno, true);
+
+    fbdev_cls();
+
+    start_mediumraw(tty);
+    qemu_set_fd_handler(tty, read_mediumraw, NULL, NULL);
+
+    /* create PixelFormat from fbdev structs */
+    fbpf.bits_per_pixel  = fb_var.bits_per_pixel;
+    fbpf.bytes_per_pixel = (fb_var.bits_per_pixel+7)/8;
+    fbpf.depth           = fb_var.bits_per_pixel == 32
+        ? 24 : fb_var.bits_per_pixel;
+    fbpf.rshift          = fb_var.red.offset;
+    fbpf.rbits           = fb_var.red.length;
+    fbpf.gshift          = fb_var.green.offset;
+    fbpf.gbits           = fb_var.green.length;
+    fbpf.bshift          = fb_var.blue.offset;
+    fbpf.bbits           = fb_var.blue.length;
+    fbpf.ashift          = fb_var.transp.offset;
+    fbpf.abits           = fb_var.transp.length;
+
+    if (fbpf.rbits) {
+        fbpf.rmax   = (1 << fbpf.rbits) - 1;
+        fbpf.rmask  = fbpf.rmax << fbpf.rshift;
+    }
+    if (fbpf.gbits) {
+        fbpf.gmax   = (1 << fbpf.gbits) - 1;
+        fbpf.gmask  = fbpf.gmax << fbpf.gshift;
+    }
+    if (fbpf.bbits) {
+        fbpf.bmax   = (1 << fbpf.bbits) - 1;
+        fbpf.bmask  = fbpf.bmax << fbpf.bshift;
+    }
+    if (fbpf.abits) {
+        fbpf.amax   = (1 << fbpf.abits) - 1;
+        fbpf.amask  = fbpf.amax << fbpf.ashift;
+    }
+    return 0;
+
+err_early:
+    if (tty > 0) {
+        close(tty);
+    }
+    close(fb);
+    return -1;
+
+err:
+    fbdev_cleanup();
+    return -1;
+}
+
+static void
+fbdev_catch_fatal_signal(int signal)
+{
+    fbdev_cleanup();
+    abort();
+}
+
+static void fbdev_catch_exit_signals(void)
+{
+    struct sigaction act, old;
+
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = fbdev_catch_fatal_signal;
+    sigemptyset(&act.sa_mask);
+    sigaction(SIGSEGV, &act, &old);
+}
+
+/* -------------------------------------------------------------------- */
+/* console switching                                                    */
+
+static void fbdev_switch_signal(int signal)
+{
+    if (signal == SIG_REL) {
+        /* release */
+        trace_fbdev_vt_release_request();
+        fb_switch_state = FB_REL_REQ;
+    }
+    if (signal == SIG_ACQ) {
+        /* acquisition */
+        trace_fbdev_vt_aquire_request();
+        fb_switch_state = FB_ACQ_REQ;
+    }
+}
+
+static void fbdev_switch_release(void)
+{
+    stop_mediumraw(tty);
+    ioctl(tty, KDSETMODE, kd_omode);
+    ioctl(tty, VT_RELDISP, 1);
+    fb_switch_state = FB_INACTIVE;
+    trace_fbdev_vt_released();
+}
+
+static void fbdev_switch_acquire(void)
+{
+    ioctl(tty, VT_RELDISP, VT_ACKACQ);
+    start_mediumraw(tty);
+    reset_keys();
+    ioctl(tty, KDSETMODE, KD_GRAPHICS);
+    fb_switch_state = FB_ACTIVE;
+    trace_fbdev_vt_aquired();
+}
+
+static int fbdev_switch_init(void)
+{
+    struct sigaction act, old;
+
+    memset(&act, 0, sizeof(act));
+    act.sa_handler  = fbdev_switch_signal;
+    sigemptyset(&act.sa_mask);
+    sigaction(SIG_REL, &act, &old);
+    sigaction(SIG_ACQ, &act, &old);
+
+    if (ioctl(tty, VT_GETMODE, &vt_mode) < 0) {
+        perror("ioctl VT_GETMODE");
+        exit(1);
+    }
+    vt_mode.mode   = VT_PROCESS;
+    vt_mode.waitv  = 0;
+    vt_mode.relsig = SIG_REL;
+    vt_mode.acqsig = SIG_ACQ;
+
+    if (ioctl(tty, VT_SETMODE, &vt_mode) < 0) {
+        perror("ioctl VT_SETMODE");
+        exit(1);
+    }
+    return 0;
+}
+
+/* -------------------------------------------------------------------- */
+/* rendering                                                            */
+
+static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
+{
+    uint8_t *dst;
+    uint8_t *src;
+    int line;
+
+    if (!conv) {
+        return;
+    }
+
+    src = ds_get_data(ds) + y * ds_get_linesize(ds)
+        + x * ds_get_bytes_per_pixel(ds);
+    dst = fb_mem + y * fb_fix.line_length
+        + x * fbpf.bytes_per_pixel;
+
+    dst += cy * fb_fix.line_length;
+    dst += cx * fbpf.bytes_per_pixel;
+
+    if (h > fb_var.yres - y) {
+        h = fb_var.yres - y;
+    }
+    if (w > fb_var.xres - x) {
+        w = fb_var.xres - x;
+    }
+
+    for (line = y; line < y+h; line++) {
+        qemu_pf_conv_run(conv, dst, src, w);
+        dst += fb_fix.line_length;
+        src += ds_get_linesize(ds);
+    }
+}
+
+/* -------------------------------------------------------------------- */
+/* qemu interfaces                                                      */
+
+static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
+{
+    if (fb_switch_state != FB_ACTIVE) {
+        return;
+    }
+
+    if (resize_screen) {
+        trace_fbdev_dpy_resize(ds_get_width(ds), ds_get_height(ds));
+        resize_screen = 0;
+        cx = 0; cy = 0;
+        cw = ds_get_width(ds);
+        ch = ds_get_height(ds);
+        if (ds_get_width(ds) < fb_var.xres) {
+            cx = (fb_var.xres - ds_get_width(ds)) / 2;
+        }
+        if (ds_get_height(ds) < fb_var.yres) {
+            cy = (fb_var.yres - ds_get_height(ds)) / 2;
+        }
+
+        if (conv) {
+            qemu_pf_conv_put(conv);
+        }
+        conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
+        if (conv == NULL) {
+            fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
+        }
+    }
+
+    if (redraw_screen) {
+        trace_fbdev_dpy_redraw();
+        redraw_screen = 0;
+        fbdev_cls();
+        x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
+    }
+
+    fbdev_render(ds, x, y, w, h);
+}
+
+static void fbdev_resize(DisplayState *ds)
+{
+    resize_screen++;
+    redraw_screen++;
+}
+
+static void fbdev_refresh(DisplayState *ds)
+{
+    switch (fb_switch_state) {
+    case FB_REL_REQ:
+        fbdev_switch_release();
+    case FB_INACTIVE:
+        return;
+    case FB_ACQ_REQ:
+        fbdev_switch_acquire();
+        redraw_screen++;
+    case FB_ACTIVE:
+        break;
+    }
+
+    vga_hw_update();
+    if (redraw_screen) {
+        fbdev_update(ds, 0, 0, 0, 0);
+    }
+}
+
+static void fbdev_exit_notifier(Notifier *notifier, void *data)
+{
+    fbdev_cleanup();
+}
+
+int fbdev_display_init(DisplayState *ds, const char *device)
+{
+    if (dcl != NULL) {
+        return 0;
+    }
+
+    if (fbdev_init(device) != 0) {
+        return -1;
+    }
+    exit_notifier.notify = fbdev_exit_notifier;
+    qemu_add_exit_notifier(&exit_notifier);
+    fbdev_switch_init();
+    fbdev_catch_exit_signals();
+    init_mouse();
+
+    fbdev_resize(ds);
+    dcl = g_new0(DisplayChangeListener, 1);
+    dcl->dpy_update  = fbdev_update;
+    dcl->dpy_resize  = fbdev_resize;
+    dcl->dpy_refresh = fbdev_refresh;
+    register_displaychangelistener(ds, dcl);
+
+    trace_fbdev_enabled();
+    return 0;
+}
+
+void fbdev_display_uninit(DisplayState *ds)
+{
+    if (dcl == NULL) {
+        return;
+    }
+
+    unregister_displaychangelistener(ds, dcl);
+    g_free(dcl);
+    dcl = NULL;
+
+    fbdev_cleanup();
+    qemu_remove_exit_notifier(&exit_notifier);
+    uninit_mouse();
+}
diff --git a/ui/linux-keynames.h b/ui/linux-keynames.h
new file mode 100644
index 0000000..058af28
--- /dev/null
+++ b/ui/linux-keynames.h
@@ -0,0 +1,388 @@
+/*
+ *   awk '/#define KEY_/ { printf("    [%s] = \"%s\",\n",$2,$2); }' \
+ *       /usr/include/linux/input.h
+ */
+    [KEY_RESERVED] = "KEY_RESERVED",
+    [KEY_ESC] = "KEY_ESC",
+    [KEY_1] = "KEY_1",
+    [KEY_2] = "KEY_2",
+    [KEY_3] = "KEY_3",
+    [KEY_4] = "KEY_4",
+    [KEY_5] = "KEY_5",
+    [KEY_6] = "KEY_6",
+    [KEY_7] = "KEY_7",
+    [KEY_8] = "KEY_8",
+    [KEY_9] = "KEY_9",
+    [KEY_0] = "KEY_0",
+    [KEY_MINUS] = "KEY_MINUS",
+    [KEY_EQUAL] = "KEY_EQUAL",
+    [KEY_BACKSPACE] = "KEY_BACKSPACE",
+    [KEY_TAB] = "KEY_TAB",
+    [KEY_Q] = "KEY_Q",
+    [KEY_W] = "KEY_W",
+    [KEY_E] = "KEY_E",
+    [KEY_R] = "KEY_R",
+    [KEY_T] = "KEY_T",
+    [KEY_Y] = "KEY_Y",
+    [KEY_U] = "KEY_U",
+    [KEY_I] = "KEY_I",
+    [KEY_O] = "KEY_O",
+    [KEY_P] = "KEY_P",
+    [KEY_LEFTBRACE] = "KEY_LEFTBRACE",
+    [KEY_RIGHTBRACE] = "KEY_RIGHTBRACE",
+    [KEY_ENTER] = "KEY_ENTER",
+    [KEY_LEFTCTRL] = "KEY_LEFTCTRL",
+    [KEY_A] = "KEY_A",
+    [KEY_S] = "KEY_S",
+    [KEY_D] = "KEY_D",
+    [KEY_F] = "KEY_F",
+    [KEY_G] = "KEY_G",
+    [KEY_H] = "KEY_H",
+    [KEY_J] = "KEY_J",
+    [KEY_K] = "KEY_K",
+    [KEY_L] = "KEY_L",
+    [KEY_SEMICOLON] = "KEY_SEMICOLON",
+    [KEY_APOSTROPHE] = "KEY_APOSTROPHE",
+    [KEY_GRAVE] = "KEY_GRAVE",
+    [KEY_LEFTSHIFT] = "KEY_LEFTSHIFT",
+    [KEY_BACKSLASH] = "KEY_BACKSLASH",
+    [KEY_Z] = "KEY_Z",
+    [KEY_X] = "KEY_X",
+    [KEY_C] = "KEY_C",
+    [KEY_V] = "KEY_V",
+    [KEY_B] = "KEY_B",
+    [KEY_N] = "KEY_N",
+    [KEY_M] = "KEY_M",
+    [KEY_COMMA] = "KEY_COMMA",
+    [KEY_DOT] = "KEY_DOT",
+    [KEY_SLASH] = "KEY_SLASH",
+    [KEY_RIGHTSHIFT] = "KEY_RIGHTSHIFT",
+    [KEY_KPASTERISK] = "KEY_KPASTERISK",
+    [KEY_LEFTALT] = "KEY_LEFTALT",
+    [KEY_SPACE] = "KEY_SPACE",
+    [KEY_CAPSLOCK] = "KEY_CAPSLOCK",
+    [KEY_F1] = "KEY_F1",
+    [KEY_F2] = "KEY_F2",
+    [KEY_F3] = "KEY_F3",
+    [KEY_F4] = "KEY_F4",
+    [KEY_F5] = "KEY_F5",
+    [KEY_F6] = "KEY_F6",
+    [KEY_F7] = "KEY_F7",
+    [KEY_F8] = "KEY_F8",
+    [KEY_F9] = "KEY_F9",
+    [KEY_F10] = "KEY_F10",
+    [KEY_NUMLOCK] = "KEY_NUMLOCK",
+    [KEY_SCROLLLOCK] = "KEY_SCROLLLOCK",
+    [KEY_KP7] = "KEY_KP7",
+    [KEY_KP8] = "KEY_KP8",
+    [KEY_KP9] = "KEY_KP9",
+    [KEY_KPMINUS] = "KEY_KPMINUS",
+    [KEY_KP4] = "KEY_KP4",
+    [KEY_KP5] = "KEY_KP5",
+    [KEY_KP6] = "KEY_KP6",
+    [KEY_KPPLUS] = "KEY_KPPLUS",
+    [KEY_KP1] = "KEY_KP1",
+    [KEY_KP2] = "KEY_KP2",
+    [KEY_KP3] = "KEY_KP3",
+    [KEY_KP0] = "KEY_KP0",
+    [KEY_KPDOT] = "KEY_KPDOT",
+    [KEY_ZENKAKUHANKAKU] = "KEY_ZENKAKUHANKAKU",
+    [KEY_102ND] = "KEY_102ND",
+    [KEY_F11] = "KEY_F11",
+    [KEY_F12] = "KEY_F12",
+    [KEY_RO] = "KEY_RO",
+    [KEY_KATAKANA] = "KEY_KATAKANA",
+    [KEY_HIRAGANA] = "KEY_HIRAGANA",
+    [KEY_HENKAN] = "KEY_HENKAN",
+    [KEY_KATAKANAHIRAGANA] = "KEY_KATAKANAHIRAGANA",
+    [KEY_MUHENKAN] = "KEY_MUHENKAN",
+    [KEY_KPJPCOMMA] = "KEY_KPJPCOMMA",
+    [KEY_KPENTER] = "KEY_KPENTER",
+    [KEY_RIGHTCTRL] = "KEY_RIGHTCTRL",
+    [KEY_KPSLASH] = "KEY_KPSLASH",
+    [KEY_SYSRQ] = "KEY_SYSRQ",
+    [KEY_RIGHTALT] = "KEY_RIGHTALT",
+    [KEY_LINEFEED] = "KEY_LINEFEED",
+    [KEY_HOME] = "KEY_HOME",
+    [KEY_UP] = "KEY_UP",
+    [KEY_PAGEUP] = "KEY_PAGEUP",
+    [KEY_LEFT] = "KEY_LEFT",
+    [KEY_RIGHT] = "KEY_RIGHT",
+    [KEY_END] = "KEY_END",
+    [KEY_DOWN] = "KEY_DOWN",
+    [KEY_PAGEDOWN] = "KEY_PAGEDOWN",
+    [KEY_INSERT] = "KEY_INSERT",
+    [KEY_DELETE] = "KEY_DELETE",
+    [KEY_MACRO] = "KEY_MACRO",
+    [KEY_MUTE] = "KEY_MUTE",
+    [KEY_VOLUMEDOWN] = "KEY_VOLUMEDOWN",
+    [KEY_VOLUMEUP] = "KEY_VOLUMEUP",
+    [KEY_POWER] = "KEY_POWER",
+    [KEY_KPEQUAL] = "KEY_KPEQUAL",
+    [KEY_KPPLUSMINUS] = "KEY_KPPLUSMINUS",
+    [KEY_PAUSE] = "KEY_PAUSE",
+    [KEY_SCALE] = "KEY_SCALE",
+    [KEY_KPCOMMA] = "KEY_KPCOMMA",
+    [KEY_HANGEUL] = "KEY_HANGEUL",
+    [KEY_HANGUEL] = "KEY_HANGUEL",
+    [KEY_HANJA] = "KEY_HANJA",
+    [KEY_YEN] = "KEY_YEN",
+    [KEY_LEFTMETA] = "KEY_LEFTMETA",
+    [KEY_RIGHTMETA] = "KEY_RIGHTMETA",
+    [KEY_COMPOSE] = "KEY_COMPOSE",
+    [KEY_STOP] = "KEY_STOP",
+    [KEY_AGAIN] = "KEY_AGAIN",
+    [KEY_PROPS] = "KEY_PROPS",
+    [KEY_UNDO] = "KEY_UNDO",
+    [KEY_FRONT] = "KEY_FRONT",
+    [KEY_COPY] = "KEY_COPY",
+    [KEY_OPEN] = "KEY_OPEN",
+    [KEY_PASTE] = "KEY_PASTE",
+    [KEY_FIND] = "KEY_FIND",
+    [KEY_CUT] = "KEY_CUT",
+    [KEY_HELP] = "KEY_HELP",
+    [KEY_MENU] = "KEY_MENU",
+    [KEY_CALC] = "KEY_CALC",
+    [KEY_SETUP] = "KEY_SETUP",
+    [KEY_SLEEP] = "KEY_SLEEP",
+    [KEY_WAKEUP] = "KEY_WAKEUP",
+    [KEY_FILE] = "KEY_FILE",
+    [KEY_SENDFILE] = "KEY_SENDFILE",
+    [KEY_DELETEFILE] = "KEY_DELETEFILE",
+    [KEY_XFER] = "KEY_XFER",
+    [KEY_PROG1] = "KEY_PROG1",
+    [KEY_PROG2] = "KEY_PROG2",
+    [KEY_WWW] = "KEY_WWW",
+    [KEY_MSDOS] = "KEY_MSDOS",
+    [KEY_COFFEE] = "KEY_COFFEE",
+    [KEY_SCREENLOCK] = "KEY_SCREENLOCK",
+    [KEY_DIRECTION] = "KEY_DIRECTION",
+    [KEY_CYCLEWINDOWS] = "KEY_CYCLEWINDOWS",
+    [KEY_MAIL] = "KEY_MAIL",
+    [KEY_BOOKMARKS] = "KEY_BOOKMARKS",
+    [KEY_COMPUTER] = "KEY_COMPUTER",
+    [KEY_BACK] = "KEY_BACK",
+    [KEY_FORWARD] = "KEY_FORWARD",
+    [KEY_CLOSECD] = "KEY_CLOSECD",
+    [KEY_EJECTCD] = "KEY_EJECTCD",
+    [KEY_EJECTCLOSECD] = "KEY_EJECTCLOSECD",
+    [KEY_NEXTSONG] = "KEY_NEXTSONG",
+    [KEY_PLAYPAUSE] = "KEY_PLAYPAUSE",
+    [KEY_PREVIOUSSONG] = "KEY_PREVIOUSSONG",
+    [KEY_STOPCD] = "KEY_STOPCD",
+    [KEY_RECORD] = "KEY_RECORD",
+    [KEY_REWIND] = "KEY_REWIND",
+    [KEY_PHONE] = "KEY_PHONE",
+    [KEY_ISO] = "KEY_ISO",
+    [KEY_CONFIG] = "KEY_CONFIG",
+    [KEY_HOMEPAGE] = "KEY_HOMEPAGE",
+    [KEY_REFRESH] = "KEY_REFRESH",
+    [KEY_EXIT] = "KEY_EXIT",
+    [KEY_MOVE] = "KEY_MOVE",
+    [KEY_EDIT] = "KEY_EDIT",
+    [KEY_SCROLLUP] = "KEY_SCROLLUP",
+    [KEY_SCROLLDOWN] = "KEY_SCROLLDOWN",
+    [KEY_KPLEFTPAREN] = "KEY_KPLEFTPAREN",
+    [KEY_KPRIGHTPAREN] = "KEY_KPRIGHTPAREN",
+    [KEY_NEW] = "KEY_NEW",
+    [KEY_REDO] = "KEY_REDO",
+    [KEY_F13] = "KEY_F13",
+    [KEY_F14] = "KEY_F14",
+    [KEY_F15] = "KEY_F15",
+    [KEY_F16] = "KEY_F16",
+    [KEY_F17] = "KEY_F17",
+    [KEY_F18] = "KEY_F18",
+    [KEY_F19] = "KEY_F19",
+    [KEY_F20] = "KEY_F20",
+    [KEY_F21] = "KEY_F21",
+    [KEY_F22] = "KEY_F22",
+    [KEY_F23] = "KEY_F23",
+    [KEY_F24] = "KEY_F24",
+    [KEY_PLAYCD] = "KEY_PLAYCD",
+    [KEY_PAUSECD] = "KEY_PAUSECD",
+    [KEY_PROG3] = "KEY_PROG3",
+    [KEY_PROG4] = "KEY_PROG4",
+    [KEY_DASHBOARD] = "KEY_DASHBOARD",
+    [KEY_SUSPEND] = "KEY_SUSPEND",
+    [KEY_CLOSE] = "KEY_CLOSE",
+    [KEY_PLAY] = "KEY_PLAY",
+    [KEY_FASTFORWARD] = "KEY_FASTFORWARD",
+    [KEY_BASSBOOST] = "KEY_BASSBOOST",
+    [KEY_PRINT] = "KEY_PRINT",
+    [KEY_HP] = "KEY_HP",
+    [KEY_CAMERA] = "KEY_CAMERA",
+    [KEY_SOUND] = "KEY_SOUND",
+    [KEY_QUESTION] = "KEY_QUESTION",
+    [KEY_EMAIL] = "KEY_EMAIL",
+    [KEY_CHAT] = "KEY_CHAT",
+    [KEY_SEARCH] = "KEY_SEARCH",
+    [KEY_CONNECT] = "KEY_CONNECT",
+    [KEY_FINANCE] = "KEY_FINANCE",
+    [KEY_SPORT] = "KEY_SPORT",
+    [KEY_SHOP] = "KEY_SHOP",
+    [KEY_ALTERASE] = "KEY_ALTERASE",
+    [KEY_CANCEL] = "KEY_CANCEL",
+    [KEY_BRIGHTNESSDOWN] = "KEY_BRIGHTNESSDOWN",
+    [KEY_BRIGHTNESSUP] = "KEY_BRIGHTNESSUP",
+    [KEY_MEDIA] = "KEY_MEDIA",
+    [KEY_SWITCHVIDEOMODE] = "KEY_SWITCHVIDEOMODE",
+    [KEY_KBDILLUMTOGGLE] = "KEY_KBDILLUMTOGGLE",
+    [KEY_KBDILLUMDOWN] = "KEY_KBDILLUMDOWN",
+    [KEY_KBDILLUMUP] = "KEY_KBDILLUMUP",
+    [KEY_SEND] = "KEY_SEND",
+    [KEY_REPLY] = "KEY_REPLY",
+    [KEY_FORWARDMAIL] = "KEY_FORWARDMAIL",
+    [KEY_SAVE] = "KEY_SAVE",
+    [KEY_DOCUMENTS] = "KEY_DOCUMENTS",
+    [KEY_BATTERY] = "KEY_BATTERY",
+    [KEY_BLUETOOTH] = "KEY_BLUETOOTH",
+    [KEY_WLAN] = "KEY_WLAN",
+    [KEY_UWB] = "KEY_UWB",
+    [KEY_UNKNOWN] = "KEY_UNKNOWN",
+    [KEY_VIDEO_NEXT] = "KEY_VIDEO_NEXT",
+    [KEY_VIDEO_PREV] = "KEY_VIDEO_PREV",
+    [KEY_BRIGHTNESS_CYCLE] = "KEY_BRIGHTNESS_CYCLE",
+    [KEY_BRIGHTNESS_ZERO] = "KEY_BRIGHTNESS_ZERO",
+    [KEY_DISPLAY_OFF] = "KEY_DISPLAY_OFF",
+    [KEY_WIMAX] = "KEY_WIMAX",
+    [KEY_OK] = "KEY_OK",
+    [KEY_SELECT] = "KEY_SELECT",
+    [KEY_GOTO] = "KEY_GOTO",
+    [KEY_CLEAR] = "KEY_CLEAR",
+    [KEY_POWER2] = "KEY_POWER2",
+    [KEY_OPTION] = "KEY_OPTION",
+    [KEY_INFO] = "KEY_INFO",
+    [KEY_TIME] = "KEY_TIME",
+    [KEY_VENDOR] = "KEY_VENDOR",
+    [KEY_ARCHIVE] = "KEY_ARCHIVE",
+    [KEY_PROGRAM] = "KEY_PROGRAM",
+    [KEY_CHANNEL] = "KEY_CHANNEL",
+    [KEY_FAVORITES] = "KEY_FAVORITES",
+    [KEY_EPG] = "KEY_EPG",
+    [KEY_PVR] = "KEY_PVR",
+    [KEY_MHP] = "KEY_MHP",
+    [KEY_LANGUAGE] = "KEY_LANGUAGE",
+    [KEY_TITLE] = "KEY_TITLE",
+    [KEY_SUBTITLE] = "KEY_SUBTITLE",
+    [KEY_ANGLE] = "KEY_ANGLE",
+    [KEY_ZOOM] = "KEY_ZOOM",
+    [KEY_MODE] = "KEY_MODE",
+    [KEY_KEYBOARD] = "KEY_KEYBOARD",
+    [KEY_SCREEN] = "KEY_SCREEN",
+    [KEY_PC] = "KEY_PC",
+    [KEY_TV] = "KEY_TV",
+    [KEY_TV2] = "KEY_TV2",
+    [KEY_VCR] = "KEY_VCR",
+    [KEY_VCR2] = "KEY_VCR2",
+    [KEY_SAT] = "KEY_SAT",
+    [KEY_SAT2] = "KEY_SAT2",
+    [KEY_CD] = "KEY_CD",
+    [KEY_TAPE] = "KEY_TAPE",
+    [KEY_RADIO] = "KEY_RADIO",
+    [KEY_TUNER] = "KEY_TUNER",
+    [KEY_PLAYER] = "KEY_PLAYER",
+    [KEY_TEXT] = "KEY_TEXT",
+    [KEY_DVD] = "KEY_DVD",
+    [KEY_AUX] = "KEY_AUX",
+    [KEY_MP3] = "KEY_MP3",
+    [KEY_AUDIO] = "KEY_AUDIO",
+    [KEY_VIDEO] = "KEY_VIDEO",
+    [KEY_DIRECTORY] = "KEY_DIRECTORY",
+    [KEY_LIST] = "KEY_LIST",
+    [KEY_MEMO] = "KEY_MEMO",
+    [KEY_CALENDAR] = "KEY_CALENDAR",
+    [KEY_RED] = "KEY_RED",
+    [KEY_GREEN] = "KEY_GREEN",
+    [KEY_YELLOW] = "KEY_YELLOW",
+    [KEY_BLUE] = "KEY_BLUE",
+    [KEY_CHANNELUP] = "KEY_CHANNELUP",
+    [KEY_CHANNELDOWN] = "KEY_CHANNELDOWN",
+    [KEY_FIRST] = "KEY_FIRST",
+    [KEY_LAST] = "KEY_LAST",
+    [KEY_AB] = "KEY_AB",
+    [KEY_NEXT] = "KEY_NEXT",
+    [KEY_RESTART] = "KEY_RESTART",
+    [KEY_SLOW] = "KEY_SLOW",
+    [KEY_SHUFFLE] = "KEY_SHUFFLE",
+    [KEY_BREAK] = "KEY_BREAK",
+    [KEY_PREVIOUS] = "KEY_PREVIOUS",
+    [KEY_DIGITS] = "KEY_DIGITS",
+    [KEY_TEEN] = "KEY_TEEN",
+    [KEY_TWEN] = "KEY_TWEN",
+    [KEY_VIDEOPHONE] = "KEY_VIDEOPHONE",
+    [KEY_GAMES] = "KEY_GAMES",
+    [KEY_ZOOMIN] = "KEY_ZOOMIN",
+    [KEY_ZOOMOUT] = "KEY_ZOOMOUT",
+    [KEY_ZOOMRESET] = "KEY_ZOOMRESET",
+    [KEY_WORDPROCESSOR] = "KEY_WORDPROCESSOR",
+    [KEY_EDITOR] = "KEY_EDITOR",
+    [KEY_SPREADSHEET] = "KEY_SPREADSHEET",
+    [KEY_GRAPHICSEDITOR] = "KEY_GRAPHICSEDITOR",
+    [KEY_PRESENTATION] = "KEY_PRESENTATION",
+    [KEY_DATABASE] = "KEY_DATABASE",
+    [KEY_NEWS] = "KEY_NEWS",
+    [KEY_VOICEMAIL] = "KEY_VOICEMAIL",
+    [KEY_ADDRESSBOOK] = "KEY_ADDRESSBOOK",
+    [KEY_MESSENGER] = "KEY_MESSENGER",
+    [KEY_DISPLAYTOGGLE] = "KEY_DISPLAYTOGGLE",
+    [KEY_SPELLCHECK] = "KEY_SPELLCHECK",
+    [KEY_LOGOFF] = "KEY_LOGOFF",
+    [KEY_DOLLAR] = "KEY_DOLLAR",
+    [KEY_EURO] = "KEY_EURO",
+    [KEY_FRAMEBACK] = "KEY_FRAMEBACK",
+    [KEY_FRAMEFORWARD] = "KEY_FRAMEFORWARD",
+    [KEY_CONTEXT_MENU] = "KEY_CONTEXT_MENU",
+    [KEY_MEDIA_REPEAT] = "KEY_MEDIA_REPEAT",
+    [KEY_10CHANNELSUP] = "KEY_10CHANNELSUP",
+    [KEY_10CHANNELSDOWN] = "KEY_10CHANNELSDOWN",
+    [KEY_DEL_EOL] = "KEY_DEL_EOL",
+    [KEY_DEL_EOS] = "KEY_DEL_EOS",
+    [KEY_INS_LINE] = "KEY_INS_LINE",
+    [KEY_DEL_LINE] = "KEY_DEL_LINE",
+    [KEY_FN] = "KEY_FN",
+    [KEY_FN_ESC] = "KEY_FN_ESC",
+    [KEY_FN_F1] = "KEY_FN_F1",
+    [KEY_FN_F2] = "KEY_FN_F2",
+    [KEY_FN_F3] = "KEY_FN_F3",
+    [KEY_FN_F4] = "KEY_FN_F4",
+    [KEY_FN_F5] = "KEY_FN_F5",
+    [KEY_FN_F6] = "KEY_FN_F6",
+    [KEY_FN_F7] = "KEY_FN_F7",
+    [KEY_FN_F8] = "KEY_FN_F8",
+    [KEY_FN_F9] = "KEY_FN_F9",
+    [KEY_FN_F10] = "KEY_FN_F10",
+    [KEY_FN_F11] = "KEY_FN_F11",
+    [KEY_FN_F12] = "KEY_FN_F12",
+    [KEY_FN_1] = "KEY_FN_1",
+    [KEY_FN_2] = "KEY_FN_2",
+    [KEY_FN_D] = "KEY_FN_D",
+    [KEY_FN_E] = "KEY_FN_E",
+    [KEY_FN_F] = "KEY_FN_F",
+    [KEY_FN_S] = "KEY_FN_S",
+    [KEY_FN_B] = "KEY_FN_B",
+    [KEY_BRL_DOT1] = "KEY_BRL_DOT1",
+    [KEY_BRL_DOT2] = "KEY_BRL_DOT2",
+    [KEY_BRL_DOT3] = "KEY_BRL_DOT3",
+    [KEY_BRL_DOT4] = "KEY_BRL_DOT4",
+    [KEY_BRL_DOT5] = "KEY_BRL_DOT5",
+    [KEY_BRL_DOT6] = "KEY_BRL_DOT6",
+    [KEY_BRL_DOT7] = "KEY_BRL_DOT7",
+    [KEY_BRL_DOT8] = "KEY_BRL_DOT8",
+    [KEY_BRL_DOT9] = "KEY_BRL_DOT9",
+    [KEY_BRL_DOT10] = "KEY_BRL_DOT10",
+    [KEY_NUMERIC_0] = "KEY_NUMERIC_0",
+    [KEY_NUMERIC_1] = "KEY_NUMERIC_1",
+    [KEY_NUMERIC_2] = "KEY_NUMERIC_2",
+    [KEY_NUMERIC_3] = "KEY_NUMERIC_3",
+    [KEY_NUMERIC_4] = "KEY_NUMERIC_4",
+    [KEY_NUMERIC_5] = "KEY_NUMERIC_5",
+    [KEY_NUMERIC_6] = "KEY_NUMERIC_6",
+    [KEY_NUMERIC_7] = "KEY_NUMERIC_7",
+    [KEY_NUMERIC_8] = "KEY_NUMERIC_8",
+    [KEY_NUMERIC_9] = "KEY_NUMERIC_9",
+    [KEY_NUMERIC_STAR] = "KEY_NUMERIC_STAR",
+    [KEY_NUMERIC_POUND] = "KEY_NUMERIC_POUND",
+    [KEY_RFKILL] = "KEY_RFKILL",
+    [KEY_MIN_INTERESTING] = "KEY_MIN_INTERESTING",
+    [KEY_MAX] = "KEY_MAX",
+    [KEY_CNT] = "KEY_CNT",
diff --git a/vl.c b/vl.c
index fbb77fe..18982b2 100644
--- a/vl.c
+++ b/vl.c
@@ -3041,6 +3041,11 @@ int main(int argc, char **argv, char **envp)
                 fprintf(stderr, "SDL support is disabled\n");
                 exit(1);
 #endif
+#ifdef CONFIG_LINUX
+            case QEMU_OPTION_fbdev:
+                display_type = DT_FBDEV;
+                break;
+#endif
             case QEMU_OPTION_pidfile:
                 pid_file = optarg;
                 break;
@@ -3681,6 +3686,13 @@ int main(int argc, char **argv, char **envp)
         curses_display_init(ds, full_screen);
         break;
 #endif
+#if defined(CONFIG_LINUX)
+    case DT_FBDEV:
+        if (fbdev_display_init(ds, NULL) != 0) {
+            exit(1);
+        }
+        break;
+#endif
 #if defined(CONFIG_SDL)
     case DT_SDL:
         sdl_display_init(ds, full_screen, no_frame);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 4/4] fbdev: add monitor command to enable/disable
  2012-09-10 14:20 [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Gerd Hoffmann
                   ` (2 preceding siblings ...)
  2012-09-10 14:20 ` [Qemu-devel] [PATCH 3/4] fbdev: add linux framebuffer display driver Gerd Hoffmann
@ 2012-09-10 14:21 ` Gerd Hoffmann
  2012-09-11  9:30   ` Markus Armbruster
  2012-09-11  8:09 ` [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Alon Levy
  4 siblings, 1 reply; 10+ messages in thread
From: Gerd Hoffmann @ 2012-09-10 14:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This patch adds a fbdev monitor command to enable/disable
the fbdev display at runtime to both qmp and hmp.

qmp: fbdev enable=on|off
hmp: fbdev on|off
---
 hmp-commands.hx  |   15 +++++++++++++++
 hmp.c            |    9 +++++++++
 hmp.h            |    1 +
 qapi-schema.json |   15 +++++++++++++++
 qmp-commands.hx  |    6 ++++++
 qmp.c            |   17 +++++++++++++++++
 6 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/hmp-commands.hx b/hmp-commands.hx
index f6104b0..1ef372b 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1378,6 +1378,21 @@ passed since 1970, i.e. unix epoch.
 ETEXI
 
     {
+        .name       = "fbdev",
+        .args_type  = "enable:b",
+        .params     = "on|off",
+        .help       = "enable/disable fbdev",
+        .mhandler.cmd = hmp_fbdev,
+    },
+
+STEXI
+@item fbdev on | off
+@findex fbdev
+
+enable/disable fbdev
+ETEXI
+
+    {
         .name       = "info",
         .args_type  = "item:s?",
         .params     = "[subcommand]",
diff --git a/hmp.c b/hmp.c
index 81c8acb..a7f9ecb 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1102,3 +1102,12 @@ void hmp_closefd(Monitor *mon, const QDict *qdict)
     qmp_closefd(fdname, &errp);
     hmp_handle_error(mon, &errp);
 }
+
+void hmp_fbdev(Monitor *mon, const QDict *qdict)
+{
+    int enable = qdict_get_bool(qdict, "enable");
+    Error *errp = NULL;
+
+    qmp_fbdev(enable, &errp);
+    hmp_handle_error(mon, &errp);
+}
diff --git a/hmp.h b/hmp.h
index 7dd93bf..1eb02aa 100644
--- a/hmp.h
+++ b/hmp.h
@@ -71,5 +71,6 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
 void hmp_getfd(Monitor *mon, const QDict *qdict);
 void hmp_closefd(Monitor *mon, const QDict *qdict);
+void hmp_fbdev(Monitor *mon, const QDict *qdict);
 
 #endif
diff --git a/qapi-schema.json b/qapi-schema.json
index bd8ad74..d83ee2b 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2493,3 +2493,18 @@
 # Since: 1.2.0
 ##
 { 'command': 'query-target', 'returns': 'TargetInfo' }
+
+##
+# @fbdev:
+#
+# Enable/disable fbdev.
+#
+# @enable: true to set the link status to be up
+#
+# Returns: Nothing on success
+#          If @name is not a valid network device, DeviceNotFound
+#
+# Since: 1.3
+#
+##
+{ 'command': 'fbdev', 'data': {'enable': 'bool'} }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 3745a21..8382020 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2514,3 +2514,9 @@ EQMP
         .args_type  = "",
         .mhandler.cmd_new = qmp_marshal_input_query_target,
     },
+
+    {
+        .name       = "fbdev",
+        .args_type  = "enable:b",
+        .mhandler.cmd_new = qmp_marshal_input_fbdev,
+    },
diff --git a/qmp.c b/qmp.c
index 8463922..d7306ab 100644
--- a/qmp.c
+++ b/qmp.c
@@ -391,6 +391,23 @@ void qmp_change(const char *device, const char *target,
     }
 }
 
+void qmp_fbdev(bool enable, Error **errp)
+{
+#if defined(CONFIG_LINUX)
+    DisplayState *ds = get_displaystate();
+
+    if (enable) {
+        if (fbdev_display_init(ds, NULL) != 0) {
+            error_set(errp, QERR_UNDEFINED_ERROR);
+        }
+    } else {
+        fbdev_display_uninit(ds);
+    }
+#else
+    error_set(errp, QERR_FEATURE_DISABLED, "fbdev");
+#endif
+}
+
 static void qom_list_types_tramp(ObjectClass *klass, void *data)
 {
     ObjectTypeInfoList *e, **pret = data;
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 0/4] linux framebuffer display driver
  2012-09-10 14:20 [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Gerd Hoffmann
                   ` (3 preceding siblings ...)
  2012-09-10 14:21 ` [Qemu-devel] [PATCH 4/4] fbdev: add monitor command to enable/disable Gerd Hoffmann
@ 2012-09-11  8:09 ` Alon Levy
  2012-09-11  8:54   ` Gerd Hoffmann
  4 siblings, 1 reply; 10+ messages in thread
From: Alon Levy @ 2012-09-11  8:09 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

> Hi,
> 
> Undusting an pretty old patch series, rebasing it, testing it, fixing
> a bug or two, fixing codestyle issues:  Qemu can run on the linux
> framebuffer console as graphical display now.

Patch 4 doesn't apply (hmp.h), missing SoB on a few patches. 

Tested with Seabios. Works as advertised, tested console switching, quit, serial console. Some minor point:
 To switch to serial: Ctrl-Alt-1. To switch back to console output you have to release Ctrl & release Alt, then repress them, i.e. can't just release 1 and press 0.

Verified systemtap points using the following script:

$ cat scripts/simple-qemu-trace.py 
#!/usr/bin/env python

import sys

start = sys.argv[1]

for line in sys.stdin:
    if not line.startswith(start):
        continue
    probe = line.split('(')[0]
    print """probe qemu.system.x86_64.%s {
    printf("%s\\n");
}
""" % (probe, probe)

./scripts/simple-qemu-trace.py fbdev < trace-events | sudo stap -v -s1 -I x86_64-softmmu -

> 
> enjoy,
>   Gerd
> 
> Gerd Hoffmann (4):
>   QLIST-ify display change listeners.
>   add unregister_displaychangelistener
>   fbdev: add linux framebuffer display driver.
>   fbdev: add monitor command to enable/disable
> 
>  console.h           |   86 +++--
>  hmp-commands.hx     |   15 +
>  hmp.c               |    9 +
>  hmp.h               |    1 +
>  hw/xenfb.c          |    2 +-
>  qapi-schema.json    |   15 +
>  qemu-options.hx     |    8 +
>  qmp-commands.hx     |    6 +
>  qmp.c               |   17 +
>  sysemu.h            |    1 +
>  trace-events        |   14 +
>  ui/Makefile.objs    |    1 +
>  ui/fbdev.c          |  953
>  +++++++++++++++++++++++++++++++++++++++++++++++++++
>  ui/linux-keynames.h |  388 +++++++++++++++++++++
>  vl.c                |   50 ++-
>  15 files changed, 1518 insertions(+), 48 deletions(-)
>  create mode 100644 ui/fbdev.c
>  create mode 100644 ui/linux-keynames.h
> 
> 
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 0/4] linux framebuffer display driver
  2012-09-11  8:09 ` [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Alon Levy
@ 2012-09-11  8:54   ` Gerd Hoffmann
  0 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-09-11  8:54 UTC (permalink / raw)
  To: Alon Levy; +Cc: qemu-devel

On 09/11/12 10:09, Alon Levy wrote:
>> Hi,
>> 
>> Undusting an pretty old patch series, rebasing it, testing it,
>> fixing a bug or two, fixing codestyle issues:  Qemu can run on the
>> linux framebuffer console as graphical display now.
> 
> Patch 4 doesn't apply (hmp.h), missing SoB on a few patches.

Yea, noticed too this morning while rebasing my stuff.

Rebased version is available here:
  git://git.kraxel.org/qemu rebase/fbdev

> Some minor point: To switch to serial:
> Ctrl-Alt-1. To switch back to console output you have to release Ctrl
> & release Alt, then repress them, i.e. can't just release 1 and press
> 0.

fbdev resets modifier state on console switches.  This causes this
effect, but without it other ugly effects can sneak in (like the guest
seeing a modifier pressed which isn't pressed any more).

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 4/4] fbdev: add monitor command to enable/disable
  2012-09-10 14:21 ` [Qemu-devel] [PATCH 4/4] fbdev: add monitor command to enable/disable Gerd Hoffmann
@ 2012-09-11  9:30   ` Markus Armbruster
  0 siblings, 0 replies; 10+ messages in thread
From: Markus Armbruster @ 2012-09-11  9:30 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

Gerd Hoffmann <kraxel@redhat.com> writes:

> This patch adds a fbdev monitor command to enable/disable
> the fbdev display at runtime to both qmp and hmp.
>
> qmp: fbdev enable=on|off
> hmp: fbdev on|off
> ---
>  hmp-commands.hx  |   15 +++++++++++++++
>  hmp.c            |    9 +++++++++
>  hmp.h            |    1 +
>  qapi-schema.json |   15 +++++++++++++++
>  qmp-commands.hx  |    6 ++++++
>  qmp.c            |   17 +++++++++++++++++
>  6 files changed, 63 insertions(+), 0 deletions(-)
>
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index f6104b0..1ef372b 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -1378,6 +1378,21 @@ passed since 1970, i.e. unix epoch.
>  ETEXI
>  
>      {
> +        .name       = "fbdev",
> +        .args_type  = "enable:b",
> +        .params     = "on|off",
> +        .help       = "enable/disable fbdev",
> +        .mhandler.cmd = hmp_fbdev,
> +    },
> +
> +STEXI
> +@item fbdev on | off
> +@findex fbdev
> +
> +enable/disable fbdev
> +ETEXI
> +
> +    {
>          .name       = "info",
>          .args_type  = "item:s?",
>          .params     = "[subcommand]",
> diff --git a/hmp.c b/hmp.c
> index 81c8acb..a7f9ecb 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1102,3 +1102,12 @@ void hmp_closefd(Monitor *mon, const QDict *qdict)
>      qmp_closefd(fdname, &errp);
>      hmp_handle_error(mon, &errp);
>  }
> +
> +void hmp_fbdev(Monitor *mon, const QDict *qdict)
> +{
> +    int enable = qdict_get_bool(qdict, "enable");
> +    Error *errp = NULL;
> +
> +    qmp_fbdev(enable, &errp);
> +    hmp_handle_error(mon, &errp);
> +}
> diff --git a/hmp.h b/hmp.h
> index 7dd93bf..1eb02aa 100644
> --- a/hmp.h
> +++ b/hmp.h
> @@ -71,5 +71,6 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
>  void hmp_netdev_del(Monitor *mon, const QDict *qdict);
>  void hmp_getfd(Monitor *mon, const QDict *qdict);
>  void hmp_closefd(Monitor *mon, const QDict *qdict);
> +void hmp_fbdev(Monitor *mon, const QDict *qdict);
>  
>  #endif
> diff --git a/qapi-schema.json b/qapi-schema.json
> index bd8ad74..d83ee2b 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -2493,3 +2493,18 @@
>  # Since: 1.2.0
>  ##
>  { 'command': 'query-target', 'returns': 'TargetInfo' }
> +
> +##
> +# @fbdev:
> +#
> +# Enable/disable fbdev.
> +#
> +# @enable: true to set the link status to be up
> +#
> +# Returns: Nothing on success
> +#          If @name is not a valid network device, DeviceNotFound

Doc pasto, please fix.

> +#
> +# Since: 1.3
> +#
> +##
> +{ 'command': 'fbdev', 'data': {'enable': 'bool'} }
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 3745a21..8382020 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -2514,3 +2514,9 @@ EQMP
>          .args_type  = "",
>          .mhandler.cmd_new = qmp_marshal_input_query_target,
>      },
> +
> +    {
> +        .name       = "fbdev",
> +        .args_type  = "enable:b",
> +        .mhandler.cmd_new = qmp_marshal_input_fbdev,
> +    },
> diff --git a/qmp.c b/qmp.c
> index 8463922..d7306ab 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -391,6 +391,23 @@ void qmp_change(const char *device, const char *target,
>      }
>  }
>  
> +void qmp_fbdev(bool enable, Error **errp)
> +{
> +#if defined(CONFIG_LINUX)
> +    DisplayState *ds = get_displaystate();
> +
> +    if (enable) {
> +        if (fbdev_display_init(ds, NULL) != 0) {
> +            error_set(errp, QERR_UNDEFINED_ERROR);

Please upgrade to error_setg(), and make the message fit for human
consumption.

> +        }
> +    } else {
> +        fbdev_display_uninit(ds);
> +    }
> +#else
> +    error_set(errp, QERR_FEATURE_DISABLED, "fbdev");
> +#endif
> +}
> +
>  static void qom_list_types_tramp(ObjectClass *klass, void *data)
>  {
>      ObjectTypeInfoList *e, **pret = data;

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] fbdev: add linux framebuffer display driver.
  2012-09-10 14:20 ` [Qemu-devel] [PATCH 3/4] fbdev: add linux framebuffer display driver Gerd Hoffmann
@ 2012-09-11 11:10   ` Markus Armbruster
  2012-09-11 20:37   ` Blue Swirl
  1 sibling, 0 replies; 10+ messages in thread
From: Markus Armbruster @ 2012-09-11 11:10 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

Gerd Hoffmann <kraxel@redhat.com> writes:

> Display works, requires truecolor framebuffer with 16 or 32 bpp on the
> host.  32bpp is recommended.  The framebuffer is used as-is, qemu
> doesn't try to switch modes.  With LCD displays mode switching is pretty
> pointless IMHO, also it wouldn't work anyway with the most common
> fbdev drivers (vesafb, KMS).  Guest display is centered on the host
> screen.
>
> Mouse works, uses /dev/input/mice.
>
> Keyboard works.  Guest screen has whatever keymap you load inside
> the guest.  Text windows (monitor, serial, ...) have a simple en-us
> keymap.  Good enougth to type monitor commands.  Not goot enougth to
> work seriously on a serial terminal.  But the qemu terminal emulation
> isn't good enougth for that anyway ;)

s/enougth/enough/g

>
> Hot keys:
>   Ctrl-Alt-F<nr>  -> host console switching.
>   Ctrl-Alt-<nr>   -> qemu console switching.
>   Ctrl-Alt-ESC    -> exit qemu.
>
> Special feature:  Sane console switching.  Switching away stops screen
> updates.  Switching back redraws the screen.  When started from the
> linux console qemu uses the vt you've started it from (requires just
> read/write access to /dev/fb0).  When starting from somewhere else qemu
> tries to open a unused virtual terminal and switch to it (usually
> requires root privileges to open /dev/tty<nr>).
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  console.h           |    4 +
>  qemu-options.hx     |    8 +
>  sysemu.h            |    1 +
>  trace-events        |   14 +
>  ui/Makefile.objs    |    1 +
>  ui/fbdev.c          |  953 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  ui/linux-keynames.h |  388 +++++++++++++++++++++
>  vl.c                |   12 +
>  8 files changed, 1381 insertions(+), 0 deletions(-)
>  create mode 100644 ui/fbdev.c
>  create mode 100644 ui/linux-keynames.h
>
> diff --git a/console.h b/console.h
> index ddf3a3b..9d1fe72 100644
> --- a/console.h
> +++ b/console.h
> @@ -384,6 +384,10 @@ void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
>  /* sdl.c */
>  void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
>  
> +/* fbdev.c */
> +int fbdev_display_init(DisplayState *ds, const char *device);
> +void fbdev_display_uninit(DisplayState *ds);
> +
>  /* cocoa.m */
>  void cocoa_display_init(DisplayState *ds, int full_screen);
>  
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 3c411c4..b02dcbb 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -923,6 +923,14 @@ Enable/disable audio stream compression (using celt 0.5.1).  Default is on.
>  @end table
>  ETEXI
>  
> +DEF("fbdev", 0, QEMU_OPTION_fbdev,
> +    "-fbdev          enable fbdev\n", QEMU_ARCH_ALL)
> +STEXI
> +@item -fbdev
> +@findex -fbdev
> +Enable fbdev (linux framebuffer).
> +ETEXI
> +
>  DEF("portrait", 0, QEMU_OPTION_portrait,
>      "-portrait       rotate graphical output 90 deg left (only PXA LCD)\n",
>      QEMU_ARCH_ALL)
> diff --git a/sysemu.h b/sysemu.h
> index 65552ac..34e6bfa 100644
> --- a/sysemu.h
> +++ b/sysemu.h
> @@ -93,6 +93,7 @@ typedef enum DisplayType
>      DT_DEFAULT,
>      DT_CURSES,
>      DT_SDL,
> +    DT_FBDEV,
>      DT_NOGRAPHIC,
>      DT_NONE,
>  } DisplayType;
> diff --git a/trace-events b/trace-events
> index 04b0723..3745db3 100644
> --- a/trace-events
> +++ b/trace-events
> @@ -978,3 +978,17 @@ spapr_pci_rtas_ibm_change_msi(unsigned func, unsigned req) "func %u, requested %
>  spapr_pci_rtas_ibm_query_interrupt_source_number(unsigned ioa, unsigned intr) "queries for #%u, IRQ%u"
>  spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq) "@%"PRIx64"<=%"PRIx64" IRQ %u"
>  spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ %u"
> +
> +# ui/fbdev.c
> +fbdev_enabled(void) ""
> +fbdev_cleanup(void) ""
> +fbdev_vt_activate(int vtno, int wait) "vtno %d, wait %d"
> +fbdev_vt_activated(void) ""
> +fbdev_vt_release_request(void) ""
> +fbdev_vt_released(void) ""
> +fbdev_vt_aquire_request(void) ""
> +fbdev_vt_aquired(void) ""
> +fbdev_kbd_raw(int enable) "enable %d"
> +fbdev_kbd_event(int keycode, const char *kname, int up) "keycode 0x%x [%s], down %d"
> +fbdev_dpy_resize(int w, int h) "%dx%d"
> +fbdev_dpy_redraw(void)
> \ No newline at end of file

Please supply the newline.

> diff --git a/ui/Makefile.objs b/ui/Makefile.objs
> index adc07be..55ddcf2 100644
> --- a/ui/Makefile.objs
> +++ b/ui/Makefile.objs
> @@ -12,3 +12,4 @@ common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
>  common-obj-$(CONFIG_COCOA) += cocoa.o
>  common-obj-$(CONFIG_CURSES) += curses.o
>  common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
> +common-obj-$(CONFIG_LINUX) += fbdev.o
> diff --git a/ui/fbdev.c b/ui/fbdev.c
> new file mode 100644
> index 0000000..32c0a3b
> --- /dev/null
> +++ b/ui/fbdev.c
> @@ -0,0 +1,953 @@
> +/*
> + * linux fbdev output driver.
> + *
> + * Author: Gerd Hoffmann <kraxel@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + *
> + */
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <signal.h>
> +#include <termios.h>
> +
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +
> +#include <linux/kd.h>
> +#include <linux/vt.h>
> +#include <linux/fb.h>
> +
> +#include "qemu-common.h"
> +#include "console.h"
> +#include "keymaps.h"
> +#include "sysemu.h"
> +#include "pflib.h"
> +
> +/*
> + * must be last so we get the linux input layer
> + * KEY_* defines, not the ncurses ones.
> + */
> +#include <linux/input.h>
> +
> +/* -------------------------------------------------------------------- */
> +
> +/* file handles */
> +static int                        tty = -1, fb = -1, mice = -1;
> +
> +/* saved state, for restore on exit */
> +static int                        orig_vtno;
> +static int                        kd_omode;
> +static struct vt_mode             vt_omode;
> +static struct fb_var_screeninfo   fb_ovar;
> +
> +/* framebuffer */
> +static struct fb_fix_screeninfo   fb_fix;
> +static struct fb_var_screeninfo   fb_var;
> +static uint8_t                   *fb_mem;
> +static int                        fb_mem_offset;
> +
> +/* linux console */
> +static int                        vtno;
> +static struct vt_mode             vt_mode;
> +static struct termios             tty_attributes;
> +static unsigned long              tty_mode;
> +static unsigned int               tty_flags;
> +static bool                       tty_mediumraw;
> +static bool                       key_down[KEY_CNT];
> +
> +/* console switching */
> +#define SIG_ACQ      (SIGRTMIN+6)
> +#define SIG_REL      (SIGRTMIN+7)
> +#define FB_ACTIVE    0
> +#define FB_REL_REQ   1
> +#define FB_INACTIVE  2
> +#define FB_ACQ_REQ   3
> +static int fb_switch_state;
> +
> +/* qdev windup */
> +static DisplayChangeListener      *dcl;
> +static QemuPfConv                 *conv;
> +static PixelFormat                fbpf;
> +static int                        resize_screen;
> +static int                        redraw_screen;
> +static int                        cx, cy, cw, ch;
> +static Notifier                   exit_notifier;
> +
> +/* fwd decls */
> +static int fbdev_activate_vt(int tty, int vtno, bool wait);
> +
> +/* -------------------------------------------------------------------- */
> +/* mouse                                                                */
> +
> +static void read_mouse(void *opaque)
> +{
> +    char buf[3];
> +    int rc, x, y, b;
> +
> +    rc = read(mice, buf, sizeof(buf));
> +    if (rc != sizeof(buf)) {
> +        return;
> +    }
> +
> +    if (fb_switch_state != FB_ACTIVE) {
> +        return;
> +    }
> +
> +    x = buf[1];
> +    y = -buf[2];
> +    b = buf[0] & 0x7;
> +
> +    if (kbd_mouse_is_absolute()) {
> +        static int ax, ay;
> +        ax += x; ay += y;
> +        if (ax < 0) {
> +            ax = 0;
> +        }
> +        if (ay < 0) {
> +            ay = 0;
> +        }
> +        if (ax >= cw) {
> +            ax = cw-1;
> +        }
> +        if (ay >= ch) {
> +            ay = ch-1;
> +        }
> +        kbd_mouse_event(ax * 0x7FFF / cw, ay * 0x7FFF / ch, 0, b);
> +    } else {
> +        kbd_mouse_event(x, y, 0, b);
> +    }
> +}
> +
> +static int init_mouse(void)
> +{
> +    mice = open("/dev/input/mice", O_RDONLY);
> +    if (-1 == mice) {

I'd prefer mice == -1.  More of the same below.

> +        return -1;
> +    }
> +    qemu_set_fd_handler(mice, read_mouse, NULL, NULL);
> +    return 0;
> +}
> +
> +static void uninit_mouse(void)
> +{
> +    if (-1 == mice) {
> +        return;
> +    }
> +    qemu_set_fd_handler(mice, NULL, NULL, NULL);
> +    close(mice);
> +    mice = -1;
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* keyboard                                                             */
> +
> +static const char *keynames[] = {
> +#include "linux-keynames.h"
> +};
> +
> +static int scancode_map[KEY_CNT] = {
> +    [KEY_ESC]           = 0x01,
> +    [KEY_1]             = 0x02,
> +    [KEY_2]             = 0x03,
> +    [KEY_3]             = 0x04,
> +    [KEY_4]             = 0x05,
> +    [KEY_5]             = 0x06,
> +    [KEY_6]             = 0x07,
> +    [KEY_7]             = 0x08,
> +    [KEY_8]             = 0x09,
> +    [KEY_9]             = 0x0a,
> +    [KEY_0]             = 0x0b,
> +    [KEY_MINUS]         = 0x0c,
> +    [KEY_EQUAL]         = 0x0d,
> +    [KEY_BACKSPACE]     = 0x0e,
> +
> +    [KEY_TAB]           = 0x0f,
> +    [KEY_Q]             = 0x10,
> +    [KEY_W]             = 0x11,
> +    [KEY_E]             = 0x12,
> +    [KEY_R]             = 0x13,
> +    [KEY_T]             = 0x14,
> +    [KEY_Y]             = 0x15,
> +    [KEY_U]             = 0x16,
> +    [KEY_I]             = 0x17,
> +    [KEY_O]             = 0x18,
> +    [KEY_P]             = 0x19,
> +    [KEY_LEFTBRACE]     = 0x1a,
> +    [KEY_RIGHTBRACE]    = 0x1b,
> +    [KEY_ENTER]         = 0x1c,
> +
> +    [KEY_A]             = 0x1e,
> +    [KEY_S]             = 0x1f,
> +    [KEY_D]             = 0x20,
> +    [KEY_F]             = 0x21,
> +    [KEY_G]             = 0x22,
> +    [KEY_H]             = 0x23,
> +    [KEY_J]             = 0x24,
> +    [KEY_K]             = 0x25,
> +    [KEY_L]             = 0x26,
> +    [KEY_SEMICOLON]     = 0x27,
> +    [KEY_APOSTROPHE]    = 0x28,
> +    [KEY_GRAVE]         = 0x29,
> +    [KEY_LEFTSHIFT]     = 0x2a,
> +    [KEY_BACKSLASH]     = 0x2b,
> +
> +    [KEY_Z]             = 0x2c,
> +    [KEY_X]             = 0x2d,
> +    [KEY_C]             = 0x2e,
> +    [KEY_V]             = 0x2f,
> +    [KEY_B]             = 0x30,
> +    [KEY_N]             = 0x31,
> +    [KEY_M]             = 0x32,
> +    [KEY_COMMA]         = 0x33,
> +    [KEY_DOT]           = 0x34,
> +    [KEY_SLASH]         = 0x35,
> +    [KEY_RIGHTSHIFT]    = 0x36,
> +    [KEY_SPACE]         = 0x39,
> +
> +    [KEY_F1]            = 0x3b,
> +    [KEY_F2]            = 0x3c,
> +    [KEY_F3]            = 0x3d,
> +    [KEY_F4]            = 0x3e,
> +    [KEY_F5]            = 0x3f,
> +    [KEY_F6]            = 0x40,
> +    [KEY_F7]            = 0x41,
> +    [KEY_F8]            = 0x42,
> +    [KEY_F9]            = 0x43,
> +    [KEY_F10]           = 0x44,
> +    [KEY_F11]           = 0x57,
> +    [KEY_F12]           = 0x58,
> +
> +    [KEY_SYSRQ]         = 0xb7,
> +    [KEY_SCROLLLOCK]    = 0x46,
> +#if 0
> +    [KEY_PAUSE]         = FIXME,
> +#endif

Add a comment to explain what exactly needs fixing?

> +    [KEY_CAPSLOCK]      = 0x3a,
> +    [KEY_102ND]         = 0x56,
> +
> +    [KEY_LEFTCTRL]      = 0x1d,
> +    [KEY_LEFTMETA]      = 0xdb,
> +    [KEY_LEFTALT]       = 0x38,
> +    [KEY_RIGHTALT]      = 0xb8,
> +    [KEY_RIGHTMETA]     = 0xdc,
> +    [KEY_RIGHTCTRL]     = 0x9d,
> +    [KEY_COMPOSE]       = 0xdd,
> +
> +    [KEY_INSERT]        = 0xd2,
> +    [KEY_DELETE]        = 0xd3,
> +    [KEY_HOME]          = 0xc7,
> +    [KEY_END]           = 0xcf,
> +    [KEY_PAGEUP]        = 0xc9,
> +    [KEY_PAGEDOWN]      = 0xd1,
> +
> +    [KEY_UP]            = 0xc8,
> +    [KEY_LEFT]          = 0xcb,
> +    [KEY_RIGHT]         = 0xcd,
> +    [KEY_DOWN]          = 0xd0,
> +
> +    [KEY_NUMLOCK]       = 0x45,
> +    [KEY_KPSLASH]       = 0xb5,
> +    [KEY_KPASTERISK]    = 0x37,
> +    [KEY_KP7]           = 0x47,
> +    [KEY_KP8]           = 0x48,
> +    [KEY_KP9]           = 0x49,
> +    [KEY_KPMINUS]       = 0x4a,
> +    [KEY_KP4]           = 0x4b,
> +    [KEY_KP5]           = 0x4c,
> +    [KEY_KP6]           = 0x4d,
> +    [KEY_KPPLUS]        = 0x4e,
> +    [KEY_KP1]           = 0x4f,
> +    [KEY_KP2]           = 0x50,
> +    [KEY_KP3]           = 0x51,
> +    [KEY_KP0]           = 0x52,
> +    [KEY_KPDOT]         = 0x53,
> +    [KEY_KPENTER]       = 0x9c,
> +};
> +
> +static struct keysym_map {
> +    int  normal, shifted;
> +} keysym_map_en_us[KEY_CNT] = {
> +    [KEY_A] = { .normal = 'a', .shifted = 'A' },
> +    [KEY_B] = { .normal = 'b', .shifted = 'B' },
> +    [KEY_C] = { .normal = 'c', .shifted = 'C' },
> +    [KEY_D] = { .normal = 'd', .shifted = 'D' },
> +    [KEY_E] = { .normal = 'e', .shifted = 'E' },
> +    [KEY_F] = { .normal = 'f', .shifted = 'F' },
> +    [KEY_G] = { .normal = 'g', .shifted = 'G' },
> +    [KEY_H] = { .normal = 'h', .shifted = 'H' },
> +    [KEY_I] = { .normal = 'i', .shifted = 'I' },
> +    [KEY_J] = { .normal = 'j', .shifted = 'J' },
> +    [KEY_K] = { .normal = 'k', .shifted = 'K' },
> +    [KEY_L] = { .normal = 'l', .shifted = 'L' },
> +    [KEY_M] = { .normal = 'm', .shifted = 'M' },
> +    [KEY_N] = { .normal = 'n', .shifted = 'N' },
> +    [KEY_O] = { .normal = 'o', .shifted = 'O' },
> +    [KEY_P] = { .normal = 'p', .shifted = 'P' },
> +    [KEY_Q] = { .normal = 'q', .shifted = 'Q' },
> +    [KEY_R] = { .normal = 'r', .shifted = 'R' },
> +    [KEY_S] = { .normal = 's', .shifted = 'S' },
> +    [KEY_T] = { .normal = 't', .shifted = 'T' },
> +    [KEY_U] = { .normal = 'u', .shifted = 'U' },
> +    [KEY_V] = { .normal = 'v', .shifted = 'V' },
> +    [KEY_W] = { .normal = 'w', .shifted = 'W' },
> +    [KEY_X] = { .normal = 'x', .shifted = 'X' },
> +    [KEY_Y] = { .normal = 'y', .shifted = 'Y' },
> +    [KEY_Z] = { .normal = 'z', .shifted = 'Z' },
> +
> +    [KEY_1] = { .normal = '1', .shifted = '!' },
> +    [KEY_2] = { .normal = '2', .shifted = '@' },
> +    [KEY_3] = { .normal = '3', .shifted = '#' },
> +    [KEY_4] = { .normal = '4', .shifted = '$' },
> +    [KEY_5] = { .normal = '5', .shifted = '%' },
> +    [KEY_6] = { .normal = '6', .shifted = '^' },
> +    [KEY_7] = { .normal = '7', .shifted = '&' },
> +    [KEY_8] = { .normal = '8', .shifted = '*' },
> +    [KEY_9] = { .normal = '9', .shifted = '(' },
> +    [KEY_0] = { .normal = '0', .shifted = ')' },
> +
> +    [KEY_MINUS]       = { .normal = '-',  .shifted = '_'  },
> +    [KEY_EQUAL]       = { .normal = '=',  .shifted = '+'  },
> +    [KEY_TAB]         = { .normal = '\t'  },
> +    [KEY_LEFTBRACE]   = { .normal = '[',  .shifted = '{'  },
> +    [KEY_RIGHTBRACE]  = { .normal = ']',  .shifted = '}'  },
> +    [KEY_ENTER]       = { .normal = '\n', },
> +    [KEY_SEMICOLON]   = { .normal = ';',  .shifted = ':'  },
> +    [KEY_APOSTROPHE]  = { .normal = '"',  .shifted = '\'' },
> +    [KEY_BACKSLASH]   = { .normal = '\\', .shifted = '|'  },
> +    [KEY_COMMA]       = { .normal = ',',  .shifted = '<'  },
> +    [KEY_DOT]         = { .normal = '.',  .shifted = '>'  },
> +    [KEY_SLASH]       = { .normal = '/',  .shifted = '?'  },
> +    [KEY_SPACE]       = { .normal = ' '   },
> +
> +    [KEY_BACKSPACE]   = { .normal = QEMU_KEY_BACKSPACE  },
> +    [KEY_UP]          = { .normal = QEMU_KEY_UP         },
> +    [KEY_DOWN]        = { .normal = QEMU_KEY_DOWN       },
> +    [KEY_LEFT]        = { .normal = QEMU_KEY_LEFT       },
> +    [KEY_RIGHT]       = { .normal = QEMU_KEY_RIGHT      },
> +};
> +
> +static void start_mediumraw(int tty)
> +{
> +    struct termios tattr;
> +
> +    if (tty_mediumraw) {
> +        return;
> +    }
> +    trace_fbdev_kbd_raw(1);
> +
> +    /* save state */
> +    tcgetattr(tty, &tty_attributes);
> +    ioctl(tty, KDGKBMODE, &tty_mode);
> +    tty_flags = fcntl(tty, F_GETFL, NULL);
> +
> +    /* setup */
> +    tattr = tty_attributes;
> +    tattr.c_cflag &= ~(IXON|IXOFF);
> +    tattr.c_lflag &= ~(ICANON|ECHO|ISIG);
> +    tattr.c_iflag = 0;
> +    tattr.c_cc[VMIN] = 1;
> +    tattr.c_cc[VTIME] = 0;
> +    tcsetattr(tty, TCSAFLUSH, &tattr);
> +    ioctl(tty, KDSKBMODE, K_MEDIUMRAW);
> +    fcntl(tty, F_SETFL, tty_flags | O_NONBLOCK);
> +
> +    tty_mediumraw = true;
> +}
> +
> +static void stop_mediumraw(int tty)
> +{
> +    if (!tty_mediumraw) {
> +        return;
> +    }
> +    trace_fbdev_kbd_raw(0);
> +
> +    /* restore state */
> +    tcsetattr(tty, TCSANOW, &tty_attributes);
> +    ioctl(tty, KDSKBMODE, tty_mode);
> +    fcntl(tty, F_SETFL, tty_flags);
> +
> +    tty_mediumraw = false;
> +}
> +
> +static void send_scancode(int keycode, int up)
> +{
> +    int scancode = scancode_map[keycode];
> +
> +    if (!scancode) {
> +        fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
> +                __func__, keycode, keynames[keycode]);
> +        return;
> +    }
> +    if (scancode & SCANCODE_GREY) {
> +        kbd_put_keycode(SCANCODE_EMUL0);
> +    }
> +    if (up) {
> +        kbd_put_keycode(scancode | SCANCODE_UP);
> +    } else {
> +        kbd_put_keycode(scancode & SCANCODE_KEYCODEMASK);
> +    }
> +}
> +
> +static void send_keysym(int keycode, int shift)
> +{
> +    struct keysym_map *keysym_map = keysym_map_en_us;
> +    int keysym;
> +
> +    if (shift && keysym_map[keycode].shifted) {
> +        keysym = keysym_map[keycode].shifted;
> +    } else if (keysym_map[keycode].normal) {
> +        keysym = keysym_map[keycode].normal;
> +    } else {
> +        fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
> +                __func__, keycode, keynames[keycode]);
> +        return;
> +    }
> +    kbd_put_keysym(keysym);
> +}
> +
> +static void reset_keys(void)
> +{
> +    int keycode;
> +
> +    for (keycode = 0; keycode < KEY_MAX; keycode++) {
> +        if (key_down[keycode]) {
> +            if (is_graphic_console()) {
> +                send_scancode(keycode, 1);
> +            }
> +            key_down[keycode] = false;
> +        }
> +    }
> +}
> +
> +static void read_mediumraw(void *opaque)
> +{
> +    uint8_t buf[32];
> +    int i, rc, up, keycode;
> +    bool ctrl, alt, shift;
> +
> +    rc = read(tty, buf, sizeof(buf));
> +    switch (rc) {
> +    case -1:
> +        perror("read tty");
> +        goto err;
> +    case 0:
> +        fprintf(stderr, "%s: eof\n", __func__);
> +        goto err;
> +    default:
> +        for (i = 0; i < rc; i++) {
> +            up      = buf[i] & 0x80;
> +            keycode = buf[i] & 0x7f;
> +            if (keycode == 0) {
> +                keycode  = (buf[i+1] & 0x7f) << 7;
> +                keycode |= buf[i+2] & 0x7f;
> +                i += 2;
> +            }
> +            if (keycode > KEY_MAX) {
> +                continue;
> +            }
> +
> +            if (up) {
> +                if (!key_down[keycode]) {
> +                    continue;
> +                }
> +                key_down[keycode] = false;
> +            } else {
> +                key_down[keycode] = true;
> +            }
> +
> +            trace_fbdev_kbd_event(keycode, keynames[keycode], !up);
> +
> +            alt   = key_down[KEY_LEFTALT]   || key_down[KEY_RIGHTALT];
> +            ctrl  = key_down[KEY_LEFTCTRL]  || key_down[KEY_RIGHTCTRL];
> +            shift = key_down[KEY_LEFTSHIFT] || key_down[KEY_RIGHTSHIFT];
> +
> +            if (ctrl && alt && !up) {
> +                if (keycode == KEY_ESC) {
> +                    fprintf(stderr, "=== fbdev emergency escape "
> +                            "(ctrl-alt-esc) ===\n");
> +                    exit(1);
> +                }
> +                if (keycode >= KEY_F1 && keycode <= KEY_F10) {
> +                    fbdev_activate_vt(tty, keycode+1-KEY_F1, false);
> +                    key_down[keycode] = false;
> +                    continue;
> +                }
> +                if (keycode >= KEY_1 && keycode <= KEY_9) {
> +                    console_select(keycode-KEY_1);
> +                    reset_keys();
> +                    continue;
> +                }
> +            }
> +
> +            if (is_graphic_console()) {
> +                send_scancode(keycode, up);
> +            } else if (!up) {
> +                send_keysym(keycode, shift);
> +            }
> +        }
> +    }
> +    return;
> +
> +err:
> +    exit(1);
> +}
> +
> +/* -------------------------------------------------------------------- */
> +
> +static void fbdev_cls(void)
> +{
> +    memset(fb_mem+fb_mem_offset, 0, fb_fix.line_length * fb_var.yres);
> +}
> +
> +static int fbdev_activate_vt(int tty, int vtno, bool wait)
> +{
> +    trace_fbdev_vt_activate(vtno, wait);
> +
> +    if (ioctl(tty, VT_ACTIVATE, vtno) < 0) {
> +        perror("ioctl VT_ACTIVATE");
> +        return -1;
> +    }
> +
> +    if (wait) {
> +        if (ioctl(tty, VT_WAITACTIVE, vtno) < 0) {
> +            perror("ioctl VT_WAITACTIVE");
> +            return -1;
> +        }
> +        trace_fbdev_vt_activated();
> +    }
> +
> +    return 0;
> +}
> +
> +static void fbdev_cleanup(void)
> +{
> +    trace_fbdev_cleanup();
> +
> +    /* restore console */
> +    if (fb_mem != NULL) {
> +        munmap(fb_mem, fb_fix.smem_len+fb_mem_offset);
> +        fb_mem = NULL;
> +    }
> +    if (fb != -1) {
> +        if (ioctl(fb, FBIOPUT_VSCREENINFO, &fb_ovar) < 0) {
> +            perror("ioctl FBIOPUT_VSCREENINFO");
> +        }
> +        close(fb);
> +        fb = -1;
> +    }
> +
> +    if (tty != -1) {
> +        stop_mediumraw(tty);
> +        if (ioctl(tty, KDSETMODE, kd_omode) < 0) {
> +            perror("ioctl KDSETMODE");
> +        }
> +        if (ioctl(tty, VT_SETMODE, &vt_omode) < 0) {
> +            perror("ioctl VT_SETMODE");
> +        }
> +        if (orig_vtno) {
> +            fbdev_activate_vt(tty, orig_vtno, true);
> +        }
> +        qemu_set_fd_handler(tty, NULL, NULL, NULL);
> +        close(tty);
> +        tty = -1;
> +    }
> +}
> +
> +static int fbdev_init(const char *device)
> +{
> +    struct vt_stat vts;
> +    unsigned long page_mask;
> +    char ttyname[32];
> +
> +    /* open framebuffer */
> +    if (device == NULL) {
> +        device = getenv("FRAMEBUFFER");
> +    }
> +    if (device == NULL) {
> +        device = "/dev/fb0";
> +    }
> +    fb = open(device, O_RDWR);
> +    if (fb == -1) {
> +        fprintf(stderr, "open %s: %s\n", device, strerror(errno));
> +        return -1;
> +    }
> +
> +    /* open virtual console */
> +    tty = 0;
> +    if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
> +        fprintf(stderr, "Not started from virtual terminal, "
> +                "trying to open one.\n");
> +
> +        snprintf(ttyname, sizeof(ttyname), "/dev/tty0");
> +        tty = open(ttyname, O_RDWR);
> +        if (tty == -1) {
> +            fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno));
> +            goto err_early;
> +        }
> +        if (ioctl(tty, VT_OPENQRY, &vtno) < 0) {
> +            perror("ioctl VT_OPENQRY");
> +            goto err_early;
> +        }
> +        if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
> +            perror("ioctl VT_GETSTATE\n");
> +            goto err_early;
> +        }
> +        close(tty);
> +
> +        snprintf(ttyname, sizeof(ttyname), "/dev/tty%d", vtno);
> +        tty = open(ttyname, O_RDWR);
> +        if (tty == -1) {
> +            fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno));
> +            goto err_early;
> +        }
> +        orig_vtno = vts.v_active;
> +        fprintf(stderr, "Switching to vt %d (current %d).\n", vtno, orig_vtno);
> +    } else {
> +        orig_vtno = 0;
> +        vtno = vts.v_active;
> +        fprintf(stderr, "Started at vt %d, using it.\n", vtno);
> +    }
> +    fbdev_activate_vt(tty, vtno, true);
> +
> +    /* get current settings (which we have to restore) */
> +    if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) < 0) {
> +        perror("ioctl FBIOGET_VSCREENINFO");
> +        goto err_early;
> +    }
> +    if (ioctl(tty, KDGETMODE, &kd_omode) < 0) {
> +        perror("ioctl KDGETMODE");
> +        goto err_early;
> +    }
> +    if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) {
> +        perror("ioctl VT_GETMODE");
> +        goto err_early;
> +    }
> +
> +    /* checks & initialisation */
> +    if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) < 0) {
> +        perror("ioctl FBIOGET_FSCREENINFO");
> +        goto err;
> +    }
> +    if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) < 0) {
> +        perror("ioctl FBIOGET_VSCREENINFO");
> +        goto err;
> +    }
> +    if (fb_fix.type != FB_TYPE_PACKED_PIXELS) {
> +        fprintf(stderr, "can handle only packed pixel frame buffers\n");
> +        goto err;
> +    }
> +    switch (fb_var.bits_per_pixel) {
> +    case 32:
> +        break;
> +    default:
> +        fprintf(stderr, "can't handle %d bpp frame buffers\n",
> +                fb_var.bits_per_pixel);
> +        goto err;
> +    }
> +
> +    page_mask = getpagesize()-1;
> +    fb_switch_state = FB_ACTIVE;
> +    fb_mem_offset = (unsigned long)(fb_fix.smem_start) & page_mask;
> +    fb_mem = mmap(NULL, fb_fix.smem_len+fb_mem_offset,
> +                  PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);
> +    if (fb_mem == MAP_FAILED) {
> +        perror("mmap");
> +        goto err;
> +    }
> +    /* move viewport to upper left corner */
> +    if (fb_var.xoffset != 0 || fb_var.yoffset != 0) {
> +        fb_var.xoffset = 0;
> +        fb_var.yoffset = 0;
> +        if (ioctl(fb, FBIOPAN_DISPLAY, &fb_var) < 0) {
> +            perror("ioctl FBIOPAN_DISPLAY");
> +            goto err;
> +        }
> +    }
> +    if (ioctl(tty, KDSETMODE, KD_GRAPHICS) < 0) {
> +        perror("ioctl KDSETMODE");
> +        goto err;
> +    }
> +    /* some fb drivers need this again after switching to graphics ... */
> +    fbdev_activate_vt(tty, vtno, true);
> +
> +    fbdev_cls();
> +
> +    start_mediumraw(tty);
> +    qemu_set_fd_handler(tty, read_mediumraw, NULL, NULL);
> +
> +    /* create PixelFormat from fbdev structs */
> +    fbpf.bits_per_pixel  = fb_var.bits_per_pixel;
> +    fbpf.bytes_per_pixel = (fb_var.bits_per_pixel+7)/8;
> +    fbpf.depth           = fb_var.bits_per_pixel == 32
> +        ? 24 : fb_var.bits_per_pixel;
> +    fbpf.rshift          = fb_var.red.offset;
> +    fbpf.rbits           = fb_var.red.length;
> +    fbpf.gshift          = fb_var.green.offset;
> +    fbpf.gbits           = fb_var.green.length;
> +    fbpf.bshift          = fb_var.blue.offset;
> +    fbpf.bbits           = fb_var.blue.length;
> +    fbpf.ashift          = fb_var.transp.offset;
> +    fbpf.abits           = fb_var.transp.length;
> +
> +    if (fbpf.rbits) {
> +        fbpf.rmax   = (1 << fbpf.rbits) - 1;
> +        fbpf.rmask  = fbpf.rmax << fbpf.rshift;
> +    }
> +    if (fbpf.gbits) {
> +        fbpf.gmax   = (1 << fbpf.gbits) - 1;
> +        fbpf.gmask  = fbpf.gmax << fbpf.gshift;
> +    }
> +    if (fbpf.bbits) {
> +        fbpf.bmax   = (1 << fbpf.bbits) - 1;
> +        fbpf.bmask  = fbpf.bmax << fbpf.bshift;
> +    }
> +    if (fbpf.abits) {
> +        fbpf.amax   = (1 << fbpf.abits) - 1;
> +        fbpf.amask  = fbpf.amax << fbpf.ashift;
> +    }
> +    return 0;
> +
> +err_early:
> +    if (tty > 0) {
> +        close(tty);
> +    }
> +    close(fb);
> +    return -1;
> +
> +err:
> +    fbdev_cleanup();
> +    return -1;
> +}
> +
> +static void
> +fbdev_catch_fatal_signal(int signal)
> +{
> +    fbdev_cleanup();
> +    abort();
> +}
> +
> +static void fbdev_catch_exit_signals(void)
> +{
> +    struct sigaction act, old;
> +
> +    memset(&act, 0, sizeof(act));
> +    act.sa_handler = fbdev_catch_fatal_signal;
> +    sigemptyset(&act.sa_mask);
> +    sigaction(SIGSEGV, &act, &old);
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* console switching                                                    */
> +
> +static void fbdev_switch_signal(int signal)
> +{
> +    if (signal == SIG_REL) {
> +        /* release */
> +        trace_fbdev_vt_release_request();
> +        fb_switch_state = FB_REL_REQ;
> +    }
> +    if (signal == SIG_ACQ) {
> +        /* acquisition */
> +        trace_fbdev_vt_aquire_request();
> +        fb_switch_state = FB_ACQ_REQ;
> +    }
> +}
> +
> +static void fbdev_switch_release(void)
> +{
> +    stop_mediumraw(tty);
> +    ioctl(tty, KDSETMODE, kd_omode);
> +    ioctl(tty, VT_RELDISP, 1);
> +    fb_switch_state = FB_INACTIVE;
> +    trace_fbdev_vt_released();
> +}
> +
> +static void fbdev_switch_acquire(void)
> +{
> +    ioctl(tty, VT_RELDISP, VT_ACKACQ);
> +    start_mediumraw(tty);
> +    reset_keys();
> +    ioctl(tty, KDSETMODE, KD_GRAPHICS);
> +    fb_switch_state = FB_ACTIVE;
> +    trace_fbdev_vt_aquired();
> +}
> +
> +static int fbdev_switch_init(void)
> +{
> +    struct sigaction act, old;
> +
> +    memset(&act, 0, sizeof(act));
> +    act.sa_handler  = fbdev_switch_signal;
> +    sigemptyset(&act.sa_mask);
> +    sigaction(SIG_REL, &act, &old);
> +    sigaction(SIG_ACQ, &act, &old);
> +
> +    if (ioctl(tty, VT_GETMODE, &vt_mode) < 0) {
> +        perror("ioctl VT_GETMODE");
> +        exit(1);
> +    }
> +    vt_mode.mode   = VT_PROCESS;
> +    vt_mode.waitv  = 0;
> +    vt_mode.relsig = SIG_REL;
> +    vt_mode.acqsig = SIG_ACQ;
> +
> +    if (ioctl(tty, VT_SETMODE, &vt_mode) < 0) {
> +        perror("ioctl VT_SETMODE");
> +        exit(1);
> +    }
> +    return 0;
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* rendering                                                            */
> +
> +static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
> +{
> +    uint8_t *dst;
> +    uint8_t *src;
> +    int line;
> +
> +    if (!conv) {
> +        return;
> +    }
> +
> +    src = ds_get_data(ds) + y * ds_get_linesize(ds)
> +        + x * ds_get_bytes_per_pixel(ds);
> +    dst = fb_mem + y * fb_fix.line_length
> +        + x * fbpf.bytes_per_pixel;
> +
> +    dst += cy * fb_fix.line_length;
> +    dst += cx * fbpf.bytes_per_pixel;
> +
> +    if (h > fb_var.yres - y) {
> +        h = fb_var.yres - y;
> +    }
> +    if (w > fb_var.xres - x) {
> +        w = fb_var.xres - x;
> +    }
> +
> +    for (line = y; line < y+h; line++) {
> +        qemu_pf_conv_run(conv, dst, src, w);
> +        dst += fb_fix.line_length;
> +        src += ds_get_linesize(ds);
> +    }
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* qemu interfaces                                                      */
> +
> +static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
> +{
> +    if (fb_switch_state != FB_ACTIVE) {
> +        return;
> +    }
> +
> +    if (resize_screen) {
> +        trace_fbdev_dpy_resize(ds_get_width(ds), ds_get_height(ds));
> +        resize_screen = 0;
> +        cx = 0; cy = 0;
> +        cw = ds_get_width(ds);
> +        ch = ds_get_height(ds);
> +        if (ds_get_width(ds) < fb_var.xres) {
> +            cx = (fb_var.xres - ds_get_width(ds)) / 2;
> +        }
> +        if (ds_get_height(ds) < fb_var.yres) {
> +            cy = (fb_var.yres - ds_get_height(ds)) / 2;
> +        }
> +
> +        if (conv) {
> +            qemu_pf_conv_put(conv);
> +        }
> +        conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
> +        if (conv == NULL) {
> +            fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
> +        }
> +    }
> +
> +    if (redraw_screen) {
> +        trace_fbdev_dpy_redraw();
> +        redraw_screen = 0;
> +        fbdev_cls();
> +        x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
> +    }
> +
> +    fbdev_render(ds, x, y, w, h);
> +}
> +
> +static void fbdev_resize(DisplayState *ds)
> +{
> +    resize_screen++;
> +    redraw_screen++;
> +}
> +
> +static void fbdev_refresh(DisplayState *ds)
> +{
> +    switch (fb_switch_state) {
> +    case FB_REL_REQ:
> +        fbdev_switch_release();
> +    case FB_INACTIVE:
> +        return;
> +    case FB_ACQ_REQ:
> +        fbdev_switch_acquire();
> +        redraw_screen++;
> +    case FB_ACTIVE:
> +        break;
> +    }
> +
> +    vga_hw_update();
> +    if (redraw_screen) {
> +        fbdev_update(ds, 0, 0, 0, 0);
> +    }
> +}
> +
> +static void fbdev_exit_notifier(Notifier *notifier, void *data)
> +{
> +    fbdev_cleanup();
> +}
> +
> +int fbdev_display_init(DisplayState *ds, const char *device)
> +{
> +    if (dcl != NULL) {
> +        return 0;
> +    }
> +
> +    if (fbdev_init(device) != 0) {
> +        return -1;
> +    }
> +    exit_notifier.notify = fbdev_exit_notifier;
> +    qemu_add_exit_notifier(&exit_notifier);
> +    fbdev_switch_init();
> +    fbdev_catch_exit_signals();
> +    init_mouse();
> +
> +    fbdev_resize(ds);
> +    dcl = g_new0(DisplayChangeListener, 1);
> +    dcl->dpy_update  = fbdev_update;
> +    dcl->dpy_resize  = fbdev_resize;
> +    dcl->dpy_refresh = fbdev_refresh;
> +    register_displaychangelistener(ds, dcl);
> +
> +    trace_fbdev_enabled();
> +    return 0;
> +}
> +
> +void fbdev_display_uninit(DisplayState *ds)
> +{
> +    if (dcl == NULL) {
> +        return;
> +    }
> +
> +    unregister_displaychangelistener(ds, dcl);
> +    g_free(dcl);
> +    dcl = NULL;
> +
> +    fbdev_cleanup();
> +    qemu_remove_exit_notifier(&exit_notifier);
> +    uninit_mouse();
> +}
> diff --git a/ui/linux-keynames.h b/ui/linux-keynames.h
> new file mode 100644
> index 0000000..058af28
> --- /dev/null
> +++ b/ui/linux-keynames.h
> @@ -0,0 +1,388 @@
> +/*
> + *   awk '/#define KEY_/ { printf("    [%s] = \"%s\",\n",$2,$2); }' \
> + *       /usr/include/linux/input.h
> + */
> +    [KEY_RESERVED] = "KEY_RESERVED",
> +    [KEY_ESC] = "KEY_ESC",
> +    [KEY_1] = "KEY_1",
> +    [KEY_2] = "KEY_2",
> +    [KEY_3] = "KEY_3",
> +    [KEY_4] = "KEY_4",
> +    [KEY_5] = "KEY_5",
> +    [KEY_6] = "KEY_6",
> +    [KEY_7] = "KEY_7",
> +    [KEY_8] = "KEY_8",
> +    [KEY_9] = "KEY_9",
> +    [KEY_0] = "KEY_0",
> +    [KEY_MINUS] = "KEY_MINUS",
> +    [KEY_EQUAL] = "KEY_EQUAL",
> +    [KEY_BACKSPACE] = "KEY_BACKSPACE",
> +    [KEY_TAB] = "KEY_TAB",
> +    [KEY_Q] = "KEY_Q",
> +    [KEY_W] = "KEY_W",
> +    [KEY_E] = "KEY_E",
> +    [KEY_R] = "KEY_R",
> +    [KEY_T] = "KEY_T",
> +    [KEY_Y] = "KEY_Y",
> +    [KEY_U] = "KEY_U",
> +    [KEY_I] = "KEY_I",
> +    [KEY_O] = "KEY_O",
> +    [KEY_P] = "KEY_P",
> +    [KEY_LEFTBRACE] = "KEY_LEFTBRACE",
> +    [KEY_RIGHTBRACE] = "KEY_RIGHTBRACE",
> +    [KEY_ENTER] = "KEY_ENTER",
> +    [KEY_LEFTCTRL] = "KEY_LEFTCTRL",
> +    [KEY_A] = "KEY_A",
> +    [KEY_S] = "KEY_S",
> +    [KEY_D] = "KEY_D",
> +    [KEY_F] = "KEY_F",
> +    [KEY_G] = "KEY_G",
> +    [KEY_H] = "KEY_H",
> +    [KEY_J] = "KEY_J",
> +    [KEY_K] = "KEY_K",
> +    [KEY_L] = "KEY_L",
> +    [KEY_SEMICOLON] = "KEY_SEMICOLON",
> +    [KEY_APOSTROPHE] = "KEY_APOSTROPHE",
> +    [KEY_GRAVE] = "KEY_GRAVE",
> +    [KEY_LEFTSHIFT] = "KEY_LEFTSHIFT",
> +    [KEY_BACKSLASH] = "KEY_BACKSLASH",
> +    [KEY_Z] = "KEY_Z",
> +    [KEY_X] = "KEY_X",
> +    [KEY_C] = "KEY_C",
> +    [KEY_V] = "KEY_V",
> +    [KEY_B] = "KEY_B",
> +    [KEY_N] = "KEY_N",
> +    [KEY_M] = "KEY_M",
> +    [KEY_COMMA] = "KEY_COMMA",
> +    [KEY_DOT] = "KEY_DOT",
> +    [KEY_SLASH] = "KEY_SLASH",
> +    [KEY_RIGHTSHIFT] = "KEY_RIGHTSHIFT",
> +    [KEY_KPASTERISK] = "KEY_KPASTERISK",
> +    [KEY_LEFTALT] = "KEY_LEFTALT",
> +    [KEY_SPACE] = "KEY_SPACE",
> +    [KEY_CAPSLOCK] = "KEY_CAPSLOCK",
> +    [KEY_F1] = "KEY_F1",
> +    [KEY_F2] = "KEY_F2",
> +    [KEY_F3] = "KEY_F3",
> +    [KEY_F4] = "KEY_F4",
> +    [KEY_F5] = "KEY_F5",
> +    [KEY_F6] = "KEY_F6",
> +    [KEY_F7] = "KEY_F7",
> +    [KEY_F8] = "KEY_F8",
> +    [KEY_F9] = "KEY_F9",
> +    [KEY_F10] = "KEY_F10",
> +    [KEY_NUMLOCK] = "KEY_NUMLOCK",
> +    [KEY_SCROLLLOCK] = "KEY_SCROLLLOCK",
> +    [KEY_KP7] = "KEY_KP7",
> +    [KEY_KP8] = "KEY_KP8",
> +    [KEY_KP9] = "KEY_KP9",
> +    [KEY_KPMINUS] = "KEY_KPMINUS",
> +    [KEY_KP4] = "KEY_KP4",
> +    [KEY_KP5] = "KEY_KP5",
> +    [KEY_KP6] = "KEY_KP6",
> +    [KEY_KPPLUS] = "KEY_KPPLUS",
> +    [KEY_KP1] = "KEY_KP1",
> +    [KEY_KP2] = "KEY_KP2",
> +    [KEY_KP3] = "KEY_KP3",
> +    [KEY_KP0] = "KEY_KP0",
> +    [KEY_KPDOT] = "KEY_KPDOT",
> +    [KEY_ZENKAKUHANKAKU] = "KEY_ZENKAKUHANKAKU",
> +    [KEY_102ND] = "KEY_102ND",
> +    [KEY_F11] = "KEY_F11",
> +    [KEY_F12] = "KEY_F12",
> +    [KEY_RO] = "KEY_RO",
> +    [KEY_KATAKANA] = "KEY_KATAKANA",
> +    [KEY_HIRAGANA] = "KEY_HIRAGANA",
> +    [KEY_HENKAN] = "KEY_HENKAN",
> +    [KEY_KATAKANAHIRAGANA] = "KEY_KATAKANAHIRAGANA",
> +    [KEY_MUHENKAN] = "KEY_MUHENKAN",
> +    [KEY_KPJPCOMMA] = "KEY_KPJPCOMMA",
> +    [KEY_KPENTER] = "KEY_KPENTER",
> +    [KEY_RIGHTCTRL] = "KEY_RIGHTCTRL",
> +    [KEY_KPSLASH] = "KEY_KPSLASH",
> +    [KEY_SYSRQ] = "KEY_SYSRQ",
> +    [KEY_RIGHTALT] = "KEY_RIGHTALT",
> +    [KEY_LINEFEED] = "KEY_LINEFEED",
> +    [KEY_HOME] = "KEY_HOME",
> +    [KEY_UP] = "KEY_UP",
> +    [KEY_PAGEUP] = "KEY_PAGEUP",
> +    [KEY_LEFT] = "KEY_LEFT",
> +    [KEY_RIGHT] = "KEY_RIGHT",
> +    [KEY_END] = "KEY_END",
> +    [KEY_DOWN] = "KEY_DOWN",
> +    [KEY_PAGEDOWN] = "KEY_PAGEDOWN",
> +    [KEY_INSERT] = "KEY_INSERT",
> +    [KEY_DELETE] = "KEY_DELETE",
> +    [KEY_MACRO] = "KEY_MACRO",
> +    [KEY_MUTE] = "KEY_MUTE",
> +    [KEY_VOLUMEDOWN] = "KEY_VOLUMEDOWN",
> +    [KEY_VOLUMEUP] = "KEY_VOLUMEUP",
> +    [KEY_POWER] = "KEY_POWER",
> +    [KEY_KPEQUAL] = "KEY_KPEQUAL",
> +    [KEY_KPPLUSMINUS] = "KEY_KPPLUSMINUS",
> +    [KEY_PAUSE] = "KEY_PAUSE",
> +    [KEY_SCALE] = "KEY_SCALE",
> +    [KEY_KPCOMMA] = "KEY_KPCOMMA",
> +    [KEY_HANGEUL] = "KEY_HANGEUL",
> +    [KEY_HANGUEL] = "KEY_HANGUEL",
> +    [KEY_HANJA] = "KEY_HANJA",
> +    [KEY_YEN] = "KEY_YEN",
> +    [KEY_LEFTMETA] = "KEY_LEFTMETA",
> +    [KEY_RIGHTMETA] = "KEY_RIGHTMETA",
> +    [KEY_COMPOSE] = "KEY_COMPOSE",
> +    [KEY_STOP] = "KEY_STOP",
> +    [KEY_AGAIN] = "KEY_AGAIN",
> +    [KEY_PROPS] = "KEY_PROPS",
> +    [KEY_UNDO] = "KEY_UNDO",
> +    [KEY_FRONT] = "KEY_FRONT",
> +    [KEY_COPY] = "KEY_COPY",
> +    [KEY_OPEN] = "KEY_OPEN",
> +    [KEY_PASTE] = "KEY_PASTE",
> +    [KEY_FIND] = "KEY_FIND",
> +    [KEY_CUT] = "KEY_CUT",
> +    [KEY_HELP] = "KEY_HELP",
> +    [KEY_MENU] = "KEY_MENU",
> +    [KEY_CALC] = "KEY_CALC",
> +    [KEY_SETUP] = "KEY_SETUP",
> +    [KEY_SLEEP] = "KEY_SLEEP",
> +    [KEY_WAKEUP] = "KEY_WAKEUP",
> +    [KEY_FILE] = "KEY_FILE",
> +    [KEY_SENDFILE] = "KEY_SENDFILE",
> +    [KEY_DELETEFILE] = "KEY_DELETEFILE",
> +    [KEY_XFER] = "KEY_XFER",
> +    [KEY_PROG1] = "KEY_PROG1",
> +    [KEY_PROG2] = "KEY_PROG2",
> +    [KEY_WWW] = "KEY_WWW",
> +    [KEY_MSDOS] = "KEY_MSDOS",
> +    [KEY_COFFEE] = "KEY_COFFEE",
> +    [KEY_SCREENLOCK] = "KEY_SCREENLOCK",
> +    [KEY_DIRECTION] = "KEY_DIRECTION",
> +    [KEY_CYCLEWINDOWS] = "KEY_CYCLEWINDOWS",
> +    [KEY_MAIL] = "KEY_MAIL",
> +    [KEY_BOOKMARKS] = "KEY_BOOKMARKS",
> +    [KEY_COMPUTER] = "KEY_COMPUTER",
> +    [KEY_BACK] = "KEY_BACK",
> +    [KEY_FORWARD] = "KEY_FORWARD",
> +    [KEY_CLOSECD] = "KEY_CLOSECD",
> +    [KEY_EJECTCD] = "KEY_EJECTCD",
> +    [KEY_EJECTCLOSECD] = "KEY_EJECTCLOSECD",
> +    [KEY_NEXTSONG] = "KEY_NEXTSONG",
> +    [KEY_PLAYPAUSE] = "KEY_PLAYPAUSE",
> +    [KEY_PREVIOUSSONG] = "KEY_PREVIOUSSONG",
> +    [KEY_STOPCD] = "KEY_STOPCD",
> +    [KEY_RECORD] = "KEY_RECORD",
> +    [KEY_REWIND] = "KEY_REWIND",
> +    [KEY_PHONE] = "KEY_PHONE",
> +    [KEY_ISO] = "KEY_ISO",
> +    [KEY_CONFIG] = "KEY_CONFIG",
> +    [KEY_HOMEPAGE] = "KEY_HOMEPAGE",
> +    [KEY_REFRESH] = "KEY_REFRESH",
> +    [KEY_EXIT] = "KEY_EXIT",
> +    [KEY_MOVE] = "KEY_MOVE",
> +    [KEY_EDIT] = "KEY_EDIT",
> +    [KEY_SCROLLUP] = "KEY_SCROLLUP",
> +    [KEY_SCROLLDOWN] = "KEY_SCROLLDOWN",
> +    [KEY_KPLEFTPAREN] = "KEY_KPLEFTPAREN",
> +    [KEY_KPRIGHTPAREN] = "KEY_KPRIGHTPAREN",
> +    [KEY_NEW] = "KEY_NEW",
> +    [KEY_REDO] = "KEY_REDO",
> +    [KEY_F13] = "KEY_F13",
> +    [KEY_F14] = "KEY_F14",
> +    [KEY_F15] = "KEY_F15",
> +    [KEY_F16] = "KEY_F16",
> +    [KEY_F17] = "KEY_F17",
> +    [KEY_F18] = "KEY_F18",
> +    [KEY_F19] = "KEY_F19",
> +    [KEY_F20] = "KEY_F20",
> +    [KEY_F21] = "KEY_F21",
> +    [KEY_F22] = "KEY_F22",
> +    [KEY_F23] = "KEY_F23",
> +    [KEY_F24] = "KEY_F24",
> +    [KEY_PLAYCD] = "KEY_PLAYCD",
> +    [KEY_PAUSECD] = "KEY_PAUSECD",
> +    [KEY_PROG3] = "KEY_PROG3",
> +    [KEY_PROG4] = "KEY_PROG4",
> +    [KEY_DASHBOARD] = "KEY_DASHBOARD",
> +    [KEY_SUSPEND] = "KEY_SUSPEND",
> +    [KEY_CLOSE] = "KEY_CLOSE",
> +    [KEY_PLAY] = "KEY_PLAY",
> +    [KEY_FASTFORWARD] = "KEY_FASTFORWARD",
> +    [KEY_BASSBOOST] = "KEY_BASSBOOST",
> +    [KEY_PRINT] = "KEY_PRINT",
> +    [KEY_HP] = "KEY_HP",
> +    [KEY_CAMERA] = "KEY_CAMERA",
> +    [KEY_SOUND] = "KEY_SOUND",
> +    [KEY_QUESTION] = "KEY_QUESTION",
> +    [KEY_EMAIL] = "KEY_EMAIL",
> +    [KEY_CHAT] = "KEY_CHAT",
> +    [KEY_SEARCH] = "KEY_SEARCH",
> +    [KEY_CONNECT] = "KEY_CONNECT",
> +    [KEY_FINANCE] = "KEY_FINANCE",
> +    [KEY_SPORT] = "KEY_SPORT",
> +    [KEY_SHOP] = "KEY_SHOP",
> +    [KEY_ALTERASE] = "KEY_ALTERASE",
> +    [KEY_CANCEL] = "KEY_CANCEL",
> +    [KEY_BRIGHTNESSDOWN] = "KEY_BRIGHTNESSDOWN",
> +    [KEY_BRIGHTNESSUP] = "KEY_BRIGHTNESSUP",
> +    [KEY_MEDIA] = "KEY_MEDIA",
> +    [KEY_SWITCHVIDEOMODE] = "KEY_SWITCHVIDEOMODE",
> +    [KEY_KBDILLUMTOGGLE] = "KEY_KBDILLUMTOGGLE",
> +    [KEY_KBDILLUMDOWN] = "KEY_KBDILLUMDOWN",
> +    [KEY_KBDILLUMUP] = "KEY_KBDILLUMUP",
> +    [KEY_SEND] = "KEY_SEND",
> +    [KEY_REPLY] = "KEY_REPLY",
> +    [KEY_FORWARDMAIL] = "KEY_FORWARDMAIL",
> +    [KEY_SAVE] = "KEY_SAVE",
> +    [KEY_DOCUMENTS] = "KEY_DOCUMENTS",
> +    [KEY_BATTERY] = "KEY_BATTERY",
> +    [KEY_BLUETOOTH] = "KEY_BLUETOOTH",
> +    [KEY_WLAN] = "KEY_WLAN",
> +    [KEY_UWB] = "KEY_UWB",
> +    [KEY_UNKNOWN] = "KEY_UNKNOWN",
> +    [KEY_VIDEO_NEXT] = "KEY_VIDEO_NEXT",
> +    [KEY_VIDEO_PREV] = "KEY_VIDEO_PREV",
> +    [KEY_BRIGHTNESS_CYCLE] = "KEY_BRIGHTNESS_CYCLE",
> +    [KEY_BRIGHTNESS_ZERO] = "KEY_BRIGHTNESS_ZERO",
> +    [KEY_DISPLAY_OFF] = "KEY_DISPLAY_OFF",
> +    [KEY_WIMAX] = "KEY_WIMAX",
> +    [KEY_OK] = "KEY_OK",
> +    [KEY_SELECT] = "KEY_SELECT",
> +    [KEY_GOTO] = "KEY_GOTO",
> +    [KEY_CLEAR] = "KEY_CLEAR",
> +    [KEY_POWER2] = "KEY_POWER2",
> +    [KEY_OPTION] = "KEY_OPTION",
> +    [KEY_INFO] = "KEY_INFO",
> +    [KEY_TIME] = "KEY_TIME",
> +    [KEY_VENDOR] = "KEY_VENDOR",
> +    [KEY_ARCHIVE] = "KEY_ARCHIVE",
> +    [KEY_PROGRAM] = "KEY_PROGRAM",
> +    [KEY_CHANNEL] = "KEY_CHANNEL",
> +    [KEY_FAVORITES] = "KEY_FAVORITES",
> +    [KEY_EPG] = "KEY_EPG",
> +    [KEY_PVR] = "KEY_PVR",
> +    [KEY_MHP] = "KEY_MHP",
> +    [KEY_LANGUAGE] = "KEY_LANGUAGE",
> +    [KEY_TITLE] = "KEY_TITLE",
> +    [KEY_SUBTITLE] = "KEY_SUBTITLE",
> +    [KEY_ANGLE] = "KEY_ANGLE",
> +    [KEY_ZOOM] = "KEY_ZOOM",
> +    [KEY_MODE] = "KEY_MODE",
> +    [KEY_KEYBOARD] = "KEY_KEYBOARD",
> +    [KEY_SCREEN] = "KEY_SCREEN",
> +    [KEY_PC] = "KEY_PC",
> +    [KEY_TV] = "KEY_TV",
> +    [KEY_TV2] = "KEY_TV2",
> +    [KEY_VCR] = "KEY_VCR",
> +    [KEY_VCR2] = "KEY_VCR2",
> +    [KEY_SAT] = "KEY_SAT",
> +    [KEY_SAT2] = "KEY_SAT2",
> +    [KEY_CD] = "KEY_CD",
> +    [KEY_TAPE] = "KEY_TAPE",
> +    [KEY_RADIO] = "KEY_RADIO",
> +    [KEY_TUNER] = "KEY_TUNER",
> +    [KEY_PLAYER] = "KEY_PLAYER",
> +    [KEY_TEXT] = "KEY_TEXT",
> +    [KEY_DVD] = "KEY_DVD",
> +    [KEY_AUX] = "KEY_AUX",
> +    [KEY_MP3] = "KEY_MP3",
> +    [KEY_AUDIO] = "KEY_AUDIO",
> +    [KEY_VIDEO] = "KEY_VIDEO",
> +    [KEY_DIRECTORY] = "KEY_DIRECTORY",
> +    [KEY_LIST] = "KEY_LIST",
> +    [KEY_MEMO] = "KEY_MEMO",
> +    [KEY_CALENDAR] = "KEY_CALENDAR",
> +    [KEY_RED] = "KEY_RED",
> +    [KEY_GREEN] = "KEY_GREEN",
> +    [KEY_YELLOW] = "KEY_YELLOW",
> +    [KEY_BLUE] = "KEY_BLUE",
> +    [KEY_CHANNELUP] = "KEY_CHANNELUP",
> +    [KEY_CHANNELDOWN] = "KEY_CHANNELDOWN",
> +    [KEY_FIRST] = "KEY_FIRST",
> +    [KEY_LAST] = "KEY_LAST",
> +    [KEY_AB] = "KEY_AB",
> +    [KEY_NEXT] = "KEY_NEXT",
> +    [KEY_RESTART] = "KEY_RESTART",
> +    [KEY_SLOW] = "KEY_SLOW",
> +    [KEY_SHUFFLE] = "KEY_SHUFFLE",
> +    [KEY_BREAK] = "KEY_BREAK",
> +    [KEY_PREVIOUS] = "KEY_PREVIOUS",
> +    [KEY_DIGITS] = "KEY_DIGITS",
> +    [KEY_TEEN] = "KEY_TEEN",
> +    [KEY_TWEN] = "KEY_TWEN",
> +    [KEY_VIDEOPHONE] = "KEY_VIDEOPHONE",
> +    [KEY_GAMES] = "KEY_GAMES",
> +    [KEY_ZOOMIN] = "KEY_ZOOMIN",
> +    [KEY_ZOOMOUT] = "KEY_ZOOMOUT",
> +    [KEY_ZOOMRESET] = "KEY_ZOOMRESET",
> +    [KEY_WORDPROCESSOR] = "KEY_WORDPROCESSOR",
> +    [KEY_EDITOR] = "KEY_EDITOR",
> +    [KEY_SPREADSHEET] = "KEY_SPREADSHEET",
> +    [KEY_GRAPHICSEDITOR] = "KEY_GRAPHICSEDITOR",
> +    [KEY_PRESENTATION] = "KEY_PRESENTATION",
> +    [KEY_DATABASE] = "KEY_DATABASE",
> +    [KEY_NEWS] = "KEY_NEWS",
> +    [KEY_VOICEMAIL] = "KEY_VOICEMAIL",
> +    [KEY_ADDRESSBOOK] = "KEY_ADDRESSBOOK",
> +    [KEY_MESSENGER] = "KEY_MESSENGER",
> +    [KEY_DISPLAYTOGGLE] = "KEY_DISPLAYTOGGLE",
> +    [KEY_SPELLCHECK] = "KEY_SPELLCHECK",
> +    [KEY_LOGOFF] = "KEY_LOGOFF",
> +    [KEY_DOLLAR] = "KEY_DOLLAR",
> +    [KEY_EURO] = "KEY_EURO",
> +    [KEY_FRAMEBACK] = "KEY_FRAMEBACK",
> +    [KEY_FRAMEFORWARD] = "KEY_FRAMEFORWARD",
> +    [KEY_CONTEXT_MENU] = "KEY_CONTEXT_MENU",
> +    [KEY_MEDIA_REPEAT] = "KEY_MEDIA_REPEAT",
> +    [KEY_10CHANNELSUP] = "KEY_10CHANNELSUP",
> +    [KEY_10CHANNELSDOWN] = "KEY_10CHANNELSDOWN",
> +    [KEY_DEL_EOL] = "KEY_DEL_EOL",
> +    [KEY_DEL_EOS] = "KEY_DEL_EOS",
> +    [KEY_INS_LINE] = "KEY_INS_LINE",
> +    [KEY_DEL_LINE] = "KEY_DEL_LINE",
> +    [KEY_FN] = "KEY_FN",
> +    [KEY_FN_ESC] = "KEY_FN_ESC",
> +    [KEY_FN_F1] = "KEY_FN_F1",
> +    [KEY_FN_F2] = "KEY_FN_F2",
> +    [KEY_FN_F3] = "KEY_FN_F3",
> +    [KEY_FN_F4] = "KEY_FN_F4",
> +    [KEY_FN_F5] = "KEY_FN_F5",
> +    [KEY_FN_F6] = "KEY_FN_F6",
> +    [KEY_FN_F7] = "KEY_FN_F7",
> +    [KEY_FN_F8] = "KEY_FN_F8",
> +    [KEY_FN_F9] = "KEY_FN_F9",
> +    [KEY_FN_F10] = "KEY_FN_F10",
> +    [KEY_FN_F11] = "KEY_FN_F11",
> +    [KEY_FN_F12] = "KEY_FN_F12",
> +    [KEY_FN_1] = "KEY_FN_1",
> +    [KEY_FN_2] = "KEY_FN_2",
> +    [KEY_FN_D] = "KEY_FN_D",
> +    [KEY_FN_E] = "KEY_FN_E",
> +    [KEY_FN_F] = "KEY_FN_F",
> +    [KEY_FN_S] = "KEY_FN_S",
> +    [KEY_FN_B] = "KEY_FN_B",
> +    [KEY_BRL_DOT1] = "KEY_BRL_DOT1",
> +    [KEY_BRL_DOT2] = "KEY_BRL_DOT2",
> +    [KEY_BRL_DOT3] = "KEY_BRL_DOT3",
> +    [KEY_BRL_DOT4] = "KEY_BRL_DOT4",
> +    [KEY_BRL_DOT5] = "KEY_BRL_DOT5",
> +    [KEY_BRL_DOT6] = "KEY_BRL_DOT6",
> +    [KEY_BRL_DOT7] = "KEY_BRL_DOT7",
> +    [KEY_BRL_DOT8] = "KEY_BRL_DOT8",
> +    [KEY_BRL_DOT9] = "KEY_BRL_DOT9",
> +    [KEY_BRL_DOT10] = "KEY_BRL_DOT10",
> +    [KEY_NUMERIC_0] = "KEY_NUMERIC_0",
> +    [KEY_NUMERIC_1] = "KEY_NUMERIC_1",
> +    [KEY_NUMERIC_2] = "KEY_NUMERIC_2",
> +    [KEY_NUMERIC_3] = "KEY_NUMERIC_3",
> +    [KEY_NUMERIC_4] = "KEY_NUMERIC_4",
> +    [KEY_NUMERIC_5] = "KEY_NUMERIC_5",
> +    [KEY_NUMERIC_6] = "KEY_NUMERIC_6",
> +    [KEY_NUMERIC_7] = "KEY_NUMERIC_7",
> +    [KEY_NUMERIC_8] = "KEY_NUMERIC_8",
> +    [KEY_NUMERIC_9] = "KEY_NUMERIC_9",
> +    [KEY_NUMERIC_STAR] = "KEY_NUMERIC_STAR",
> +    [KEY_NUMERIC_POUND] = "KEY_NUMERIC_POUND",
> +    [KEY_RFKILL] = "KEY_RFKILL",
> +    [KEY_MIN_INTERESTING] = "KEY_MIN_INTERESTING",
> +    [KEY_MAX] = "KEY_MAX",
> +    [KEY_CNT] = "KEY_CNT",
> diff --git a/vl.c b/vl.c
> index fbb77fe..18982b2 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -3041,6 +3041,11 @@ int main(int argc, char **argv, char **envp)
>                  fprintf(stderr, "SDL support is disabled\n");
>                  exit(1);
>  #endif
> +#ifdef CONFIG_LINUX
> +            case QEMU_OPTION_fbdev:
> +                display_type = DT_FBDEV;
> +                break;
> +#endif
>              case QEMU_OPTION_pidfile:
>                  pid_file = optarg;
>                  break;
> @@ -3681,6 +3686,13 @@ int main(int argc, char **argv, char **envp)
>          curses_display_init(ds, full_screen);
>          break;
>  #endif
> +#if defined(CONFIG_LINUX)
> +    case DT_FBDEV:
> +        if (fbdev_display_init(ds, NULL) != 0) {
> +            exit(1);
> +        }
> +        break;
> +#endif
>  #if defined(CONFIG_SDL)
>      case DT_SDL:
>          sdl_display_init(ds, full_screen, no_frame);

Unlike the other display types, DT_FBDEV doesn't have its own
CONFIG_FBDEV, but uses CONFIG_LINUX instead.

configure --disable-fbdev would be nice.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] fbdev: add linux framebuffer display driver.
  2012-09-10 14:20 ` [Qemu-devel] [PATCH 3/4] fbdev: add linux framebuffer display driver Gerd Hoffmann
  2012-09-11 11:10   ` Markus Armbruster
@ 2012-09-11 20:37   ` Blue Swirl
  1 sibling, 0 replies; 10+ messages in thread
From: Blue Swirl @ 2012-09-11 20:37 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On Mon, Sep 10, 2012 at 2:20 PM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> Display works, requires truecolor framebuffer with 16 or 32 bpp on the
> host.  32bpp is recommended.  The framebuffer is used as-is, qemu
> doesn't try to switch modes.  With LCD displays mode switching is pretty
> pointless IMHO, also it wouldn't work anyway with the most common
> fbdev drivers (vesafb, KMS).  Guest display is centered on the host
> screen.
>
> Mouse works, uses /dev/input/mice.
>
> Keyboard works.  Guest screen has whatever keymap you load inside
> the guest.  Text windows (monitor, serial, ...) have a simple en-us
> keymap.  Good enougth to type monitor commands.  Not goot enougth to
> work seriously on a serial terminal.  But the qemu terminal emulation
> isn't good enougth for that anyway ;)
>
> Hot keys:
>   Ctrl-Alt-F<nr>  -> host console switching.
>   Ctrl-Alt-<nr>   -> qemu console switching.
>   Ctrl-Alt-ESC    -> exit qemu.
>
> Special feature:  Sane console switching.  Switching away stops screen
> updates.  Switching back redraws the screen.  When started from the
> linux console qemu uses the vt you've started it from (requires just
> read/write access to /dev/fb0).  When starting from somewhere else qemu
> tries to open a unused virtual terminal and switch to it (usually
> requires root privileges to open /dev/tty<nr>).
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  console.h           |    4 +
>  qemu-options.hx     |    8 +
>  sysemu.h            |    1 +
>  trace-events        |   14 +
>  ui/Makefile.objs    |    1 +
>  ui/fbdev.c          |  953 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  ui/linux-keynames.h |  388 +++++++++++++++++++++
>  vl.c                |   12 +
>  8 files changed, 1381 insertions(+), 0 deletions(-)
>  create mode 100644 ui/fbdev.c
>  create mode 100644 ui/linux-keynames.h
>
> diff --git a/console.h b/console.h
> index ddf3a3b..9d1fe72 100644
> --- a/console.h
> +++ b/console.h
> @@ -384,6 +384,10 @@ void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
>  /* sdl.c */
>  void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
>
> +/* fbdev.c */
> +int fbdev_display_init(DisplayState *ds, const char *device);
> +void fbdev_display_uninit(DisplayState *ds);
> +
>  /* cocoa.m */
>  void cocoa_display_init(DisplayState *ds, int full_screen);
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 3c411c4..b02dcbb 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -923,6 +923,14 @@ Enable/disable audio stream compression (using celt 0.5.1).  Default is on.
>  @end table
>  ETEXI
>
> +DEF("fbdev", 0, QEMU_OPTION_fbdev,
> +    "-fbdev          enable fbdev\n", QEMU_ARCH_ALL)
> +STEXI
> +@item -fbdev
> +@findex -fbdev
> +Enable fbdev (linux framebuffer).
> +ETEXI
> +
>  DEF("portrait", 0, QEMU_OPTION_portrait,
>      "-portrait       rotate graphical output 90 deg left (only PXA LCD)\n",
>      QEMU_ARCH_ALL)
> diff --git a/sysemu.h b/sysemu.h
> index 65552ac..34e6bfa 100644
> --- a/sysemu.h
> +++ b/sysemu.h
> @@ -93,6 +93,7 @@ typedef enum DisplayType
>      DT_DEFAULT,
>      DT_CURSES,
>      DT_SDL,
> +    DT_FBDEV,
>      DT_NOGRAPHIC,
>      DT_NONE,
>  } DisplayType;
> diff --git a/trace-events b/trace-events
> index 04b0723..3745db3 100644
> --- a/trace-events
> +++ b/trace-events
> @@ -978,3 +978,17 @@ spapr_pci_rtas_ibm_change_msi(unsigned func, unsigned req) "func %u, requested %
>  spapr_pci_rtas_ibm_query_interrupt_source_number(unsigned ioa, unsigned intr) "queries for #%u, IRQ%u"
>  spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq) "@%"PRIx64"<=%"PRIx64" IRQ %u"
>  spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ %u"
> +
> +# ui/fbdev.c
> +fbdev_enabled(void) ""
> +fbdev_cleanup(void) ""
> +fbdev_vt_activate(int vtno, int wait) "vtno %d, wait %d"
> +fbdev_vt_activated(void) ""
> +fbdev_vt_release_request(void) ""
> +fbdev_vt_released(void) ""
> +fbdev_vt_aquire_request(void) ""
> +fbdev_vt_aquired(void) ""
> +fbdev_kbd_raw(int enable) "enable %d"
> +fbdev_kbd_event(int keycode, const char *kname, int up) "keycode 0x%x [%s], down %d"
> +fbdev_dpy_resize(int w, int h) "%dx%d"
> +fbdev_dpy_redraw(void)
> \ No newline at end of file
> diff --git a/ui/Makefile.objs b/ui/Makefile.objs
> index adc07be..55ddcf2 100644
> --- a/ui/Makefile.objs
> +++ b/ui/Makefile.objs
> @@ -12,3 +12,4 @@ common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
>  common-obj-$(CONFIG_COCOA) += cocoa.o
>  common-obj-$(CONFIG_CURSES) += curses.o
>  common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
> +common-obj-$(CONFIG_LINUX) += fbdev.o
> diff --git a/ui/fbdev.c b/ui/fbdev.c
> new file mode 100644
> index 0000000..32c0a3b
> --- /dev/null
> +++ b/ui/fbdev.c
> @@ -0,0 +1,953 @@
> +/*
> + * linux fbdev output driver.
> + *
> + * Author: Gerd Hoffmann <kraxel@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See

GPLv2only?

> + * the COPYING file in the top-level directory.
> + *
> + */
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <signal.h>
> +#include <termios.h>
> +
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +
> +#include <linux/kd.h>
> +#include <linux/vt.h>
> +#include <linux/fb.h>
> +
> +#include "qemu-common.h"
> +#include "console.h"
> +#include "keymaps.h"
> +#include "sysemu.h"
> +#include "pflib.h"
> +
> +/*
> + * must be last so we get the linux input layer
> + * KEY_* defines, not the ncurses ones.
> + */
> +#include <linux/input.h>
> +
> +/* -------------------------------------------------------------------- */
> +
> +/* file handles */
> +static int                        tty = -1, fb = -1, mice = -1;
> +
> +/* saved state, for restore on exit */
> +static int                        orig_vtno;
> +static int                        kd_omode;
> +static struct vt_mode             vt_omode;
> +static struct fb_var_screeninfo   fb_ovar;
> +
> +/* framebuffer */
> +static struct fb_fix_screeninfo   fb_fix;
> +static struct fb_var_screeninfo   fb_var;
> +static uint8_t                   *fb_mem;
> +static int                        fb_mem_offset;
> +
> +/* linux console */
> +static int                        vtno;
> +static struct vt_mode             vt_mode;
> +static struct termios             tty_attributes;
> +static unsigned long              tty_mode;
> +static unsigned int               tty_flags;
> +static bool                       tty_mediumraw;
> +static bool                       key_down[KEY_CNT];
> +
> +/* console switching */
> +#define SIG_ACQ      (SIGRTMIN+6)
> +#define SIG_REL      (SIGRTMIN+7)
> +#define FB_ACTIVE    0
> +#define FB_REL_REQ   1
> +#define FB_INACTIVE  2
> +#define FB_ACQ_REQ   3
> +static int fb_switch_state;
> +
> +/* qdev windup */
> +static DisplayChangeListener      *dcl;
> +static QemuPfConv                 *conv;
> +static PixelFormat                fbpf;
> +static int                        resize_screen;
> +static int                        redraw_screen;
> +static int                        cx, cy, cw, ch;
> +static Notifier                   exit_notifier;
> +
> +/* fwd decls */
> +static int fbdev_activate_vt(int tty, int vtno, bool wait);
> +
> +/* -------------------------------------------------------------------- */
> +/* mouse                                                                */
> +
> +static void read_mouse(void *opaque)
> +{
> +    char buf[3];
> +    int rc, x, y, b;
> +
> +    rc = read(mice, buf, sizeof(buf));
> +    if (rc != sizeof(buf)) {
> +        return;
> +    }
> +
> +    if (fb_switch_state != FB_ACTIVE) {
> +        return;
> +    }
> +
> +    x = buf[1];
> +    y = -buf[2];
> +    b = buf[0] & 0x7;
> +
> +    if (kbd_mouse_is_absolute()) {
> +        static int ax, ay;
> +        ax += x; ay += y;
> +        if (ax < 0) {
> +            ax = 0;
> +        }
> +        if (ay < 0) {
> +            ay = 0;
> +        }
> +        if (ax >= cw) {
> +            ax = cw-1;
> +        }
> +        if (ay >= ch) {
> +            ay = ch-1;
> +        }
> +        kbd_mouse_event(ax * 0x7FFF / cw, ay * 0x7FFF / ch, 0, b);
> +    } else {
> +        kbd_mouse_event(x, y, 0, b);
> +    }
> +}
> +
> +static int init_mouse(void)
> +{
> +    mice = open("/dev/input/mice", O_RDONLY);
> +    if (-1 == mice) {

You probably guess what I'm going to say, but actually I read a nice
article why this order should be preferred. It's not common in QEMU
however.

> +        return -1;
> +    }
> +    qemu_set_fd_handler(mice, read_mouse, NULL, NULL);
> +    return 0;
> +}
> +
> +static void uninit_mouse(void)
> +{
> +    if (-1 == mice) {
> +        return;
> +    }
> +    qemu_set_fd_handler(mice, NULL, NULL, NULL);
> +    close(mice);
> +    mice = -1;
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* keyboard                                                             */
> +
> +static const char *keynames[] = {
> +#include "linux-keynames.h"
> +};
> +
> +static int scancode_map[KEY_CNT] = {

'const'?

> +    [KEY_ESC]           = 0x01,
> +    [KEY_1]             = 0x02,
> +    [KEY_2]             = 0x03,
> +    [KEY_3]             = 0x04,
> +    [KEY_4]             = 0x05,
> +    [KEY_5]             = 0x06,
> +    [KEY_6]             = 0x07,
> +    [KEY_7]             = 0x08,
> +    [KEY_8]             = 0x09,
> +    [KEY_9]             = 0x0a,
> +    [KEY_0]             = 0x0b,
> +    [KEY_MINUS]         = 0x0c,
> +    [KEY_EQUAL]         = 0x0d,
> +    [KEY_BACKSPACE]     = 0x0e,
> +
> +    [KEY_TAB]           = 0x0f,
> +    [KEY_Q]             = 0x10,
> +    [KEY_W]             = 0x11,
> +    [KEY_E]             = 0x12,
> +    [KEY_R]             = 0x13,
> +    [KEY_T]             = 0x14,
> +    [KEY_Y]             = 0x15,
> +    [KEY_U]             = 0x16,
> +    [KEY_I]             = 0x17,
> +    [KEY_O]             = 0x18,
> +    [KEY_P]             = 0x19,
> +    [KEY_LEFTBRACE]     = 0x1a,
> +    [KEY_RIGHTBRACE]    = 0x1b,
> +    [KEY_ENTER]         = 0x1c,
> +
> +    [KEY_A]             = 0x1e,
> +    [KEY_S]             = 0x1f,
> +    [KEY_D]             = 0x20,
> +    [KEY_F]             = 0x21,
> +    [KEY_G]             = 0x22,
> +    [KEY_H]             = 0x23,
> +    [KEY_J]             = 0x24,
> +    [KEY_K]             = 0x25,
> +    [KEY_L]             = 0x26,
> +    [KEY_SEMICOLON]     = 0x27,
> +    [KEY_APOSTROPHE]    = 0x28,
> +    [KEY_GRAVE]         = 0x29,
> +    [KEY_LEFTSHIFT]     = 0x2a,
> +    [KEY_BACKSLASH]     = 0x2b,
> +
> +    [KEY_Z]             = 0x2c,
> +    [KEY_X]             = 0x2d,
> +    [KEY_C]             = 0x2e,
> +    [KEY_V]             = 0x2f,
> +    [KEY_B]             = 0x30,
> +    [KEY_N]             = 0x31,
> +    [KEY_M]             = 0x32,
> +    [KEY_COMMA]         = 0x33,
> +    [KEY_DOT]           = 0x34,
> +    [KEY_SLASH]         = 0x35,
> +    [KEY_RIGHTSHIFT]    = 0x36,
> +    [KEY_SPACE]         = 0x39,
> +
> +    [KEY_F1]            = 0x3b,
> +    [KEY_F2]            = 0x3c,
> +    [KEY_F3]            = 0x3d,
> +    [KEY_F4]            = 0x3e,
> +    [KEY_F5]            = 0x3f,
> +    [KEY_F6]            = 0x40,
> +    [KEY_F7]            = 0x41,
> +    [KEY_F8]            = 0x42,
> +    [KEY_F9]            = 0x43,
> +    [KEY_F10]           = 0x44,
> +    [KEY_F11]           = 0x57,
> +    [KEY_F12]           = 0x58,
> +
> +    [KEY_SYSRQ]         = 0xb7,
> +    [KEY_SCROLLLOCK]    = 0x46,
> +#if 0
> +    [KEY_PAUSE]         = FIXME,
> +#endif
> +    [KEY_CAPSLOCK]      = 0x3a,
> +    [KEY_102ND]         = 0x56,
> +
> +    [KEY_LEFTCTRL]      = 0x1d,
> +    [KEY_LEFTMETA]      = 0xdb,
> +    [KEY_LEFTALT]       = 0x38,
> +    [KEY_RIGHTALT]      = 0xb8,
> +    [KEY_RIGHTMETA]     = 0xdc,
> +    [KEY_RIGHTCTRL]     = 0x9d,
> +    [KEY_COMPOSE]       = 0xdd,
> +
> +    [KEY_INSERT]        = 0xd2,
> +    [KEY_DELETE]        = 0xd3,
> +    [KEY_HOME]          = 0xc7,
> +    [KEY_END]           = 0xcf,
> +    [KEY_PAGEUP]        = 0xc9,
> +    [KEY_PAGEDOWN]      = 0xd1,
> +
> +    [KEY_UP]            = 0xc8,
> +    [KEY_LEFT]          = 0xcb,
> +    [KEY_RIGHT]         = 0xcd,
> +    [KEY_DOWN]          = 0xd0,
> +
> +    [KEY_NUMLOCK]       = 0x45,
> +    [KEY_KPSLASH]       = 0xb5,
> +    [KEY_KPASTERISK]    = 0x37,
> +    [KEY_KP7]           = 0x47,
> +    [KEY_KP8]           = 0x48,
> +    [KEY_KP9]           = 0x49,
> +    [KEY_KPMINUS]       = 0x4a,
> +    [KEY_KP4]           = 0x4b,
> +    [KEY_KP5]           = 0x4c,
> +    [KEY_KP6]           = 0x4d,
> +    [KEY_KPPLUS]        = 0x4e,
> +    [KEY_KP1]           = 0x4f,
> +    [KEY_KP2]           = 0x50,
> +    [KEY_KP3]           = 0x51,
> +    [KEY_KP0]           = 0x52,
> +    [KEY_KPDOT]         = 0x53,
> +    [KEY_KPENTER]       = 0x9c,
> +};
> +
> +static struct keysym_map {

const?

> +    int  normal, shifted;
> +} keysym_map_en_us[KEY_CNT] = {
> +    [KEY_A] = { .normal = 'a', .shifted = 'A' },
> +    [KEY_B] = { .normal = 'b', .shifted = 'B' },
> +    [KEY_C] = { .normal = 'c', .shifted = 'C' },
> +    [KEY_D] = { .normal = 'd', .shifted = 'D' },
> +    [KEY_E] = { .normal = 'e', .shifted = 'E' },
> +    [KEY_F] = { .normal = 'f', .shifted = 'F' },
> +    [KEY_G] = { .normal = 'g', .shifted = 'G' },
> +    [KEY_H] = { .normal = 'h', .shifted = 'H' },
> +    [KEY_I] = { .normal = 'i', .shifted = 'I' },
> +    [KEY_J] = { .normal = 'j', .shifted = 'J' },
> +    [KEY_K] = { .normal = 'k', .shifted = 'K' },
> +    [KEY_L] = { .normal = 'l', .shifted = 'L' },
> +    [KEY_M] = { .normal = 'm', .shifted = 'M' },
> +    [KEY_N] = { .normal = 'n', .shifted = 'N' },
> +    [KEY_O] = { .normal = 'o', .shifted = 'O' },
> +    [KEY_P] = { .normal = 'p', .shifted = 'P' },
> +    [KEY_Q] = { .normal = 'q', .shifted = 'Q' },
> +    [KEY_R] = { .normal = 'r', .shifted = 'R' },
> +    [KEY_S] = { .normal = 's', .shifted = 'S' },
> +    [KEY_T] = { .normal = 't', .shifted = 'T' },
> +    [KEY_U] = { .normal = 'u', .shifted = 'U' },
> +    [KEY_V] = { .normal = 'v', .shifted = 'V' },
> +    [KEY_W] = { .normal = 'w', .shifted = 'W' },
> +    [KEY_X] = { .normal = 'x', .shifted = 'X' },
> +    [KEY_Y] = { .normal = 'y', .shifted = 'Y' },
> +    [KEY_Z] = { .normal = 'z', .shifted = 'Z' },
> +
> +    [KEY_1] = { .normal = '1', .shifted = '!' },
> +    [KEY_2] = { .normal = '2', .shifted = '@' },
> +    [KEY_3] = { .normal = '3', .shifted = '#' },
> +    [KEY_4] = { .normal = '4', .shifted = '$' },
> +    [KEY_5] = { .normal = '5', .shifted = '%' },
> +    [KEY_6] = { .normal = '6', .shifted = '^' },
> +    [KEY_7] = { .normal = '7', .shifted = '&' },
> +    [KEY_8] = { .normal = '8', .shifted = '*' },
> +    [KEY_9] = { .normal = '9', .shifted = '(' },
> +    [KEY_0] = { .normal = '0', .shifted = ')' },
> +
> +    [KEY_MINUS]       = { .normal = '-',  .shifted = '_'  },
> +    [KEY_EQUAL]       = { .normal = '=',  .shifted = '+'  },
> +    [KEY_TAB]         = { .normal = '\t'  },
> +    [KEY_LEFTBRACE]   = { .normal = '[',  .shifted = '{'  },
> +    [KEY_RIGHTBRACE]  = { .normal = ']',  .shifted = '}'  },
> +    [KEY_ENTER]       = { .normal = '\n', },
> +    [KEY_SEMICOLON]   = { .normal = ';',  .shifted = ':'  },
> +    [KEY_APOSTROPHE]  = { .normal = '"',  .shifted = '\'' },
> +    [KEY_BACKSLASH]   = { .normal = '\\', .shifted = '|'  },
> +    [KEY_COMMA]       = { .normal = ',',  .shifted = '<'  },
> +    [KEY_DOT]         = { .normal = '.',  .shifted = '>'  },
> +    [KEY_SLASH]       = { .normal = '/',  .shifted = '?'  },
> +    [KEY_SPACE]       = { .normal = ' '   },
> +
> +    [KEY_BACKSPACE]   = { .normal = QEMU_KEY_BACKSPACE  },
> +    [KEY_UP]          = { .normal = QEMU_KEY_UP         },
> +    [KEY_DOWN]        = { .normal = QEMU_KEY_DOWN       },
> +    [KEY_LEFT]        = { .normal = QEMU_KEY_LEFT       },
> +    [KEY_RIGHT]       = { .normal = QEMU_KEY_RIGHT      },
> +};
> +
> +static void start_mediumraw(int tty)
> +{
> +    struct termios tattr;
> +
> +    if (tty_mediumraw) {
> +        return;
> +    }
> +    trace_fbdev_kbd_raw(1);
> +
> +    /* save state */
> +    tcgetattr(tty, &tty_attributes);
> +    ioctl(tty, KDGKBMODE, &tty_mode);
> +    tty_flags = fcntl(tty, F_GETFL, NULL);
> +
> +    /* setup */
> +    tattr = tty_attributes;
> +    tattr.c_cflag &= ~(IXON|IXOFF);
> +    tattr.c_lflag &= ~(ICANON|ECHO|ISIG);
> +    tattr.c_iflag = 0;
> +    tattr.c_cc[VMIN] = 1;
> +    tattr.c_cc[VTIME] = 0;
> +    tcsetattr(tty, TCSAFLUSH, &tattr);
> +    ioctl(tty, KDSKBMODE, K_MEDIUMRAW);
> +    fcntl(tty, F_SETFL, tty_flags | O_NONBLOCK);
> +
> +    tty_mediumraw = true;
> +}
> +
> +static void stop_mediumraw(int tty)
> +{
> +    if (!tty_mediumraw) {
> +        return;
> +    }
> +    trace_fbdev_kbd_raw(0);
> +
> +    /* restore state */
> +    tcsetattr(tty, TCSANOW, &tty_attributes);
> +    ioctl(tty, KDSKBMODE, tty_mode);
> +    fcntl(tty, F_SETFL, tty_flags);
> +
> +    tty_mediumraw = false;
> +}
> +
> +static void send_scancode(int keycode, int up)
> +{
> +    int scancode = scancode_map[keycode];
> +
> +    if (!scancode) {
> +        fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
> +                __func__, keycode, keynames[keycode]);
> +        return;
> +    }
> +    if (scancode & SCANCODE_GREY) {
> +        kbd_put_keycode(SCANCODE_EMUL0);
> +    }
> +    if (up) {
> +        kbd_put_keycode(scancode | SCANCODE_UP);
> +    } else {
> +        kbd_put_keycode(scancode & SCANCODE_KEYCODEMASK);
> +    }
> +}
> +
> +static void send_keysym(int keycode, int shift)
> +{
> +    struct keysym_map *keysym_map = keysym_map_en_us;
> +    int keysym;
> +
> +    if (shift && keysym_map[keycode].shifted) {
> +        keysym = keysym_map[keycode].shifted;
> +    } else if (keysym_map[keycode].normal) {
> +        keysym = keysym_map[keycode].normal;
> +    } else {
> +        fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
> +                __func__, keycode, keynames[keycode]);
> +        return;
> +    }
> +    kbd_put_keysym(keysym);
> +}
> +
> +static void reset_keys(void)
> +{
> +    int keycode;
> +
> +    for (keycode = 0; keycode < KEY_MAX; keycode++) {
> +        if (key_down[keycode]) {
> +            if (is_graphic_console()) {
> +                send_scancode(keycode, 1);
> +            }
> +            key_down[keycode] = false;
> +        }
> +    }
> +}
> +
> +static void read_mediumraw(void *opaque)
> +{
> +    uint8_t buf[32];
> +    int i, rc, up, keycode;
> +    bool ctrl, alt, shift;
> +
> +    rc = read(tty, buf, sizeof(buf));
> +    switch (rc) {
> +    case -1:
> +        perror("read tty");
> +        goto err;
> +    case 0:
> +        fprintf(stderr, "%s: eof\n", __func__);
> +        goto err;
> +    default:
> +        for (i = 0; i < rc; i++) {
> +            up      = buf[i] & 0x80;
> +            keycode = buf[i] & 0x7f;
> +            if (keycode == 0) {
> +                keycode  = (buf[i+1] & 0x7f) << 7;
> +                keycode |= buf[i+2] & 0x7f;
> +                i += 2;
> +            }
> +            if (keycode > KEY_MAX) {
> +                continue;
> +            }
> +
> +            if (up) {
> +                if (!key_down[keycode]) {
> +                    continue;
> +                }
> +                key_down[keycode] = false;
> +            } else {
> +                key_down[keycode] = true;
> +            }
> +
> +            trace_fbdev_kbd_event(keycode, keynames[keycode], !up);
> +
> +            alt   = key_down[KEY_LEFTALT]   || key_down[KEY_RIGHTALT];
> +            ctrl  = key_down[KEY_LEFTCTRL]  || key_down[KEY_RIGHTCTRL];
> +            shift = key_down[KEY_LEFTSHIFT] || key_down[KEY_RIGHTSHIFT];
> +
> +            if (ctrl && alt && !up) {
> +                if (keycode == KEY_ESC) {
> +                    fprintf(stderr, "=== fbdev emergency escape "
> +                            "(ctrl-alt-esc) ===\n");
> +                    exit(1);
> +                }
> +                if (keycode >= KEY_F1 && keycode <= KEY_F10) {
> +                    fbdev_activate_vt(tty, keycode+1-KEY_F1, false);
> +                    key_down[keycode] = false;
> +                    continue;
> +                }
> +                if (keycode >= KEY_1 && keycode <= KEY_9) {
> +                    console_select(keycode-KEY_1);
> +                    reset_keys();
> +                    continue;
> +                }
> +            }
> +
> +            if (is_graphic_console()) {
> +                send_scancode(keycode, up);
> +            } else if (!up) {
> +                send_keysym(keycode, shift);
> +            }
> +        }
> +    }
> +    return;
> +
> +err:
> +    exit(1);
> +}
> +
> +/* -------------------------------------------------------------------- */
> +
> +static void fbdev_cls(void)
> +{
> +    memset(fb_mem+fb_mem_offset, 0, fb_fix.line_length * fb_var.yres);

Spaces around '+', maybe checkpatch.pl can catch these.

> +}
> +
> +static int fbdev_activate_vt(int tty, int vtno, bool wait)
> +{
> +    trace_fbdev_vt_activate(vtno, wait);
> +
> +    if (ioctl(tty, VT_ACTIVATE, vtno) < 0) {
> +        perror("ioctl VT_ACTIVATE");
> +        return -1;
> +    }
> +
> +    if (wait) {
> +        if (ioctl(tty, VT_WAITACTIVE, vtno) < 0) {
> +            perror("ioctl VT_WAITACTIVE");
> +            return -1;
> +        }
> +        trace_fbdev_vt_activated();
> +    }
> +
> +    return 0;
> +}
> +
> +static void fbdev_cleanup(void)
> +{
> +    trace_fbdev_cleanup();
> +
> +    /* restore console */
> +    if (fb_mem != NULL) {
> +        munmap(fb_mem, fb_fix.smem_len+fb_mem_offset);
> +        fb_mem = NULL;
> +    }
> +    if (fb != -1) {
> +        if (ioctl(fb, FBIOPUT_VSCREENINFO, &fb_ovar) < 0) {
> +            perror("ioctl FBIOPUT_VSCREENINFO");
> +        }
> +        close(fb);
> +        fb = -1;
> +    }
> +
> +    if (tty != -1) {
> +        stop_mediumraw(tty);
> +        if (ioctl(tty, KDSETMODE, kd_omode) < 0) {
> +            perror("ioctl KDSETMODE");
> +        }
> +        if (ioctl(tty, VT_SETMODE, &vt_omode) < 0) {
> +            perror("ioctl VT_SETMODE");
> +        }
> +        if (orig_vtno) {
> +            fbdev_activate_vt(tty, orig_vtno, true);
> +        }
> +        qemu_set_fd_handler(tty, NULL, NULL, NULL);
> +        close(tty);
> +        tty = -1;
> +    }
> +}
> +
> +static int fbdev_init(const char *device)
> +{
> +    struct vt_stat vts;
> +    unsigned long page_mask;
> +    char ttyname[32];
> +
> +    /* open framebuffer */
> +    if (device == NULL) {
> +        device = getenv("FRAMEBUFFER");
> +    }
> +    if (device == NULL) {
> +        device = "/dev/fb0";
> +    }
> +    fb = open(device, O_RDWR);
> +    if (fb == -1) {
> +        fprintf(stderr, "open %s: %s\n", device, strerror(errno));
> +        return -1;
> +    }
> +
> +    /* open virtual console */
> +    tty = 0;
> +    if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
> +        fprintf(stderr, "Not started from virtual terminal, "
> +                "trying to open one.\n");
> +
> +        snprintf(ttyname, sizeof(ttyname), "/dev/tty0");
> +        tty = open(ttyname, O_RDWR);
> +        if (tty == -1) {
> +            fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno));
> +            goto err_early;
> +        }
> +        if (ioctl(tty, VT_OPENQRY, &vtno) < 0) {
> +            perror("ioctl VT_OPENQRY");
> +            goto err_early;
> +        }
> +        if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
> +            perror("ioctl VT_GETSTATE\n");
> +            goto err_early;
> +        }
> +        close(tty);
> +
> +        snprintf(ttyname, sizeof(ttyname), "/dev/tty%d", vtno);
> +        tty = open(ttyname, O_RDWR);
> +        if (tty == -1) {
> +            fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno));
> +            goto err_early;
> +        }
> +        orig_vtno = vts.v_active;
> +        fprintf(stderr, "Switching to vt %d (current %d).\n", vtno, orig_vtno);
> +    } else {
> +        orig_vtno = 0;
> +        vtno = vts.v_active;
> +        fprintf(stderr, "Started at vt %d, using it.\n", vtno);
> +    }
> +    fbdev_activate_vt(tty, vtno, true);
> +
> +    /* get current settings (which we have to restore) */
> +    if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) < 0) {
> +        perror("ioctl FBIOGET_VSCREENINFO");
> +        goto err_early;
> +    }
> +    if (ioctl(tty, KDGETMODE, &kd_omode) < 0) {
> +        perror("ioctl KDGETMODE");
> +        goto err_early;
> +    }
> +    if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) {
> +        perror("ioctl VT_GETMODE");
> +        goto err_early;
> +    }
> +
> +    /* checks & initialisation */
> +    if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) < 0) {
> +        perror("ioctl FBIOGET_FSCREENINFO");
> +        goto err;
> +    }
> +    if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) < 0) {
> +        perror("ioctl FBIOGET_VSCREENINFO");
> +        goto err;
> +    }
> +    if (fb_fix.type != FB_TYPE_PACKED_PIXELS) {
> +        fprintf(stderr, "can handle only packed pixel frame buffers\n");
> +        goto err;
> +    }
> +    switch (fb_var.bits_per_pixel) {
> +    case 32:
> +        break;
> +    default:
> +        fprintf(stderr, "can't handle %d bpp frame buffers\n",
> +                fb_var.bits_per_pixel);
> +        goto err;
> +    }
> +
> +    page_mask = getpagesize()-1;
> +    fb_switch_state = FB_ACTIVE;
> +    fb_mem_offset = (unsigned long)(fb_fix.smem_start) & page_mask;
> +    fb_mem = mmap(NULL, fb_fix.smem_len+fb_mem_offset,
> +                  PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);
> +    if (fb_mem == MAP_FAILED) {
> +        perror("mmap");
> +        goto err;
> +    }
> +    /* move viewport to upper left corner */
> +    if (fb_var.xoffset != 0 || fb_var.yoffset != 0) {
> +        fb_var.xoffset = 0;
> +        fb_var.yoffset = 0;
> +        if (ioctl(fb, FBIOPAN_DISPLAY, &fb_var) < 0) {
> +            perror("ioctl FBIOPAN_DISPLAY");
> +            goto err;
> +        }
> +    }
> +    if (ioctl(tty, KDSETMODE, KD_GRAPHICS) < 0) {
> +        perror("ioctl KDSETMODE");
> +        goto err;
> +    }
> +    /* some fb drivers need this again after switching to graphics ... */
> +    fbdev_activate_vt(tty, vtno, true);
> +
> +    fbdev_cls();
> +
> +    start_mediumraw(tty);
> +    qemu_set_fd_handler(tty, read_mediumraw, NULL, NULL);
> +
> +    /* create PixelFormat from fbdev structs */
> +    fbpf.bits_per_pixel  = fb_var.bits_per_pixel;
> +    fbpf.bytes_per_pixel = (fb_var.bits_per_pixel+7)/8;
> +    fbpf.depth           = fb_var.bits_per_pixel == 32
> +        ? 24 : fb_var.bits_per_pixel;
> +    fbpf.rshift          = fb_var.red.offset;
> +    fbpf.rbits           = fb_var.red.length;
> +    fbpf.gshift          = fb_var.green.offset;
> +    fbpf.gbits           = fb_var.green.length;
> +    fbpf.bshift          = fb_var.blue.offset;
> +    fbpf.bbits           = fb_var.blue.length;
> +    fbpf.ashift          = fb_var.transp.offset;
> +    fbpf.abits           = fb_var.transp.length;
> +
> +    if (fbpf.rbits) {
> +        fbpf.rmax   = (1 << fbpf.rbits) - 1;
> +        fbpf.rmask  = fbpf.rmax << fbpf.rshift;
> +    }
> +    if (fbpf.gbits) {
> +        fbpf.gmax   = (1 << fbpf.gbits) - 1;
> +        fbpf.gmask  = fbpf.gmax << fbpf.gshift;
> +    }
> +    if (fbpf.bbits) {
> +        fbpf.bmax   = (1 << fbpf.bbits) - 1;
> +        fbpf.bmask  = fbpf.bmax << fbpf.bshift;
> +    }
> +    if (fbpf.abits) {
> +        fbpf.amax   = (1 << fbpf.abits) - 1;
> +        fbpf.amask  = fbpf.amax << fbpf.ashift;
> +    }
> +    return 0;
> +
> +err_early:
> +    if (tty > 0) {
> +        close(tty);
> +    }
> +    close(fb);
> +    return -1;
> +
> +err:
> +    fbdev_cleanup();
> +    return -1;
> +}
> +
> +static void
> +fbdev_catch_fatal_signal(int signal)
> +{
> +    fbdev_cleanup();
> +    abort();
> +}
> +
> +static void fbdev_catch_exit_signals(void)
> +{
> +    struct sigaction act, old;
> +
> +    memset(&act, 0, sizeof(act));
> +    act.sa_handler = fbdev_catch_fatal_signal;
> +    sigemptyset(&act.sa_mask);
> +    sigaction(SIGSEGV, &act, &old);
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* console switching                                                    */
> +
> +static void fbdev_switch_signal(int signal)
> +{
> +    if (signal == SIG_REL) {
> +        /* release */
> +        trace_fbdev_vt_release_request();
> +        fb_switch_state = FB_REL_REQ;
> +    }
> +    if (signal == SIG_ACQ) {
> +        /* acquisition */
> +        trace_fbdev_vt_aquire_request();
> +        fb_switch_state = FB_ACQ_REQ;
> +    }
> +}
> +
> +static void fbdev_switch_release(void)
> +{
> +    stop_mediumraw(tty);
> +    ioctl(tty, KDSETMODE, kd_omode);
> +    ioctl(tty, VT_RELDISP, 1);
> +    fb_switch_state = FB_INACTIVE;
> +    trace_fbdev_vt_released();
> +}
> +
> +static void fbdev_switch_acquire(void)
> +{
> +    ioctl(tty, VT_RELDISP, VT_ACKACQ);
> +    start_mediumraw(tty);
> +    reset_keys();
> +    ioctl(tty, KDSETMODE, KD_GRAPHICS);
> +    fb_switch_state = FB_ACTIVE;
> +    trace_fbdev_vt_aquired();
> +}
> +
> +static int fbdev_switch_init(void)
> +{
> +    struct sigaction act, old;
> +
> +    memset(&act, 0, sizeof(act));
> +    act.sa_handler  = fbdev_switch_signal;
> +    sigemptyset(&act.sa_mask);
> +    sigaction(SIG_REL, &act, &old);
> +    sigaction(SIG_ACQ, &act, &old);
> +
> +    if (ioctl(tty, VT_GETMODE, &vt_mode) < 0) {
> +        perror("ioctl VT_GETMODE");
> +        exit(1);
> +    }
> +    vt_mode.mode   = VT_PROCESS;
> +    vt_mode.waitv  = 0;
> +    vt_mode.relsig = SIG_REL;
> +    vt_mode.acqsig = SIG_ACQ;
> +
> +    if (ioctl(tty, VT_SETMODE, &vt_mode) < 0) {
> +        perror("ioctl VT_SETMODE");
> +        exit(1);
> +    }
> +    return 0;
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* rendering                                                            */
> +
> +static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
> +{
> +    uint8_t *dst;
> +    uint8_t *src;
> +    int line;
> +
> +    if (!conv) {
> +        return;
> +    }
> +
> +    src = ds_get_data(ds) + y * ds_get_linesize(ds)
> +        + x * ds_get_bytes_per_pixel(ds);
> +    dst = fb_mem + y * fb_fix.line_length
> +        + x * fbpf.bytes_per_pixel;
> +
> +    dst += cy * fb_fix.line_length;
> +    dst += cx * fbpf.bytes_per_pixel;
> +
> +    if (h > fb_var.yres - y) {
> +        h = fb_var.yres - y;
> +    }
> +    if (w > fb_var.xres - x) {
> +        w = fb_var.xres - x;
> +    }
> +
> +    for (line = y; line < y+h; line++) {
> +        qemu_pf_conv_run(conv, dst, src, w);
> +        dst += fb_fix.line_length;
> +        src += ds_get_linesize(ds);
> +    }
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* qemu interfaces                                                      */
> +
> +static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
> +{
> +    if (fb_switch_state != FB_ACTIVE) {
> +        return;
> +    }
> +
> +    if (resize_screen) {
> +        trace_fbdev_dpy_resize(ds_get_width(ds), ds_get_height(ds));
> +        resize_screen = 0;
> +        cx = 0; cy = 0;
> +        cw = ds_get_width(ds);
> +        ch = ds_get_height(ds);
> +        if (ds_get_width(ds) < fb_var.xres) {
> +            cx = (fb_var.xres - ds_get_width(ds)) / 2;
> +        }
> +        if (ds_get_height(ds) < fb_var.yres) {
> +            cy = (fb_var.yres - ds_get_height(ds)) / 2;
> +        }
> +
> +        if (conv) {
> +            qemu_pf_conv_put(conv);
> +        }
> +        conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
> +        if (conv == NULL) {
> +            fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
> +        }
> +    }
> +
> +    if (redraw_screen) {
> +        trace_fbdev_dpy_redraw();
> +        redraw_screen = 0;
> +        fbdev_cls();
> +        x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
> +    }
> +
> +    fbdev_render(ds, x, y, w, h);
> +}
> +
> +static void fbdev_resize(DisplayState *ds)
> +{
> +    resize_screen++;
> +    redraw_screen++;
> +}
> +
> +static void fbdev_refresh(DisplayState *ds)
> +{
> +    switch (fb_switch_state) {
> +    case FB_REL_REQ:
> +        fbdev_switch_release();
> +    case FB_INACTIVE:
> +        return;
> +    case FB_ACQ_REQ:
> +        fbdev_switch_acquire();
> +        redraw_screen++;
> +    case FB_ACTIVE:
> +        break;
> +    }
> +
> +    vga_hw_update();
> +    if (redraw_screen) {
> +        fbdev_update(ds, 0, 0, 0, 0);
> +    }
> +}
> +
> +static void fbdev_exit_notifier(Notifier *notifier, void *data)
> +{
> +    fbdev_cleanup();
> +}
> +
> +int fbdev_display_init(DisplayState *ds, const char *device)
> +{
> +    if (dcl != NULL) {
> +        return 0;
> +    }
> +
> +    if (fbdev_init(device) != 0) {
> +        return -1;
> +    }
> +    exit_notifier.notify = fbdev_exit_notifier;
> +    qemu_add_exit_notifier(&exit_notifier);
> +    fbdev_switch_init();
> +    fbdev_catch_exit_signals();
> +    init_mouse();
> +
> +    fbdev_resize(ds);
> +    dcl = g_new0(DisplayChangeListener, 1);
> +    dcl->dpy_update  = fbdev_update;
> +    dcl->dpy_resize  = fbdev_resize;
> +    dcl->dpy_refresh = fbdev_refresh;
> +    register_displaychangelistener(ds, dcl);
> +
> +    trace_fbdev_enabled();
> +    return 0;
> +}
> +
> +void fbdev_display_uninit(DisplayState *ds)
> +{
> +    if (dcl == NULL) {
> +        return;
> +    }
> +
> +    unregister_displaychangelistener(ds, dcl);
> +    g_free(dcl);
> +    dcl = NULL;
> +
> +    fbdev_cleanup();
> +    qemu_remove_exit_notifier(&exit_notifier);
> +    uninit_mouse();
> +}
> diff --git a/ui/linux-keynames.h b/ui/linux-keynames.h
> new file mode 100644
> index 0000000..058af28
> --- /dev/null
> +++ b/ui/linux-keynames.h
> @@ -0,0 +1,388 @@
> +/*
> + *   awk '/#define KEY_/ { printf("    [%s] = \"%s\",\n",$2,$2); }' \
> + *       /usr/include/linux/input.h
> + */
> +    [KEY_RESERVED] = "KEY_RESERVED",
> +    [KEY_ESC] = "KEY_ESC",
> +    [KEY_1] = "KEY_1",
> +    [KEY_2] = "KEY_2",
> +    [KEY_3] = "KEY_3",
> +    [KEY_4] = "KEY_4",
> +    [KEY_5] = "KEY_5",
> +    [KEY_6] = "KEY_6",
> +    [KEY_7] = "KEY_7",
> +    [KEY_8] = "KEY_8",
> +    [KEY_9] = "KEY_9",
> +    [KEY_0] = "KEY_0",
> +    [KEY_MINUS] = "KEY_MINUS",
> +    [KEY_EQUAL] = "KEY_EQUAL",
> +    [KEY_BACKSPACE] = "KEY_BACKSPACE",
> +    [KEY_TAB] = "KEY_TAB",
> +    [KEY_Q] = "KEY_Q",
> +    [KEY_W] = "KEY_W",
> +    [KEY_E] = "KEY_E",
> +    [KEY_R] = "KEY_R",
> +    [KEY_T] = "KEY_T",
> +    [KEY_Y] = "KEY_Y",
> +    [KEY_U] = "KEY_U",
> +    [KEY_I] = "KEY_I",
> +    [KEY_O] = "KEY_O",
> +    [KEY_P] = "KEY_P",
> +    [KEY_LEFTBRACE] = "KEY_LEFTBRACE",
> +    [KEY_RIGHTBRACE] = "KEY_RIGHTBRACE",
> +    [KEY_ENTER] = "KEY_ENTER",
> +    [KEY_LEFTCTRL] = "KEY_LEFTCTRL",
> +    [KEY_A] = "KEY_A",
> +    [KEY_S] = "KEY_S",
> +    [KEY_D] = "KEY_D",
> +    [KEY_F] = "KEY_F",
> +    [KEY_G] = "KEY_G",
> +    [KEY_H] = "KEY_H",
> +    [KEY_J] = "KEY_J",
> +    [KEY_K] = "KEY_K",
> +    [KEY_L] = "KEY_L",
> +    [KEY_SEMICOLON] = "KEY_SEMICOLON",
> +    [KEY_APOSTROPHE] = "KEY_APOSTROPHE",
> +    [KEY_GRAVE] = "KEY_GRAVE",
> +    [KEY_LEFTSHIFT] = "KEY_LEFTSHIFT",
> +    [KEY_BACKSLASH] = "KEY_BACKSLASH",
> +    [KEY_Z] = "KEY_Z",
> +    [KEY_X] = "KEY_X",
> +    [KEY_C] = "KEY_C",
> +    [KEY_V] = "KEY_V",
> +    [KEY_B] = "KEY_B",
> +    [KEY_N] = "KEY_N",
> +    [KEY_M] = "KEY_M",
> +    [KEY_COMMA] = "KEY_COMMA",
> +    [KEY_DOT] = "KEY_DOT",
> +    [KEY_SLASH] = "KEY_SLASH",
> +    [KEY_RIGHTSHIFT] = "KEY_RIGHTSHIFT",
> +    [KEY_KPASTERISK] = "KEY_KPASTERISK",
> +    [KEY_LEFTALT] = "KEY_LEFTALT",
> +    [KEY_SPACE] = "KEY_SPACE",
> +    [KEY_CAPSLOCK] = "KEY_CAPSLOCK",
> +    [KEY_F1] = "KEY_F1",
> +    [KEY_F2] = "KEY_F2",
> +    [KEY_F3] = "KEY_F3",
> +    [KEY_F4] = "KEY_F4",
> +    [KEY_F5] = "KEY_F5",
> +    [KEY_F6] = "KEY_F6",
> +    [KEY_F7] = "KEY_F7",
> +    [KEY_F8] = "KEY_F8",
> +    [KEY_F9] = "KEY_F9",
> +    [KEY_F10] = "KEY_F10",
> +    [KEY_NUMLOCK] = "KEY_NUMLOCK",
> +    [KEY_SCROLLLOCK] = "KEY_SCROLLLOCK",
> +    [KEY_KP7] = "KEY_KP7",
> +    [KEY_KP8] = "KEY_KP8",
> +    [KEY_KP9] = "KEY_KP9",
> +    [KEY_KPMINUS] = "KEY_KPMINUS",
> +    [KEY_KP4] = "KEY_KP4",
> +    [KEY_KP5] = "KEY_KP5",
> +    [KEY_KP6] = "KEY_KP6",
> +    [KEY_KPPLUS] = "KEY_KPPLUS",
> +    [KEY_KP1] = "KEY_KP1",
> +    [KEY_KP2] = "KEY_KP2",
> +    [KEY_KP3] = "KEY_KP3",
> +    [KEY_KP0] = "KEY_KP0",
> +    [KEY_KPDOT] = "KEY_KPDOT",
> +    [KEY_ZENKAKUHANKAKU] = "KEY_ZENKAKUHANKAKU",
> +    [KEY_102ND] = "KEY_102ND",
> +    [KEY_F11] = "KEY_F11",
> +    [KEY_F12] = "KEY_F12",
> +    [KEY_RO] = "KEY_RO",
> +    [KEY_KATAKANA] = "KEY_KATAKANA",
> +    [KEY_HIRAGANA] = "KEY_HIRAGANA",
> +    [KEY_HENKAN] = "KEY_HENKAN",
> +    [KEY_KATAKANAHIRAGANA] = "KEY_KATAKANAHIRAGANA",
> +    [KEY_MUHENKAN] = "KEY_MUHENKAN",
> +    [KEY_KPJPCOMMA] = "KEY_KPJPCOMMA",
> +    [KEY_KPENTER] = "KEY_KPENTER",
> +    [KEY_RIGHTCTRL] = "KEY_RIGHTCTRL",
> +    [KEY_KPSLASH] = "KEY_KPSLASH",
> +    [KEY_SYSRQ] = "KEY_SYSRQ",
> +    [KEY_RIGHTALT] = "KEY_RIGHTALT",
> +    [KEY_LINEFEED] = "KEY_LINEFEED",
> +    [KEY_HOME] = "KEY_HOME",
> +    [KEY_UP] = "KEY_UP",
> +    [KEY_PAGEUP] = "KEY_PAGEUP",
> +    [KEY_LEFT] = "KEY_LEFT",
> +    [KEY_RIGHT] = "KEY_RIGHT",
> +    [KEY_END] = "KEY_END",
> +    [KEY_DOWN] = "KEY_DOWN",
> +    [KEY_PAGEDOWN] = "KEY_PAGEDOWN",
> +    [KEY_INSERT] = "KEY_INSERT",
> +    [KEY_DELETE] = "KEY_DELETE",
> +    [KEY_MACRO] = "KEY_MACRO",
> +    [KEY_MUTE] = "KEY_MUTE",
> +    [KEY_VOLUMEDOWN] = "KEY_VOLUMEDOWN",
> +    [KEY_VOLUMEUP] = "KEY_VOLUMEUP",
> +    [KEY_POWER] = "KEY_POWER",
> +    [KEY_KPEQUAL] = "KEY_KPEQUAL",
> +    [KEY_KPPLUSMINUS] = "KEY_KPPLUSMINUS",
> +    [KEY_PAUSE] = "KEY_PAUSE",
> +    [KEY_SCALE] = "KEY_SCALE",
> +    [KEY_KPCOMMA] = "KEY_KPCOMMA",
> +    [KEY_HANGEUL] = "KEY_HANGEUL",
> +    [KEY_HANGUEL] = "KEY_HANGUEL",
> +    [KEY_HANJA] = "KEY_HANJA",
> +    [KEY_YEN] = "KEY_YEN",
> +    [KEY_LEFTMETA] = "KEY_LEFTMETA",
> +    [KEY_RIGHTMETA] = "KEY_RIGHTMETA",
> +    [KEY_COMPOSE] = "KEY_COMPOSE",
> +    [KEY_STOP] = "KEY_STOP",
> +    [KEY_AGAIN] = "KEY_AGAIN",
> +    [KEY_PROPS] = "KEY_PROPS",
> +    [KEY_UNDO] = "KEY_UNDO",
> +    [KEY_FRONT] = "KEY_FRONT",
> +    [KEY_COPY] = "KEY_COPY",
> +    [KEY_OPEN] = "KEY_OPEN",
> +    [KEY_PASTE] = "KEY_PASTE",
> +    [KEY_FIND] = "KEY_FIND",
> +    [KEY_CUT] = "KEY_CUT",
> +    [KEY_HELP] = "KEY_HELP",
> +    [KEY_MENU] = "KEY_MENU",
> +    [KEY_CALC] = "KEY_CALC",
> +    [KEY_SETUP] = "KEY_SETUP",
> +    [KEY_SLEEP] = "KEY_SLEEP",
> +    [KEY_WAKEUP] = "KEY_WAKEUP",
> +    [KEY_FILE] = "KEY_FILE",
> +    [KEY_SENDFILE] = "KEY_SENDFILE",
> +    [KEY_DELETEFILE] = "KEY_DELETEFILE",
> +    [KEY_XFER] = "KEY_XFER",
> +    [KEY_PROG1] = "KEY_PROG1",
> +    [KEY_PROG2] = "KEY_PROG2",
> +    [KEY_WWW] = "KEY_WWW",
> +    [KEY_MSDOS] = "KEY_MSDOS",
> +    [KEY_COFFEE] = "KEY_COFFEE",
> +    [KEY_SCREENLOCK] = "KEY_SCREENLOCK",
> +    [KEY_DIRECTION] = "KEY_DIRECTION",
> +    [KEY_CYCLEWINDOWS] = "KEY_CYCLEWINDOWS",
> +    [KEY_MAIL] = "KEY_MAIL",
> +    [KEY_BOOKMARKS] = "KEY_BOOKMARKS",
> +    [KEY_COMPUTER] = "KEY_COMPUTER",
> +    [KEY_BACK] = "KEY_BACK",
> +    [KEY_FORWARD] = "KEY_FORWARD",
> +    [KEY_CLOSECD] = "KEY_CLOSECD",
> +    [KEY_EJECTCD] = "KEY_EJECTCD",
> +    [KEY_EJECTCLOSECD] = "KEY_EJECTCLOSECD",
> +    [KEY_NEXTSONG] = "KEY_NEXTSONG",
> +    [KEY_PLAYPAUSE] = "KEY_PLAYPAUSE",
> +    [KEY_PREVIOUSSONG] = "KEY_PREVIOUSSONG",
> +    [KEY_STOPCD] = "KEY_STOPCD",
> +    [KEY_RECORD] = "KEY_RECORD",
> +    [KEY_REWIND] = "KEY_REWIND",
> +    [KEY_PHONE] = "KEY_PHONE",
> +    [KEY_ISO] = "KEY_ISO",
> +    [KEY_CONFIG] = "KEY_CONFIG",
> +    [KEY_HOMEPAGE] = "KEY_HOMEPAGE",
> +    [KEY_REFRESH] = "KEY_REFRESH",
> +    [KEY_EXIT] = "KEY_EXIT",
> +    [KEY_MOVE] = "KEY_MOVE",
> +    [KEY_EDIT] = "KEY_EDIT",
> +    [KEY_SCROLLUP] = "KEY_SCROLLUP",
> +    [KEY_SCROLLDOWN] = "KEY_SCROLLDOWN",
> +    [KEY_KPLEFTPAREN] = "KEY_KPLEFTPAREN",
> +    [KEY_KPRIGHTPAREN] = "KEY_KPRIGHTPAREN",
> +    [KEY_NEW] = "KEY_NEW",
> +    [KEY_REDO] = "KEY_REDO",
> +    [KEY_F13] = "KEY_F13",
> +    [KEY_F14] = "KEY_F14",
> +    [KEY_F15] = "KEY_F15",
> +    [KEY_F16] = "KEY_F16",
> +    [KEY_F17] = "KEY_F17",
> +    [KEY_F18] = "KEY_F18",
> +    [KEY_F19] = "KEY_F19",
> +    [KEY_F20] = "KEY_F20",
> +    [KEY_F21] = "KEY_F21",
> +    [KEY_F22] = "KEY_F22",
> +    [KEY_F23] = "KEY_F23",
> +    [KEY_F24] = "KEY_F24",
> +    [KEY_PLAYCD] = "KEY_PLAYCD",
> +    [KEY_PAUSECD] = "KEY_PAUSECD",
> +    [KEY_PROG3] = "KEY_PROG3",
> +    [KEY_PROG4] = "KEY_PROG4",
> +    [KEY_DASHBOARD] = "KEY_DASHBOARD",
> +    [KEY_SUSPEND] = "KEY_SUSPEND",
> +    [KEY_CLOSE] = "KEY_CLOSE",
> +    [KEY_PLAY] = "KEY_PLAY",
> +    [KEY_FASTFORWARD] = "KEY_FASTFORWARD",
> +    [KEY_BASSBOOST] = "KEY_BASSBOOST",
> +    [KEY_PRINT] = "KEY_PRINT",
> +    [KEY_HP] = "KEY_HP",
> +    [KEY_CAMERA] = "KEY_CAMERA",
> +    [KEY_SOUND] = "KEY_SOUND",
> +    [KEY_QUESTION] = "KEY_QUESTION",
> +    [KEY_EMAIL] = "KEY_EMAIL",
> +    [KEY_CHAT] = "KEY_CHAT",
> +    [KEY_SEARCH] = "KEY_SEARCH",
> +    [KEY_CONNECT] = "KEY_CONNECT",
> +    [KEY_FINANCE] = "KEY_FINANCE",
> +    [KEY_SPORT] = "KEY_SPORT",
> +    [KEY_SHOP] = "KEY_SHOP",
> +    [KEY_ALTERASE] = "KEY_ALTERASE",
> +    [KEY_CANCEL] = "KEY_CANCEL",
> +    [KEY_BRIGHTNESSDOWN] = "KEY_BRIGHTNESSDOWN",
> +    [KEY_BRIGHTNESSUP] = "KEY_BRIGHTNESSUP",
> +    [KEY_MEDIA] = "KEY_MEDIA",
> +    [KEY_SWITCHVIDEOMODE] = "KEY_SWITCHVIDEOMODE",
> +    [KEY_KBDILLUMTOGGLE] = "KEY_KBDILLUMTOGGLE",
> +    [KEY_KBDILLUMDOWN] = "KEY_KBDILLUMDOWN",
> +    [KEY_KBDILLUMUP] = "KEY_KBDILLUMUP",
> +    [KEY_SEND] = "KEY_SEND",
> +    [KEY_REPLY] = "KEY_REPLY",
> +    [KEY_FORWARDMAIL] = "KEY_FORWARDMAIL",
> +    [KEY_SAVE] = "KEY_SAVE",
> +    [KEY_DOCUMENTS] = "KEY_DOCUMENTS",
> +    [KEY_BATTERY] = "KEY_BATTERY",
> +    [KEY_BLUETOOTH] = "KEY_BLUETOOTH",
> +    [KEY_WLAN] = "KEY_WLAN",
> +    [KEY_UWB] = "KEY_UWB",
> +    [KEY_UNKNOWN] = "KEY_UNKNOWN",
> +    [KEY_VIDEO_NEXT] = "KEY_VIDEO_NEXT",
> +    [KEY_VIDEO_PREV] = "KEY_VIDEO_PREV",
> +    [KEY_BRIGHTNESS_CYCLE] = "KEY_BRIGHTNESS_CYCLE",
> +    [KEY_BRIGHTNESS_ZERO] = "KEY_BRIGHTNESS_ZERO",
> +    [KEY_DISPLAY_OFF] = "KEY_DISPLAY_OFF",
> +    [KEY_WIMAX] = "KEY_WIMAX",
> +    [KEY_OK] = "KEY_OK",
> +    [KEY_SELECT] = "KEY_SELECT",
> +    [KEY_GOTO] = "KEY_GOTO",
> +    [KEY_CLEAR] = "KEY_CLEAR",
> +    [KEY_POWER2] = "KEY_POWER2",
> +    [KEY_OPTION] = "KEY_OPTION",
> +    [KEY_INFO] = "KEY_INFO",
> +    [KEY_TIME] = "KEY_TIME",
> +    [KEY_VENDOR] = "KEY_VENDOR",
> +    [KEY_ARCHIVE] = "KEY_ARCHIVE",
> +    [KEY_PROGRAM] = "KEY_PROGRAM",
> +    [KEY_CHANNEL] = "KEY_CHANNEL",
> +    [KEY_FAVORITES] = "KEY_FAVORITES",
> +    [KEY_EPG] = "KEY_EPG",
> +    [KEY_PVR] = "KEY_PVR",
> +    [KEY_MHP] = "KEY_MHP",
> +    [KEY_LANGUAGE] = "KEY_LANGUAGE",
> +    [KEY_TITLE] = "KEY_TITLE",
> +    [KEY_SUBTITLE] = "KEY_SUBTITLE",
> +    [KEY_ANGLE] = "KEY_ANGLE",
> +    [KEY_ZOOM] = "KEY_ZOOM",
> +    [KEY_MODE] = "KEY_MODE",
> +    [KEY_KEYBOARD] = "KEY_KEYBOARD",
> +    [KEY_SCREEN] = "KEY_SCREEN",
> +    [KEY_PC] = "KEY_PC",
> +    [KEY_TV] = "KEY_TV",
> +    [KEY_TV2] = "KEY_TV2",
> +    [KEY_VCR] = "KEY_VCR",
> +    [KEY_VCR2] = "KEY_VCR2",
> +    [KEY_SAT] = "KEY_SAT",
> +    [KEY_SAT2] = "KEY_SAT2",
> +    [KEY_CD] = "KEY_CD",
> +    [KEY_TAPE] = "KEY_TAPE",
> +    [KEY_RADIO] = "KEY_RADIO",
> +    [KEY_TUNER] = "KEY_TUNER",
> +    [KEY_PLAYER] = "KEY_PLAYER",
> +    [KEY_TEXT] = "KEY_TEXT",
> +    [KEY_DVD] = "KEY_DVD",
> +    [KEY_AUX] = "KEY_AUX",
> +    [KEY_MP3] = "KEY_MP3",
> +    [KEY_AUDIO] = "KEY_AUDIO",
> +    [KEY_VIDEO] = "KEY_VIDEO",
> +    [KEY_DIRECTORY] = "KEY_DIRECTORY",
> +    [KEY_LIST] = "KEY_LIST",
> +    [KEY_MEMO] = "KEY_MEMO",
> +    [KEY_CALENDAR] = "KEY_CALENDAR",
> +    [KEY_RED] = "KEY_RED",
> +    [KEY_GREEN] = "KEY_GREEN",
> +    [KEY_YELLOW] = "KEY_YELLOW",
> +    [KEY_BLUE] = "KEY_BLUE",
> +    [KEY_CHANNELUP] = "KEY_CHANNELUP",
> +    [KEY_CHANNELDOWN] = "KEY_CHANNELDOWN",
> +    [KEY_FIRST] = "KEY_FIRST",
> +    [KEY_LAST] = "KEY_LAST",
> +    [KEY_AB] = "KEY_AB",
> +    [KEY_NEXT] = "KEY_NEXT",
> +    [KEY_RESTART] = "KEY_RESTART",
> +    [KEY_SLOW] = "KEY_SLOW",
> +    [KEY_SHUFFLE] = "KEY_SHUFFLE",
> +    [KEY_BREAK] = "KEY_BREAK",
> +    [KEY_PREVIOUS] = "KEY_PREVIOUS",
> +    [KEY_DIGITS] = "KEY_DIGITS",
> +    [KEY_TEEN] = "KEY_TEEN",
> +    [KEY_TWEN] = "KEY_TWEN",
> +    [KEY_VIDEOPHONE] = "KEY_VIDEOPHONE",
> +    [KEY_GAMES] = "KEY_GAMES",
> +    [KEY_ZOOMIN] = "KEY_ZOOMIN",
> +    [KEY_ZOOMOUT] = "KEY_ZOOMOUT",
> +    [KEY_ZOOMRESET] = "KEY_ZOOMRESET",
> +    [KEY_WORDPROCESSOR] = "KEY_WORDPROCESSOR",
> +    [KEY_EDITOR] = "KEY_EDITOR",
> +    [KEY_SPREADSHEET] = "KEY_SPREADSHEET",
> +    [KEY_GRAPHICSEDITOR] = "KEY_GRAPHICSEDITOR",
> +    [KEY_PRESENTATION] = "KEY_PRESENTATION",
> +    [KEY_DATABASE] = "KEY_DATABASE",
> +    [KEY_NEWS] = "KEY_NEWS",
> +    [KEY_VOICEMAIL] = "KEY_VOICEMAIL",
> +    [KEY_ADDRESSBOOK] = "KEY_ADDRESSBOOK",
> +    [KEY_MESSENGER] = "KEY_MESSENGER",
> +    [KEY_DISPLAYTOGGLE] = "KEY_DISPLAYTOGGLE",
> +    [KEY_SPELLCHECK] = "KEY_SPELLCHECK",
> +    [KEY_LOGOFF] = "KEY_LOGOFF",
> +    [KEY_DOLLAR] = "KEY_DOLLAR",
> +    [KEY_EURO] = "KEY_EURO",
> +    [KEY_FRAMEBACK] = "KEY_FRAMEBACK",
> +    [KEY_FRAMEFORWARD] = "KEY_FRAMEFORWARD",
> +    [KEY_CONTEXT_MENU] = "KEY_CONTEXT_MENU",
> +    [KEY_MEDIA_REPEAT] = "KEY_MEDIA_REPEAT",
> +    [KEY_10CHANNELSUP] = "KEY_10CHANNELSUP",
> +    [KEY_10CHANNELSDOWN] = "KEY_10CHANNELSDOWN",
> +    [KEY_DEL_EOL] = "KEY_DEL_EOL",
> +    [KEY_DEL_EOS] = "KEY_DEL_EOS",
> +    [KEY_INS_LINE] = "KEY_INS_LINE",
> +    [KEY_DEL_LINE] = "KEY_DEL_LINE",
> +    [KEY_FN] = "KEY_FN",
> +    [KEY_FN_ESC] = "KEY_FN_ESC",
> +    [KEY_FN_F1] = "KEY_FN_F1",
> +    [KEY_FN_F2] = "KEY_FN_F2",
> +    [KEY_FN_F3] = "KEY_FN_F3",
> +    [KEY_FN_F4] = "KEY_FN_F4",
> +    [KEY_FN_F5] = "KEY_FN_F5",
> +    [KEY_FN_F6] = "KEY_FN_F6",
> +    [KEY_FN_F7] = "KEY_FN_F7",
> +    [KEY_FN_F8] = "KEY_FN_F8",
> +    [KEY_FN_F9] = "KEY_FN_F9",
> +    [KEY_FN_F10] = "KEY_FN_F10",
> +    [KEY_FN_F11] = "KEY_FN_F11",
> +    [KEY_FN_F12] = "KEY_FN_F12",
> +    [KEY_FN_1] = "KEY_FN_1",
> +    [KEY_FN_2] = "KEY_FN_2",
> +    [KEY_FN_D] = "KEY_FN_D",
> +    [KEY_FN_E] = "KEY_FN_E",
> +    [KEY_FN_F] = "KEY_FN_F",
> +    [KEY_FN_S] = "KEY_FN_S",
> +    [KEY_FN_B] = "KEY_FN_B",
> +    [KEY_BRL_DOT1] = "KEY_BRL_DOT1",
> +    [KEY_BRL_DOT2] = "KEY_BRL_DOT2",
> +    [KEY_BRL_DOT3] = "KEY_BRL_DOT3",
> +    [KEY_BRL_DOT4] = "KEY_BRL_DOT4",
> +    [KEY_BRL_DOT5] = "KEY_BRL_DOT5",
> +    [KEY_BRL_DOT6] = "KEY_BRL_DOT6",
> +    [KEY_BRL_DOT7] = "KEY_BRL_DOT7",
> +    [KEY_BRL_DOT8] = "KEY_BRL_DOT8",
> +    [KEY_BRL_DOT9] = "KEY_BRL_DOT9",
> +    [KEY_BRL_DOT10] = "KEY_BRL_DOT10",
> +    [KEY_NUMERIC_0] = "KEY_NUMERIC_0",
> +    [KEY_NUMERIC_1] = "KEY_NUMERIC_1",
> +    [KEY_NUMERIC_2] = "KEY_NUMERIC_2",
> +    [KEY_NUMERIC_3] = "KEY_NUMERIC_3",
> +    [KEY_NUMERIC_4] = "KEY_NUMERIC_4",
> +    [KEY_NUMERIC_5] = "KEY_NUMERIC_5",
> +    [KEY_NUMERIC_6] = "KEY_NUMERIC_6",
> +    [KEY_NUMERIC_7] = "KEY_NUMERIC_7",
> +    [KEY_NUMERIC_8] = "KEY_NUMERIC_8",
> +    [KEY_NUMERIC_9] = "KEY_NUMERIC_9",
> +    [KEY_NUMERIC_STAR] = "KEY_NUMERIC_STAR",
> +    [KEY_NUMERIC_POUND] = "KEY_NUMERIC_POUND",
> +    [KEY_RFKILL] = "KEY_RFKILL",
> +    [KEY_MIN_INTERESTING] = "KEY_MIN_INTERESTING",
> +    [KEY_MAX] = "KEY_MAX",
> +    [KEY_CNT] = "KEY_CNT",
> diff --git a/vl.c b/vl.c
> index fbb77fe..18982b2 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -3041,6 +3041,11 @@ int main(int argc, char **argv, char **envp)
>                  fprintf(stderr, "SDL support is disabled\n");
>                  exit(1);
>  #endif
> +#ifdef CONFIG_LINUX
> +            case QEMU_OPTION_fbdev:
> +                display_type = DT_FBDEV;
> +                break;
> +#endif
>              case QEMU_OPTION_pidfile:
>                  pid_file = optarg;
>                  break;
> @@ -3681,6 +3686,13 @@ int main(int argc, char **argv, char **envp)
>          curses_display_init(ds, full_screen);
>          break;
>  #endif
> +#if defined(CONFIG_LINUX)
> +    case DT_FBDEV:
> +        if (fbdev_display_init(ds, NULL) != 0) {
> +            exit(1);
> +        }
> +        break;
> +#endif
>  #if defined(CONFIG_SDL)
>      case DT_SDL:
>          sdl_display_init(ds, full_screen, no_frame);
> --
> 1.7.1
>
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2012-09-11 20:38 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-10 14:20 [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Gerd Hoffmann
2012-09-10 14:20 ` [Qemu-devel] [PATCH 1/4] QLIST-ify display change listeners Gerd Hoffmann
2012-09-10 14:20 ` [Qemu-devel] [PATCH 2/4] add unregister_displaychangelistener Gerd Hoffmann
2012-09-10 14:20 ` [Qemu-devel] [PATCH 3/4] fbdev: add linux framebuffer display driver Gerd Hoffmann
2012-09-11 11:10   ` Markus Armbruster
2012-09-11 20:37   ` Blue Swirl
2012-09-10 14:21 ` [Qemu-devel] [PATCH 4/4] fbdev: add monitor command to enable/disable Gerd Hoffmann
2012-09-11  9:30   ` Markus Armbruster
2012-09-11  8:09 ` [Qemu-devel] [PATCH 0/4] linux framebuffer display driver Alon Levy
2012-09-11  8:54   ` Gerd Hoffmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).