* [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver @ 2012-09-19 11:15 Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann ` (8 more replies) 0 siblings, 9 replies; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann Hi, Next round of the framebuffer display driver patches, including git tree for your convinience. Changes: - fix bisectability. - Use 'struct Error' to report initialization errors, so we can pass up more detailed errors to the monitor. - change monitor command names. Sneak preview: branch rebase/fbdev has patches to switch over spice to pixman (http://www.kraxel.org/cgit/qemu/log/?h=rebase/fbdev). cheers, Gerd The following changes since commit 6b80f7db8a7f84d21e46d01e30c8497733bb23a0: Merge remote-tracking branch 'kiszka/queues/slirp' into staging (2012-09-17 10:23:20 -0500) are available in the git repository at: git://git.kraxel.org/qemu fbdev.2 Gerd Hoffmann (9): QLIST-ify display change listeners. add unregister_displaychangelistener move set_mouse + cursor_define callbacks fbdev: add linux framebuffer display driver. fbdev: add monitor command to enable/disable fbdev: make configurable at compile time. fbdev: move to pixman fbdev: add mouse pointer support fbdev: add display scaling support configure | 24 ++ console.c | 2 +- console.h | 123 ++++-- hmp-commands.hx | 15 + hmp.c | 9 + hmp.h | 1 + hw/jazz_led.c | 2 +- hw/qxl-render.c | 2 +- hw/vga.c | 10 +- hw/vmware_vga.c | 11 +- hw/xenfb.c | 2 +- qapi-schema.json | 13 + qemu-options.hx | 8 + qmp-commands.hx | 6 + qmp.c | 19 + sysemu.h | 1 + trace-events | 15 + ui/Makefile.objs | 1 + ui/fbdev.c | 1143 +++++++++++++++++++++++++++++++++++++++++++++++++++ ui/linux-keynames.h | 388 +++++++++++++++++ ui/sdl.c | 8 +- ui/spice-display.c | 4 +- ui/vnc.c | 8 +- vl.c | 57 ++- 24 files changed, 1798 insertions(+), 74 deletions(-) create mode 100644 ui/fbdev.c create mode 100644 ui/linux-keynames.h ^ permalink raw reply [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners. 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann @ 2012-09-19 11:15 ` Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann ` (7 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- 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 f990684..646ad4b 100644 --- a/console.h +++ b/console.h @@ -164,7 +164,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 { @@ -179,7 +179,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); @@ -231,72 +231,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] 24+ messages in thread
* [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann @ 2012-09-19 11:15 ` Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann ` (6 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 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. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- console.h | 10 ++++++++++ vl.c | 31 +++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/console.h b/console.h index 646ad4b..48fef22 100644 --- a/console.h +++ b/console.h @@ -229,9 +229,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] 24+ messages in thread
* [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann @ 2012-09-19 11:15 ` Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann ` (5 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann When adding DisplayChangeListeners the set_mouse and cursor_define callbacks have been left in DisplayState for some reason. Fix it. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- console.c | 2 +- console.h | 39 +++++++++++++++++++++++++++++++++++---- hw/jazz_led.c | 2 +- hw/qxl-render.c | 2 +- hw/vga.c | 10 +++++----- hw/vmware_vga.c | 11 ++++++----- ui/sdl.c | 8 ++++---- ui/spice-display.c | 4 ++-- ui/vnc.c | 8 ++++---- 9 files changed, 59 insertions(+), 27 deletions(-) diff --git a/console.c b/console.c index a8bcc42..cc0479b 100644 --- a/console.c +++ b/console.c @@ -1239,7 +1239,7 @@ static void text_console_update(void *opaque, console_ch_t *chardata) s->text_y[1] = 0; } if (s->cursor_invalidate) { - dpy_cursor(s->ds, s->x, s->y); + dpy_text_cursor(s->ds, s->x, s->y); s->cursor_invalidate = 0; } } diff --git a/console.h b/console.h index 48fef22..bef2d2d 100644 --- a/console.h +++ b/console.h @@ -164,6 +164,9 @@ struct DisplayChangeListener { int w, int h, uint32_t c); void (*dpy_text_cursor)(struct DisplayState *s, int x, int y); + void (*dpy_mouse_set)(struct DisplayState *s, int x, int y, int on); + void (*dpy_cursor_define)(struct DisplayState *s, QEMUCursor *cursor); + QLIST_ENTRY(DisplayChangeListener) next; }; @@ -181,9 +184,6 @@ struct DisplayState { struct DisplayAllocator* allocator; QLIST_HEAD(, DisplayChangeListener) listeners; - void (*mouse_set)(int x, int y, int on); - void (*cursor_define)(QEMUCursor *cursor); - struct DisplayState *next; }; @@ -304,7 +304,7 @@ static inline void dpy_fill(struct DisplayState *s, int x, int y, } } -static inline void dpy_cursor(struct DisplayState *s, int x, int y) +static inline void dpy_text_cursor(struct DisplayState *s, int x, int y) { struct DisplayChangeListener *dcl; QLIST_FOREACH(dcl, &s->listeners, next) { @@ -314,6 +314,37 @@ static inline void dpy_cursor(struct DisplayState *s, int x, int y) } } +static inline void dpy_mouse_set(struct DisplayState *s, int x, int y, int on) +{ + struct DisplayChangeListener *dcl; + QLIST_FOREACH(dcl, &s->listeners, next) { + if (dcl->dpy_mouse_set) { + dcl->dpy_mouse_set(s, x, y, on); + } + } +} + +static inline void dpy_cursor_define(struct DisplayState *s, QEMUCursor *cursor) +{ + struct DisplayChangeListener *dcl; + QLIST_FOREACH(dcl, &s->listeners, next) { + if (dcl->dpy_cursor_define) { + dcl->dpy_cursor_define(s, cursor); + } + } +} + +static inline bool dpy_cursor_define_supported(struct DisplayState *s) +{ + struct DisplayChangeListener *dcl; + QLIST_FOREACH(dcl, &s->listeners, next) { + if (dcl->dpy_cursor_define) { + return true; + } + } + return false; +} + static inline int ds_get_linesize(DisplayState *ds) { return ds->surface->linesize; diff --git a/hw/jazz_led.c b/hw/jazz_led.c index 6486523..c4d54e2 100644 --- a/hw/jazz_led.c +++ b/hw/jazz_led.c @@ -210,7 +210,7 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata) LedState *s = opaque; char buf[2]; - dpy_cursor(s->ds, -1, -1); + dpy_text_cursor(s->ds, -1, -1); qemu_console_resize(s->ds, 2, 1); /* TODO: draw the segments */ diff --git a/hw/qxl-render.c b/hw/qxl-render.c index e2e3fe2..085a090 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -238,7 +238,7 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) return 1; } - if (!qxl->ssd.ds->mouse_set || !qxl->ssd.ds->cursor_define) { + if (!dpy_cursor_define_supported(qxl->ssd.ds)) { return 0; } diff --git a/hw/vga.c b/hw/vga.c index afaef0d..ec4f0c5 100644 --- a/hw/vga.c +++ b/hw/vga.c @@ -2081,11 +2081,11 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end || full_update) { cursor_visible = !(s->cr[VGA_CRTC_CURSOR_START] & 0x20); if (cursor_visible && cursor_offset < size && cursor_offset >= 0) - dpy_cursor(s->ds, - TEXTMODE_X(cursor_offset), - TEXTMODE_Y(cursor_offset)); + dpy_text_cursor(s->ds, + TEXTMODE_X(cursor_offset), + TEXTMODE_Y(cursor_offset)); else - dpy_cursor(s->ds, -1, -1); + dpy_text_cursor(s->ds, -1, -1); s->cursor_offset = cursor_offset; s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; @@ -2146,7 +2146,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) /* Display a message */ s->last_width = 60; s->last_height = height = 3; - dpy_cursor(s->ds, -1, -1); + dpy_text_cursor(s->ds, -1, -1); s->ds->surface->width = s->last_width; s->ds->surface->height = height; dpy_resize(s->ds); diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c index e815a04..e105b5a 100644 --- a/hw/vmware_vga.c +++ b/hw/vmware_vga.c @@ -479,8 +479,7 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s, qc = cursor_builtin_left_ptr(); } - if (s->vga.ds->cursor_define) - s->vga.ds->cursor_define(qc); + dpy_cursor_define(s->vga.ds, qc); cursor_put(qc); } #endif @@ -755,9 +754,10 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address) caps |= SVGA_CAP_RECT_FILL; #endif #ifdef HW_MOUSE_ACCEL - if (s->vga.ds->mouse_set) + if (dpy_cursor_define_supported(s->vga.ds)) { caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 | SVGA_CAP_CURSOR_BYPASS; + } #endif return caps; @@ -904,8 +904,9 @@ static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value) s->cursor.on |= (value == SVGA_CURSOR_ON_SHOW); s->cursor.on &= (value != SVGA_CURSOR_ON_HIDE); #ifdef HW_MOUSE_ACCEL - if (s->vga.ds->mouse_set && value <= SVGA_CURSOR_ON_SHOW) - s->vga.ds->mouse_set(s->cursor.x, s->cursor.y, s->cursor.on); + if (value <= SVGA_CURSOR_ON_SHOW) { + dpy_mouse_set(s->vga.ds, s->cursor.x, s->cursor.y, s->cursor.on); + } #endif break; diff --git a/ui/sdl.c b/ui/sdl.c index f6f711c..f8ead93 100644 --- a/ui/sdl.c +++ b/ui/sdl.c @@ -905,7 +905,7 @@ static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c) SDL_FillRect(real_screen, &dst, c); } -static void sdl_mouse_warp(int x, int y, int on) +static void sdl_mouse_warp(DisplayState *ds, int x, int y, int on) { if (on) { if (!guest_cursor) @@ -921,7 +921,7 @@ static void sdl_mouse_warp(int x, int y, int on) guest_x = x, guest_y = y; } -static void sdl_mouse_define(QEMUCursor *c) +static void sdl_mouse_define(DisplayState *ds, QEMUCursor *c) { uint8_t *image, *mask; int bpl; @@ -1025,8 +1025,8 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) dcl->dpy_refresh = sdl_refresh; dcl->dpy_setdata = sdl_setdata; dcl->dpy_fill = sdl_fill; - ds->mouse_set = sdl_mouse_warp; - ds->cursor_define = sdl_mouse_define; + dcl->dpy_mouse_set = sdl_mouse_warp; + dcl->dpy_cursor_define = sdl_mouse_define; register_displaychangelistener(ds, dcl); da = g_malloc0(sizeof(DisplayAllocator)); diff --git a/ui/spice-display.c b/ui/spice-display.c index 50fbefb..5180428 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -441,12 +441,12 @@ void qemu_spice_display_resize(SimpleSpiceDisplay *ssd) void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) { if (ssd->cursor) { - ssd->ds->cursor_define(ssd->cursor); + dpy_cursor_define(ssd->ds, ssd->cursor); cursor_put(ssd->cursor); ssd->cursor = NULL; } if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { - ssd->ds->mouse_set(ssd->mouse_x, ssd->mouse_y, 1); + dpy_mouse_set(ssd->ds, ssd->mouse_x, ssd->mouse_y, 1); ssd->mouse_x = -1; ssd->mouse_y = -1; } diff --git a/ui/vnc.c b/ui/vnc.c index 385e345..f8f058d 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -798,7 +798,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int } } -static void vnc_mouse_set(int x, int y, int visible) +static void vnc_mouse_set(DisplayState *ds, int x, int y, int visible) { /* can we ask the client(s) to move the pointer ??? */ } @@ -825,7 +825,7 @@ static int vnc_cursor_define(VncState *vs) return -1; } -static void vnc_dpy_cursor_define(QEMUCursor *c) +static void vnc_dpy_cursor_define(DisplayState *ds, QEMUCursor *c) { VncDisplay *vd = vnc_display; VncState *vs; @@ -2751,9 +2751,9 @@ void vnc_display_init(DisplayState *ds) dcl->dpy_update = vnc_dpy_update; dcl->dpy_resize = vnc_dpy_resize; dcl->dpy_setdata = vnc_dpy_setdata; + dcl->dpy_mouse_set = vnc_mouse_set; + dcl->dpy_cursor_define = vnc_dpy_cursor_define; register_displaychangelistener(ds, dcl); - ds->mouse_set = vnc_mouse_set; - ds->cursor_define = vnc_dpy_cursor_define; } -- 1.7.1 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver. 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann ` (2 preceding siblings ...) 2012-09-19 11:15 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann @ 2012-09-19 11:15 ` Gerd Hoffmann 2012-09-19 18:09 ` Stefano Stabellini 2012-09-19 11:15 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann ` (4 subsequent siblings) 8 siblings, 1 reply; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 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 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<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 | 15 + ui/Makefile.objs | 1 + ui/fbdev.c | 965 +++++++++++++++++++++++++++++++++++++++++++++++++++ ui/linux-keynames.h | 388 +++++++++++++++++++++ vl.c | 19 + 8 files changed, 1401 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 bef2d2d..aa9959b 100644 --- a/console.h +++ b/console.h @@ -417,6 +417,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, Error **err); +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 09c86c4..3445655 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -947,6 +947,14 @@ Enable/disable spice seamless migration. Default is off. @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 b48fe2d..0d0b7fa 100644 --- a/trace-events +++ b/trace-events @@ -994,3 +994,18 @@ 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) + 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..6dfbacc --- /dev/null +++ b/ui/fbdev.c @@ -0,0 +1,965 @@ +/* + * linux fbdev output driver. + * + * Author: Gerd Hoffmann <kraxel@redhat.com> + * + * 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 <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 (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 (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, 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) { + error_setg(err, "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); + + /* 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 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(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, 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(); + + 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..85f2d37 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,20 @@ int main(int argc, char **argv, char **envp) curses_display_init(ds, full_screen); break; #endif +#if defined(CONFIG_LINUX) + case DT_FBDEV: + { + Error *errp = NULL; + if (fbdev_display_init(ds, NULL, &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.1 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver. 2012-09-19 11:15 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann @ 2012-09-19 18:09 ` Stefano Stabellini 2012-09-21 12:28 ` Gerd Hoffmann 0 siblings, 1 reply; 24+ messages in thread From: Stefano Stabellini @ 2012-09-19 18:09 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org On Wed, 19 Sep 2012, Gerd Hoffmann wrote: > +/* -------------------------------------------------------------------- */ > +/* 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++; Rather than introducing redraw_screen, I would just call vga_hw_invalidate instead here. It is always a win if we can avoid to introduce one more state machine. > + case FB_ACTIVE: > + break; > + } > + > + vga_hw_update(); > + if (redraw_screen) { > + fbdev_update(ds, 0, 0, 0, 0); > + } > +} ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver. 2012-09-19 18:09 ` Stefano Stabellini @ 2012-09-21 12:28 ` Gerd Hoffmann 2012-09-24 11:06 ` Stefano Stabellini 0 siblings, 1 reply; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-21 12:28 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org Hi, >> + case FB_ACQ_REQ: >> + fbdev_switch_acquire(); >> + redraw_screen++; > > Rather than introducing redraw_screen, I would just call vga_hw_invalidate > instead here. It is always a win if we can avoid to introduce one more > state machine. Wouldn't that make the vga emulation do additional work which is simply not needed? cheers, Gerd ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver. 2012-09-21 12:28 ` Gerd Hoffmann @ 2012-09-24 11:06 ` Stefano Stabellini 2012-09-24 13:09 ` Gerd Hoffmann 0 siblings, 1 reply; 24+ messages in thread From: Stefano Stabellini @ 2012-09-24 11:06 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org, Stefano Stabellini On Fri, 21 Sep 2012, Gerd Hoffmann wrote: > Hi, > > >> + case FB_ACQ_REQ: > >> + fbdev_switch_acquire(); > >> + redraw_screen++; > > > > Rather than introducing redraw_screen, I would just call vga_hw_invalidate > > instead here. It is always a win if we can avoid to introduce one more > > state machine. > > Wouldn't that make the vga emulation do additional work which is simply > not needed? It causes full_update to be set and that causes vga_draw_line to be called for every line on the screen. However in the important cases (32bpp and 16bpp) the buffer is shared and nothing happens. So no more work. ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver. 2012-09-24 11:06 ` Stefano Stabellini @ 2012-09-24 13:09 ` Gerd Hoffmann 0 siblings, 0 replies; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-24 13:09 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org On 09/24/12 13:06, Stefano Stabellini wrote: > On Fri, 21 Sep 2012, Gerd Hoffmann wrote: >> Hi, >> >>>> + case FB_ACQ_REQ: >>>> + fbdev_switch_acquire(); >>>> + redraw_screen++; >>> >>> Rather than introducing redraw_screen, I would just call vga_hw_invalidate >>> instead here. It is always a win if we can avoid to introduce one more >>> state machine. >> >> Wouldn't that make the vga emulation do additional work which is simply >> not needed? > > It causes full_update to be set and that causes vga_draw_line to be > called for every line on the screen. > However in the important cases (32bpp and 16bpp) the buffer is shared > and nothing happens. So no more work. When looking at the code I feel again like this is one relict we should just get rid of ... The reason it exists in the first place seems to be console switching. The qemu-internal one, via Ctrl-Alt-<nr>, to serial / monitor text consoles. In that case the vga emulation must be able to restore the display, and vga_hw_invalidate will handle it. That logic predates the display surfaces though. I think today we could just assign a displaysurface to each qemu console and be done with it. Console switching will just switch display surfaces without any re-rendering needed. Likewise screendumping can stop hoping through all the loops it hops today. Any other vga_hw_invalidate calls creped in over time (ab-)use it to kick a redraw in the ui code. They kick the vga emulation for no reason, there is nothing the vga emulation must redraw. cheers, Gerd ^ permalink raw reply [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann ` (3 preceding siblings ...) 2012-09-19 11:15 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann @ 2012-09-19 11:15 ` Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time Gerd Hoffmann ` (3 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 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: framebuffer-display enable=on|off hmp: framebuffer-display on|off Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- hmp-commands.hx | 15 +++++++++++++++ hmp.c | 9 +++++++++ hmp.h | 1 + qapi-schema.json | 13 +++++++++++++ qmp-commands.hx | 6 ++++++ qmp.c | 19 +++++++++++++++++++ 6 files changed, 63 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index ed67e99..93e75ab 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1377,6 +1377,21 @@ passed since 1970, i.e. unix epoch. ETEXI { + .name = "framebuffer-display", + .args_type = "enable:b", + .params = "on|off", + .help = "enable/disable linux console framebuffer display", + .mhandler.cmd = hmp_framebuffer_display, + }, + +STEXI +@item framebuffer-display on | off +@findex framebuffer-display + +enable/disable linux console framebuffer display. +ETEXI + + { .name = "info", .args_type = "item:s?", .params = "[subcommand]", diff --git a/hmp.c b/hmp.c index ba6fbd3..9677ef6 100644 --- a/hmp.c +++ b/hmp.c @@ -1168,3 +1168,12 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +void hmp_framebuffer_display(Monitor *mon, const QDict *qdict) +{ + int enable = qdict_get_bool(qdict, "enable"); + Error *errp = NULL; + + qmp_framebuffer_display(enable, &errp); + hmp_handle_error(mon, &errp); +} diff --git a/hmp.h b/hmp.h index 48b9c59..20aa08c 100644 --- a/hmp.h +++ b/hmp.h @@ -73,5 +73,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_framebuffer_display(Monitor *mon, const QDict *qdict); #endif diff --git a/qapi-schema.json b/qapi-schema.json index 14e4419..87c77f7 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2619,3 +2619,16 @@ # Since: 0.14.0 ## { 'command': 'screendump', 'data': {'filename': 'str'} } + +# @framebuffer-display: +# +# Enable/disable linux console framebuffer display. +# +# @enable: whenever the framebuffer display should be enabled or disabled. +# +# Returns: Nothing. +# +# Since: 1.3 +# +## +{ 'command': 'framebuffer-display', 'data': {'enable': 'bool'} } diff --git a/qmp-commands.hx b/qmp-commands.hx index 6e21ddb..e0a747a 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -2539,3 +2539,9 @@ EQMP .args_type = "", .mhandler.cmd_new = qmp_marshal_input_query_target, }, + + { + .name = "framebuffer-display", + .args_type = "enable:b", + .mhandler.cmd_new = qmp_marshal_input_framebuffer_display, + }, diff --git a/qmp.c b/qmp.c index 8463922..56e007f 100644 --- a/qmp.c +++ b/qmp.c @@ -391,6 +391,25 @@ void qmp_change(const char *device, const char *target, } } +void qmp_framebuffer_display(bool enable, Error **errp) +{ +#if defined(CONFIG_LINUX) + DisplayState *ds = get_displaystate(); + + if (enable) { + if (fbdev_display_init(ds, NULL, errp) != 0) { + if (!error_is_set(errp)) { + error_setg(errp, "fbdev initialization failed"); + } + } + } else { + fbdev_display_uninit(ds); + } +#else + error_setg(errp, "fbdev support disabled at compile time"); +#endif +} + static void qom_list_types_tramp(ObjectClass *klass, void *data) { ObjectTypeInfoList *e, **pret = data; -- 1.7.1 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time. 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann ` (4 preceding siblings ...) 2012-09-19 11:15 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann @ 2012-09-19 11:15 ` Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann ` (2 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann Add CONFIG_FBDEV, add --enable-fbdev and --disable-fbdev configure switches so fbdev can be enabled/disabled at compile time. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- configure | 12 ++++++++++++ qmp.c | 2 +- ui/Makefile.objs | 2 +- vl.c | 4 ++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/configure b/configure index 8564142..c4ba338 100755 --- a/configure +++ b/configure @@ -148,6 +148,7 @@ docs="" fdt="" nptl="" sdl="" +fbdev="no" virtfs="" vnc="yes" sparse="no" @@ -527,6 +528,7 @@ Haiku) usb="linux" kvm="yes" vhost_net="yes" + fbdev="yes" if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then audio_possible_drivers="$audio_possible_drivers fmod" fi @@ -658,6 +660,10 @@ for opt do ;; --enable-sdl) sdl="yes" ;; + --disable-fbdev) fbdev="no" + ;; + --enable-fbdev) fbdev="yes" + ;; --disable-virtfs) virtfs="no" ;; --enable-virtfs) virtfs="yes" @@ -1070,6 +1076,8 @@ echo " --disable-strip disable stripping binaries" echo " --disable-werror disable compilation abort on warning" echo " --disable-sdl disable SDL" echo " --enable-sdl enable SDL" +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" @@ -3159,6 +3167,7 @@ if test "$darwin" = "yes" ; then echo "Cocoa support $cocoa" fi echo "SDL support $sdl" +echo "fbdev support $fbdev" echo "curses support $curses" echo "curl support $curl" echo "mingw32 support $mingw32" @@ -3367,6 +3376,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/qmp.c b/qmp.c index 56e007f..712ab58 100644 --- a/qmp.c +++ b/qmp.c @@ -393,7 +393,7 @@ void qmp_change(const char *device, const char *target, void qmp_framebuffer_display(bool enable, Error **errp) { -#if defined(CONFIG_LINUX) +#if defined(CONFIG_FBDEV) DisplayState *ds = get_displaystate(); if (enable) { diff --git a/ui/Makefile.objs b/ui/Makefile.objs index 55ddcf2..479cd01 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -12,4 +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 +common-obj-$(CONFIG_FBDEV) += fbdev.o diff --git a/vl.c b/vl.c index 85f2d37..4159035 100644 --- a/vl.c +++ b/vl.c @@ -3041,7 +3041,7 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "SDL support is disabled\n"); exit(1); #endif -#ifdef CONFIG_LINUX +#ifdef CONFIG_FBDEV case QEMU_OPTION_fbdev: display_type = DT_FBDEV; break; @@ -3686,7 +3686,7 @@ int main(int argc, char **argv, char **envp) curses_display_init(ds, full_screen); break; #endif -#if defined(CONFIG_LINUX) +#if defined(CONFIG_FBDEV) case DT_FBDEV: { Error *errp = NULL; -- 1.7.1 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann ` (5 preceding siblings ...) 2012-09-19 11:15 ` [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time Gerd Hoffmann @ 2012-09-19 11:15 ` Gerd Hoffmann 2012-09-19 18:10 ` Stefano Stabellini 2012-09-19 11:15 ` [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support Gerd Hoffmann 8 siblings, 1 reply; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann Stop reinventing the wheel, use the pixman library for raster ops. Also add setdata callback. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- configure | 12 ++++ ui/fbdev.c | 182 +++++++++++++++++++++++++++++++++++++----------------------- 2 files changed, 124 insertions(+), 70 deletions(-) diff --git a/configure b/configure index c4ba338..d10ff78 100755 --- a/configure +++ b/configure @@ -148,6 +148,7 @@ docs="" fdt="" nptl="" sdl="" +pixman="" fbdev="no" virtfs="" vnc="yes" @@ -2153,6 +2154,17 @@ else exit 1 fi +if $pkg_config pixman-1 > /dev/null 2>&1 +then + pixman="yes" + pixman_cflags=`$pkg_config --cflags pixman-1 2>/dev/null` + pixman_libs=`$pkg_config --libs pixman-1 2>/dev/null` + QEMU_CFLAGS="$QEMU_CFLAGS $pixman_cflags" + libs_softmmu="$libs_softmmu $pixman_libs" +else + fbdev="no" +fi + ########################################## # libcap probe diff --git a/ui/fbdev.c b/ui/fbdev.c index 6dfbacc..d55850e 100644 --- a/ui/fbdev.c +++ b/ui/fbdev.c @@ -23,11 +23,12 @@ #include <linux/vt.h> #include <linux/fb.h> +#include <pixman.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 @@ -70,19 +71,82 @@ static bool key_down[KEY_CNT]; #define FB_ACQ_REQ 3 static int fb_switch_state; -/* qdev windup */ +/* qemu 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; +static pixman_image_t *surface; +static pixman_image_t *framebuffer; +static pixman_transform_t transform; +static pixman_region16_t dirty; /* fwd decls */ static int fbdev_activate_vt(int tty, int vtno, bool wait); /* -------------------------------------------------------------------- */ +/* pixman helpers */ + +static int pixman_shifts_to_type(int rshift, int gshift, int bshift) +{ + int type = PIXMAN_TYPE_OTHER; + + if (rshift > gshift && gshift > bshift) { + if (bshift == 0) { + type = PIXMAN_TYPE_ARGB; + } else { +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0, 21, 8) + type = PIXMAN_TYPE_RGBA; +#endif + } + } else if (rshift < gshift && gshift < bshift) { + if (rshift == 0) { + type = PIXMAN_TYPE_ABGR; + } else { + type = PIXMAN_TYPE_BGRA; + } + } + return type; +} + +static pixman_image_t *pixman_from_displaystate(DisplayState *ds) +{ + PixelFormat *pf = &ds->surface->pf; + pixman_format_code_t format; + pixman_image_t *image; + int type; + + type = pixman_shifts_to_type(pf->rshift, pf->gshift, pf->bshift); + format = PIXMAN_FORMAT(pf->bits_per_pixel, type, + pf->abits, pf->rbits, pf->gbits, pf->bbits); + image = pixman_image_create_bits(format, ds_get_width(ds), + ds_get_height(ds), + (void *)ds_get_data(ds), + ds_get_linesize(ds)); + return image; +} + +static pixman_image_t *pixman_from_framebuffer(void) +{ + pixman_format_code_t format; + pixman_image_t *image; + int type; + + type = pixman_shifts_to_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; +} + +/* -------------------------------------------------------------------- */ /* mouse */ static void read_mouse(void *opaque) @@ -529,6 +593,17 @@ static void fbdev_cleanup(void) { trace_fbdev_cleanup(); + /* release pixman stuff */ + pixman_region_fini(&dirty); + if (framebuffer) { + pixman_image_unref(framebuffer); + framebuffer = NULL; + } + if (surface) { + pixman_image_unref(surface); + surface = NULL; + } + /* restore console */ if (fb_mem != NULL) { munmap(fb_mem, fb_fix.smem_len+fb_mem_offset); @@ -682,36 +757,8 @@ static int fbdev_init(const char *device, Error **err) 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; - } + framebuffer = pixman_from_framebuffer(); + pixman_region_init(&dirty); return 0; err_early: @@ -819,36 +866,15 @@ static int fbdev_switch_init(void) /* -------------------------------------------------------------------- */ /* rendering */ -static void fbdev_render(DisplayState *ds, int x, int y, int w, int h) +static void fbdev_render(DisplayState *ds) { - uint8_t *dst; - uint8_t *src; - int line; + assert(surface); - 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); - } + pixman_image_set_clip_region(surface, &dirty); + pixman_image_composite(PIXMAN_OP_SRC, surface, NULL, framebuffer, + 0, 0, 0, 0, 0, 0, fb_var.xres, fb_var.yres); + pixman_region_fini(&dirty); + pixman_region_init(&dirty); } /* -------------------------------------------------------------------- */ @@ -872,14 +898,16 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h) 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 (surface) { + pixman_image_unref(surface); } + surface = pixman_from_displaystate(ds); + + pixman_transform_init_identity(&transform); + pixman_transform_translate(&transform, NULL, + pixman_int_to_fixed(-cx), + pixman_int_to_fixed(-cy)); + pixman_image_set_transform(surface, &transform); } if (redraw_screen) { @@ -889,7 +917,7 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h) x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds); } - fbdev_render(ds, x, y, w, h); + pixman_region_union_rect(&dirty, &dirty, x, y, w, h); } static void fbdev_resize(DisplayState *ds) @@ -898,6 +926,15 @@ static void fbdev_resize(DisplayState *ds) redraw_screen++; } +static void fbdev_setdata(DisplayState *ds) +{ + if (surface) { + pixman_image_unref(surface); + } + surface = pixman_from_displaystate(ds); + redraw_screen++; +} + static void fbdev_refresh(DisplayState *ds) { switch (fb_switch_state) { @@ -916,6 +953,10 @@ static void fbdev_refresh(DisplayState *ds) if (redraw_screen) { fbdev_update(ds, 0, 0, 0, 0); } + + if (pixman_region_not_empty(&dirty)) { + fbdev_render(ds); + } } static void fbdev_exit_notifier(Notifier *notifier, void *data) @@ -942,6 +983,7 @@ int fbdev_display_init(DisplayState *ds, const char *device, Error **err) dcl = g_new0(DisplayChangeListener, 1); dcl->dpy_update = fbdev_update; dcl->dpy_resize = fbdev_resize; + dcl->dpy_setdata = fbdev_setdata; dcl->dpy_refresh = fbdev_refresh; register_displaychangelistener(ds, dcl); -- 1.7.1 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-19 11:15 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann @ 2012-09-19 18:10 ` Stefano Stabellini 2012-09-20 6:16 ` Gerd Hoffmann 0 siblings, 1 reply; 24+ messages in thread From: Stefano Stabellini @ 2012-09-19 18:10 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org On Wed, 19 Sep 2012, Gerd Hoffmann wrote: > Stop reinventing the wheel, use the pixman library for raster ops. > Also add setdata callback. > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > configure | 12 ++++ > ui/fbdev.c | 182 +++++++++++++++++++++++++++++++++++++----------------------- > 2 files changed, 124 insertions(+), 70 deletions(-) > > diff --git a/configure b/configure > index c4ba338..d10ff78 100755 > --- a/configure > +++ b/configure > @@ -148,6 +148,7 @@ docs="" > fdt="" > nptl="" > sdl="" > +pixman="" > fbdev="no" > virtfs="" > vnc="yes" > @@ -2153,6 +2154,17 @@ else > exit 1 > fi > > +if $pkg_config pixman-1 > /dev/null 2>&1 > +then > + pixman="yes" > + pixman_cflags=`$pkg_config --cflags pixman-1 2>/dev/null` > + pixman_libs=`$pkg_config --libs pixman-1 2>/dev/null` > + QEMU_CFLAGS="$QEMU_CFLAGS $pixman_cflags" > + libs_softmmu="$libs_softmmu $pixman_libs" > +else > + fbdev="no" > +fi > + > ########################################## > # libcap probe > > diff --git a/ui/fbdev.c b/ui/fbdev.c > index 6dfbacc..d55850e 100644 > --- a/ui/fbdev.c > +++ b/ui/fbdev.c > @@ -23,11 +23,12 @@ > #include <linux/vt.h> > #include <linux/fb.h> > > +#include <pixman.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 > @@ -70,19 +71,82 @@ static bool key_down[KEY_CNT]; > #define FB_ACQ_REQ 3 > static int fb_switch_state; > > -/* qdev windup */ > +/* qemu 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; > +static pixman_image_t *surface; > +static pixman_image_t *framebuffer; > +static pixman_transform_t transform; > +static pixman_region16_t dirty; > > /* fwd decls */ > static int fbdev_activate_vt(int tty, int vtno, bool wait); > > /* -------------------------------------------------------------------- */ > +/* pixman helpers */ > + > +static int pixman_shifts_to_type(int rshift, int gshift, int bshift) > +{ > + int type = PIXMAN_TYPE_OTHER; > + > + if (rshift > gshift && gshift > bshift) { > + if (bshift == 0) { > + type = PIXMAN_TYPE_ARGB; > + } else { > +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0, 21, 8) > + type = PIXMAN_TYPE_RGBA; > +#endif > + } > + } else if (rshift < gshift && gshift < bshift) { > + if (rshift == 0) { > + type = PIXMAN_TYPE_ABGR; > + } else { > + type = PIXMAN_TYPE_BGRA; > + } > + } > + return type; > +} > + > +static pixman_image_t *pixman_from_displaystate(DisplayState *ds) > +{ > + PixelFormat *pf = &ds->surface->pf; > + pixman_format_code_t format; > + pixman_image_t *image; > + int type; > + > + type = pixman_shifts_to_type(pf->rshift, pf->gshift, pf->bshift); > + format = PIXMAN_FORMAT(pf->bits_per_pixel, type, > + pf->abits, pf->rbits, pf->gbits, pf->bbits); > + image = pixman_image_create_bits(format, ds_get_width(ds), > + ds_get_height(ds), > + (void *)ds_get_data(ds), > + ds_get_linesize(ds)); > + return image; > +} > + > +static pixman_image_t *pixman_from_framebuffer(void) > +{ > + pixman_format_code_t format; > + pixman_image_t *image; > + int type; > + > + type = pixman_shifts_to_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; > +} > + > +/* -------------------------------------------------------------------- */ > /* mouse */ > > static void read_mouse(void *opaque) > @@ -529,6 +593,17 @@ static void fbdev_cleanup(void) > { > trace_fbdev_cleanup(); > > + /* release pixman stuff */ > + pixman_region_fini(&dirty); > + if (framebuffer) { > + pixman_image_unref(framebuffer); > + framebuffer = NULL; > + } > + if (surface) { > + pixman_image_unref(surface); > + surface = NULL; > + } > + > /* restore console */ > if (fb_mem != NULL) { > munmap(fb_mem, fb_fix.smem_len+fb_mem_offset); > @@ -682,36 +757,8 @@ static int fbdev_init(const char *device, Error **err) > 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; > - } > + framebuffer = pixman_from_framebuffer(); > + pixman_region_init(&dirty); > return 0; > > err_early: > @@ -819,36 +866,15 @@ static int fbdev_switch_init(void) > /* -------------------------------------------------------------------- */ > /* rendering */ > > -static void fbdev_render(DisplayState *ds, int x, int y, int w, int h) > +static void fbdev_render(DisplayState *ds) > { > - uint8_t *dst; > - uint8_t *src; > - int line; > + assert(surface); > > - 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); > - } > + pixman_image_set_clip_region(surface, &dirty); > + pixman_image_composite(PIXMAN_OP_SRC, surface, NULL, framebuffer, > + 0, 0, 0, 0, 0, 0, fb_var.xres, fb_var.yres); > + pixman_region_fini(&dirty); > + pixman_region_init(&dirty); > } > > /* -------------------------------------------------------------------- */ > @@ -872,14 +898,16 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h) > 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 (surface) { > + pixman_image_unref(surface); > } > + surface = pixman_from_displaystate(ds); Am I reading this right? Are you creating a new pixman surface in every call to fbdev_update? > + pixman_transform_init_identity(&transform); > + pixman_transform_translate(&transform, NULL, > + pixman_int_to_fixed(-cx), > + pixman_int_to_fixed(-cy)); > + pixman_image_set_transform(surface, &transform); > } > > if (redraw_screen) { > @@ -889,7 +917,7 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h) > x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds); > } > > - fbdev_render(ds, x, y, w, h); > + pixman_region_union_rect(&dirty, &dirty, x, y, w, h); > } > > static void fbdev_resize(DisplayState *ds) > @@ -898,6 +926,15 @@ static void fbdev_resize(DisplayState *ds) > redraw_screen++; > } > > +static void fbdev_setdata(DisplayState *ds) > +{ > + if (surface) { > + pixman_image_unref(surface); > + } > + surface = pixman_from_displaystate(ds); > + redraw_screen++; > +} > + > static void fbdev_refresh(DisplayState *ds) > { > switch (fb_switch_state) { > @@ -916,6 +953,10 @@ static void fbdev_refresh(DisplayState *ds) > if (redraw_screen) { > fbdev_update(ds, 0, 0, 0, 0); > } > + > + if (pixman_region_not_empty(&dirty)) { > + fbdev_render(ds); > + } Why are you using fbdev_refresh for rendering instead of fbdev_update? >From consistency with sdl and vnc as well as the semantics of these callbacks I think it would be better to do the rendering from fbdev_update and just call vga_hw_update here. > } > > static void fbdev_exit_notifier(Notifier *notifier, void *data) > @@ -942,6 +983,7 @@ int fbdev_display_init(DisplayState *ds, const char *device, Error **err) > dcl = g_new0(DisplayChangeListener, 1); > dcl->dpy_update = fbdev_update; > dcl->dpy_resize = fbdev_resize; > + dcl->dpy_setdata = fbdev_setdata; > dcl->dpy_refresh = fbdev_refresh; > register_displaychangelistener(ds, dcl); Pixman or non-pixman, I still think that this could benefit from implementing a DisplayAllocator interface: it would avoid a memcpy whenever there is no need for scaling and pixel conversions. It should be able to make visible difference from the usability perspective. ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-19 18:10 ` Stefano Stabellini @ 2012-09-20 6:16 ` Gerd Hoffmann 2012-09-20 11:33 ` Stefano Stabellini 0 siblings, 1 reply; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-20 6:16 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org Hi, >> + if (surface) { >> + pixman_image_unref(surface); >> } >> + surface = pixman_from_displaystate(ds); > > Am I reading this right? Are you creating a new pixman surface in every > call to fbdev_update? No. The whole block doing this is wrapped into "if (resize_screen)" (which you don't see in the patch due to context being too small). >> @@ -916,6 +953,10 @@ static void fbdev_refresh(DisplayState *ds) >> if (redraw_screen) { >> fbdev_update(ds, 0, 0, 0, 0); >> } >> + >> + if (pixman_region_not_empty(&dirty)) { >> + fbdev_render(ds); >> + } > > Why are you using fbdev_refresh for rendering instead of fbdev_update? > From consistency with sdl and vnc as well as the semantics of these > callbacks I think it would be better to do the rendering from > fbdev_update and just call vga_hw_update here. It _does_ call vga_hw_update. The fbdev_update callbacks triggered by this collect the updated regions in the dirty variable. Finally we'll render everything in one go (assuming we got any updates). Performs better than doing the rendering in the fbdev_update callback. > Pixman or non-pixman, I still think that this could benefit from > implementing a DisplayAllocator interface: it would avoid a memcpy > whenever there is no need for scaling and pixel conversions. There is one more issue I didn't mention yet: The framebuffer memory should better be treaded as write-only memory as this is what gfx cards are optimized for. Read access works of course, but can be _very_ slow depending on the hardware. So implementing a DisplayAllocator and thereby making the vga emulation operate directly on framebuffer memory is a very bad idea IMO. Most likely it will make certain operations (like cirrus bitblits) slower even though it saves a memcpy. cheers, Gerd ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-20 6:16 ` Gerd Hoffmann @ 2012-09-20 11:33 ` Stefano Stabellini 2012-09-20 13:51 ` Gerd Hoffmann 0 siblings, 1 reply; 24+ messages in thread From: Stefano Stabellini @ 2012-09-20 11:33 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org, Stefano Stabellini On Thu, 20 Sep 2012, Gerd Hoffmann wrote: > > Pixman or non-pixman, I still think that this could benefit from > > implementing a DisplayAllocator interface: it would avoid a memcpy > > whenever there is no need for scaling and pixel conversions. > > There is one more issue I didn't mention yet: The framebuffer memory > should better be treaded as write-only memory as this is what gfx cards > are optimized for. Read access works of course, but can be _very_ slow > depending on the hardware. That is true, however do we actually read the framebuffer back anywhere? I don't think that the vga emulator reads back anything. In any case if the memory is WC, there might still be some benefit in writing the framebuffer all in one go but I don't think that the difference would noticeble. If it is, it might be a good idea to get rid of DisplayAllocator altogether. > So implementing a DisplayAllocator and thereby making the vga emulation > operate directly on framebuffer memory is a very bad idea IMO. Most > likely it will make certain operations (like cirrus bitblits) slower > even though it saves a memcpy. It is not just one memcpy less, it is also about zero latency. Otherwise we have the typical problem of having to wait 30ms before the screen update. Not nice. ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-20 11:33 ` Stefano Stabellini @ 2012-09-20 13:51 ` Gerd Hoffmann 2012-09-20 15:20 ` Stefano Stabellini 0 siblings, 1 reply; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-20 13:51 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org Hi, > It might be a good idea to get rid > of DisplayAllocator altogether. After some digging in the source code: Yes, I think so. Look, we have *two* concepts for avoiding memcpy: The first is the DisplayAllocator. Only implemented by SDL, which is scheduled to be downgraded by anthonys gtk patches. Doesn't really fit into the concept of displaychangelisteners coming and going at runtime, and also not of having multiple displaychangelisteners (like sdl+vnc at the same time). It allows vga emulation to render directly into a SDL buffer. The second is qemu_create_displaysurface_from(). It allows vga emulation hand out a surface with direct pointer to the guests video memory for displaychangelisteners to read from. You can't have both (i.e. the guest will never ever write directly into the SDL buffer), there will always be at least one memcpy. So what happens in practice? In any graphics mode relevant today vga emulation will use qemu_create_displaysurface_from(). Whenever a DisplayAllocator is present or not doesn't make any difference then. In case vga emulation has to render something because the guests video memory can't be represented directly as displaysurface (text mode, gfx modes with <= 256 colors) it will allocate a surface where it will render the screen to and will use the SDL DisplayAllocator if registered. I somehow doubt text mode acceleration is worth the complexity+confusion DisplayAllocator adds to the picture. Also I'd like to have reference counting for display surfaces because I can offload the display scaling to a separate thread then. Guess this is easier to implement when zapping DisplayAllocator first. cheers, Gerd ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-20 13:51 ` Gerd Hoffmann @ 2012-09-20 15:20 ` Stefano Stabellini 2012-09-20 15:27 ` Gerd Hoffmann 0 siblings, 1 reply; 24+ messages in thread From: Stefano Stabellini @ 2012-09-20 15:20 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org, Stefano Stabellini On Thu, 20 Sep 2012, Gerd Hoffmann wrote: > Hi, > > > It might be a good idea to get rid > > of DisplayAllocator altogether. > > After some digging in the source code: Yes, I think so. > > Look, we have *two* concepts for avoiding memcpy: > > The first is the DisplayAllocator. Only implemented by SDL, which is > scheduled to be downgraded by anthonys gtk patches. Doesn't really fit > into the concept of displaychangelisteners coming and going at runtime, > and also not of having multiple displaychangelisteners (like sdl+vnc at > the same time). It allows vga emulation to render directly into a SDL > buffer. > > The second is qemu_create_displaysurface_from(). It allows vga > emulation hand out a surface with direct pointer to the guests video > memory for displaychangelisteners to read from. > > You can't have both (i.e. the guest will never ever write directly into > the SDL buffer), there will always be at least one memcpy. > > So what happens in practice? > > In any graphics mode relevant today vga emulation will use > qemu_create_displaysurface_from(). Whenever a DisplayAllocator is > present or not doesn't make any difference then. Unfortunately if my memory doesn't fail me, Windows uses 24 bpp. So actually the DisplayAllocator interface is the one that is going to be used all the time. <sarcasm> You have got to love Windows </sarcasm> ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-20 15:20 ` Stefano Stabellini @ 2012-09-20 15:27 ` Gerd Hoffmann 2012-09-20 15:28 ` Stefano Stabellini 0 siblings, 1 reply; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-20 15:27 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org Hi, >> In any graphics mode relevant today vga emulation will use >> qemu_create_displaysurface_from(). Whenever a DisplayAllocator is >> present or not doesn't make any difference then. > > Unfortunately if my memory doesn't fail me, Windows uses 24 bpp. > So actually the DisplayAllocator interface is the one that is going to be > used all the time. Guess we want implement 24bpp support in displaylisteners then. cheers, Gerd ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-20 15:27 ` Gerd Hoffmann @ 2012-09-20 15:28 ` Stefano Stabellini 2012-09-20 15:33 ` Stefano Stabellini 0 siblings, 1 reply; 24+ messages in thread From: Stefano Stabellini @ 2012-09-20 15:28 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org, Stefano Stabellini On Thu, 20 Sep 2012, Gerd Hoffmann wrote: > Hi, > > >> In any graphics mode relevant today vga emulation will use > >> qemu_create_displaysurface_from(). Whenever a DisplayAllocator is > >> present or not doesn't make any difference then. > > > > Unfortunately if my memory doesn't fail me, Windows uses 24 bpp. > > So actually the DisplayAllocator interface is the one that is going to be > > used all the time. > > Guess we want implement 24bpp support in displaylisteners then. vnc doesn't support 24bpp ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-20 15:28 ` Stefano Stabellini @ 2012-09-20 15:33 ` Stefano Stabellini 2012-09-21 5:40 ` Gerd Hoffmann 0 siblings, 1 reply; 24+ messages in thread From: Stefano Stabellini @ 2012-09-20 15:33 UTC (permalink / raw) To: Stefano Stabellini; +Cc: Gerd Hoffmann, qemu-devel@nongnu.org On Thu, 20 Sep 2012, Stefano Stabellini wrote: > On Thu, 20 Sep 2012, Gerd Hoffmann wrote: > > Hi, > > > > >> In any graphics mode relevant today vga emulation will use > > >> qemu_create_displaysurface_from(). Whenever a DisplayAllocator is > > >> present or not doesn't make any difference then. > > > > > > Unfortunately if my memory doesn't fail me, Windows uses 24 bpp. > > > So actually the DisplayAllocator interface is the one that is going to be > > > used all the time. > > > > Guess we want implement 24bpp support in displaylisteners then. > > vnc doesn't support 24bpp I mean the vnc protocol doesn't support 24bpp, so it couldn't help vnc (I am aware that at the moment vnc is not using a DisplayAllocator, but I guess it could in the future). However I do agree that implementing 24bpp support in DisplayListeners would be a better solution and if we did that I wouldn't object to getting rid of DisplayAllocator. Ideally we could also introduce a simple vnc protocol extension to export 24bpp directly to clients. ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-20 15:33 ` Stefano Stabellini @ 2012-09-21 5:40 ` Gerd Hoffmann 2012-09-21 10:48 ` Stefano Stabellini 0 siblings, 1 reply; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-21 5:40 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org On 09/20/12 17:33, Stefano Stabellini wrote: > On Thu, 20 Sep 2012, Stefano Stabellini wrote: >> On Thu, 20 Sep 2012, Gerd Hoffmann wrote: >>> Hi, >>> >>>>> In any graphics mode relevant today vga emulation will use >>>>> qemu_create_displaysurface_from(). Whenever a DisplayAllocator is >>>>> present or not doesn't make any difference then. >>>> >>>> Unfortunately if my memory doesn't fail me, Windows uses 24 bpp. >>>> So actually the DisplayAllocator interface is the one that is going to be >>>> used all the time. >>> >>> Guess we want implement 24bpp support in displaylisteners then. >> >> vnc doesn't support 24bpp > > I mean the vnc protocol doesn't support 24bpp, so it couldn't help vnc > (I am aware that at the moment vnc is not using a DisplayAllocator, but > I guess it could in the future). Yes, vnc should transform 24bpp into 32bpp. Given that vnc keeps a shadow copy of the guest display _anyway_ (to figure which parts of the guest display did _really_ change) we don't have to do any extra copying work in vnc. We can just keep the shadow at 32bpp. The 'compare+copy' code in vnc_refresh_server_surface must be able to cope with 24bpp guest + 32bpp server surface. Done. And we've dropped the 24->32 bpp conversion in the vga emulation along the way. cheers, Gerd ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-21 5:40 ` Gerd Hoffmann @ 2012-09-21 10:48 ` Stefano Stabellini 0 siblings, 0 replies; 24+ messages in thread From: Stefano Stabellini @ 2012-09-21 10:48 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org, Stefano Stabellini On Fri, 21 Sep 2012, Gerd Hoffmann wrote: > On 09/20/12 17:33, Stefano Stabellini wrote: > > On Thu, 20 Sep 2012, Stefano Stabellini wrote: > >> On Thu, 20 Sep 2012, Gerd Hoffmann wrote: > >>> Hi, > >>> > >>>>> In any graphics mode relevant today vga emulation will use > >>>>> qemu_create_displaysurface_from(). Whenever a DisplayAllocator is > >>>>> present or not doesn't make any difference then. > >>>> > >>>> Unfortunately if my memory doesn't fail me, Windows uses 24 bpp. > >>>> So actually the DisplayAllocator interface is the one that is going to be > >>>> used all the time. > >>> > >>> Guess we want implement 24bpp support in displaylisteners then. > >> > >> vnc doesn't support 24bpp > > > > I mean the vnc protocol doesn't support 24bpp, so it couldn't help vnc > > (I am aware that at the moment vnc is not using a DisplayAllocator, but > > I guess it could in the future). > > Yes, vnc should transform 24bpp into 32bpp. Given that vnc keeps a > shadow copy of the guest display _anyway_ (to figure which parts of the > guest display did _really_ change) we don't have to do any extra copying > work in vnc. We can just keep the shadow at 32bpp. The 'compare+copy' > code in vnc_refresh_server_surface must be able to cope with 24bpp guest > + 32bpp server surface. Done. And we've dropped the 24->32 bpp > conversion in the vga emulation along the way. OK, I am sold :) ^ permalink raw reply [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann ` (6 preceding siblings ...) 2012-09-19 11:15 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann @ 2012-09-19 11:15 ` Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support Gerd Hoffmann 8 siblings, 0 replies; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann Add mouse_set and cursor_define DisplayChangeListener callbacks and mouse pointer rendering support. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- ui/fbdev.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 95 insertions(+), 0 deletions(-) diff --git a/ui/fbdev.c b/ui/fbdev.c index d55850e..d859d84 100644 --- a/ui/fbdev.c +++ b/ui/fbdev.c @@ -82,6 +82,12 @@ static pixman_image_t *framebuffer; static pixman_transform_t transform; static pixman_region16_t dirty; +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; + /* fwd decls */ static int fbdev_activate_vt(int tty, int vtno, bool wait); @@ -877,6 +883,51 @@ static void fbdev_render(DisplayState *ds) pixman_region_init(&dirty); } +static void fbdev_unrender_ptr(DisplayState *ds) +{ + if (!pw && !ph) { + return; + } + pixman_region_union_rect(&dirty, &dirty, px, py, pw, ph); + ph = pw = 0; +} + +static void fbdev_render_ptr(DisplayState *ds) +{ + 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)); + 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 */ @@ -918,6 +969,9 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h) } pixman_region_union_rect(&dirty, &dirty, x, y, w, h); + if (ptr_image && mon && pw && ph) { + ptr_refresh++; + } } static void fbdev_resize(DisplayState *ds) @@ -954,9 +1008,48 @@ static void fbdev_refresh(DisplayState *ds) fbdev_update(ds, 0, 0, 0, 0); } + if (ptr_refresh) { + fbdev_unrender_ptr(ds); + } if (pixman_region_not_empty(&dirty)) { fbdev_render(ds); } + if (ptr_refresh) { + fbdev_render_ptr(ds); + } +} + +static void fbdev_mouse_set(DisplayState *ds, int x, int y, int on) +{ + ptr_refresh++; + mx = x; + my = y; + mon = on; +} + +static void fbdev_cursor_define(DisplayState *ds, 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); } static void fbdev_exit_notifier(Notifier *notifier, void *data) @@ -985,6 +1078,8 @@ int fbdev_display_init(DisplayState *ds, const char *device, Error **err) dcl->dpy_resize = fbdev_resize; dcl->dpy_setdata = fbdev_setdata; dcl->dpy_refresh = fbdev_refresh; + dcl->dpy_mouse_set = fbdev_mouse_set; + dcl->dpy_cursor_define = fbdev_cursor_define; register_displaychangelistener(ds, dcl); trace_fbdev_enabled(); -- 1.7.1 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann ` (7 preceding siblings ...) 2012-09-19 11:15 ` [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support Gerd Hoffmann @ 2012-09-19 11:15 ` Gerd Hoffmann 8 siblings, 0 replies; 24+ messages in thread From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann Add support for scaling the guest display. Ctrl-Alt-S hotkey toggles scaling. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- ui/fbdev.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 51 insertions(+), 10 deletions(-) diff --git a/ui/fbdev.c b/ui/fbdev.c index d859d84..0f40259 100644 --- a/ui/fbdev.c +++ b/ui/fbdev.c @@ -81,6 +81,7 @@ static pixman_image_t *surface; 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; @@ -88,6 +89,10 @@ static int ptr_refresh; static int px, py, pw, ph; static int mx, my, mon; +/* options */ +static int use_scale = 1; +static pixman_filter_t pfilter = PIXMAN_FILTER_GOOD; + /* fwd decls */ static int fbdev_activate_vt(int tty, int vtno, bool wait); @@ -182,13 +187,14 @@ static void read_mouse(void *opaque) if (ay < 0) { ay = 0; } - if (ax >= cw) { - ax = cw-1; + if (ax >= cw*scale) { + ax = cw*scale-1; } - if (ay >= ch) { - ay = ch-1; + if (ay >= ch*scale) { + ay = ch*scale-1; } - kbd_mouse_event(ax * 0x7FFF / cw, ay * 0x7FFF / ch, 0, b); + kbd_mouse_event(ax * 0x7FFF / (cw*scale), + ay * 0x7FFF / (ch*scale), 0, b); } else { kbd_mouse_event(x, y, 0, b); } @@ -543,6 +549,12 @@ static void read_mediumraw(void *opaque) "(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; @@ -913,6 +925,11 @@ static void fbdev_render_ptr(DisplayState *ds) 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)); @@ -938,16 +955,32 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h) } if (resize_screen) { + double xs, ys; + 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 (use_scale) { + xs = (double)fb_var.xres / cw; + ys = (double)fb_var.yres / ch; + if (xs > ys) { + scale = ys; + cx = (fb_var.xres - ds_get_width(ds)*scale) / 2; + } else { + scale = xs; + cy = (fb_var.yres - ds_get_height(ds)*scale) / 2; + } + } else { + scale = 1; + 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 (surface) { pixman_image_unref(surface); @@ -958,7 +991,14 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h) 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(surface, &transform); + + pixman_image_set_filter(surface, pfilter, NULL, 0); } if (redraw_screen) { @@ -1050,6 +1090,7 @@ static void fbdev_cursor_define(DisplayState *ds, QEMUCursor *cursor) cursor->width, cursor->height, cursor->data, cursor->width * 4); + pixman_image_set_filter(ptr_image, pfilter, NULL, 0); } static void fbdev_exit_notifier(Notifier *notifier, void *data) -- 1.7.1 ^ permalink raw reply related [flat|nested] 24+ messages in thread
end of thread, other threads:[~2012-09-24 13:09 UTC | newest] Thread overview: 24+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann 2012-09-19 18:09 ` Stefano Stabellini 2012-09-21 12:28 ` Gerd Hoffmann 2012-09-24 11:06 ` Stefano Stabellini 2012-09-24 13:09 ` Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann 2012-09-19 18:10 ` Stefano Stabellini 2012-09-20 6:16 ` Gerd Hoffmann 2012-09-20 11:33 ` Stefano Stabellini 2012-09-20 13:51 ` Gerd Hoffmann 2012-09-20 15:20 ` Stefano Stabellini 2012-09-20 15:27 ` Gerd Hoffmann 2012-09-20 15:28 ` Stefano Stabellini 2012-09-20 15:33 ` Stefano Stabellini 2012-09-21 5:40 ` Gerd Hoffmann 2012-09-21 10:48 ` Stefano Stabellini 2012-09-19 11:15 ` [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support Gerd Hoffmann 2012-09-19 11:15 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support 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).