* [Qemu-devel] [PULL 0/9] linux framebuffer display driver @ 2012-09-18 7:17 Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann ` (8 more replies) 0 siblings, 9 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann Hi, Third round of the framebuffer display driver patches, including git tree for pull as I think it's ready now. Changes: Addressed review comments from Markus. Catch a few more fatal signals, especially SIGABRT, so fbdev restores your console when qemu runs into an assert(). Misc little tweaks, no major changes. 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.1 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 | 14 + qemu-options.hx | 8 + qmp-commands.hx | 6 + qmp.c | 17 + sysemu.h | 1 + trace-events | 15 + ui/Makefile.objs | 1 + ui/fbdev.c | 1142 +++++++++++++++++++++++++++++++++++++++++++++++++++ ui/linux-keynames.h | 388 +++++++++++++++++ ui/sdl.c | 8 +- ui/spice-display.c | 4 +- ui/vnc.c | 8 +- vl.c | 50 ++- 24 files changed, 1789 insertions(+), 74 deletions(-) create mode 100644 ui/fbdev.c create mode 100644 ui/linux-keynames.h ^ permalink raw reply [flat|nested] 40+ messages in thread
* [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners. 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann @ 2012-09-18 7:17 ` Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann ` (7 subsequent siblings) 8 siblings, 0 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 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] 40+ messages in thread
* [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann @ 2012-09-18 7:17 ` Gerd Hoffmann 2012-09-18 10:54 ` Stefano Stabellini 2012-09-18 7:17 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann ` (6 subsequent siblings) 8 siblings, 1 reply; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 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] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener 2012-09-18 7:17 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann @ 2012-09-18 10:54 ` Stefano Stabellini 0 siblings, 0 replies; 40+ messages in thread From: Stefano Stabellini @ 2012-09-18 10:54 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org On Tue, 18 Sep 2012, Gerd Hoffmann wrote: > 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> Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.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 [flat|nested] 40+ messages in thread
* [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann @ 2012-09-18 7:17 ` Gerd Hoffmann 2012-09-18 14:10 ` Stefano Stabellini 2012-09-18 7:17 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann ` (5 subsequent siblings) 8 siblings, 1 reply; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 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] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks 2012-09-18 7:17 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann @ 2012-09-18 14:10 ` Stefano Stabellini 2012-09-18 16:31 ` Gerd Hoffmann 0 siblings, 1 reply; 40+ messages in thread From: Stefano Stabellini @ 2012-09-18 14:10 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org On Tue, 18 Sep 2012, Gerd Hoffmann wrote: > 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> This patch is good. The one thing I don't like is dpy_cursor_define_supported, because it enforces the idea that we cannot register/deregister DisplayChangeListeners at run time. Theoretically a new DisplayChangeListener that support cursor_define could show up at any time. Is dpy_cursor_define_supported really necessary? > 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 [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks 2012-09-18 14:10 ` Stefano Stabellini @ 2012-09-18 16:31 ` Gerd Hoffmann 2012-09-18 16:44 ` Stefano Stabellini 0 siblings, 1 reply; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 16:31 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org On 09/18/12 16:10, Stefano Stabellini wrote: > On Tue, 18 Sep 2012, Gerd Hoffmann wrote: >> 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> > > This patch is good. > The one thing I don't like is dpy_cursor_define_supported, because it > enforces the idea that we cannot register/deregister > DisplayChangeListeners at run time. Theoretically a new > DisplayChangeListener that support cursor_define could show up at any > time. Depends on how it is used. qxl is fine, it uses dpy_cursor_define_supported only to skip some work in case nobody cares anyway. vmware_vga uses it to signal the capability to the guest, which obviously doesn't fly with displaysurfaces coming & going ... cheers, Gerd ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks 2012-09-18 16:31 ` Gerd Hoffmann @ 2012-09-18 16:44 ` Stefano Stabellini 0 siblings, 0 replies; 40+ messages in thread From: Stefano Stabellini @ 2012-09-18 16:44 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org, Stefano Stabellini On Tue, 18 Sep 2012, Gerd Hoffmann wrote: > On 09/18/12 16:10, Stefano Stabellini wrote: > > On Tue, 18 Sep 2012, Gerd Hoffmann wrote: > >> 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> > > > > This patch is good. > > The one thing I don't like is dpy_cursor_define_supported, because it > > enforces the idea that we cannot register/deregister > > DisplayChangeListeners at run time. Theoretically a new > > DisplayChangeListener that support cursor_define could show up at any > > time. > > Depends on how it is used. > > qxl is fine, it uses dpy_cursor_define_supported only to skip some work > in case nobody cares anyway. > > vmware_vga uses it to signal the capability to the guest, which > obviously doesn't fly with displaysurfaces coming & going ... yeah.. that is what I am talking about. Maybe adding a comment on top the "offending code" in vmware_vga could suffice for now. ^ permalink raw reply [flat|nested] 40+ messages in thread
* [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver. 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann ` (2 preceding siblings ...) 2012-09-18 7:17 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann @ 2012-09-18 7:17 ` Gerd Hoffmann 2012-09-18 15:01 ` Stefano Stabellini 2012-09-19 18:37 ` Blue Swirl 2012-09-18 7:17 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann ` (4 subsequent siblings) 8 siblings, 2 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 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 | 974 +++++++++++++++++++++++++++++++++++++++++++++++++++ ui/linux-keynames.h | 388 ++++++++++++++++++++ vl.c | 12 + 8 files changed, 1403 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..0a3bae2 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); +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..40fc7d4 --- /dev/null +++ b/ui/fbdev.c @@ -0,0 +1,974 @@ +/* + * 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) +{ + struct vt_stat vts; + unsigned long page_mask; + char ttyname[32]; + + /* open framebuffer */ + if (device == NULL) { + device = getenv("FRAMEBUFFER"); + } + if (device == NULL) { + device = "/dev/fb0"; + } + fb = open(device, O_RDWR); + if (fb == -1) { + fprintf(stderr, "open %s: %s\n", device, strerror(errno)); + return -1; + } + + /* open virtual console */ + tty = 0; + if (ioctl(tty, VT_GETSTATE, &vts) < 0) { + fprintf(stderr, "Not started from virtual terminal, " + "trying to open one.\n"); + + snprintf(ttyname, sizeof(ttyname), "/dev/tty0"); + tty = open(ttyname, O_RDWR); + if (tty == -1) { + fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno)); + goto err_early; + } + if (ioctl(tty, VT_OPENQRY, &vtno) < 0) { + perror("ioctl VT_OPENQRY"); + goto err_early; + } + if (ioctl(tty, VT_GETSTATE, &vts) < 0) { + perror("ioctl VT_GETSTATE\n"); + goto err_early; + } + close(tty); + + snprintf(ttyname, sizeof(ttyname), "/dev/tty%d", vtno); + tty = open(ttyname, O_RDWR); + if (tty == -1) { + fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno)); + goto err_early; + } + orig_vtno = vts.v_active; + fprintf(stderr, "Switching to vt %d (current %d).\n", vtno, orig_vtno); + } else { + orig_vtno = 0; + vtno = vts.v_active; + fprintf(stderr, "Started at vt %d, using it.\n", vtno); + } + fbdev_activate_vt(tty, vtno, true); + + /* get current settings (which we have to restore) */ + if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) < 0) { + perror("ioctl FBIOGET_VSCREENINFO"); + goto err_early; + } + if (ioctl(tty, KDGETMODE, &kd_omode) < 0) { + perror("ioctl KDGETMODE"); + goto err_early; + } + if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) { + perror("ioctl VT_GETMODE"); + goto err_early; + } + + /* checks & initialisation */ + if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) < 0) { + perror("ioctl FBIOGET_FSCREENINFO"); + goto err; + } + if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) < 0) { + perror("ioctl FBIOGET_VSCREENINFO"); + goto err; + } + if (fb_fix.type != FB_TYPE_PACKED_PIXELS) { + fprintf(stderr, "can handle only packed pixel frame buffers\n"); + goto err; + } + switch (fb_var.bits_per_pixel) { + case 32: + break; + default: + fprintf(stderr, "can't handle %d bpp frame buffers\n", + fb_var.bits_per_pixel); + goto err; + } + + page_mask = getpagesize()-1; + fb_switch_state = FB_ACTIVE; + fb_mem_offset = (unsigned long)(fb_fix.smem_start) & page_mask; + fb_mem = mmap(NULL, fb_fix.smem_len+fb_mem_offset, + PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0); + if (fb_mem == MAP_FAILED) { + perror("mmap"); + goto err; + } + /* move viewport to upper left corner */ + if (fb_var.xoffset != 0 || fb_var.yoffset != 0) { + fb_var.xoffset = 0; + fb_var.yoffset = 0; + if (ioctl(fb, FBIOPAN_DISPLAY, &fb_var) < 0) { + perror("ioctl FBIOPAN_DISPLAY"); + goto err; + } + } + if (ioctl(tty, KDSETMODE, KD_GRAPHICS) < 0) { + perror("ioctl KDSETMODE"); + goto err; + } + /* some fb drivers need this again after switching to graphics ... */ + fbdev_activate_vt(tty, vtno, true); + + fbdev_cls(); + + start_mediumraw(tty); + qemu_set_fd_handler(tty, read_mediumraw, NULL, NULL); + + /* create PixelFormat from fbdev structs */ + fbpf.bits_per_pixel = fb_var.bits_per_pixel; + fbpf.bytes_per_pixel = (fb_var.bits_per_pixel+7)/8; + fbpf.depth = fb_var.bits_per_pixel == 32 + ? 24 : fb_var.bits_per_pixel; + fbpf.rshift = fb_var.red.offset; + fbpf.rbits = fb_var.red.length; + fbpf.gshift = fb_var.green.offset; + fbpf.gbits = fb_var.green.length; + fbpf.bshift = fb_var.blue.offset; + fbpf.bbits = fb_var.blue.length; + fbpf.ashift = fb_var.transp.offset; + fbpf.abits = fb_var.transp.length; + + if (fbpf.rbits) { + fbpf.rmax = (1 << fbpf.rbits) - 1; + fbpf.rmask = fbpf.rmax << fbpf.rshift; + } + if (fbpf.gbits) { + fbpf.gmax = (1 << fbpf.gbits) - 1; + fbpf.gmask = fbpf.gmax << fbpf.gshift; + } + if (fbpf.bbits) { + fbpf.bmax = (1 << fbpf.bbits) - 1; + fbpf.bmask = fbpf.bmax << fbpf.bshift; + } + if (fbpf.abits) { + fbpf.amax = (1 << fbpf.abits) - 1; + fbpf.amask = fbpf.amax << fbpf.ashift; + } + return 0; + +err_early: + if (tty > 0) { + close(tty); + } + close(fb); + return -1; + +err: + fbdev_cleanup(); + return -1; +} + +static void +fbdev_catch_fatal_signal(int 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_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) { + case FB_REL_REQ: + fbdev_switch_release(); + case FB_INACTIVE: + return; + case FB_ACQ_REQ: + fbdev_switch_acquire(); + redraw_screen++; + case FB_ACTIVE: + break; + } + + vga_hw_update(); + if (redraw_screen) { + fbdev_update(ds, 0, 0, 0, 0); + } +} + +static void fbdev_exit_notifier(Notifier *notifier, void *data) +{ + fbdev_cleanup(); +} + +int fbdev_display_init(DisplayState *ds, const char *device) +{ + if (dcl != NULL) { + return 0; + } + + if (fbdev_init(device) != 0) { + return -1; + } + exit_notifier.notify = fbdev_exit_notifier; + qemu_add_exit_notifier(&exit_notifier); + fbdev_switch_init(); + fbdev_catch_exit_signals(); + init_mouse(); + + fbdev_resize(ds); + dcl = g_new0(DisplayChangeListener, 1); + dcl->dpy_update = fbdev_update; + dcl->dpy_resize = fbdev_resize; + dcl->dpy_setdata = fbdev_setdata; + dcl->dpy_refresh = fbdev_refresh; + register_displaychangelistener(ds, dcl); + + trace_fbdev_enabled(); + return 0; +} + +void fbdev_display_uninit(DisplayState *ds) +{ + if (dcl == NULL) { + return; + } + + unregister_displaychangelistener(ds, dcl); + g_free(dcl); + dcl = NULL; + + fbdev_cleanup(); + qemu_remove_exit_notifier(&exit_notifier); + uninit_mouse(); +} diff --git a/ui/linux-keynames.h b/ui/linux-keynames.h new file mode 100644 index 0000000..058af28 --- /dev/null +++ b/ui/linux-keynames.h @@ -0,0 +1,388 @@ +/* + * awk '/#define KEY_/ { printf(" [%s] = \"%s\",\n",$2,$2); }' \ + * /usr/include/linux/input.h + */ + [KEY_RESERVED] = "KEY_RESERVED", + [KEY_ESC] = "KEY_ESC", + [KEY_1] = "KEY_1", + [KEY_2] = "KEY_2", + [KEY_3] = "KEY_3", + [KEY_4] = "KEY_4", + [KEY_5] = "KEY_5", + [KEY_6] = "KEY_6", + [KEY_7] = "KEY_7", + [KEY_8] = "KEY_8", + [KEY_9] = "KEY_9", + [KEY_0] = "KEY_0", + [KEY_MINUS] = "KEY_MINUS", + [KEY_EQUAL] = "KEY_EQUAL", + [KEY_BACKSPACE] = "KEY_BACKSPACE", + [KEY_TAB] = "KEY_TAB", + [KEY_Q] = "KEY_Q", + [KEY_W] = "KEY_W", + [KEY_E] = "KEY_E", + [KEY_R] = "KEY_R", + [KEY_T] = "KEY_T", + [KEY_Y] = "KEY_Y", + [KEY_U] = "KEY_U", + [KEY_I] = "KEY_I", + [KEY_O] = "KEY_O", + [KEY_P] = "KEY_P", + [KEY_LEFTBRACE] = "KEY_LEFTBRACE", + [KEY_RIGHTBRACE] = "KEY_RIGHTBRACE", + [KEY_ENTER] = "KEY_ENTER", + [KEY_LEFTCTRL] = "KEY_LEFTCTRL", + [KEY_A] = "KEY_A", + [KEY_S] = "KEY_S", + [KEY_D] = "KEY_D", + [KEY_F] = "KEY_F", + [KEY_G] = "KEY_G", + [KEY_H] = "KEY_H", + [KEY_J] = "KEY_J", + [KEY_K] = "KEY_K", + [KEY_L] = "KEY_L", + [KEY_SEMICOLON] = "KEY_SEMICOLON", + [KEY_APOSTROPHE] = "KEY_APOSTROPHE", + [KEY_GRAVE] = "KEY_GRAVE", + [KEY_LEFTSHIFT] = "KEY_LEFTSHIFT", + [KEY_BACKSLASH] = "KEY_BACKSLASH", + [KEY_Z] = "KEY_Z", + [KEY_X] = "KEY_X", + [KEY_C] = "KEY_C", + [KEY_V] = "KEY_V", + [KEY_B] = "KEY_B", + [KEY_N] = "KEY_N", + [KEY_M] = "KEY_M", + [KEY_COMMA] = "KEY_COMMA", + [KEY_DOT] = "KEY_DOT", + [KEY_SLASH] = "KEY_SLASH", + [KEY_RIGHTSHIFT] = "KEY_RIGHTSHIFT", + [KEY_KPASTERISK] = "KEY_KPASTERISK", + [KEY_LEFTALT] = "KEY_LEFTALT", + [KEY_SPACE] = "KEY_SPACE", + [KEY_CAPSLOCK] = "KEY_CAPSLOCK", + [KEY_F1] = "KEY_F1", + [KEY_F2] = "KEY_F2", + [KEY_F3] = "KEY_F3", + [KEY_F4] = "KEY_F4", + [KEY_F5] = "KEY_F5", + [KEY_F6] = "KEY_F6", + [KEY_F7] = "KEY_F7", + [KEY_F8] = "KEY_F8", + [KEY_F9] = "KEY_F9", + [KEY_F10] = "KEY_F10", + [KEY_NUMLOCK] = "KEY_NUMLOCK", + [KEY_SCROLLLOCK] = "KEY_SCROLLLOCK", + [KEY_KP7] = "KEY_KP7", + [KEY_KP8] = "KEY_KP8", + [KEY_KP9] = "KEY_KP9", + [KEY_KPMINUS] = "KEY_KPMINUS", + [KEY_KP4] = "KEY_KP4", + [KEY_KP5] = "KEY_KP5", + [KEY_KP6] = "KEY_KP6", + [KEY_KPPLUS] = "KEY_KPPLUS", + [KEY_KP1] = "KEY_KP1", + [KEY_KP2] = "KEY_KP2", + [KEY_KP3] = "KEY_KP3", + [KEY_KP0] = "KEY_KP0", + [KEY_KPDOT] = "KEY_KPDOT", + [KEY_ZENKAKUHANKAKU] = "KEY_ZENKAKUHANKAKU", + [KEY_102ND] = "KEY_102ND", + [KEY_F11] = "KEY_F11", + [KEY_F12] = "KEY_F12", + [KEY_RO] = "KEY_RO", + [KEY_KATAKANA] = "KEY_KATAKANA", + [KEY_HIRAGANA] = "KEY_HIRAGANA", + [KEY_HENKAN] = "KEY_HENKAN", + [KEY_KATAKANAHIRAGANA] = "KEY_KATAKANAHIRAGANA", + [KEY_MUHENKAN] = "KEY_MUHENKAN", + [KEY_KPJPCOMMA] = "KEY_KPJPCOMMA", + [KEY_KPENTER] = "KEY_KPENTER", + [KEY_RIGHTCTRL] = "KEY_RIGHTCTRL", + [KEY_KPSLASH] = "KEY_KPSLASH", + [KEY_SYSRQ] = "KEY_SYSRQ", + [KEY_RIGHTALT] = "KEY_RIGHTALT", + [KEY_LINEFEED] = "KEY_LINEFEED", + [KEY_HOME] = "KEY_HOME", + [KEY_UP] = "KEY_UP", + [KEY_PAGEUP] = "KEY_PAGEUP", + [KEY_LEFT] = "KEY_LEFT", + [KEY_RIGHT] = "KEY_RIGHT", + [KEY_END] = "KEY_END", + [KEY_DOWN] = "KEY_DOWN", + [KEY_PAGEDOWN] = "KEY_PAGEDOWN", + [KEY_INSERT] = "KEY_INSERT", + [KEY_DELETE] = "KEY_DELETE", + [KEY_MACRO] = "KEY_MACRO", + [KEY_MUTE] = "KEY_MUTE", + [KEY_VOLUMEDOWN] = "KEY_VOLUMEDOWN", + [KEY_VOLUMEUP] = "KEY_VOLUMEUP", + [KEY_POWER] = "KEY_POWER", + [KEY_KPEQUAL] = "KEY_KPEQUAL", + [KEY_KPPLUSMINUS] = "KEY_KPPLUSMINUS", + [KEY_PAUSE] = "KEY_PAUSE", + [KEY_SCALE] = "KEY_SCALE", + [KEY_KPCOMMA] = "KEY_KPCOMMA", + [KEY_HANGEUL] = "KEY_HANGEUL", + [KEY_HANGUEL] = "KEY_HANGUEL", + [KEY_HANJA] = "KEY_HANJA", + [KEY_YEN] = "KEY_YEN", + [KEY_LEFTMETA] = "KEY_LEFTMETA", + [KEY_RIGHTMETA] = "KEY_RIGHTMETA", + [KEY_COMPOSE] = "KEY_COMPOSE", + [KEY_STOP] = "KEY_STOP", + [KEY_AGAIN] = "KEY_AGAIN", + [KEY_PROPS] = "KEY_PROPS", + [KEY_UNDO] = "KEY_UNDO", + [KEY_FRONT] = "KEY_FRONT", + [KEY_COPY] = "KEY_COPY", + [KEY_OPEN] = "KEY_OPEN", + [KEY_PASTE] = "KEY_PASTE", + [KEY_FIND] = "KEY_FIND", + [KEY_CUT] = "KEY_CUT", + [KEY_HELP] = "KEY_HELP", + [KEY_MENU] = "KEY_MENU", + [KEY_CALC] = "KEY_CALC", + [KEY_SETUP] = "KEY_SETUP", + [KEY_SLEEP] = "KEY_SLEEP", + [KEY_WAKEUP] = "KEY_WAKEUP", + [KEY_FILE] = "KEY_FILE", + [KEY_SENDFILE] = "KEY_SENDFILE", + [KEY_DELETEFILE] = "KEY_DELETEFILE", + [KEY_XFER] = "KEY_XFER", + [KEY_PROG1] = "KEY_PROG1", + [KEY_PROG2] = "KEY_PROG2", + [KEY_WWW] = "KEY_WWW", + [KEY_MSDOS] = "KEY_MSDOS", + [KEY_COFFEE] = "KEY_COFFEE", + [KEY_SCREENLOCK] = "KEY_SCREENLOCK", + [KEY_DIRECTION] = "KEY_DIRECTION", + [KEY_CYCLEWINDOWS] = "KEY_CYCLEWINDOWS", + [KEY_MAIL] = "KEY_MAIL", + [KEY_BOOKMARKS] = "KEY_BOOKMARKS", + [KEY_COMPUTER] = "KEY_COMPUTER", + [KEY_BACK] = "KEY_BACK", + [KEY_FORWARD] = "KEY_FORWARD", + [KEY_CLOSECD] = "KEY_CLOSECD", + [KEY_EJECTCD] = "KEY_EJECTCD", + [KEY_EJECTCLOSECD] = "KEY_EJECTCLOSECD", + [KEY_NEXTSONG] = "KEY_NEXTSONG", + [KEY_PLAYPAUSE] = "KEY_PLAYPAUSE", + [KEY_PREVIOUSSONG] = "KEY_PREVIOUSSONG", + [KEY_STOPCD] = "KEY_STOPCD", + [KEY_RECORD] = "KEY_RECORD", + [KEY_REWIND] = "KEY_REWIND", + [KEY_PHONE] = "KEY_PHONE", + [KEY_ISO] = "KEY_ISO", + [KEY_CONFIG] = "KEY_CONFIG", + [KEY_HOMEPAGE] = "KEY_HOMEPAGE", + [KEY_REFRESH] = "KEY_REFRESH", + [KEY_EXIT] = "KEY_EXIT", + [KEY_MOVE] = "KEY_MOVE", + [KEY_EDIT] = "KEY_EDIT", + [KEY_SCROLLUP] = "KEY_SCROLLUP", + [KEY_SCROLLDOWN] = "KEY_SCROLLDOWN", + [KEY_KPLEFTPAREN] = "KEY_KPLEFTPAREN", + [KEY_KPRIGHTPAREN] = "KEY_KPRIGHTPAREN", + [KEY_NEW] = "KEY_NEW", + [KEY_REDO] = "KEY_REDO", + [KEY_F13] = "KEY_F13", + [KEY_F14] = "KEY_F14", + [KEY_F15] = "KEY_F15", + [KEY_F16] = "KEY_F16", + [KEY_F17] = "KEY_F17", + [KEY_F18] = "KEY_F18", + [KEY_F19] = "KEY_F19", + [KEY_F20] = "KEY_F20", + [KEY_F21] = "KEY_F21", + [KEY_F22] = "KEY_F22", + [KEY_F23] = "KEY_F23", + [KEY_F24] = "KEY_F24", + [KEY_PLAYCD] = "KEY_PLAYCD", + [KEY_PAUSECD] = "KEY_PAUSECD", + [KEY_PROG3] = "KEY_PROG3", + [KEY_PROG4] = "KEY_PROG4", + [KEY_DASHBOARD] = "KEY_DASHBOARD", + [KEY_SUSPEND] = "KEY_SUSPEND", + [KEY_CLOSE] = "KEY_CLOSE", + [KEY_PLAY] = "KEY_PLAY", + [KEY_FASTFORWARD] = "KEY_FASTFORWARD", + [KEY_BASSBOOST] = "KEY_BASSBOOST", + [KEY_PRINT] = "KEY_PRINT", + [KEY_HP] = "KEY_HP", + [KEY_CAMERA] = "KEY_CAMERA", + [KEY_SOUND] = "KEY_SOUND", + [KEY_QUESTION] = "KEY_QUESTION", + [KEY_EMAIL] = "KEY_EMAIL", + [KEY_CHAT] = "KEY_CHAT", + [KEY_SEARCH] = "KEY_SEARCH", + [KEY_CONNECT] = "KEY_CONNECT", + [KEY_FINANCE] = "KEY_FINANCE", + [KEY_SPORT] = "KEY_SPORT", + [KEY_SHOP] = "KEY_SHOP", + [KEY_ALTERASE] = "KEY_ALTERASE", + [KEY_CANCEL] = "KEY_CANCEL", + [KEY_BRIGHTNESSDOWN] = "KEY_BRIGHTNESSDOWN", + [KEY_BRIGHTNESSUP] = "KEY_BRIGHTNESSUP", + [KEY_MEDIA] = "KEY_MEDIA", + [KEY_SWITCHVIDEOMODE] = "KEY_SWITCHVIDEOMODE", + [KEY_KBDILLUMTOGGLE] = "KEY_KBDILLUMTOGGLE", + [KEY_KBDILLUMDOWN] = "KEY_KBDILLUMDOWN", + [KEY_KBDILLUMUP] = "KEY_KBDILLUMUP", + [KEY_SEND] = "KEY_SEND", + [KEY_REPLY] = "KEY_REPLY", + [KEY_FORWARDMAIL] = "KEY_FORWARDMAIL", + [KEY_SAVE] = "KEY_SAVE", + [KEY_DOCUMENTS] = "KEY_DOCUMENTS", + [KEY_BATTERY] = "KEY_BATTERY", + [KEY_BLUETOOTH] = "KEY_BLUETOOTH", + [KEY_WLAN] = "KEY_WLAN", + [KEY_UWB] = "KEY_UWB", + [KEY_UNKNOWN] = "KEY_UNKNOWN", + [KEY_VIDEO_NEXT] = "KEY_VIDEO_NEXT", + [KEY_VIDEO_PREV] = "KEY_VIDEO_PREV", + [KEY_BRIGHTNESS_CYCLE] = "KEY_BRIGHTNESS_CYCLE", + [KEY_BRIGHTNESS_ZERO] = "KEY_BRIGHTNESS_ZERO", + [KEY_DISPLAY_OFF] = "KEY_DISPLAY_OFF", + [KEY_WIMAX] = "KEY_WIMAX", + [KEY_OK] = "KEY_OK", + [KEY_SELECT] = "KEY_SELECT", + [KEY_GOTO] = "KEY_GOTO", + [KEY_CLEAR] = "KEY_CLEAR", + [KEY_POWER2] = "KEY_POWER2", + [KEY_OPTION] = "KEY_OPTION", + [KEY_INFO] = "KEY_INFO", + [KEY_TIME] = "KEY_TIME", + [KEY_VENDOR] = "KEY_VENDOR", + [KEY_ARCHIVE] = "KEY_ARCHIVE", + [KEY_PROGRAM] = "KEY_PROGRAM", + [KEY_CHANNEL] = "KEY_CHANNEL", + [KEY_FAVORITES] = "KEY_FAVORITES", + [KEY_EPG] = "KEY_EPG", + [KEY_PVR] = "KEY_PVR", + [KEY_MHP] = "KEY_MHP", + [KEY_LANGUAGE] = "KEY_LANGUAGE", + [KEY_TITLE] = "KEY_TITLE", + [KEY_SUBTITLE] = "KEY_SUBTITLE", + [KEY_ANGLE] = "KEY_ANGLE", + [KEY_ZOOM] = "KEY_ZOOM", + [KEY_MODE] = "KEY_MODE", + [KEY_KEYBOARD] = "KEY_KEYBOARD", + [KEY_SCREEN] = "KEY_SCREEN", + [KEY_PC] = "KEY_PC", + [KEY_TV] = "KEY_TV", + [KEY_TV2] = "KEY_TV2", + [KEY_VCR] = "KEY_VCR", + [KEY_VCR2] = "KEY_VCR2", + [KEY_SAT] = "KEY_SAT", + [KEY_SAT2] = "KEY_SAT2", + [KEY_CD] = "KEY_CD", + [KEY_TAPE] = "KEY_TAPE", + [KEY_RADIO] = "KEY_RADIO", + [KEY_TUNER] = "KEY_TUNER", + [KEY_PLAYER] = "KEY_PLAYER", + [KEY_TEXT] = "KEY_TEXT", + [KEY_DVD] = "KEY_DVD", + [KEY_AUX] = "KEY_AUX", + [KEY_MP3] = "KEY_MP3", + [KEY_AUDIO] = "KEY_AUDIO", + [KEY_VIDEO] = "KEY_VIDEO", + [KEY_DIRECTORY] = "KEY_DIRECTORY", + [KEY_LIST] = "KEY_LIST", + [KEY_MEMO] = "KEY_MEMO", + [KEY_CALENDAR] = "KEY_CALENDAR", + [KEY_RED] = "KEY_RED", + [KEY_GREEN] = "KEY_GREEN", + [KEY_YELLOW] = "KEY_YELLOW", + [KEY_BLUE] = "KEY_BLUE", + [KEY_CHANNELUP] = "KEY_CHANNELUP", + [KEY_CHANNELDOWN] = "KEY_CHANNELDOWN", + [KEY_FIRST] = "KEY_FIRST", + [KEY_LAST] = "KEY_LAST", + [KEY_AB] = "KEY_AB", + [KEY_NEXT] = "KEY_NEXT", + [KEY_RESTART] = "KEY_RESTART", + [KEY_SLOW] = "KEY_SLOW", + [KEY_SHUFFLE] = "KEY_SHUFFLE", + [KEY_BREAK] = "KEY_BREAK", + [KEY_PREVIOUS] = "KEY_PREVIOUS", + [KEY_DIGITS] = "KEY_DIGITS", + [KEY_TEEN] = "KEY_TEEN", + [KEY_TWEN] = "KEY_TWEN", + [KEY_VIDEOPHONE] = "KEY_VIDEOPHONE", + [KEY_GAMES] = "KEY_GAMES", + [KEY_ZOOMIN] = "KEY_ZOOMIN", + [KEY_ZOOMOUT] = "KEY_ZOOMOUT", + [KEY_ZOOMRESET] = "KEY_ZOOMRESET", + [KEY_WORDPROCESSOR] = "KEY_WORDPROCESSOR", + [KEY_EDITOR] = "KEY_EDITOR", + [KEY_SPREADSHEET] = "KEY_SPREADSHEET", + [KEY_GRAPHICSEDITOR] = "KEY_GRAPHICSEDITOR", + [KEY_PRESENTATION] = "KEY_PRESENTATION", + [KEY_DATABASE] = "KEY_DATABASE", + [KEY_NEWS] = "KEY_NEWS", + [KEY_VOICEMAIL] = "KEY_VOICEMAIL", + [KEY_ADDRESSBOOK] = "KEY_ADDRESSBOOK", + [KEY_MESSENGER] = "KEY_MESSENGER", + [KEY_DISPLAYTOGGLE] = "KEY_DISPLAYTOGGLE", + [KEY_SPELLCHECK] = "KEY_SPELLCHECK", + [KEY_LOGOFF] = "KEY_LOGOFF", + [KEY_DOLLAR] = "KEY_DOLLAR", + [KEY_EURO] = "KEY_EURO", + [KEY_FRAMEBACK] = "KEY_FRAMEBACK", + [KEY_FRAMEFORWARD] = "KEY_FRAMEFORWARD", + [KEY_CONTEXT_MENU] = "KEY_CONTEXT_MENU", + [KEY_MEDIA_REPEAT] = "KEY_MEDIA_REPEAT", + [KEY_10CHANNELSUP] = "KEY_10CHANNELSUP", + [KEY_10CHANNELSDOWN] = "KEY_10CHANNELSDOWN", + [KEY_DEL_EOL] = "KEY_DEL_EOL", + [KEY_DEL_EOS] = "KEY_DEL_EOS", + [KEY_INS_LINE] = "KEY_INS_LINE", + [KEY_DEL_LINE] = "KEY_DEL_LINE", + [KEY_FN] = "KEY_FN", + [KEY_FN_ESC] = "KEY_FN_ESC", + [KEY_FN_F1] = "KEY_FN_F1", + [KEY_FN_F2] = "KEY_FN_F2", + [KEY_FN_F3] = "KEY_FN_F3", + [KEY_FN_F4] = "KEY_FN_F4", + [KEY_FN_F5] = "KEY_FN_F5", + [KEY_FN_F6] = "KEY_FN_F6", + [KEY_FN_F7] = "KEY_FN_F7", + [KEY_FN_F8] = "KEY_FN_F8", + [KEY_FN_F9] = "KEY_FN_F9", + [KEY_FN_F10] = "KEY_FN_F10", + [KEY_FN_F11] = "KEY_FN_F11", + [KEY_FN_F12] = "KEY_FN_F12", + [KEY_FN_1] = "KEY_FN_1", + [KEY_FN_2] = "KEY_FN_2", + [KEY_FN_D] = "KEY_FN_D", + [KEY_FN_E] = "KEY_FN_E", + [KEY_FN_F] = "KEY_FN_F", + [KEY_FN_S] = "KEY_FN_S", + [KEY_FN_B] = "KEY_FN_B", + [KEY_BRL_DOT1] = "KEY_BRL_DOT1", + [KEY_BRL_DOT2] = "KEY_BRL_DOT2", + [KEY_BRL_DOT3] = "KEY_BRL_DOT3", + [KEY_BRL_DOT4] = "KEY_BRL_DOT4", + [KEY_BRL_DOT5] = "KEY_BRL_DOT5", + [KEY_BRL_DOT6] = "KEY_BRL_DOT6", + [KEY_BRL_DOT7] = "KEY_BRL_DOT7", + [KEY_BRL_DOT8] = "KEY_BRL_DOT8", + [KEY_BRL_DOT9] = "KEY_BRL_DOT9", + [KEY_BRL_DOT10] = "KEY_BRL_DOT10", + [KEY_NUMERIC_0] = "KEY_NUMERIC_0", + [KEY_NUMERIC_1] = "KEY_NUMERIC_1", + [KEY_NUMERIC_2] = "KEY_NUMERIC_2", + [KEY_NUMERIC_3] = "KEY_NUMERIC_3", + [KEY_NUMERIC_4] = "KEY_NUMERIC_4", + [KEY_NUMERIC_5] = "KEY_NUMERIC_5", + [KEY_NUMERIC_6] = "KEY_NUMERIC_6", + [KEY_NUMERIC_7] = "KEY_NUMERIC_7", + [KEY_NUMERIC_8] = "KEY_NUMERIC_8", + [KEY_NUMERIC_9] = "KEY_NUMERIC_9", + [KEY_NUMERIC_STAR] = "KEY_NUMERIC_STAR", + [KEY_NUMERIC_POUND] = "KEY_NUMERIC_POUND", + [KEY_RFKILL] = "KEY_RFKILL", + [KEY_MIN_INTERESTING] = "KEY_MIN_INTERESTING", + [KEY_MAX] = "KEY_MAX", + [KEY_CNT] = "KEY_CNT", diff --git a/vl.c b/vl.c index fbb77fe..18982b2 100644 --- a/vl.c +++ b/vl.c @@ -3041,6 +3041,11 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "SDL support is disabled\n"); exit(1); #endif +#ifdef CONFIG_LINUX + case QEMU_OPTION_fbdev: + display_type = DT_FBDEV; + break; +#endif case QEMU_OPTION_pidfile: pid_file = optarg; break; @@ -3681,6 +3686,13 @@ int main(int argc, char **argv, char **envp) curses_display_init(ds, full_screen); break; #endif +#if defined(CONFIG_LINUX) + case DT_FBDEV: + if (fbdev_display_init(ds, NULL) != 0) { + exit(1); + } + break; +#endif #if defined(CONFIG_SDL) case DT_SDL: sdl_display_init(ds, full_screen, no_frame); -- 1.7.1 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver. 2012-09-18 7:17 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann @ 2012-09-18 15:01 ` Stefano Stabellini 2012-09-19 5:19 ` Gerd Hoffmann 2012-09-19 18:37 ` Blue Swirl 1 sibling, 1 reply; 40+ messages in thread From: Stefano Stabellini @ 2012-09-18 15:01 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org On Tue, 18 Sep 2012, Gerd Hoffmann wrote: > Display works, requires truecolor framebuffer with 16 or 32 bpp on the > host. 32bpp is recommended. The framebuffer is used as-is, qemu > doesn't try to switch modes. With LCD displays mode switching is pretty > pointless IMHO, also it wouldn't work anyway with the most common > fbdev drivers (vesafb, KMS). Guest display is centered on the host > screen. > > Mouse works, uses /dev/input/mice. > > Keyboard works. Guest screen has whatever keymap you load inside > the guest. Text windows (monitor, serial, ...) have a simple en-us > keymap. Good 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> This series is not bisectable: this patch references functions and variables only defined in later patches (surface, pixman_from_displaystate). Please make it bisectable. It also makes it harder to review. At the very least the Makefile changes should be in the last patch. > console.h | 4 + > qemu-options.hx | 8 + > sysemu.h | 1 + > trace-events | 15 + > ui/Makefile.objs | 1 + > ui/fbdev.c | 974 +++++++++++++++++++++++++++++++++++++++++++++++++++ > ui/linux-keynames.h | 388 ++++++++++++++++++++ > vl.c | 12 + > 8 files changed, 1403 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..0a3bae2 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); > +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..40fc7d4 > --- /dev/null > +++ b/ui/fbdev.c > @@ -0,0 +1,974 @@ > +/* > + * 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) > +{ > + struct vt_stat vts; > + unsigned long page_mask; > + char ttyname[32]; > + > + /* open framebuffer */ > + if (device == NULL) { > + device = getenv("FRAMEBUFFER"); > + } > + if (device == NULL) { > + device = "/dev/fb0"; > + } > + fb = open(device, O_RDWR); > + if (fb == -1) { > + fprintf(stderr, "open %s: %s\n", device, strerror(errno)); > + return -1; > + } > + > + /* open virtual console */ > + tty = 0; > + if (ioctl(tty, VT_GETSTATE, &vts) < 0) { > + fprintf(stderr, "Not started from virtual terminal, " > + "trying to open one.\n"); > + > + snprintf(ttyname, sizeof(ttyname), "/dev/tty0"); > + tty = open(ttyname, O_RDWR); > + if (tty == -1) { > + fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno)); > + goto err_early; > + } > + if (ioctl(tty, VT_OPENQRY, &vtno) < 0) { > + perror("ioctl VT_OPENQRY"); > + goto err_early; > + } > + if (ioctl(tty, VT_GETSTATE, &vts) < 0) { > + perror("ioctl VT_GETSTATE\n"); > + goto err_early; > + } > + close(tty); > + > + snprintf(ttyname, sizeof(ttyname), "/dev/tty%d", vtno); > + tty = open(ttyname, O_RDWR); > + if (tty == -1) { > + fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno)); > + goto err_early; > + } > + orig_vtno = vts.v_active; > + fprintf(stderr, "Switching to vt %d (current %d).\n", vtno, orig_vtno); > + } else { > + orig_vtno = 0; > + vtno = vts.v_active; > + fprintf(stderr, "Started at vt %d, using it.\n", vtno); > + } > + fbdev_activate_vt(tty, vtno, true); > + > + /* get current settings (which we have to restore) */ > + if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) < 0) { > + perror("ioctl FBIOGET_VSCREENINFO"); > + goto err_early; > + } > + if (ioctl(tty, KDGETMODE, &kd_omode) < 0) { > + perror("ioctl KDGETMODE"); > + goto err_early; > + } > + if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) { > + perror("ioctl VT_GETMODE"); > + goto err_early; > + } > + > + /* checks & initialisation */ > + if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) < 0) { > + perror("ioctl FBIOGET_FSCREENINFO"); > + goto err; > + } > + if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) < 0) { > + perror("ioctl FBIOGET_VSCREENINFO"); > + goto err; > + } > + if (fb_fix.type != FB_TYPE_PACKED_PIXELS) { > + fprintf(stderr, "can handle only packed pixel frame buffers\n"); > + goto err; > + } > + switch (fb_var.bits_per_pixel) { > + case 32: > + break; > + default: > + fprintf(stderr, "can't handle %d bpp frame buffers\n", > + fb_var.bits_per_pixel); > + goto err; > + } > + > + page_mask = getpagesize()-1; > + fb_switch_state = FB_ACTIVE; > + fb_mem_offset = (unsigned long)(fb_fix.smem_start) & page_mask; > + fb_mem = mmap(NULL, fb_fix.smem_len+fb_mem_offset, > + PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0); > + if (fb_mem == MAP_FAILED) { > + perror("mmap"); > + goto err; > + } > + /* move viewport to upper left corner */ > + if (fb_var.xoffset != 0 || fb_var.yoffset != 0) { > + fb_var.xoffset = 0; > + fb_var.yoffset = 0; > + if (ioctl(fb, FBIOPAN_DISPLAY, &fb_var) < 0) { > + perror("ioctl FBIOPAN_DISPLAY"); > + goto err; > + } > + } > + if (ioctl(tty, KDSETMODE, KD_GRAPHICS) < 0) { > + perror("ioctl KDSETMODE"); > + goto err; > + } > + /* some fb drivers need this again after switching to graphics ... */ > + fbdev_activate_vt(tty, vtno, true); > + > + fbdev_cls(); > + > + start_mediumraw(tty); > + qemu_set_fd_handler(tty, read_mediumraw, NULL, NULL); > + > + /* create PixelFormat from fbdev structs */ > + fbpf.bits_per_pixel = fb_var.bits_per_pixel; > + fbpf.bytes_per_pixel = (fb_var.bits_per_pixel+7)/8; > + fbpf.depth = fb_var.bits_per_pixel == 32 > + ? 24 : fb_var.bits_per_pixel; > + fbpf.rshift = fb_var.red.offset; > + fbpf.rbits = fb_var.red.length; > + fbpf.gshift = fb_var.green.offset; > + fbpf.gbits = fb_var.green.length; > + fbpf.bshift = fb_var.blue.offset; > + fbpf.bbits = fb_var.blue.length; > + fbpf.ashift = fb_var.transp.offset; > + fbpf.abits = fb_var.transp.length; the conversion should probably be in an separate helper function > + 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_setdata(DisplayState *ds) > +{ > + if (surface) { ^ where is this coming from? > + pixman_image_unref(surface); > + } > + surface = pixman_from_displaystate(ds); > + redraw_screen++; > +} > + > +static void fbdev_refresh(DisplayState *ds) > +{ > + switch (fb_switch_state) { > + case FB_REL_REQ: > + fbdev_switch_release(); > + case FB_INACTIVE: > + return; > + case FB_ACQ_REQ: > + fbdev_switch_acquire(); > + redraw_screen++; > + case FB_ACTIVE: > + break; > + } > + > + vga_hw_update(); > + if (redraw_screen) { > + fbdev_update(ds, 0, 0, 0, 0); > + } > +} > + > +static void fbdev_exit_notifier(Notifier *notifier, void *data) > +{ > + fbdev_cleanup(); > +} > + > +int fbdev_display_init(DisplayState *ds, const char *device) > +{ > + if (dcl != NULL) { > + return 0; > + } is it actually possible that fbdev_display_init gets called multiple times? > + if (fbdev_init(device) != 0) { > + return -1; > + } > + exit_notifier.notify = fbdev_exit_notifier; > + qemu_add_exit_notifier(&exit_notifier); > + fbdev_switch_init(); > + fbdev_catch_exit_signals(); > + init_mouse(); > + > + fbdev_resize(ds); > + dcl = g_new0(DisplayChangeListener, 1); > + dcl->dpy_update = fbdev_update; > + dcl->dpy_resize = fbdev_resize; > + dcl->dpy_setdata = fbdev_setdata; > + dcl->dpy_refresh = fbdev_refresh; > + register_displaychangelistener(ds, dcl); > The fbdev driver could benefit from registering a DisplayAllocator (see sdl.c). > + 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(); > +} ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver. 2012-09-18 15:01 ` Stefano Stabellini @ 2012-09-19 5:19 ` Gerd Hoffmann 0 siblings, 0 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-19 5:19 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org On 09/18/12 17:01, Stefano Stabellini wrote: > On Tue, 18 Sep 2012, Gerd Hoffmann wrote: >> Display works, requires truecolor framebuffer with 16 or 32 bpp on the >> host. 32bpp is recommended. The framebuffer is used as-is, qemu >> doesn't try to switch modes. With LCD displays mode switching is pretty >> pointless IMHO, also it wouldn't work anyway with the most common >> fbdev drivers (vesafb, KMS). Guest display is centered on the host >> screen. >> >> Mouse works, uses /dev/input/mice. >> >> Keyboard works. Guest screen has whatever keymap you load inside >> the guest. Text windows (monitor, serial, ...) have a simple en-us >> keymap. Good 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> > > This series is not bisectable: this patch references functions and > variables only defined in later patches (surface, > pixman_from_displaystate). Please make it bisectable. Oops, that wasn't intentionally, squashed bugfix into the wrong patch, will fixup. >> +static void fbdev_setdata(DisplayState *ds) >> +{ >> + if (surface) { > ^ where is this coming from? > See above ;) >> +int fbdev_display_init(DisplayState *ds, const char *device) >> +{ >> + if (dcl != NULL) { >> + return 0; >> + } > > is it actually possible that fbdev_display_init gets called multiple > times? With the next patch (enable/disable fbdev via monitor) yes. >> + fbdev_resize(ds); >> + 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); > > The fbdev driver could benefit from registering a DisplayAllocator (see > sdl.c). I don't think so, at least not once the whole series is applied and support for scaling & cursor rendering is there. cheers, Gerd ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver. 2012-09-18 7:17 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann 2012-09-18 15:01 ` Stefano Stabellini @ 2012-09-19 18:37 ` Blue Swirl 1 sibling, 0 replies; 40+ messages in thread From: Blue Swirl @ 2012-09-19 18:37 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel On Tue, Sep 18, 2012 at 7:17 AM, Gerd Hoffmann <kraxel@redhat.com> wrote: > Display works, requires truecolor framebuffer with 16 or 32 bpp on the > host. 32bpp is recommended. The framebuffer is used as-is, qemu > doesn't try to switch modes. With LCD displays mode switching is pretty > pointless IMHO, also it wouldn't work anyway with the most common > fbdev drivers (vesafb, KMS). Guest display is centered on the host > screen. > > Mouse works, uses /dev/input/mice. > > Keyboard works. Guest screen has whatever keymap you load inside > the guest. Text windows (monitor, serial, ...) have a simple en-us > keymap. Good 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 | 974 +++++++++++++++++++++++++++++++++++++++++++++++++++ > ui/linux-keynames.h | 388 ++++++++++++++++++++ > vl.c | 12 + > 8 files changed, 1403 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..0a3bae2 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); > +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..40fc7d4 > --- /dev/null > +++ b/ui/fbdev.c > @@ -0,0 +1,974 @@ > +/* > + * 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) > +{ > + struct vt_stat vts; > + unsigned long page_mask; > + char ttyname[32]; > + > + /* open framebuffer */ > + if (device == NULL) { > + device = getenv("FRAMEBUFFER"); > + } > + if (device == NULL) { > + device = "/dev/fb0"; > + } > + fb = open(device, O_RDWR); > + if (fb == -1) { > + fprintf(stderr, "open %s: %s\n", device, strerror(errno)); > + return -1; > + } > + > + /* open virtual console */ > + tty = 0; > + if (ioctl(tty, VT_GETSTATE, &vts) < 0) { > + fprintf(stderr, "Not started from virtual terminal, " > + "trying to open one.\n"); > + > + snprintf(ttyname, sizeof(ttyname), "/dev/tty0"); > + tty = open(ttyname, O_RDWR); > + if (tty == -1) { > + fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno)); > + goto err_early; > + } > + if (ioctl(tty, VT_OPENQRY, &vtno) < 0) { > + perror("ioctl VT_OPENQRY"); > + goto err_early; > + } > + if (ioctl(tty, VT_GETSTATE, &vts) < 0) { > + perror("ioctl VT_GETSTATE\n"); > + goto err_early; > + } > + close(tty); > + > + snprintf(ttyname, sizeof(ttyname), "/dev/tty%d", vtno); > + tty = open(ttyname, O_RDWR); > + if (tty == -1) { > + fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno)); > + goto err_early; > + } > + orig_vtno = vts.v_active; > + fprintf(stderr, "Switching to vt %d (current %d).\n", vtno, orig_vtno); > + } else { > + orig_vtno = 0; > + vtno = vts.v_active; > + fprintf(stderr, "Started at vt %d, using it.\n", vtno); > + } > + fbdev_activate_vt(tty, vtno, true); > + > + /* get current settings (which we have to restore) */ > + if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) < 0) { > + perror("ioctl FBIOGET_VSCREENINFO"); > + goto err_early; > + } > + if (ioctl(tty, KDGETMODE, &kd_omode) < 0) { > + perror("ioctl KDGETMODE"); > + goto err_early; > + } > + if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) { > + perror("ioctl VT_GETMODE"); > + goto err_early; > + } > + > + /* checks & initialisation */ > + if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) < 0) { > + perror("ioctl FBIOGET_FSCREENINFO"); > + goto err; > + } > + if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) < 0) { > + perror("ioctl FBIOGET_VSCREENINFO"); > + goto err; > + } > + if (fb_fix.type != FB_TYPE_PACKED_PIXELS) { > + fprintf(stderr, "can handle only packed pixel frame buffers\n"); > + goto err; > + } > + switch (fb_var.bits_per_pixel) { > + case 32: > + break; > + default: > + fprintf(stderr, "can't handle %d bpp frame buffers\n", > + fb_var.bits_per_pixel); > + goto err; > + } > + > + page_mask = getpagesize()-1; > + fb_switch_state = FB_ACTIVE; > + fb_mem_offset = (unsigned long)(fb_fix.smem_start) & page_mask; > + fb_mem = mmap(NULL, fb_fix.smem_len+fb_mem_offset, > + PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0); > + if (fb_mem == MAP_FAILED) { > + perror("mmap"); > + goto err; > + } > + /* move viewport to upper left corner */ > + if (fb_var.xoffset != 0 || fb_var.yoffset != 0) { > + fb_var.xoffset = 0; > + fb_var.yoffset = 0; > + if (ioctl(fb, FBIOPAN_DISPLAY, &fb_var) < 0) { > + perror("ioctl FBIOPAN_DISPLAY"); > + goto err; > + } > + } > + if (ioctl(tty, KDSETMODE, KD_GRAPHICS) < 0) { > + perror("ioctl KDSETMODE"); > + goto err; > + } > + /* some fb drivers need this again after switching to graphics ... */ > + fbdev_activate_vt(tty, vtno, true); > + > + fbdev_cls(); > + > + start_mediumraw(tty); > + qemu_set_fd_handler(tty, read_mediumraw, NULL, NULL); > + > + /* create PixelFormat from fbdev structs */ > + fbpf.bits_per_pixel = fb_var.bits_per_pixel; > + fbpf.bytes_per_pixel = (fb_var.bits_per_pixel+7)/8; > + fbpf.depth = fb_var.bits_per_pixel == 32 > + ? 24 : fb_var.bits_per_pixel; > + fbpf.rshift = fb_var.red.offset; > + fbpf.rbits = fb_var.red.length; > + fbpf.gshift = fb_var.green.offset; > + fbpf.gbits = fb_var.green.length; > + fbpf.bshift = fb_var.blue.offset; > + fbpf.bbits = fb_var.blue.length; > + fbpf.ashift = fb_var.transp.offset; > + fbpf.abits = fb_var.transp.length; > + > + if (fbpf.rbits) { > + fbpf.rmax = (1 << fbpf.rbits) - 1; > + fbpf.rmask = fbpf.rmax << fbpf.rshift; > + } > + if (fbpf.gbits) { > + fbpf.gmax = (1 << fbpf.gbits) - 1; > + fbpf.gmask = fbpf.gmax << fbpf.gshift; > + } > + if (fbpf.bbits) { > + fbpf.bmax = (1 << fbpf.bbits) - 1; > + fbpf.bmask = fbpf.bmax << fbpf.bshift; > + } > + if (fbpf.abits) { > + fbpf.amax = (1 << fbpf.abits) - 1; > + fbpf.amask = fbpf.amax << fbpf.ashift; > + } > + return 0; > + > +err_early: > + if (tty > 0) { > + close(tty); > + } > + close(fb); > + return -1; > + > +err: > + fbdev_cleanup(); > + return -1; > +} > + > +static void > +fbdev_catch_fatal_signal(int 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_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) { > + case FB_REL_REQ: > + fbdev_switch_release(); Missing comment about fallthrough or 'break'. > + case FB_INACTIVE: > + return; > + case FB_ACQ_REQ: > + fbdev_switch_acquire(); > + redraw_screen++; Also here. > + case FB_ACTIVE: > + break; > + } > + > + vga_hw_update(); > + if (redraw_screen) { > + fbdev_update(ds, 0, 0, 0, 0); > + } > +} > + > +static void fbdev_exit_notifier(Notifier *notifier, void *data) > +{ > + fbdev_cleanup(); > +} > + > +int fbdev_display_init(DisplayState *ds, const char *device) > +{ > + if (dcl != NULL) { > + return 0; > + } > + > + if (fbdev_init(device) != 0) { > + return -1; > + } > + exit_notifier.notify = fbdev_exit_notifier; > + qemu_add_exit_notifier(&exit_notifier); > + fbdev_switch_init(); > + fbdev_catch_exit_signals(); > + init_mouse(); > + > + fbdev_resize(ds); > + dcl = g_new0(DisplayChangeListener, 1); > + dcl->dpy_update = fbdev_update; > + dcl->dpy_resize = fbdev_resize; > + dcl->dpy_setdata = fbdev_setdata; > + dcl->dpy_refresh = fbdev_refresh; > + register_displaychangelistener(ds, dcl); > + > + trace_fbdev_enabled(); > + return 0; > +} > + > +void fbdev_display_uninit(DisplayState *ds) > +{ > + if (dcl == NULL) { > + return; > + } > + > + unregister_displaychangelistener(ds, dcl); > + g_free(dcl); > + dcl = NULL; > + > + fbdev_cleanup(); > + qemu_remove_exit_notifier(&exit_notifier); > + uninit_mouse(); > +} > diff --git a/ui/linux-keynames.h b/ui/linux-keynames.h > new file mode 100644 > index 0000000..058af28 > --- /dev/null > +++ b/ui/linux-keynames.h > @@ -0,0 +1,388 @@ > +/* > + * awk '/#define KEY_/ { printf(" [%s] = \"%s\",\n",$2,$2); }' \ > + * /usr/include/linux/input.h > + */ > + [KEY_RESERVED] = "KEY_RESERVED", > + [KEY_ESC] = "KEY_ESC", > + [KEY_1] = "KEY_1", > + [KEY_2] = "KEY_2", > + [KEY_3] = "KEY_3", > + [KEY_4] = "KEY_4", > + [KEY_5] = "KEY_5", > + [KEY_6] = "KEY_6", > + [KEY_7] = "KEY_7", > + [KEY_8] = "KEY_8", > + [KEY_9] = "KEY_9", > + [KEY_0] = "KEY_0", > + [KEY_MINUS] = "KEY_MINUS", > + [KEY_EQUAL] = "KEY_EQUAL", > + [KEY_BACKSPACE] = "KEY_BACKSPACE", > + [KEY_TAB] = "KEY_TAB", > + [KEY_Q] = "KEY_Q", > + [KEY_W] = "KEY_W", > + [KEY_E] = "KEY_E", > + [KEY_R] = "KEY_R", > + [KEY_T] = "KEY_T", > + [KEY_Y] = "KEY_Y", > + [KEY_U] = "KEY_U", > + [KEY_I] = "KEY_I", > + [KEY_O] = "KEY_O", > + [KEY_P] = "KEY_P", > + [KEY_LEFTBRACE] = "KEY_LEFTBRACE", > + [KEY_RIGHTBRACE] = "KEY_RIGHTBRACE", > + [KEY_ENTER] = "KEY_ENTER", > + [KEY_LEFTCTRL] = "KEY_LEFTCTRL", > + [KEY_A] = "KEY_A", > + [KEY_S] = "KEY_S", > + [KEY_D] = "KEY_D", > + [KEY_F] = "KEY_F", > + [KEY_G] = "KEY_G", > + [KEY_H] = "KEY_H", > + [KEY_J] = "KEY_J", > + [KEY_K] = "KEY_K", > + [KEY_L] = "KEY_L", > + [KEY_SEMICOLON] = "KEY_SEMICOLON", > + [KEY_APOSTROPHE] = "KEY_APOSTROPHE", > + [KEY_GRAVE] = "KEY_GRAVE", > + [KEY_LEFTSHIFT] = "KEY_LEFTSHIFT", > + [KEY_BACKSLASH] = "KEY_BACKSLASH", > + [KEY_Z] = "KEY_Z", > + [KEY_X] = "KEY_X", > + [KEY_C] = "KEY_C", > + [KEY_V] = "KEY_V", > + [KEY_B] = "KEY_B", > + [KEY_N] = "KEY_N", > + [KEY_M] = "KEY_M", > + [KEY_COMMA] = "KEY_COMMA", > + [KEY_DOT] = "KEY_DOT", > + [KEY_SLASH] = "KEY_SLASH", > + [KEY_RIGHTSHIFT] = "KEY_RIGHTSHIFT", > + [KEY_KPASTERISK] = "KEY_KPASTERISK", > + [KEY_LEFTALT] = "KEY_LEFTALT", > + [KEY_SPACE] = "KEY_SPACE", > + [KEY_CAPSLOCK] = "KEY_CAPSLOCK", > + [KEY_F1] = "KEY_F1", > + [KEY_F2] = "KEY_F2", > + [KEY_F3] = "KEY_F3", > + [KEY_F4] = "KEY_F4", > + [KEY_F5] = "KEY_F5", > + [KEY_F6] = "KEY_F6", > + [KEY_F7] = "KEY_F7", > + [KEY_F8] = "KEY_F8", > + [KEY_F9] = "KEY_F9", > + [KEY_F10] = "KEY_F10", > + [KEY_NUMLOCK] = "KEY_NUMLOCK", > + [KEY_SCROLLLOCK] = "KEY_SCROLLLOCK", > + [KEY_KP7] = "KEY_KP7", > + [KEY_KP8] = "KEY_KP8", > + [KEY_KP9] = "KEY_KP9", > + [KEY_KPMINUS] = "KEY_KPMINUS", > + [KEY_KP4] = "KEY_KP4", > + [KEY_KP5] = "KEY_KP5", > + [KEY_KP6] = "KEY_KP6", > + [KEY_KPPLUS] = "KEY_KPPLUS", > + [KEY_KP1] = "KEY_KP1", > + [KEY_KP2] = "KEY_KP2", > + [KEY_KP3] = "KEY_KP3", > + [KEY_KP0] = "KEY_KP0", > + [KEY_KPDOT] = "KEY_KPDOT", > + [KEY_ZENKAKUHANKAKU] = "KEY_ZENKAKUHANKAKU", > + [KEY_102ND] = "KEY_102ND", > + [KEY_F11] = "KEY_F11", > + [KEY_F12] = "KEY_F12", > + [KEY_RO] = "KEY_RO", > + [KEY_KATAKANA] = "KEY_KATAKANA", > + [KEY_HIRAGANA] = "KEY_HIRAGANA", > + [KEY_HENKAN] = "KEY_HENKAN", > + [KEY_KATAKANAHIRAGANA] = "KEY_KATAKANAHIRAGANA", > + [KEY_MUHENKAN] = "KEY_MUHENKAN", > + [KEY_KPJPCOMMA] = "KEY_KPJPCOMMA", > + [KEY_KPENTER] = "KEY_KPENTER", > + [KEY_RIGHTCTRL] = "KEY_RIGHTCTRL", > + [KEY_KPSLASH] = "KEY_KPSLASH", > + [KEY_SYSRQ] = "KEY_SYSRQ", > + [KEY_RIGHTALT] = "KEY_RIGHTALT", > + [KEY_LINEFEED] = "KEY_LINEFEED", > + [KEY_HOME] = "KEY_HOME", > + [KEY_UP] = "KEY_UP", > + [KEY_PAGEUP] = "KEY_PAGEUP", > + [KEY_LEFT] = "KEY_LEFT", > + [KEY_RIGHT] = "KEY_RIGHT", > + [KEY_END] = "KEY_END", > + [KEY_DOWN] = "KEY_DOWN", > + [KEY_PAGEDOWN] = "KEY_PAGEDOWN", > + [KEY_INSERT] = "KEY_INSERT", > + [KEY_DELETE] = "KEY_DELETE", > + [KEY_MACRO] = "KEY_MACRO", > + [KEY_MUTE] = "KEY_MUTE", > + [KEY_VOLUMEDOWN] = "KEY_VOLUMEDOWN", > + [KEY_VOLUMEUP] = "KEY_VOLUMEUP", > + [KEY_POWER] = "KEY_POWER", > + [KEY_KPEQUAL] = "KEY_KPEQUAL", > + [KEY_KPPLUSMINUS] = "KEY_KPPLUSMINUS", > + [KEY_PAUSE] = "KEY_PAUSE", > + [KEY_SCALE] = "KEY_SCALE", > + [KEY_KPCOMMA] = "KEY_KPCOMMA", > + [KEY_HANGEUL] = "KEY_HANGEUL", > + [KEY_HANGUEL] = "KEY_HANGUEL", > + [KEY_HANJA] = "KEY_HANJA", > + [KEY_YEN] = "KEY_YEN", > + [KEY_LEFTMETA] = "KEY_LEFTMETA", > + [KEY_RIGHTMETA] = "KEY_RIGHTMETA", > + [KEY_COMPOSE] = "KEY_COMPOSE", > + [KEY_STOP] = "KEY_STOP", > + [KEY_AGAIN] = "KEY_AGAIN", > + [KEY_PROPS] = "KEY_PROPS", > + [KEY_UNDO] = "KEY_UNDO", > + [KEY_FRONT] = "KEY_FRONT", > + [KEY_COPY] = "KEY_COPY", > + [KEY_OPEN] = "KEY_OPEN", > + [KEY_PASTE] = "KEY_PASTE", > + [KEY_FIND] = "KEY_FIND", > + [KEY_CUT] = "KEY_CUT", > + [KEY_HELP] = "KEY_HELP", > + [KEY_MENU] = "KEY_MENU", > + [KEY_CALC] = "KEY_CALC", > + [KEY_SETUP] = "KEY_SETUP", > + [KEY_SLEEP] = "KEY_SLEEP", > + [KEY_WAKEUP] = "KEY_WAKEUP", > + [KEY_FILE] = "KEY_FILE", > + [KEY_SENDFILE] = "KEY_SENDFILE", > + [KEY_DELETEFILE] = "KEY_DELETEFILE", > + [KEY_XFER] = "KEY_XFER", > + [KEY_PROG1] = "KEY_PROG1", > + [KEY_PROG2] = "KEY_PROG2", > + [KEY_WWW] = "KEY_WWW", > + [KEY_MSDOS] = "KEY_MSDOS", > + [KEY_COFFEE] = "KEY_COFFEE", > + [KEY_SCREENLOCK] = "KEY_SCREENLOCK", > + [KEY_DIRECTION] = "KEY_DIRECTION", > + [KEY_CYCLEWINDOWS] = "KEY_CYCLEWINDOWS", > + [KEY_MAIL] = "KEY_MAIL", > + [KEY_BOOKMARKS] = "KEY_BOOKMARKS", > + [KEY_COMPUTER] = "KEY_COMPUTER", > + [KEY_BACK] = "KEY_BACK", > + [KEY_FORWARD] = "KEY_FORWARD", > + [KEY_CLOSECD] = "KEY_CLOSECD", > + [KEY_EJECTCD] = "KEY_EJECTCD", > + [KEY_EJECTCLOSECD] = "KEY_EJECTCLOSECD", > + [KEY_NEXTSONG] = "KEY_NEXTSONG", > + [KEY_PLAYPAUSE] = "KEY_PLAYPAUSE", > + [KEY_PREVIOUSSONG] = "KEY_PREVIOUSSONG", > + [KEY_STOPCD] = "KEY_STOPCD", > + [KEY_RECORD] = "KEY_RECORD", > + [KEY_REWIND] = "KEY_REWIND", > + [KEY_PHONE] = "KEY_PHONE", > + [KEY_ISO] = "KEY_ISO", > + [KEY_CONFIG] = "KEY_CONFIG", > + [KEY_HOMEPAGE] = "KEY_HOMEPAGE", > + [KEY_REFRESH] = "KEY_REFRESH", > + [KEY_EXIT] = "KEY_EXIT", > + [KEY_MOVE] = "KEY_MOVE", > + [KEY_EDIT] = "KEY_EDIT", > + [KEY_SCROLLUP] = "KEY_SCROLLUP", > + [KEY_SCROLLDOWN] = "KEY_SCROLLDOWN", > + [KEY_KPLEFTPAREN] = "KEY_KPLEFTPAREN", > + [KEY_KPRIGHTPAREN] = "KEY_KPRIGHTPAREN", > + [KEY_NEW] = "KEY_NEW", > + [KEY_REDO] = "KEY_REDO", > + [KEY_F13] = "KEY_F13", > + [KEY_F14] = "KEY_F14", > + [KEY_F15] = "KEY_F15", > + [KEY_F16] = "KEY_F16", > + [KEY_F17] = "KEY_F17", > + [KEY_F18] = "KEY_F18", > + [KEY_F19] = "KEY_F19", > + [KEY_F20] = "KEY_F20", > + [KEY_F21] = "KEY_F21", > + [KEY_F22] = "KEY_F22", > + [KEY_F23] = "KEY_F23", > + [KEY_F24] = "KEY_F24", > + [KEY_PLAYCD] = "KEY_PLAYCD", > + [KEY_PAUSECD] = "KEY_PAUSECD", > + [KEY_PROG3] = "KEY_PROG3", > + [KEY_PROG4] = "KEY_PROG4", > + [KEY_DASHBOARD] = "KEY_DASHBOARD", > + [KEY_SUSPEND] = "KEY_SUSPEND", > + [KEY_CLOSE] = "KEY_CLOSE", > + [KEY_PLAY] = "KEY_PLAY", > + [KEY_FASTFORWARD] = "KEY_FASTFORWARD", > + [KEY_BASSBOOST] = "KEY_BASSBOOST", > + [KEY_PRINT] = "KEY_PRINT", > + [KEY_HP] = "KEY_HP", > + [KEY_CAMERA] = "KEY_CAMERA", > + [KEY_SOUND] = "KEY_SOUND", > + [KEY_QUESTION] = "KEY_QUESTION", > + [KEY_EMAIL] = "KEY_EMAIL", > + [KEY_CHAT] = "KEY_CHAT", > + [KEY_SEARCH] = "KEY_SEARCH", > + [KEY_CONNECT] = "KEY_CONNECT", > + [KEY_FINANCE] = "KEY_FINANCE", > + [KEY_SPORT] = "KEY_SPORT", > + [KEY_SHOP] = "KEY_SHOP", > + [KEY_ALTERASE] = "KEY_ALTERASE", > + [KEY_CANCEL] = "KEY_CANCEL", > + [KEY_BRIGHTNESSDOWN] = "KEY_BRIGHTNESSDOWN", > + [KEY_BRIGHTNESSUP] = "KEY_BRIGHTNESSUP", > + [KEY_MEDIA] = "KEY_MEDIA", > + [KEY_SWITCHVIDEOMODE] = "KEY_SWITCHVIDEOMODE", > + [KEY_KBDILLUMTOGGLE] = "KEY_KBDILLUMTOGGLE", > + [KEY_KBDILLUMDOWN] = "KEY_KBDILLUMDOWN", > + [KEY_KBDILLUMUP] = "KEY_KBDILLUMUP", > + [KEY_SEND] = "KEY_SEND", > + [KEY_REPLY] = "KEY_REPLY", > + [KEY_FORWARDMAIL] = "KEY_FORWARDMAIL", > + [KEY_SAVE] = "KEY_SAVE", > + [KEY_DOCUMENTS] = "KEY_DOCUMENTS", > + [KEY_BATTERY] = "KEY_BATTERY", > + [KEY_BLUETOOTH] = "KEY_BLUETOOTH", > + [KEY_WLAN] = "KEY_WLAN", > + [KEY_UWB] = "KEY_UWB", > + [KEY_UNKNOWN] = "KEY_UNKNOWN", > + [KEY_VIDEO_NEXT] = "KEY_VIDEO_NEXT", > + [KEY_VIDEO_PREV] = "KEY_VIDEO_PREV", > + [KEY_BRIGHTNESS_CYCLE] = "KEY_BRIGHTNESS_CYCLE", > + [KEY_BRIGHTNESS_ZERO] = "KEY_BRIGHTNESS_ZERO", > + [KEY_DISPLAY_OFF] = "KEY_DISPLAY_OFF", > + [KEY_WIMAX] = "KEY_WIMAX", > + [KEY_OK] = "KEY_OK", > + [KEY_SELECT] = "KEY_SELECT", > + [KEY_GOTO] = "KEY_GOTO", > + [KEY_CLEAR] = "KEY_CLEAR", > + [KEY_POWER2] = "KEY_POWER2", > + [KEY_OPTION] = "KEY_OPTION", > + [KEY_INFO] = "KEY_INFO", > + [KEY_TIME] = "KEY_TIME", > + [KEY_VENDOR] = "KEY_VENDOR", > + [KEY_ARCHIVE] = "KEY_ARCHIVE", > + [KEY_PROGRAM] = "KEY_PROGRAM", > + [KEY_CHANNEL] = "KEY_CHANNEL", > + [KEY_FAVORITES] = "KEY_FAVORITES", > + [KEY_EPG] = "KEY_EPG", > + [KEY_PVR] = "KEY_PVR", > + [KEY_MHP] = "KEY_MHP", > + [KEY_LANGUAGE] = "KEY_LANGUAGE", > + [KEY_TITLE] = "KEY_TITLE", > + [KEY_SUBTITLE] = "KEY_SUBTITLE", > + [KEY_ANGLE] = "KEY_ANGLE", > + [KEY_ZOOM] = "KEY_ZOOM", > + [KEY_MODE] = "KEY_MODE", > + [KEY_KEYBOARD] = "KEY_KEYBOARD", > + [KEY_SCREEN] = "KEY_SCREEN", > + [KEY_PC] = "KEY_PC", > + [KEY_TV] = "KEY_TV", > + [KEY_TV2] = "KEY_TV2", > + [KEY_VCR] = "KEY_VCR", > + [KEY_VCR2] = "KEY_VCR2", > + [KEY_SAT] = "KEY_SAT", > + [KEY_SAT2] = "KEY_SAT2", > + [KEY_CD] = "KEY_CD", > + [KEY_TAPE] = "KEY_TAPE", > + [KEY_RADIO] = "KEY_RADIO", > + [KEY_TUNER] = "KEY_TUNER", > + [KEY_PLAYER] = "KEY_PLAYER", > + [KEY_TEXT] = "KEY_TEXT", > + [KEY_DVD] = "KEY_DVD", > + [KEY_AUX] = "KEY_AUX", > + [KEY_MP3] = "KEY_MP3", > + [KEY_AUDIO] = "KEY_AUDIO", > + [KEY_VIDEO] = "KEY_VIDEO", > + [KEY_DIRECTORY] = "KEY_DIRECTORY", > + [KEY_LIST] = "KEY_LIST", > + [KEY_MEMO] = "KEY_MEMO", > + [KEY_CALENDAR] = "KEY_CALENDAR", > + [KEY_RED] = "KEY_RED", > + [KEY_GREEN] = "KEY_GREEN", > + [KEY_YELLOW] = "KEY_YELLOW", > + [KEY_BLUE] = "KEY_BLUE", > + [KEY_CHANNELUP] = "KEY_CHANNELUP", > + [KEY_CHANNELDOWN] = "KEY_CHANNELDOWN", > + [KEY_FIRST] = "KEY_FIRST", > + [KEY_LAST] = "KEY_LAST", > + [KEY_AB] = "KEY_AB", > + [KEY_NEXT] = "KEY_NEXT", > + [KEY_RESTART] = "KEY_RESTART", > + [KEY_SLOW] = "KEY_SLOW", > + [KEY_SHUFFLE] = "KEY_SHUFFLE", > + [KEY_BREAK] = "KEY_BREAK", > + [KEY_PREVIOUS] = "KEY_PREVIOUS", > + [KEY_DIGITS] = "KEY_DIGITS", > + [KEY_TEEN] = "KEY_TEEN", > + [KEY_TWEN] = "KEY_TWEN", > + [KEY_VIDEOPHONE] = "KEY_VIDEOPHONE", > + [KEY_GAMES] = "KEY_GAMES", > + [KEY_ZOOMIN] = "KEY_ZOOMIN", > + [KEY_ZOOMOUT] = "KEY_ZOOMOUT", > + [KEY_ZOOMRESET] = "KEY_ZOOMRESET", > + [KEY_WORDPROCESSOR] = "KEY_WORDPROCESSOR", > + [KEY_EDITOR] = "KEY_EDITOR", > + [KEY_SPREADSHEET] = "KEY_SPREADSHEET", > + [KEY_GRAPHICSEDITOR] = "KEY_GRAPHICSEDITOR", > + [KEY_PRESENTATION] = "KEY_PRESENTATION", > + [KEY_DATABASE] = "KEY_DATABASE", > + [KEY_NEWS] = "KEY_NEWS", > + [KEY_VOICEMAIL] = "KEY_VOICEMAIL", > + [KEY_ADDRESSBOOK] = "KEY_ADDRESSBOOK", > + [KEY_MESSENGER] = "KEY_MESSENGER", > + [KEY_DISPLAYTOGGLE] = "KEY_DISPLAYTOGGLE", > + [KEY_SPELLCHECK] = "KEY_SPELLCHECK", > + [KEY_LOGOFF] = "KEY_LOGOFF", > + [KEY_DOLLAR] = "KEY_DOLLAR", > + [KEY_EURO] = "KEY_EURO", > + [KEY_FRAMEBACK] = "KEY_FRAMEBACK", > + [KEY_FRAMEFORWARD] = "KEY_FRAMEFORWARD", > + [KEY_CONTEXT_MENU] = "KEY_CONTEXT_MENU", > + [KEY_MEDIA_REPEAT] = "KEY_MEDIA_REPEAT", > + [KEY_10CHANNELSUP] = "KEY_10CHANNELSUP", > + [KEY_10CHANNELSDOWN] = "KEY_10CHANNELSDOWN", > + [KEY_DEL_EOL] = "KEY_DEL_EOL", > + [KEY_DEL_EOS] = "KEY_DEL_EOS", > + [KEY_INS_LINE] = "KEY_INS_LINE", > + [KEY_DEL_LINE] = "KEY_DEL_LINE", > + [KEY_FN] = "KEY_FN", > + [KEY_FN_ESC] = "KEY_FN_ESC", > + [KEY_FN_F1] = "KEY_FN_F1", > + [KEY_FN_F2] = "KEY_FN_F2", > + [KEY_FN_F3] = "KEY_FN_F3", > + [KEY_FN_F4] = "KEY_FN_F4", > + [KEY_FN_F5] = "KEY_FN_F5", > + [KEY_FN_F6] = "KEY_FN_F6", > + [KEY_FN_F7] = "KEY_FN_F7", > + [KEY_FN_F8] = "KEY_FN_F8", > + [KEY_FN_F9] = "KEY_FN_F9", > + [KEY_FN_F10] = "KEY_FN_F10", > + [KEY_FN_F11] = "KEY_FN_F11", > + [KEY_FN_F12] = "KEY_FN_F12", > + [KEY_FN_1] = "KEY_FN_1", > + [KEY_FN_2] = "KEY_FN_2", > + [KEY_FN_D] = "KEY_FN_D", > + [KEY_FN_E] = "KEY_FN_E", > + [KEY_FN_F] = "KEY_FN_F", > + [KEY_FN_S] = "KEY_FN_S", > + [KEY_FN_B] = "KEY_FN_B", > + [KEY_BRL_DOT1] = "KEY_BRL_DOT1", > + [KEY_BRL_DOT2] = "KEY_BRL_DOT2", > + [KEY_BRL_DOT3] = "KEY_BRL_DOT3", > + [KEY_BRL_DOT4] = "KEY_BRL_DOT4", > + [KEY_BRL_DOT5] = "KEY_BRL_DOT5", > + [KEY_BRL_DOT6] = "KEY_BRL_DOT6", > + [KEY_BRL_DOT7] = "KEY_BRL_DOT7", > + [KEY_BRL_DOT8] = "KEY_BRL_DOT8", > + [KEY_BRL_DOT9] = "KEY_BRL_DOT9", > + [KEY_BRL_DOT10] = "KEY_BRL_DOT10", > + [KEY_NUMERIC_0] = "KEY_NUMERIC_0", > + [KEY_NUMERIC_1] = "KEY_NUMERIC_1", > + [KEY_NUMERIC_2] = "KEY_NUMERIC_2", > + [KEY_NUMERIC_3] = "KEY_NUMERIC_3", > + [KEY_NUMERIC_4] = "KEY_NUMERIC_4", > + [KEY_NUMERIC_5] = "KEY_NUMERIC_5", > + [KEY_NUMERIC_6] = "KEY_NUMERIC_6", > + [KEY_NUMERIC_7] = "KEY_NUMERIC_7", > + [KEY_NUMERIC_8] = "KEY_NUMERIC_8", > + [KEY_NUMERIC_9] = "KEY_NUMERIC_9", > + [KEY_NUMERIC_STAR] = "KEY_NUMERIC_STAR", > + [KEY_NUMERIC_POUND] = "KEY_NUMERIC_POUND", > + [KEY_RFKILL] = "KEY_RFKILL", > + [KEY_MIN_INTERESTING] = "KEY_MIN_INTERESTING", > + [KEY_MAX] = "KEY_MAX", > + [KEY_CNT] = "KEY_CNT", > diff --git a/vl.c b/vl.c > index fbb77fe..18982b2 100644 > --- a/vl.c > +++ b/vl.c > @@ -3041,6 +3041,11 @@ int main(int argc, char **argv, char **envp) > fprintf(stderr, "SDL support is disabled\n"); > exit(1); > #endif > +#ifdef CONFIG_LINUX > + case QEMU_OPTION_fbdev: > + display_type = DT_FBDEV; > + break; > +#endif > case QEMU_OPTION_pidfile: > pid_file = optarg; > break; > @@ -3681,6 +3686,13 @@ int main(int argc, char **argv, char **envp) > curses_display_init(ds, full_screen); > break; > #endif > +#if defined(CONFIG_LINUX) > + case DT_FBDEV: > + if (fbdev_display_init(ds, NULL) != 0) { > + exit(1); > + } > + break; > +#endif > #if defined(CONFIG_SDL) > case DT_SDL: > sdl_display_init(ds, full_screen, no_frame); > -- > 1.7.1 > > ^ permalink raw reply [flat|nested] 40+ messages in thread
* [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann ` (3 preceding siblings ...) 2012-09-18 7:17 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann @ 2012-09-18 7:17 ` Gerd Hoffmann 2012-09-18 12:47 ` Luiz Capitulino 2012-09-18 7:17 ` [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time Gerd Hoffmann ` (3 subsequent siblings) 8 siblings, 1 reply; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann This patch adds a fbdev monitor command to enable/disable the fbdev display at runtime to both qmp and hmp. qmp: fbdev enable=on|off hmp: fbdev on|off Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- hmp-commands.hx | 15 +++++++++++++++ hmp.c | 9 +++++++++ hmp.h | 1 + qapi-schema.json | 14 ++++++++++++++ qmp-commands.hx | 6 ++++++ qmp.c | 17 +++++++++++++++++ 6 files changed, 62 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index ed67e99..366a92b 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1377,6 +1377,21 @@ passed since 1970, i.e. unix epoch. ETEXI { + .name = "fbdev", + .args_type = "enable:b", + .params = "on|off", + .help = "enable/disable fbdev", + .mhandler.cmd = hmp_fbdev, + }, + +STEXI +@item fbdev on | off +@findex fbdev + +enable/disable fbdev +ETEXI + + { .name = "info", .args_type = "item:s?", .params = "[subcommand]", diff --git a/hmp.c b/hmp.c index ba6fbd3..a7feec5 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_fbdev(Monitor *mon, const QDict *qdict) +{ + int enable = qdict_get_bool(qdict, "enable"); + Error *errp = NULL; + + qmp_fbdev(enable, &errp); + hmp_handle_error(mon, &errp); +} diff --git a/hmp.h b/hmp.h index 48b9c59..9c3d315 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_fbdev(Monitor *mon, const QDict *qdict); #endif diff --git a/qapi-schema.json b/qapi-schema.json index 14e4419..901c2e8 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2619,3 +2619,17 @@ # Since: 0.14.0 ## { 'command': 'screendump', 'data': {'filename': 'str'} } + +# @fbdev: +# +# Enable/disable fbdev. +# +# @enable: whenever fbdev should be enabled or disabled. +# +# Returns: Nothing on success +# GenericError on failure. +# +# Since: 1.3 +# +## +{ 'command': 'fbdev', 'data': {'enable': 'bool'} } diff --git a/qmp-commands.hx b/qmp-commands.hx index 6e21ddb..4b95fd0 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 = "fbdev", + .args_type = "enable:b", + .mhandler.cmd_new = qmp_marshal_input_fbdev, + }, diff --git a/qmp.c b/qmp.c index 8463922..7f6cc0b 100644 --- a/qmp.c +++ b/qmp.c @@ -391,6 +391,23 @@ void qmp_change(const char *device, const char *target, } } +void qmp_fbdev(bool enable, Error **errp) +{ +#if defined(CONFIG_LINUX) + DisplayState *ds = get_displaystate(); + + if (enable) { + if (fbdev_display_init(ds, NULL) != 0) { + error_setg(errp, "fbdev initialization failed"); + } + } else { + fbdev_display_uninit(ds); + } +#else + error_set(errp, QERR_FEATURE_DISABLED, "fbdev"); +#endif +} + static void qom_list_types_tramp(ObjectClass *klass, void *data) { ObjectTypeInfoList *e, **pret = data; -- 1.7.1 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable 2012-09-18 7:17 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann @ 2012-09-18 12:47 ` Luiz Capitulino 0 siblings, 0 replies; 40+ messages in thread From: Luiz Capitulino @ 2012-09-18 12:47 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel On Tue, 18 Sep 2012 09:17:10 +0200 Gerd Hoffmann <kraxel@redhat.com> wrote: > This patch adds a fbdev monitor command to enable/disable > the fbdev display at runtime to both qmp and hmp. > > qmp: fbdev enable=on|off > hmp: fbdev on|off > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > hmp-commands.hx | 15 +++++++++++++++ > hmp.c | 9 +++++++++ > hmp.h | 1 + > qapi-schema.json | 14 ++++++++++++++ > qmp-commands.hx | 6 ++++++ > qmp.c | 17 +++++++++++++++++ > 6 files changed, 62 insertions(+), 0 deletions(-) > > diff --git a/hmp-commands.hx b/hmp-commands.hx > index ed67e99..366a92b 100644 > --- a/hmp-commands.hx > +++ b/hmp-commands.hx > @@ -1377,6 +1377,21 @@ passed since 1970, i.e. unix epoch. > ETEXI > > { > + .name = "fbdev", > + .args_type = "enable:b", > + .params = "on|off", > + .help = "enable/disable fbdev", > + .mhandler.cmd = hmp_fbdev, > + }, > + > +STEXI > +@item fbdev on | off > +@findex fbdev > + > +enable/disable fbdev > +ETEXI > + > + { > .name = "info", > .args_type = "item:s?", > .params = "[subcommand]", > diff --git a/hmp.c b/hmp.c > index ba6fbd3..a7feec5 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_fbdev(Monitor *mon, const QDict *qdict) > +{ > + int enable = qdict_get_bool(qdict, "enable"); > + Error *errp = NULL; > + > + qmp_fbdev(enable, &errp); > + hmp_handle_error(mon, &errp); > +} > diff --git a/hmp.h b/hmp.h > index 48b9c59..9c3d315 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_fbdev(Monitor *mon, const QDict *qdict); > > #endif > diff --git a/qapi-schema.json b/qapi-schema.json > index 14e4419..901c2e8 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -2619,3 +2619,17 @@ > # Since: 0.14.0 > ## > { 'command': 'screendump', 'data': {'filename': 'str'} } > + > +# @fbdev: Please, use more descriptive names for qmp. Maybe something like frame-buffer-device-add/-enable. > +# > +# Enable/disable fbdev. > +# > +# @enable: whenever fbdev should be enabled or disabled. > +# > +# Returns: Nothing on success > +# GenericError on failure. It's not needed to list GenericError as an error. > +# > +# Since: 1.3 > +# > +## > +{ 'command': 'fbdev', 'data': {'enable': 'bool'} } > diff --git a/qmp-commands.hx b/qmp-commands.hx > index 6e21ddb..4b95fd0 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 = "fbdev", > + .args_type = "enable:b", > + .mhandler.cmd_new = qmp_marshal_input_fbdev, > + }, > diff --git a/qmp.c b/qmp.c > index 8463922..7f6cc0b 100644 > --- a/qmp.c > +++ b/qmp.c > @@ -391,6 +391,23 @@ void qmp_change(const char *device, const char *target, > } > } > > +void qmp_fbdev(bool enable, Error **errp) > +{ > +#if defined(CONFIG_LINUX) > + DisplayState *ds = get_displaystate(); > + > + if (enable) { > + if (fbdev_display_init(ds, NULL) != 0) { > + error_setg(errp, "fbdev initialization failed"); Would be nice to tell the reason if you have it (error_setg() has printf()-likeformat). > + } > + } else { > + fbdev_display_uninit(ds); > + } > +#else > + error_set(errp, QERR_FEATURE_DISABLED, "fbdev"); We shouldn't use QERR_ macros in new code. You have two options: 1. use error_setg() 2. add error_set_disabled() in error.h, similar to error_setg(), and use it > +#endif > +} > + > static void qom_list_types_tramp(ObjectClass *klass, void *data) > { > ObjectTypeInfoList *e, **pret = data; ^ permalink raw reply [flat|nested] 40+ messages in thread
* [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time. 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann ` (4 preceding siblings ...) 2012-09-18 7:17 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann @ 2012-09-18 7:17 ` Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann ` (2 subsequent siblings) 8 siblings, 0 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 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 7f6cc0b..060d804 100644 --- a/qmp.c +++ b/qmp.c @@ -393,7 +393,7 @@ void qmp_change(const char *device, const char *target, void qmp_fbdev(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 18982b2..d39352c 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: if (fbdev_display_init(ds, NULL) != 0) { exit(1); -- 1.7.1 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann ` (5 preceding siblings ...) 2012-09-18 7:17 ` [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time Gerd Hoffmann @ 2012-09-18 7:17 ` Gerd Hoffmann 2012-09-18 15:01 ` Stefano Stabellini ` (2 more replies) 2012-09-18 7:17 ` [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support Gerd Hoffmann 8 siblings, 3 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 UTC (permalink / raw) To: qemu-devel; +Cc: Gerd Hoffmann Stop reinventing the wheel. Use the pixman library for raster ops. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- configure | 12 ++++ ui/fbdev.c | 172 +++++++++++++++++++++++++++++++++++------------------------ 2 files changed, 114 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 40fc7d4..4cb4d1d 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); @@ -681,36 +756,8 @@ static int fbdev_init(const char *device) 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: @@ -818,36 +865,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; - - 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; + assert(surface); - 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); } /* -------------------------------------------------------------------- */ @@ -871,14 +897,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) { @@ -888,7 +916,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) @@ -924,6 +952,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) -- 1.7.1 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-18 7:17 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann @ 2012-09-18 15:01 ` Stefano Stabellini 2012-09-19 5:51 ` Gerd Hoffmann 2012-09-18 19:14 ` Anthony Liguori 2012-09-18 20:30 ` Søren Sandmann 2 siblings, 1 reply; 40+ messages in thread From: Stefano Stabellini @ 2012-09-18 15:01 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org On Tue, 18 Sep 2012, Gerd Hoffmann wrote: > Stop reinventing the wheel. Use the pixman library for raster ops. I would separate the pixmap changes from this series: either we use this library everywhere or nowhere. At the very least vnc could use it. Also considering that you only support 32bpp in fbdev, I am not sure whether it actually is that useful in this case: you don't really need to handle any pixel format conversions, at most you need to handle differences in linesizes. > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > configure | 12 ++++ > ui/fbdev.c | 172 +++++++++++++++++++++++++++++++++++------------------------ > 2 files changed, 114 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 40fc7d4..4cb4d1d 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); > @@ -681,36 +756,8 @@ static int fbdev_init(const char *device) > 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: > @@ -818,36 +865,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; > - > - 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; > + assert(surface); > > - 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); > } > > /* -------------------------------------------------------------------- */ > @@ -871,14 +897,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) { > @@ -888,7 +916,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) > @@ -924,6 +952,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) > -- > 1.7.1 > > ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-18 15:01 ` Stefano Stabellini @ 2012-09-19 5:51 ` Gerd Hoffmann 0 siblings, 0 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-19 5:51 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org On 09/18/12 17:01, Stefano Stabellini wrote: > On Tue, 18 Sep 2012, Gerd Hoffmann wrote: >> Stop reinventing the wheel. Use the pixman library for raster ops. > > I would separate the pixmap changes from this series: either we use > this library everywhere or nowhere. At the very least vnc could use it. The secret long term plan is to use pixman everywhere, but that is certainly way to much for a single series anyway. I have patches in the queue to switch over spice to pixman (and remove pflib.[ch]). vnc and sdl are obviously candidates. Possibly device emulation too: vga textmode font rendering, cirrus 2d ops. But that is surely for another discussion as this would make pixman a hard dependency for qemu. > Also considering that you only support 32bpp in fbdev, I am not sure > whether it actually is that useful in this case: you don't really need > to handle any pixel format conversions, at most you need to handle > differences in linesizes. DisplaySurfaces can be 16bpp too. The limitation to 32bpp can go away, that is just a leftover from earlier revisions, 15+16 bpp should just work too. Also with the full series applied we let pixman do alot more than just pixel copying: scaling, dirty area tracking, alpha bleeding. cheers, Gerd ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-18 7:17 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann 2012-09-18 15:01 ` Stefano Stabellini @ 2012-09-18 19:14 ` Anthony Liguori 2012-09-18 21:08 ` Anthony Liguori 2012-11-26 18:42 ` Alexander Graf 2012-09-18 20:30 ` Søren Sandmann 2 siblings, 2 replies; 40+ messages in thread From: Anthony Liguori @ 2012-09-18 19:14 UTC (permalink / raw) To: Gerd Hoffmann, qemu-devel Gerd Hoffmann <kraxel@redhat.com> writes: > Stop reinventing the wheel. Use the pixman library for raster ops. > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Why not cairo? I already have a cairo backend that uses GTK that supports scaling. That would be a good opportunity for sharing even more code. Regards, Anthony Liguori > --- > configure | 12 ++++ > ui/fbdev.c | 172 +++++++++++++++++++++++++++++++++++------------------------ > 2 files changed, 114 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 40fc7d4..4cb4d1d 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); > @@ -681,36 +756,8 @@ static int fbdev_init(const char *device) > 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: > @@ -818,36 +865,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; > - > - 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; > + assert(surface); > > - 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); > } > > /* -------------------------------------------------------------------- */ > @@ -871,14 +897,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) { > @@ -888,7 +916,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) > @@ -924,6 +952,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) > -- > 1.7.1 ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-18 19:14 ` Anthony Liguori @ 2012-09-18 21:08 ` Anthony Liguori 2012-11-26 18:42 ` Alexander Graf 1 sibling, 0 replies; 40+ messages in thread From: Anthony Liguori @ 2012-09-18 21:08 UTC (permalink / raw) To: Gerd Hoffmann, qemu-devel Anthony Liguori <anthony@codemonkey.ws> writes: > Gerd Hoffmann <kraxel@redhat.com> writes: > >> Stop reinventing the wheel. Use the pixman library for raster ops. >> >> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > > Why not cairo? I already have a cairo backend that uses GTK that > supports scaling. That would be a good opportunity for sharing even > more code. Nevermind, I see that cairo uses pixman under the covers... Regards, Anthony Liguori > > Regards, > > Anthony Liguori > >> --- >> configure | 12 ++++ >> ui/fbdev.c | 172 +++++++++++++++++++++++++++++++++++------------------------ >> 2 files changed, 114 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 40fc7d4..4cb4d1d 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); >> @@ -681,36 +756,8 @@ static int fbdev_init(const char *device) >> 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: >> @@ -818,36 +865,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; >> - >> - 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; >> + assert(surface); >> >> - 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); >> } >> >> /* -------------------------------------------------------------------- */ >> @@ -871,14 +897,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) { >> @@ -888,7 +916,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) >> @@ -924,6 +952,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) >> -- >> 1.7.1 ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-18 19:14 ` Anthony Liguori 2012-09-18 21:08 ` Anthony Liguori @ 2012-11-26 18:42 ` Alexander Graf 2012-11-26 20:01 ` Gerd Hoffmann 1 sibling, 1 reply; 40+ messages in thread From: Alexander Graf @ 2012-11-26 18:42 UTC (permalink / raw) To: Anthony Liguori; +Cc: Gerd Hoffmann, qemu-devel Replying here because my INBOX doesn't contain the original message. On 18.09.2012, at 21:14, Anthony Liguori wrote: > Gerd Hoffmann <kraxel@redhat.com> writes: > >> Stop reinventing the wheel. Use the pixman library for raster ops. >> >> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > > Why not cairo? I already have a cairo backend that uses GTK that > supports scaling. That would be a good opportunity for sharing even > more code. > > Regards, > > Anthony Liguori > >> --- >> configure | 12 ++++ >> ui/fbdev.c | 172 +++++++++++++++++++++++++++++++++++------------------------ >> 2 files changed, 114 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 40fc7d4..4cb4d1d 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; This breaks for me: qemu-pixman.c: In function ‘qemu_pixman_get_type’: qemu-pixman.c:24: error: ‘PIXMAN_TYPE_BGRA’ undeclared (first use in this function) qemu-pixman.c:24: error: (Each undeclared identifier is reported only once qemu-pixman.c:24: error: for each function it appears in.) Alex ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-11-26 18:42 ` Alexander Graf @ 2012-11-26 20:01 ` Gerd Hoffmann 2012-11-26 20:05 ` Alexander Graf 0 siblings, 1 reply; 40+ messages in thread From: Gerd Hoffmann @ 2012-11-26 20:01 UTC (permalink / raw) To: Alexander Graf; +Cc: qemu-devel, Anthony Liguori On 11/26/12 19:42, Alexander Graf wrote: > Replying here because my INBOX doesn't contain the original message. > >>> +#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; > > This breaks for me: > > qemu-pixman.c: In function ‘qemu_pixman_get_type’: > qemu-pixman.c:24: error: ‘PIXMAN_TYPE_BGRA’ undeclared (first use in this function) > qemu-pixman.c:24: error: (Each undeclared identifier is reported only once > qemu-pixman.c:24: error: for each function it appears in.) Which pixman version is this? cheers, Gerd ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-11-26 20:01 ` Gerd Hoffmann @ 2012-11-26 20:05 ` Alexander Graf 0 siblings, 0 replies; 40+ messages in thread From: Alexander Graf @ 2012-11-26 20:05 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel, Anthony Liguori On 26.11.2012, at 21:01, Gerd Hoffmann wrote: > On 11/26/12 19:42, Alexander Graf wrote: >> Replying here because my INBOX doesn't contain the original message. >> >>>> +#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; >> >> This breaks for me: >> >> qemu-pixman.c: In function ‘qemu_pixman_get_type’: >> qemu-pixman.c:24: error: ‘PIXMAN_TYPE_BGRA’ undeclared (first use in this function) >> qemu-pixman.c:24: error: (Each undeclared identifier is reported only once >> qemu-pixman.c:24: error: for each function it appears in.) > > Which pixman version is this? #define PIXMAN_VERSION_STRING "0.12.0" Alex ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-18 7:17 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann 2012-09-18 15:01 ` Stefano Stabellini 2012-09-18 19:14 ` Anthony Liguori @ 2012-09-18 20:30 ` Søren Sandmann 2012-09-19 5:56 ` Gerd Hoffmann 2 siblings, 1 reply; 40+ messages in thread From: Søren Sandmann @ 2012-09-18 20:30 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel Gerd Hoffmann <kraxel@redhat.com> writes: > +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; > +} You may want to call pixman_format_supported_source/destination() here to ensure that the format in question is supported by pixman. > -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; > - > - 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; > + assert(surface); > > - 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); > } The fini()/init() here could be done with pixman_region_clear() which was introduced in 0.26.0. Søren ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] fbdev: move to pixman 2012-09-18 20:30 ` Søren Sandmann @ 2012-09-19 5:56 ` Gerd Hoffmann 0 siblings, 0 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-19 5:56 UTC (permalink / raw) To: Søren Sandmann; +Cc: qemu-devel On 09/18/12 22:30, Søren Sandmann wrote: > Gerd Hoffmann <kraxel@redhat.com> writes: > >> +static pixman_image_t *pixman_from_displaystate(DisplayState *ds) >> +{ >> +static pixman_image_t *pixman_from_framebuffer(void) >> +{ > You may want to call pixman_format_supported_source/destination() here > to ensure that the format in question is supported by pixman. Ok. >> + pixman_region_fini(&dirty); >> + pixman_region_init(&dirty); >> } > > The fini()/init() here could be done with pixman_region_clear() which > was introduced in 0.26.0. Saw that, but rhel6 ships 0.18.something which hasn't this function yet. cheers, Gerd ^ permalink raw reply [flat|nested] 40+ messages in thread
* [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann ` (6 preceding siblings ...) 2012-09-18 7:17 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann @ 2012-09-18 7:17 ` Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support Gerd Hoffmann 8 siblings, 0 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 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 4cb4d1d..6835fef 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); @@ -876,6 +882,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 */ @@ -917,6 +968,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) @@ -953,9 +1007,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) @@ -984,6 +1077,8 @@ int fbdev_display_init(DisplayState *ds, const char *device) 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] 40+ messages in thread
* [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann ` (7 preceding siblings ...) 2012-09-18 7:17 ` [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support Gerd Hoffmann @ 2012-09-18 7:17 ` Gerd Hoffmann 2012-09-18 15:02 ` Stefano Stabellini 8 siblings, 1 reply; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-18 7:17 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 6835fef..55793ab 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; @@ -912,6 +924,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)); @@ -937,16 +954,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); @@ -957,7 +990,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) { @@ -1049,6 +1089,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] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support 2012-09-18 7:17 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support Gerd Hoffmann @ 2012-09-18 15:02 ` Stefano Stabellini 2012-09-19 5:52 ` Gerd Hoffmann 0 siblings, 1 reply; 40+ messages in thread From: Stefano Stabellini @ 2012-09-18 15:02 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org On Tue, 18 Sep 2012, Gerd Hoffmann wrote: > Add support for scaling the guest display. > Ctrl-Alt-S hotkey toggles scaling. We could make sdl_zoom generic: sdl_zoom_blit doesn't actually depend on sdl. > 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 6835fef..55793ab 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; > @@ -912,6 +924,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)); > @@ -937,16 +954,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); > @@ -957,7 +990,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) { > @@ -1049,6 +1089,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 [flat|nested] 40+ messages in thread
* Re: [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support 2012-09-18 15:02 ` Stefano Stabellini @ 2012-09-19 5:52 ` Gerd Hoffmann 0 siblings, 0 replies; 40+ messages in thread From: Gerd Hoffmann @ 2012-09-19 5:52 UTC (permalink / raw) To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org On 09/18/12 17:02, Stefano Stabellini wrote: > On Tue, 18 Sep 2012, Gerd Hoffmann wrote: >> Add support for scaling the guest display. >> Ctrl-Alt-S hotkey toggles scaling. > > We could make sdl_zoom generic: sdl_zoom_blit doesn't actually depend on > sdl. I'd rather go the other way around and make sdl zoom use pixman. cheers, Gerd ^ permalink raw reply [flat|nested] 40+ messages in thread
* [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 7/9] fbdev: move to pixman Gerd Hoffmann 0 siblings, 1 reply; 40+ 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] 40+ 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 @ 2012-09-19 11:15 ` Gerd Hoffmann 2012-09-19 18:10 ` Stefano Stabellini 0 siblings, 1 reply; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ messages in thread
end of thread, other threads:[~2012-11-26 20:05 UTC | newest] Thread overview: 40+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann 2012-09-18 10:54 ` Stefano Stabellini 2012-09-18 7:17 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann 2012-09-18 14:10 ` Stefano Stabellini 2012-09-18 16:31 ` Gerd Hoffmann 2012-09-18 16:44 ` Stefano Stabellini 2012-09-18 7:17 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann 2012-09-18 15:01 ` Stefano Stabellini 2012-09-19 5:19 ` Gerd Hoffmann 2012-09-19 18:37 ` Blue Swirl 2012-09-18 7:17 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann 2012-09-18 12:47 ` Luiz Capitulino 2012-09-18 7:17 ` [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann 2012-09-18 15:01 ` Stefano Stabellini 2012-09-19 5:51 ` Gerd Hoffmann 2012-09-18 19:14 ` Anthony Liguori 2012-09-18 21:08 ` Anthony Liguori 2012-11-26 18:42 ` Alexander Graf 2012-11-26 20:01 ` Gerd Hoffmann 2012-11-26 20:05 ` Alexander Graf 2012-09-18 20:30 ` Søren Sandmann 2012-09-19 5:56 ` Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support Gerd Hoffmann 2012-09-18 7:17 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support Gerd Hoffmann 2012-09-18 15:02 ` Stefano Stabellini 2012-09-19 5:52 ` Gerd Hoffmann -- strict thread matches above, loose matches on Subject: below -- 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 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
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).