From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57993) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UkfyT-0001Sm-DI for qemu-devel@nongnu.org; Thu, 06 Jun 2013 15:36:01 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UkfyK-0006Z7-H1 for qemu-devel@nongnu.org; Thu, 06 Jun 2013 15:35:53 -0400 Received: from e34.co.us.ibm.com ([32.97.110.152]:38477) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UkfyK-0006Z0-2X for qemu-devel@nongnu.org; Thu, 06 Jun 2013 15:35:44 -0400 Received: from /spool/local by e34.co.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 6 Jun 2013 13:35:42 -0600 Received: from d03relay05.boulder.ibm.com (d03relay05.boulder.ibm.com [9.17.195.107]) by d03dlp02.boulder.ibm.com (Postfix) with ESMTP id 9C41F3E4007A for ; Thu, 6 Jun 2013 13:33:54 -0600 (MDT) Received: from d03av03.boulder.ibm.com (d03av03.boulder.ibm.com [9.17.195.169]) by d03relay05.boulder.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id r56JXvNB104396 for ; Thu, 6 Jun 2013 13:33:58 -0600 Received: from d03av03.boulder.ibm.com (loopback [127.0.0.1]) by d03av03.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id r56JXrGj026377 for ; Thu, 6 Jun 2013 13:33:53 -0600 From: Anthony Liguori In-Reply-To: <1370533048-19229-1-git-send-email-kraxel@redhat.com> References: <1370533048-19229-1-git-send-email-kraxel@redhat.com> Date: Thu, 06 Jun 2013 14:33:51 -0500 Message-ID: <87mwr3ghk0.fsf@codemonkey.ws> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Subject: Re: [Qemu-devel] [PATCH 1/2] fbdev: add linux framebuffer display driver. List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Gerd Hoffmann , qemu-devel@nongnu.org Gerd Hoffmann 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 enough to type monitor commands. Not goot enough to > work seriously on a serial terminal. But the qemu terminal emulation > isn't good enough for that anyway ;) > > Hot keys: > Ctrl-Alt-F -> host console switching. > Ctrl-Alt- -> qemu console switching. > Ctrl-Alt-S -> toggle display scaling. > 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). > > Signed-off-by: Gerd Hoffmann > --- > configure | 12 + > include/sysemu/sysemu.h | 1 + > include/ui/console.h | 4 + > qemu-options.hx | 4 +- > trace-events | 14 + > ui/Makefile.objs | 1 + > ui/fbdev.c | 1123 +++++++++++++++++++++++++++++++++++++++++++++++ > ui/linux-keynames.h | 388 ++++++++++++++++ > vl.c | 21 + > 9 files changed, 1567 insertions(+), 1 deletion(-) > create mode 100644 ui/fbdev.c > create mode 100644 ui/linux-keynames.h > > diff --git a/configure b/configure > index 1654413..c385753 100755 > --- a/configure > +++ b/configure > @@ -158,6 +158,7 @@ fdt="" > nptl="" > pixman="" > sdl="" > +fbdev="no" > virtfs="" > vnc="yes" > sparse="no" > @@ -544,6 +545,7 @@ Haiku) > kvm="yes" > vhost_net="yes" > vhost_scsi="yes" > + fbdev="yes" > if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then > audio_possible_drivers="$audio_possible_drivers fmod" > fi > @@ -693,6 +695,10 @@ for opt do > ;; > --enable-qom-cast-debug) qom_cast_debug="yes" > ;; > + --disable-fbdev) fbdev="no" > + ;; > + --enable-fbdev) fbdev="yes" > + ;; > --disable-virtfs) virtfs="no" > ;; > --enable-virtfs) virtfs="yes" > @@ -1044,6 +1050,8 @@ echo " --disable-sdl disable SDL" > echo " --enable-sdl enable SDL" > echo " --disable-gtk disable gtk UI" > echo " --enable-gtk enable gtk UI" > +echo " --disable-fbdev disable linux framebuffer" > +echo " --enable-fbdev enable linux framebuffer" > echo " --disable-virtfs disable VirtFS" > echo " --enable-virtfs enable VirtFS" > echo " --disable-vnc disable VNC" > @@ -3485,6 +3493,7 @@ fi > echo "pixman $pixman" > echo "SDL support $sdl" > echo "GTK support $gtk" > +echo "fbdev support $fbdev" > echo "curses support $curses" > echo "curl support $curl" > echo "mingw32 support $mingw32" > @@ -3716,6 +3725,9 @@ if test "$sdl" = "yes" ; then > echo "CONFIG_SDL=y" >> $config_host_mak > echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak > fi > +if test "$fbdev" = "yes" ; then > + echo "CONFIG_FBDEV=y" >> $config_host_mak > +fi > if test "$cocoa" = "yes" ; then > echo "CONFIG_COCOA=y" >> $config_host_mak > fi > diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h > index 2fb71af..5922311 100644 > --- a/include/sysemu/sysemu.h > +++ b/include/sysemu/sysemu.h > @@ -91,6 +91,7 @@ typedef enum DisplayType > DT_CURSES, > DT_SDL, > DT_GTK, > + DT_FBDEV, > DT_NOGRAPHIC, > DT_NONE, > } DisplayType; > diff --git a/include/ui/console.h b/include/ui/console.h > index 4307b5f..eb65739 100644 > --- a/include/ui/console.h > +++ b/include/ui/console.h > @@ -308,6 +308,10 @@ void register_vc_handler(VcHandler *handler); > /* sdl.c */ > void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); > > +/* fbdev.c */ > +int fbdev_display_init(const char *device, bool scale, Error **err); > +void fbdev_display_uninit(void); > + > /* cocoa.m */ > void cocoa_display_init(DisplayState *ds, int full_screen); > > diff --git a/qemu-options.hx b/qemu-options.hx > index bf94862..8e02863 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -804,7 +804,7 @@ ETEXI > > DEF("display", HAS_ARG, QEMU_OPTION_display, > "-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]\n" > - " [,window_close=on|off]|curses|none|\n" > + " [,window_close=on|off]|curses|fbdev|none|\n" > " vnc=[,]\n" > " select display type\n", QEMU_ARCH_ALL) > STEXI > @@ -822,6 +822,8 @@ support a text mode, QEMU can display this output using a > curses/ncurses interface. Nothing is displayed when the graphics > device is in graphical mode or if the graphics device does not support > a text mode. Generally only the VGA device models support text mode. > +@item fbdev > +Display video output on a linux framebuffer console. > @item none > Do not display video output. The guest will still see an emulated > graphics card, but its output will not be displayed to the QEMU > diff --git a/trace-events b/trace-events > index c5f1ccb..7a73b2a 100644 > --- a/trace-events > +++ b/trace-events > @@ -1161,3 +1161,17 @@ kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d" > # qom/object.c > object_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)" > object_class_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)" > + > +# 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) > diff --git a/ui/Makefile.objs b/ui/Makefile.objs > index 6ddc0de..ee96ad5 100644 > --- a/ui/Makefile.objs > +++ b/ui/Makefile.objs > @@ -14,6 +14,7 @@ common-obj-$(CONFIG_COCOA) += cocoa.o > common-obj-$(CONFIG_CURSES) += curses.o > common-obj-$(CONFIG_VNC) += $(vnc-obj-y) > common-obj-$(CONFIG_GTK) += gtk.o x_keymap.o > +common-obj-$(CONFIG_FBDEV) += fbdev.o > > $(obj)/sdl.o $(obj)/sdl_zoom.o: QEMU_CFLAGS += $(SDL_CFLAGS) > > diff --git a/ui/fbdev.c b/ui/fbdev.c > new file mode 100644 > index 0000000..4fe34d6 > --- /dev/null > +++ b/ui/fbdev.c > @@ -0,0 +1,1123 @@ > +/* > + * linux fbdev output driver. > + * > + * Author: Gerd Hoffmann > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > + * See the COPYING file in the top-level directory. > + * > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#include > +#include > +#include > + > +#include "qemu-common.h" > +#include "keymaps.h" > +#include "ui/qemu-pixman.h" > +#include "ui/console.h" > +#include "sysemu/sysemu.h" > + > +/* > + * must be last so we get the linux input layer > + * KEY_* defines, not the ncurses ones. > + */ > +#include > + > +/* -------------------------------------------------------------------- */ > + > +/* 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; > + > +/* qemu windup */ > +static DisplayChangeListener *dcl; > +static int resize_screen; > +static int redraw_screen; > +static int cx, cy, cw, ch; > +static Notifier exit_notifier; > +static DisplaySurface *surface; > +static pixman_image_t *sref, *swork; > +static pixman_image_t *framebuffer; > +static pixman_transform_t transform; > +static pixman_region16_t dirty; > +static double scale; > + > +static QEMUCursor *ptr_cursor; > +static pixman_image_t *ptr_image; > +static int ptr_refresh; > +static int px, py, pw, ph; > +static int mx, my, mon; > + > +/* options */ > +static int use_scale; > +static pixman_filter_t pfilter = PIXMAN_FILTER_GOOD; Should stick all of this within a structure and pass it around where possible. > +/* fwd decls */ > +static int fbdev_activate_vt(int tty, int vtno, bool wait); > + > +/* -------------------------------------------------------------------- */ > +/* pixman helpers */ > + > +static pixman_image_t *pixman_from_framebuffer(void) > +{ > + pixman_format_code_t format; > + pixman_image_t *image; > + int type; > + > + type = qemu_pixman_get_type(fb_var.red.offset, > + fb_var.green.offset, > + fb_var.blue.offset); > + format = PIXMAN_FORMAT(fb_var.bits_per_pixel, type, > + fb_var.transp.length, > + fb_var.red.length, > + fb_var.green.length, > + fb_var.blue.length); > + image = pixman_image_create_bits(format, fb_var.xres, fb_var.yres, > + (void *)fb_mem, fb_fix.line_length); > + return image; > +} > + > +static pixman_image_t *pixman_image_clone(pixman_image_t *i) > +{ > + return pixman_image_create_bits(pixman_image_get_format(i), > + pixman_image_get_width(i), > + pixman_image_get_height(i), > + pixman_image_get_data(i), > + pixman_image_get_stride(i)); > +} > + > +/* -------------------------------------------------------------------- */ > +/* mouse */ > + > +static void read_mouse(void *opaque) > +{ > + char buf[3]; Can't assume char is signed, you should use an int8_t here. > + 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*scale) { > + ax = cw*scale-1; > + } > + if (ay >= ch*scale) { > + ay = ch*scale-1; > + } Can use MIN/MAX here. > + kbd_mouse_event(ax * 0x7FFF / (cw*scale), > + ay * 0x7FFF / (ch*scale), 0, b); > + } else { > + kbd_mouse_event(x, y, 0, b); > + } > +} > + > +static int init_mouse(void) > +{ > + mice = open("/dev/input/mice", O_RDONLY); > + if (mice == -1) { > + return -1; > + } > + qemu_set_fd_handler(mice, read_mouse, NULL, NULL); > + return 0; > +} > + > +static void uninit_mouse(void) > +{ > + if (mice == -1) { > + return; > + } > + qemu_set_fd_handler(mice, NULL, NULL, NULL); > + close(mice); > + mice = -1; > +} > + > +/* -------------------------------------------------------------------- */ > +/* keyboard */ > + > +static const char *keynames[] = { > +#include "linux-keynames.h" > +}; > + > +static const 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 const 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) > +{ > + const 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 (qemu_console_is_graphic(NULL)) { > + 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_S) { > + use_scale = !use_scale; > + resize_screen++; > + redraw_screen++; > + continue; > + } > + 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 (qemu_console_is_graphic(NULL)) { > + send_scancode(keycode, up); > + } else if (!up) { > + send_keysym(keycode, shift); I'm confused here... Why can't use use the normal keymap code with the keycode value? Regards, Anthony Liguori > + } > + } > + } > + 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(); > + > + /* release pixman stuff */ > + pixman_region_fini(&dirty); > + if (framebuffer) { > + pixman_image_unref(framebuffer); > + framebuffer = NULL; > + } > + if (sref) { > + pixman_image_unref(sref); > + sref = NULL; > + } > + if (swork) { > + pixman_image_unref(swork); > + swork = NULL; > + } > + > + /* 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, Error **err) > +{ > + 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) { > + error_setg(err, "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) { > + error_setg(err, "open %s: %s\n", ttyname, strerror(errno)); > + goto err_early; > + } > + if (ioctl(tty, VT_OPENQRY, &vtno) < 0) { > + error_setg(err, "ioctl VT_OPENQRY: %s\n", strerror(errno)); > + goto err_early; > + } > + if (ioctl(tty, VT_GETSTATE, &vts) < 0) { > + error_setg(err, "ioctl VT_GETSTATE: %s\n", strerror(errno)); > + goto err_early; > + } > + close(tty); > + > + snprintf(ttyname, sizeof(ttyname), "/dev/tty%d", vtno); > + tty = open(ttyname, O_RDWR); > + if (tty == -1) { > + error_setg(err, "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) { > + error_setg(err, "ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno)); > + goto err_early; > + } > + if (ioctl(tty, KDGETMODE, &kd_omode) < 0) { > + error_setg(err, "ioctl KDGETMODE: %s\n", strerror(errno)); > + goto err_early; > + } > + if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) { > + error_setg(err, "ioctl VT_GETMODE: %s\n", strerror(errno)); > + goto err_early; > + } > + > + /* checks & initialisation */ > + if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) < 0) { > + error_setg(err, "ioctl : %s\n", strerror(errno)); > + perror("ioctl FBIOGET_FSCREENINFO"); > + goto err; > + } > + if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) < 0) { > + error_setg(err, "ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno)); > + goto err; > + } > + if (fb_fix.type != FB_TYPE_PACKED_PIXELS) { > + error_setg(err, "can handle only packed pixel frame buffers\n"); > + goto err; > + } > + switch (fb_var.bits_per_pixel) { > + case 32: > + break; > + default: > + error_setg(err, "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) { > + error_setg(err, "mmap: %s\n", strerror(errno)); > + 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) { > + error_setg(err, "ioctl FBIOPAN_DISPLAY: %s\n", strerror(errno)); > + goto err; > + } > + } > + if (ioctl(tty, KDSETMODE, KD_GRAPHICS) < 0) { > + error_setg(err, "ioctl KDSETMODE: %s\n", strerror(errno)); > + 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); > + > + framebuffer = pixman_from_framebuffer(); > + pixman_region_init(&dirty); > + 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 signr) > +{ > + fprintf(stderr, "%s: %s, restoring linux console state ...\n", > + __func__, strsignal(signr)); > + fbdev_cleanup(); > + signal(SIGABRT, SIG_DFL); > + fprintf(stderr, "%s: ... done, going abort() now.\n", __func__); > + abort(); > +} > + > +static void fbdev_catch_exit_signals(void) > +{ > + static const int signals[] = { > + SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGBUS > + }; > + struct sigaction act, old; > + int i; > + > + memset(&act, 0, sizeof(act)); > + act.sa_handler = fbdev_catch_fatal_signal; > + act.sa_flags = SA_RESETHAND; > + sigemptyset(&act.sa_mask); > + for (i = 0; i < ARRAY_SIZE(signals); i++) { > + sigaction(signals[i], &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(void) > +{ > + assert(surface); > + > + pixman_image_set_clip_region(swork, &dirty); > + pixman_image_composite(PIXMAN_OP_SRC, swork, NULL, framebuffer, > + 0, 0, 0, 0, 0, 0, fb_var.xres, fb_var.yres); > + pixman_region_fini(&dirty); > + pixman_region_init(&dirty); > +} > + > +static void fbdev_unrender_ptr(void) > +{ > + if (!pw && !ph) { > + return; > + } > + pixman_region_union_rect(&dirty, &dirty, px, py, pw, ph); > + ph = pw = 0; > +} > + > +static void fbdev_render_ptr(void) > +{ > + pixman_region16_t region; > + pixman_transform_t transform; > + > + if (!mon || !ptr_image) { > + return; > + } > + if (mx < 0 || mx >= cw || my < 0 || my >= ch) { > + return; > + } > + > + px = mx - ptr_cursor->hot_x; > + py = my - ptr_cursor->hot_y; > + pw = ptr_cursor->width; > + ph = ptr_cursor->height; > + > + pixman_transform_init_identity(&transform); > + pixman_transform_translate(&transform, NULL, > + pixman_int_to_fixed(-cx), > + pixman_int_to_fixed(-cy)); > + if (use_scale) { > + pixman_transform_scale(&transform, NULL, > + pixman_double_to_fixed(1/scale), > + pixman_double_to_fixed(1/scale)); > + } > + pixman_transform_translate(&transform, NULL, > + pixman_int_to_fixed(-px), > + pixman_int_to_fixed(-py)); > + pixman_image_set_transform(ptr_image, &transform); > + > + pixman_region_init_rect(®ion, 0, 0, pw, ph); > + pixman_image_set_clip_region(ptr_image, ®ion); > + > + pixman_image_composite(PIXMAN_OP_OVER, ptr_image, NULL, framebuffer, > + 0, 0, 0, 0, 0, 0, fb_var.xres, fb_var.yres); > + > + pixman_region_fini(®ion); > + ptr_refresh = 0; > +} > + > +/* -------------------------------------------------------------------- */ > +/* qemu interfaces */ > + > +static void fbdev_update(DisplayChangeListener *dcl, > + int x, int y, int w, int h) > +{ > + if (fb_switch_state != FB_ACTIVE) { > + return; > + } > + > + if (resize_screen) { > + double xs, ys; > + > + trace_fbdev_dpy_resize(surface_width(surface), > + surface_height(surface)); > + resize_screen = 0; > + cx = 0; cy = 0; > + cw = surface_width(surface); > + ch = surface_height(surface); > + > + if (use_scale) { > + xs = (double)fb_var.xres / cw; > + ys = (double)fb_var.yres / ch; > + if (xs > ys) { > + scale = ys; > + cx = (fb_var.xres - surface_width(surface)*scale) / 2; > + } else { > + scale = xs; > + cy = (fb_var.yres - surface_height(surface)*scale) / 2; > + } > + } else { > + scale = 1; > + if (surface_width(surface) < fb_var.xres) { > + cx = (fb_var.xres - surface_width(surface)) / 2; > + } > + if (surface_height(surface) < fb_var.yres) { > + cy = (fb_var.yres - surface_height(surface)) / 2; > + } > + } > + if (sref) { > + pixman_image_unref(sref); > + } > + sref = pixman_image_ref(surface->image); > + > + if (swork) { > + pixman_image_unref(swork); > + } > + swork = pixman_image_clone(sref); > + > + pixman_transform_init_identity(&transform); > + pixman_transform_translate(&transform, NULL, > + pixman_int_to_fixed(-cx), > + pixman_int_to_fixed(-cy)); > + if (use_scale) { > + pixman_transform_scale(&transform, NULL, > + pixman_double_to_fixed(1/scale), > + pixman_double_to_fixed(1/scale)); > + } > + pixman_image_set_transform(swork, &transform); > + > + pixman_image_set_filter(swork, pfilter, NULL, 0); > + } > + > + if (redraw_screen) { > + trace_fbdev_dpy_redraw(); > + redraw_screen = 0; > + fbdev_cls(); > + x = 0; y = 0; w = surface_width(surface); h = surface_height(surface); > + } > + > + pixman_region_union_rect(&dirty, &dirty, x, y, w, h); > + if (ptr_image && mon && pw && ph) { > + ptr_refresh++; > + } > +} > + > +static void fbdev_switch(DisplayChangeListener *dcl, > + DisplaySurface *new_surface) > +{ > + surface = new_surface, > + resize_screen++; > + redraw_screen++; > +} > + > +static void fbdev_refresh(DisplayChangeListener *dcl) > +{ > + switch (fb_switch_state) { > + case FB_REL_REQ: > + fbdev_switch_release(); > + /* fall though */ > + case FB_INACTIVE: > + return; > + case FB_ACQ_REQ: > + fbdev_switch_acquire(); > + redraw_screen++; > + /* fall though */ > + case FB_ACTIVE: > + break; > + } > + > + graphic_hw_update(NULL); > + if (redraw_screen) { > + fbdev_update(dcl, 0, 0, 0, 0); > + } > + > + if (ptr_refresh) { > + fbdev_unrender_ptr(); > + } > + if (pixman_region_not_empty(&dirty)) { > + fbdev_render(); > + } > + if (ptr_refresh) { > + fbdev_render_ptr(); > + } > +} > + > +static void fbdev_mouse_set(DisplayChangeListener *dcl, int x, int y, int on) > +{ > + ptr_refresh++; > + mx = x; > + my = y; > + mon = on; > +} > + > +static void fbdev_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor) > +{ > + ptr_refresh++; > + > + if (ptr_cursor) { > + cursor_put(ptr_cursor); > + ptr_cursor = NULL; > + } > + if (ptr_image) { > + pixman_image_unref(ptr_image); > + ptr_image = NULL; > + } > + > + if (!cursor) { > + return; > + } > + > + ptr_cursor = cursor; > + cursor_get(ptr_cursor); > + ptr_image = pixman_image_create_bits(PIXMAN_a8r8g8b8, > + cursor->width, cursor->height, > + cursor->data, > + cursor->width * 4); > + pixman_image_set_filter(ptr_image, pfilter, NULL, 0); > +} > + > +static const DisplayChangeListenerOps fbdev_ops = { > + .dpy_name = "fbdev", > + .dpy_gfx_update = fbdev_update, > + .dpy_gfx_switch = fbdev_switch, > + .dpy_refresh = fbdev_refresh, > + .dpy_mouse_set = fbdev_mouse_set, > + .dpy_cursor_define = fbdev_cursor_define, > +}; > + > +static void fbdev_exit_notifier(Notifier *notifier, void *data) > +{ > + fbdev_cleanup(); > +} > + > +int fbdev_display_init(const char *device, bool scale, Error **err) > +{ > + if (dcl != NULL) { > + return 0; > + } > + > + if (fbdev_init(device, err) != 0) { > + return -1; > + } > + exit_notifier.notify = fbdev_exit_notifier; > + qemu_add_exit_notifier(&exit_notifier); > + fbdev_switch_init(); > + fbdev_catch_exit_signals(); > + init_mouse(); > + use_scale = scale; > + > + dcl = g_new0(DisplayChangeListener, 1); > + dcl->ops = &fbdev_ops; > + register_displaychangelistener(dcl); > + > + trace_fbdev_enabled(); > + return 0; > +} > + > +void fbdev_display_uninit(void) > +{ > + if (dcl == NULL) { > + return; > + } > + > + unregister_displaychangelistener(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 47ab45d..aa688d5 100644 > --- a/vl.c > +++ b/vl.c > @@ -2241,6 +2241,13 @@ static DisplayType select_display(const char *p) > fprintf(stderr, "GTK support is disabled\n"); > exit(1); > #endif > + } else if (strstart(p, "fbdev", &opts)) { > +#ifdef CONFIG_FBDEV > + display = DT_FBDEV; > +#else > + fprintf(stderr, "fbdev support is disabled\n"); > + exit(1); > +#endif > } else if (strstart(p, "none", &opts)) { > display = DT_NONE; > } else { > @@ -4336,6 +4343,20 @@ int main(int argc, char **argv, char **envp) > curses_display_init(ds, full_screen); > break; > #endif > +#if defined(CONFIG_FBDEV) > + case DT_FBDEV: > + { > + Error *errp = NULL; > + if (fbdev_display_init(NULL, false, &errp) != 0) { > + if (error_is_set(&errp)) { > + fprintf(stderr, "%s\n", error_get_pretty(errp)); > + error_free(errp); > + } > + exit(1); > + } > + break; > + } > +#endif > #if defined(CONFIG_SDL) > case DT_SDL: > sdl_display_init(ds, full_screen, no_frame); > -- > 1.7.9.7