* [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 ` Gerd Hoffmann
2012-09-18 10:54 ` Stefano Stabellini
0 siblings, 1 reply; 26+ 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] 26+ 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; 26+ 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] 26+ 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 1/9] QLIST-ify display change listeners Gerd Hoffmann
` (8 more replies)
0 siblings, 9 replies; 26+ 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] 26+ messages in thread
* [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners.
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
@ 2012-09-19 11:15 ` Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann
` (7 subsequent siblings)
8 siblings, 0 replies; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
console.h | 72 +++++++++++++++++++++++++++++++----------------------------
hw/xenfb.c | 2 +-
vl.c | 9 ++-----
3 files changed, 42 insertions(+), 41 deletions(-)
diff --git a/console.h b/console.h
index f990684..646ad4b 100644
--- a/console.h
+++ b/console.h
@@ -164,7 +164,7 @@ struct DisplayChangeListener {
int w, int h, uint32_t c);
void (*dpy_text_cursor)(struct DisplayState *s, int x, int y);
- struct DisplayChangeListener *next;
+ QLIST_ENTRY(DisplayChangeListener) next;
};
struct DisplayAllocator {
@@ -179,7 +179,7 @@ struct DisplayState {
struct QEMUTimer *gui_timer;
struct DisplayAllocator* allocator;
- struct DisplayChangeListener* listeners;
+ QLIST_HEAD(, DisplayChangeListener) listeners;
void (*mouse_set)(int x, int y, int on);
void (*cursor_define)(QEMUCursor *cursor);
@@ -231,72 +231,76 @@ static inline int is_buffer_shared(DisplaySurface *surface)
static inline void register_displaychangelistener(DisplayState *ds, DisplayChangeListener *dcl)
{
- dcl->next = ds->listeners;
- ds->listeners = dcl;
+ QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
}
static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
{
- struct DisplayChangeListener *dcl = s->listeners;
- while (dcl != NULL) {
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
dcl->dpy_update(s, x, y, w, h);
- dcl = dcl->next;
}
}
static inline void dpy_resize(DisplayState *s)
{
- struct DisplayChangeListener *dcl = s->listeners;
- while (dcl != NULL) {
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
dcl->dpy_resize(s);
- dcl = dcl->next;
}
}
static inline void dpy_setdata(DisplayState *s)
{
- struct DisplayChangeListener *dcl = s->listeners;
- while (dcl != NULL) {
- if (dcl->dpy_setdata) dcl->dpy_setdata(s);
- dcl = dcl->next;
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (dcl->dpy_setdata) {
+ dcl->dpy_setdata(s);
+ }
}
}
static inline void dpy_refresh(DisplayState *s)
{
- struct DisplayChangeListener *dcl = s->listeners;
- while (dcl != NULL) {
- if (dcl->dpy_refresh) dcl->dpy_refresh(s);
- dcl = dcl->next;
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (dcl->dpy_refresh) {
+ dcl->dpy_refresh(s);
+ }
}
}
static inline void dpy_copy(struct DisplayState *s, int src_x, int src_y,
- int dst_x, int dst_y, int w, int h) {
- struct DisplayChangeListener *dcl = s->listeners;
- while (dcl != NULL) {
- if (dcl->dpy_copy)
+ int dst_x, int dst_y, int w, int h)
+{
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (dcl->dpy_copy) {
dcl->dpy_copy(s, src_x, src_y, dst_x, dst_y, w, h);
- else /* TODO */
+ } else { /* TODO */
dcl->dpy_update(s, dst_x, dst_y, w, h);
- dcl = dcl->next;
+ }
}
}
static inline void dpy_fill(struct DisplayState *s, int x, int y,
- int w, int h, uint32_t c) {
- struct DisplayChangeListener *dcl = s->listeners;
- while (dcl != NULL) {
- if (dcl->dpy_fill) dcl->dpy_fill(s, x, y, w, h, c);
- dcl = dcl->next;
+ int w, int h, uint32_t c)
+{
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (dcl->dpy_fill) {
+ dcl->dpy_fill(s, x, y, w, h, c);
+ }
}
}
-static inline void dpy_cursor(struct DisplayState *s, int x, int y) {
- struct DisplayChangeListener *dcl = s->listeners;
- while (dcl != NULL) {
- if (dcl->dpy_text_cursor) dcl->dpy_text_cursor(s, x, y);
- dcl = dcl->next;
+static inline void dpy_cursor(struct DisplayState *s, int x, int y)
+{
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (dcl->dpy_text_cursor) {
+ dcl->dpy_text_cursor(s, x, y);
+ }
}
}
diff --git a/hw/xenfb.c b/hw/xenfb.c
index 338800a..ef24c33 100644
--- a/hw/xenfb.c
+++ b/hw/xenfb.c
@@ -717,7 +717,7 @@ static void xenfb_update(void *opaque)
if (xenfb_queue_full(xenfb))
return;
- for (l = xenfb->c.ds->listeners; l != NULL; l = l->next) {
+ QLIST_FOREACH(l, &xenfb->c.ds->listeners, next) {
if (l->idle)
continue;
idle = 0;
diff --git a/vl.c b/vl.c
index 7c577fa..2a7c92a 100644
--- a/vl.c
+++ b/vl.c
@@ -1276,15 +1276,14 @@ static void gui_update(void *opaque)
{
uint64_t interval = GUI_REFRESH_INTERVAL;
DisplayState *ds = opaque;
- DisplayChangeListener *dcl = ds->listeners;
+ DisplayChangeListener *dcl;
dpy_refresh(ds);
- while (dcl != NULL) {
+ QLIST_FOREACH(dcl, &ds->listeners, next) {
if (dcl->gui_timer_interval &&
dcl->gui_timer_interval < interval)
interval = dcl->gui_timer_interval;
- dcl = dcl->next;
}
qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock));
}
@@ -3699,14 +3698,12 @@ int main(int argc, char **argv, char **envp)
/* display setup */
dpy_resize(ds);
- dcl = ds->listeners;
- while (dcl != NULL) {
+ QLIST_FOREACH(dcl, &ds->listeners, next) {
if (dcl->dpy_refresh != NULL) {
ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
break;
}
- dcl = dcl->next;
}
text_consoles_set_display(ds);
--
1.7.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann
@ 2012-09-19 11:15 ` Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann
` (6 subsequent siblings)
8 siblings, 0 replies; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Also change the way the gui_timer is initialized: each time a
displaychangelistener is registered or unregistered we'll check
whenever we need a timer (due to dpy_refresh callback being present)
and if so setup a timer, otherwise zap it. This way the gui timer works
correctly with displaychangelisteners coming and going.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
console.h | 10 ++++++++++
vl.c | 31 +++++++++++++++++++++++--------
2 files changed, 33 insertions(+), 8 deletions(-)
diff --git a/console.h b/console.h
index 646ad4b..48fef22 100644
--- a/console.h
+++ b/console.h
@@ -229,9 +229,19 @@ static inline int is_buffer_shared(DisplaySurface *surface)
!(surface->flags & QEMU_REALPIXELS_FLAG));
}
+void gui_setup_refresh(DisplayState *ds);
+
static inline void register_displaychangelistener(DisplayState *ds, DisplayChangeListener *dcl)
{
QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
+ gui_setup_refresh(ds);
+}
+
+static inline void unregister_displaychangelistener(DisplayState *ds,
+ DisplayChangeListener *dcl)
+{
+ QLIST_REMOVE(dcl, next);
+ gui_setup_refresh(ds);
}
static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
diff --git a/vl.c b/vl.c
index 2a7c92a..fbb77fe 100644
--- a/vl.c
+++ b/vl.c
@@ -1288,6 +1288,29 @@ static void gui_update(void *opaque)
qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock));
}
+void gui_setup_refresh(DisplayState *ds)
+{
+ DisplayChangeListener *dcl;
+ bool need_timer = false;
+
+ QLIST_FOREACH(dcl, &ds->listeners, next) {
+ if (dcl->dpy_refresh != NULL) {
+ need_timer = true;
+ break;
+ }
+ }
+
+ if (need_timer && ds->gui_timer == NULL) {
+ ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
+ qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
+ }
+ if (!need_timer && ds->gui_timer != NULL) {
+ qemu_del_timer(ds->gui_timer);
+ qemu_free_timer(ds->gui_timer);
+ ds->gui_timer = NULL;
+ }
+}
+
struct vm_change_state_entry {
VMChangeStateHandler *cb;
void *opaque;
@@ -2350,7 +2373,6 @@ int main(int argc, char **argv, char **envp)
const char *kernel_filename, *kernel_cmdline;
char boot_devices[33] = "cad"; /* default to HD->floppy->CD-ROM */
DisplayState *ds;
- DisplayChangeListener *dcl;
int cyls, heads, secs, translation;
QemuOpts *hda_opts = NULL, *opts, *machine_opts;
QemuOptsList *olist;
@@ -3698,13 +3720,6 @@ int main(int argc, char **argv, char **envp)
/* display setup */
dpy_resize(ds);
- QLIST_FOREACH(dcl, &ds->listeners, next) {
- if (dcl->dpy_refresh != NULL) {
- ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
- qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
- break;
- }
- }
text_consoles_set_display(ds);
if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
--
1.7.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann
@ 2012-09-19 11:15 ` Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann
` (5 subsequent siblings)
8 siblings, 0 replies; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
When adding DisplayChangeListeners the set_mouse and cursor_define
callbacks have been left in DisplayState for some reason. Fix it.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
console.c | 2 +-
console.h | 39 +++++++++++++++++++++++++++++++++++----
hw/jazz_led.c | 2 +-
hw/qxl-render.c | 2 +-
hw/vga.c | 10 +++++-----
hw/vmware_vga.c | 11 ++++++-----
ui/sdl.c | 8 ++++----
ui/spice-display.c | 4 ++--
ui/vnc.c | 8 ++++----
9 files changed, 59 insertions(+), 27 deletions(-)
diff --git a/console.c b/console.c
index a8bcc42..cc0479b 100644
--- a/console.c
+++ b/console.c
@@ -1239,7 +1239,7 @@ static void text_console_update(void *opaque, console_ch_t *chardata)
s->text_y[1] = 0;
}
if (s->cursor_invalidate) {
- dpy_cursor(s->ds, s->x, s->y);
+ dpy_text_cursor(s->ds, s->x, s->y);
s->cursor_invalidate = 0;
}
}
diff --git a/console.h b/console.h
index 48fef22..bef2d2d 100644
--- a/console.h
+++ b/console.h
@@ -164,6 +164,9 @@ struct DisplayChangeListener {
int w, int h, uint32_t c);
void (*dpy_text_cursor)(struct DisplayState *s, int x, int y);
+ void (*dpy_mouse_set)(struct DisplayState *s, int x, int y, int on);
+ void (*dpy_cursor_define)(struct DisplayState *s, QEMUCursor *cursor);
+
QLIST_ENTRY(DisplayChangeListener) next;
};
@@ -181,9 +184,6 @@ struct DisplayState {
struct DisplayAllocator* allocator;
QLIST_HEAD(, DisplayChangeListener) listeners;
- void (*mouse_set)(int x, int y, int on);
- void (*cursor_define)(QEMUCursor *cursor);
-
struct DisplayState *next;
};
@@ -304,7 +304,7 @@ static inline void dpy_fill(struct DisplayState *s, int x, int y,
}
}
-static inline void dpy_cursor(struct DisplayState *s, int x, int y)
+static inline void dpy_text_cursor(struct DisplayState *s, int x, int y)
{
struct DisplayChangeListener *dcl;
QLIST_FOREACH(dcl, &s->listeners, next) {
@@ -314,6 +314,37 @@ static inline void dpy_cursor(struct DisplayState *s, int x, int y)
}
}
+static inline void dpy_mouse_set(struct DisplayState *s, int x, int y, int on)
+{
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (dcl->dpy_mouse_set) {
+ dcl->dpy_mouse_set(s, x, y, on);
+ }
+ }
+}
+
+static inline void dpy_cursor_define(struct DisplayState *s, QEMUCursor *cursor)
+{
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (dcl->dpy_cursor_define) {
+ dcl->dpy_cursor_define(s, cursor);
+ }
+ }
+}
+
+static inline bool dpy_cursor_define_supported(struct DisplayState *s)
+{
+ struct DisplayChangeListener *dcl;
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (dcl->dpy_cursor_define) {
+ return true;
+ }
+ }
+ return false;
+}
+
static inline int ds_get_linesize(DisplayState *ds)
{
return ds->surface->linesize;
diff --git a/hw/jazz_led.c b/hw/jazz_led.c
index 6486523..c4d54e2 100644
--- a/hw/jazz_led.c
+++ b/hw/jazz_led.c
@@ -210,7 +210,7 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
LedState *s = opaque;
char buf[2];
- dpy_cursor(s->ds, -1, -1);
+ dpy_text_cursor(s->ds, -1, -1);
qemu_console_resize(s->ds, 2, 1);
/* TODO: draw the segments */
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
index e2e3fe2..085a090 100644
--- a/hw/qxl-render.c
+++ b/hw/qxl-render.c
@@ -238,7 +238,7 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
return 1;
}
- if (!qxl->ssd.ds->mouse_set || !qxl->ssd.ds->cursor_define) {
+ if (!dpy_cursor_define_supported(qxl->ssd.ds)) {
return 0;
}
diff --git a/hw/vga.c b/hw/vga.c
index afaef0d..ec4f0c5 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -2081,11 +2081,11 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end || full_update) {
cursor_visible = !(s->cr[VGA_CRTC_CURSOR_START] & 0x20);
if (cursor_visible && cursor_offset < size && cursor_offset >= 0)
- dpy_cursor(s->ds,
- TEXTMODE_X(cursor_offset),
- TEXTMODE_Y(cursor_offset));
+ dpy_text_cursor(s->ds,
+ TEXTMODE_X(cursor_offset),
+ TEXTMODE_Y(cursor_offset));
else
- dpy_cursor(s->ds, -1, -1);
+ dpy_text_cursor(s->ds, -1, -1);
s->cursor_offset = cursor_offset;
s->cursor_start = s->cr[VGA_CRTC_CURSOR_START];
s->cursor_end = s->cr[VGA_CRTC_CURSOR_END];
@@ -2146,7 +2146,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
/* Display a message */
s->last_width = 60;
s->last_height = height = 3;
- dpy_cursor(s->ds, -1, -1);
+ dpy_text_cursor(s->ds, -1, -1);
s->ds->surface->width = s->last_width;
s->ds->surface->height = height;
dpy_resize(s->ds);
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index e815a04..e105b5a 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -479,8 +479,7 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s,
qc = cursor_builtin_left_ptr();
}
- if (s->vga.ds->cursor_define)
- s->vga.ds->cursor_define(qc);
+ dpy_cursor_define(s->vga.ds, qc);
cursor_put(qc);
}
#endif
@@ -755,9 +754,10 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
caps |= SVGA_CAP_RECT_FILL;
#endif
#ifdef HW_MOUSE_ACCEL
- if (s->vga.ds->mouse_set)
+ if (dpy_cursor_define_supported(s->vga.ds)) {
caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 |
SVGA_CAP_CURSOR_BYPASS;
+ }
#endif
return caps;
@@ -904,8 +904,9 @@ static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
s->cursor.on |= (value == SVGA_CURSOR_ON_SHOW);
s->cursor.on &= (value != SVGA_CURSOR_ON_HIDE);
#ifdef HW_MOUSE_ACCEL
- if (s->vga.ds->mouse_set && value <= SVGA_CURSOR_ON_SHOW)
- s->vga.ds->mouse_set(s->cursor.x, s->cursor.y, s->cursor.on);
+ if (value <= SVGA_CURSOR_ON_SHOW) {
+ dpy_mouse_set(s->vga.ds, s->cursor.x, s->cursor.y, s->cursor.on);
+ }
#endif
break;
diff --git a/ui/sdl.c b/ui/sdl.c
index f6f711c..f8ead93 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -905,7 +905,7 @@ static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c)
SDL_FillRect(real_screen, &dst, c);
}
-static void sdl_mouse_warp(int x, int y, int on)
+static void sdl_mouse_warp(DisplayState *ds, int x, int y, int on)
{
if (on) {
if (!guest_cursor)
@@ -921,7 +921,7 @@ static void sdl_mouse_warp(int x, int y, int on)
guest_x = x, guest_y = y;
}
-static void sdl_mouse_define(QEMUCursor *c)
+static void sdl_mouse_define(DisplayState *ds, QEMUCursor *c)
{
uint8_t *image, *mask;
int bpl;
@@ -1025,8 +1025,8 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
dcl->dpy_refresh = sdl_refresh;
dcl->dpy_setdata = sdl_setdata;
dcl->dpy_fill = sdl_fill;
- ds->mouse_set = sdl_mouse_warp;
- ds->cursor_define = sdl_mouse_define;
+ dcl->dpy_mouse_set = sdl_mouse_warp;
+ dcl->dpy_cursor_define = sdl_mouse_define;
register_displaychangelistener(ds, dcl);
da = g_malloc0(sizeof(DisplayAllocator));
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 50fbefb..5180428 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -441,12 +441,12 @@ void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
{
if (ssd->cursor) {
- ssd->ds->cursor_define(ssd->cursor);
+ dpy_cursor_define(ssd->ds, ssd->cursor);
cursor_put(ssd->cursor);
ssd->cursor = NULL;
}
if (ssd->mouse_x != -1 && ssd->mouse_y != -1) {
- ssd->ds->mouse_set(ssd->mouse_x, ssd->mouse_y, 1);
+ dpy_mouse_set(ssd->ds, ssd->mouse_x, ssd->mouse_y, 1);
ssd->mouse_x = -1;
ssd->mouse_y = -1;
}
diff --git a/ui/vnc.c b/ui/vnc.c
index 385e345..f8f058d 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -798,7 +798,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int
}
}
-static void vnc_mouse_set(int x, int y, int visible)
+static void vnc_mouse_set(DisplayState *ds, int x, int y, int visible)
{
/* can we ask the client(s) to move the pointer ??? */
}
@@ -825,7 +825,7 @@ static int vnc_cursor_define(VncState *vs)
return -1;
}
-static void vnc_dpy_cursor_define(QEMUCursor *c)
+static void vnc_dpy_cursor_define(DisplayState *ds, QEMUCursor *c)
{
VncDisplay *vd = vnc_display;
VncState *vs;
@@ -2751,9 +2751,9 @@ void vnc_display_init(DisplayState *ds)
dcl->dpy_update = vnc_dpy_update;
dcl->dpy_resize = vnc_dpy_resize;
dcl->dpy_setdata = vnc_dpy_setdata;
+ dcl->dpy_mouse_set = vnc_mouse_set;
+ dcl->dpy_cursor_define = vnc_dpy_cursor_define;
register_displaychangelistener(ds, dcl);
- ds->mouse_set = vnc_mouse_set;
- ds->cursor_define = vnc_dpy_cursor_define;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver.
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
` (2 preceding siblings ...)
2012-09-19 11:15 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann
@ 2012-09-19 11:15 ` Gerd Hoffmann
2012-09-19 18:09 ` Stefano Stabellini
2012-09-19 11:15 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann
` (4 subsequent siblings)
8 siblings, 1 reply; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Display works, requires truecolor framebuffer with 16 or 32 bpp on the
host. 32bpp is recommended. The framebuffer is used as-is, qemu
doesn't try to switch modes. With LCD displays mode switching is pretty
pointless IMHO, also it wouldn't work anyway with the most common
fbdev drivers (vesafb, KMS). Guest display is centered on the host
screen.
Mouse works, uses /dev/input/mice.
Keyboard works. Guest screen has whatever keymap you load inside
the guest. Text windows (monitor, serial, ...) have a simple en-us
keymap. Good enough to type monitor commands. Not goot enough to
work seriously on a serial terminal. But the qemu terminal emulation
isn't good enough for that anyway ;)
Hot keys:
Ctrl-Alt-F<nr> -> host console switching.
Ctrl-Alt-<nr> -> qemu console switching.
Ctrl-Alt-ESC -> exit qemu.
Special feature: Sane console switching. Switching away stops screen
updates. Switching back redraws the screen. When started from the
linux console qemu uses the vt you've started it from (requires just
read/write access to /dev/fb0). When starting from somewhere else qemu
tries to open a unused virtual terminal and switch to it (usually
requires root privileges to open /dev/tty<nr>).
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
console.h | 4 +
qemu-options.hx | 8 +
sysemu.h | 1 +
trace-events | 15 +
ui/Makefile.objs | 1 +
ui/fbdev.c | 965 +++++++++++++++++++++++++++++++++++++++++++++++++++
ui/linux-keynames.h | 388 +++++++++++++++++++++
vl.c | 19 +
8 files changed, 1401 insertions(+), 0 deletions(-)
create mode 100644 ui/fbdev.c
create mode 100644 ui/linux-keynames.h
diff --git a/console.h b/console.h
index bef2d2d..aa9959b 100644
--- a/console.h
+++ b/console.h
@@ -417,6 +417,10 @@ void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
/* sdl.c */
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
+/* fbdev.c */
+int fbdev_display_init(DisplayState *ds, const char *device, Error **err);
+void fbdev_display_uninit(DisplayState *ds);
+
/* cocoa.m */
void cocoa_display_init(DisplayState *ds, int full_screen);
diff --git a/qemu-options.hx b/qemu-options.hx
index 09c86c4..3445655 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -947,6 +947,14 @@ Enable/disable spice seamless migration. Default is off.
@end table
ETEXI
+DEF("fbdev", 0, QEMU_OPTION_fbdev,
+ "-fbdev enable fbdev\n", QEMU_ARCH_ALL)
+STEXI
+@item -fbdev
+@findex -fbdev
+Enable fbdev (linux framebuffer).
+ETEXI
+
DEF("portrait", 0, QEMU_OPTION_portrait,
"-portrait rotate graphical output 90 deg left (only PXA LCD)\n",
QEMU_ARCH_ALL)
diff --git a/sysemu.h b/sysemu.h
index 65552ac..34e6bfa 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -93,6 +93,7 @@ typedef enum DisplayType
DT_DEFAULT,
DT_CURSES,
DT_SDL,
+ DT_FBDEV,
DT_NOGRAPHIC,
DT_NONE,
} DisplayType;
diff --git a/trace-events b/trace-events
index b48fe2d..0d0b7fa 100644
--- a/trace-events
+++ b/trace-events
@@ -994,3 +994,18 @@ spapr_pci_rtas_ibm_change_msi(unsigned func, unsigned req) "func %u, requested %
spapr_pci_rtas_ibm_query_interrupt_source_number(unsigned ioa, unsigned intr) "queries for #%u, IRQ%u"
spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq) "@%"PRIx64"<=%"PRIx64" IRQ %u"
spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ %u"
+
+# ui/fbdev.c
+fbdev_enabled(void) ""
+fbdev_cleanup(void) ""
+fbdev_vt_activate(int vtno, int wait) "vtno %d, wait %d"
+fbdev_vt_activated(void) ""
+fbdev_vt_release_request(void) ""
+fbdev_vt_released(void) ""
+fbdev_vt_aquire_request(void) ""
+fbdev_vt_aquired(void) ""
+fbdev_kbd_raw(int enable) "enable %d"
+fbdev_kbd_event(int keycode, const char *kname, int up) "keycode 0x%x [%s], down %d"
+fbdev_dpy_resize(int w, int h) "%dx%d"
+fbdev_dpy_redraw(void)
+
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index adc07be..55ddcf2 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -12,3 +12,4 @@ common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
common-obj-$(CONFIG_COCOA) += cocoa.o
common-obj-$(CONFIG_CURSES) += curses.o
common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
+common-obj-$(CONFIG_LINUX) += fbdev.o
diff --git a/ui/fbdev.c b/ui/fbdev.c
new file mode 100644
index 0000000..6dfbacc
--- /dev/null
+++ b/ui/fbdev.c
@@ -0,0 +1,965 @@
+/*
+ * linux fbdev output driver.
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <linux/fb.h>
+
+#include "qemu-common.h"
+#include "console.h"
+#include "keymaps.h"
+#include "sysemu.h"
+#include "pflib.h"
+
+/*
+ * must be last so we get the linux input layer
+ * KEY_* defines, not the ncurses ones.
+ */
+#include <linux/input.h>
+
+/* -------------------------------------------------------------------- */
+
+/* file handles */
+static int tty = -1, fb = -1, mice = -1;
+
+/* saved state, for restore on exit */
+static int orig_vtno;
+static int kd_omode;
+static struct vt_mode vt_omode;
+static struct fb_var_screeninfo fb_ovar;
+
+/* framebuffer */
+static struct fb_fix_screeninfo fb_fix;
+static struct fb_var_screeninfo fb_var;
+static uint8_t *fb_mem;
+static int fb_mem_offset;
+
+/* linux console */
+static int vtno;
+static struct vt_mode vt_mode;
+static struct termios tty_attributes;
+static unsigned long tty_mode;
+static unsigned int tty_flags;
+static bool tty_mediumraw;
+static bool key_down[KEY_CNT];
+
+/* console switching */
+#define SIG_ACQ (SIGRTMIN+6)
+#define SIG_REL (SIGRTMIN+7)
+#define FB_ACTIVE 0
+#define FB_REL_REQ 1
+#define FB_INACTIVE 2
+#define FB_ACQ_REQ 3
+static int fb_switch_state;
+
+/* qdev windup */
+static DisplayChangeListener *dcl;
+static QemuPfConv *conv;
+static PixelFormat fbpf;
+static int resize_screen;
+static int redraw_screen;
+static int cx, cy, cw, ch;
+static Notifier exit_notifier;
+
+/* fwd decls */
+static int fbdev_activate_vt(int tty, int vtno, bool wait);
+
+/* -------------------------------------------------------------------- */
+/* mouse */
+
+static void read_mouse(void *opaque)
+{
+ char buf[3];
+ int rc, x, y, b;
+
+ rc = read(mice, buf, sizeof(buf));
+ if (rc != sizeof(buf)) {
+ return;
+ }
+
+ if (fb_switch_state != FB_ACTIVE) {
+ return;
+ }
+
+ x = buf[1];
+ y = -buf[2];
+ b = buf[0] & 0x7;
+
+ if (kbd_mouse_is_absolute()) {
+ static int ax, ay;
+ ax += x; ay += y;
+ if (ax < 0) {
+ ax = 0;
+ }
+ if (ay < 0) {
+ ay = 0;
+ }
+ if (ax >= cw) {
+ ax = cw-1;
+ }
+ if (ay >= ch) {
+ ay = ch-1;
+ }
+ kbd_mouse_event(ax * 0x7FFF / cw, ay * 0x7FFF / ch, 0, b);
+ } else {
+ kbd_mouse_event(x, y, 0, b);
+ }
+}
+
+static int init_mouse(void)
+{
+ mice = open("/dev/input/mice", O_RDONLY);
+ if (mice == -1) {
+ return -1;
+ }
+ qemu_set_fd_handler(mice, read_mouse, NULL, NULL);
+ return 0;
+}
+
+static void uninit_mouse(void)
+{
+ if (mice == -1) {
+ return;
+ }
+ qemu_set_fd_handler(mice, NULL, NULL, NULL);
+ close(mice);
+ mice = -1;
+}
+
+/* -------------------------------------------------------------------- */
+/* keyboard */
+
+static const char *keynames[] = {
+#include "linux-keynames.h"
+};
+
+static const int scancode_map[KEY_CNT] = {
+ [KEY_ESC] = 0x01,
+ [KEY_1] = 0x02,
+ [KEY_2] = 0x03,
+ [KEY_3] = 0x04,
+ [KEY_4] = 0x05,
+ [KEY_5] = 0x06,
+ [KEY_6] = 0x07,
+ [KEY_7] = 0x08,
+ [KEY_8] = 0x09,
+ [KEY_9] = 0x0a,
+ [KEY_0] = 0x0b,
+ [KEY_MINUS] = 0x0c,
+ [KEY_EQUAL] = 0x0d,
+ [KEY_BACKSPACE] = 0x0e,
+
+ [KEY_TAB] = 0x0f,
+ [KEY_Q] = 0x10,
+ [KEY_W] = 0x11,
+ [KEY_E] = 0x12,
+ [KEY_R] = 0x13,
+ [KEY_T] = 0x14,
+ [KEY_Y] = 0x15,
+ [KEY_U] = 0x16,
+ [KEY_I] = 0x17,
+ [KEY_O] = 0x18,
+ [KEY_P] = 0x19,
+ [KEY_LEFTBRACE] = 0x1a,
+ [KEY_RIGHTBRACE] = 0x1b,
+ [KEY_ENTER] = 0x1c,
+
+ [KEY_A] = 0x1e,
+ [KEY_S] = 0x1f,
+ [KEY_D] = 0x20,
+ [KEY_F] = 0x21,
+ [KEY_G] = 0x22,
+ [KEY_H] = 0x23,
+ [KEY_J] = 0x24,
+ [KEY_K] = 0x25,
+ [KEY_L] = 0x26,
+ [KEY_SEMICOLON] = 0x27,
+ [KEY_APOSTROPHE] = 0x28,
+ [KEY_GRAVE] = 0x29,
+ [KEY_LEFTSHIFT] = 0x2a,
+ [KEY_BACKSLASH] = 0x2b,
+
+ [KEY_Z] = 0x2c,
+ [KEY_X] = 0x2d,
+ [KEY_C] = 0x2e,
+ [KEY_V] = 0x2f,
+ [KEY_B] = 0x30,
+ [KEY_N] = 0x31,
+ [KEY_M] = 0x32,
+ [KEY_COMMA] = 0x33,
+ [KEY_DOT] = 0x34,
+ [KEY_SLASH] = 0x35,
+ [KEY_RIGHTSHIFT] = 0x36,
+ [KEY_SPACE] = 0x39,
+
+ [KEY_F1] = 0x3b,
+ [KEY_F2] = 0x3c,
+ [KEY_F3] = 0x3d,
+ [KEY_F4] = 0x3e,
+ [KEY_F5] = 0x3f,
+ [KEY_F6] = 0x40,
+ [KEY_F7] = 0x41,
+ [KEY_F8] = 0x42,
+ [KEY_F9] = 0x43,
+ [KEY_F10] = 0x44,
+ [KEY_F11] = 0x57,
+ [KEY_F12] = 0x58,
+
+ [KEY_SYSRQ] = 0xb7,
+ [KEY_SCROLLLOCK] = 0x46,
+#if 0
+ [KEY_PAUSE] = FIXME,
+#endif
+ [KEY_CAPSLOCK] = 0x3a,
+ [KEY_102ND] = 0x56,
+
+ [KEY_LEFTCTRL] = 0x1d,
+ [KEY_LEFTMETA] = 0xdb,
+ [KEY_LEFTALT] = 0x38,
+ [KEY_RIGHTALT] = 0xb8,
+ [KEY_RIGHTMETA] = 0xdc,
+ [KEY_RIGHTCTRL] = 0x9d,
+ [KEY_COMPOSE] = 0xdd,
+
+ [KEY_INSERT] = 0xd2,
+ [KEY_DELETE] = 0xd3,
+ [KEY_HOME] = 0xc7,
+ [KEY_END] = 0xcf,
+ [KEY_PAGEUP] = 0xc9,
+ [KEY_PAGEDOWN] = 0xd1,
+
+ [KEY_UP] = 0xc8,
+ [KEY_LEFT] = 0xcb,
+ [KEY_RIGHT] = 0xcd,
+ [KEY_DOWN] = 0xd0,
+
+ [KEY_NUMLOCK] = 0x45,
+ [KEY_KPSLASH] = 0xb5,
+ [KEY_KPASTERISK] = 0x37,
+ [KEY_KP7] = 0x47,
+ [KEY_KP8] = 0x48,
+ [KEY_KP9] = 0x49,
+ [KEY_KPMINUS] = 0x4a,
+ [KEY_KP4] = 0x4b,
+ [KEY_KP5] = 0x4c,
+ [KEY_KP6] = 0x4d,
+ [KEY_KPPLUS] = 0x4e,
+ [KEY_KP1] = 0x4f,
+ [KEY_KP2] = 0x50,
+ [KEY_KP3] = 0x51,
+ [KEY_KP0] = 0x52,
+ [KEY_KPDOT] = 0x53,
+ [KEY_KPENTER] = 0x9c,
+};
+
+static const struct keysym_map {
+ int normal, shifted;
+} keysym_map_en_us[KEY_CNT] = {
+ [KEY_A] = { .normal = 'a', .shifted = 'A' },
+ [KEY_B] = { .normal = 'b', .shifted = 'B' },
+ [KEY_C] = { .normal = 'c', .shifted = 'C' },
+ [KEY_D] = { .normal = 'd', .shifted = 'D' },
+ [KEY_E] = { .normal = 'e', .shifted = 'E' },
+ [KEY_F] = { .normal = 'f', .shifted = 'F' },
+ [KEY_G] = { .normal = 'g', .shifted = 'G' },
+ [KEY_H] = { .normal = 'h', .shifted = 'H' },
+ [KEY_I] = { .normal = 'i', .shifted = 'I' },
+ [KEY_J] = { .normal = 'j', .shifted = 'J' },
+ [KEY_K] = { .normal = 'k', .shifted = 'K' },
+ [KEY_L] = { .normal = 'l', .shifted = 'L' },
+ [KEY_M] = { .normal = 'm', .shifted = 'M' },
+ [KEY_N] = { .normal = 'n', .shifted = 'N' },
+ [KEY_O] = { .normal = 'o', .shifted = 'O' },
+ [KEY_P] = { .normal = 'p', .shifted = 'P' },
+ [KEY_Q] = { .normal = 'q', .shifted = 'Q' },
+ [KEY_R] = { .normal = 'r', .shifted = 'R' },
+ [KEY_S] = { .normal = 's', .shifted = 'S' },
+ [KEY_T] = { .normal = 't', .shifted = 'T' },
+ [KEY_U] = { .normal = 'u', .shifted = 'U' },
+ [KEY_V] = { .normal = 'v', .shifted = 'V' },
+ [KEY_W] = { .normal = 'w', .shifted = 'W' },
+ [KEY_X] = { .normal = 'x', .shifted = 'X' },
+ [KEY_Y] = { .normal = 'y', .shifted = 'Y' },
+ [KEY_Z] = { .normal = 'z', .shifted = 'Z' },
+
+ [KEY_1] = { .normal = '1', .shifted = '!' },
+ [KEY_2] = { .normal = '2', .shifted = '@' },
+ [KEY_3] = { .normal = '3', .shifted = '#' },
+ [KEY_4] = { .normal = '4', .shifted = '$' },
+ [KEY_5] = { .normal = '5', .shifted = '%' },
+ [KEY_6] = { .normal = '6', .shifted = '^' },
+ [KEY_7] = { .normal = '7', .shifted = '&' },
+ [KEY_8] = { .normal = '8', .shifted = '*' },
+ [KEY_9] = { .normal = '9', .shifted = '(' },
+ [KEY_0] = { .normal = '0', .shifted = ')' },
+
+ [KEY_MINUS] = { .normal = '-', .shifted = '_' },
+ [KEY_EQUAL] = { .normal = '=', .shifted = '+' },
+ [KEY_TAB] = { .normal = '\t' },
+ [KEY_LEFTBRACE] = { .normal = '[', .shifted = '{' },
+ [KEY_RIGHTBRACE] = { .normal = ']', .shifted = '}' },
+ [KEY_ENTER] = { .normal = '\n', },
+ [KEY_SEMICOLON] = { .normal = ';', .shifted = ':' },
+ [KEY_APOSTROPHE] = { .normal = '"', .shifted = '\'' },
+ [KEY_BACKSLASH] = { .normal = '\\', .shifted = '|' },
+ [KEY_COMMA] = { .normal = ',', .shifted = '<' },
+ [KEY_DOT] = { .normal = '.', .shifted = '>' },
+ [KEY_SLASH] = { .normal = '/', .shifted = '?' },
+ [KEY_SPACE] = { .normal = ' ' },
+
+ [KEY_BACKSPACE] = { .normal = QEMU_KEY_BACKSPACE },
+ [KEY_UP] = { .normal = QEMU_KEY_UP },
+ [KEY_DOWN] = { .normal = QEMU_KEY_DOWN },
+ [KEY_LEFT] = { .normal = QEMU_KEY_LEFT },
+ [KEY_RIGHT] = { .normal = QEMU_KEY_RIGHT },
+};
+
+static void start_mediumraw(int tty)
+{
+ struct termios tattr;
+
+ if (tty_mediumraw) {
+ return;
+ }
+ trace_fbdev_kbd_raw(1);
+
+ /* save state */
+ tcgetattr(tty, &tty_attributes);
+ ioctl(tty, KDGKBMODE, &tty_mode);
+ tty_flags = fcntl(tty, F_GETFL, NULL);
+
+ /* setup */
+ tattr = tty_attributes;
+ tattr.c_cflag &= ~(IXON|IXOFF);
+ tattr.c_lflag &= ~(ICANON|ECHO|ISIG);
+ tattr.c_iflag = 0;
+ tattr.c_cc[VMIN] = 1;
+ tattr.c_cc[VTIME] = 0;
+ tcsetattr(tty, TCSAFLUSH, &tattr);
+ ioctl(tty, KDSKBMODE, K_MEDIUMRAW);
+ fcntl(tty, F_SETFL, tty_flags | O_NONBLOCK);
+
+ tty_mediumraw = true;
+}
+
+static void stop_mediumraw(int tty)
+{
+ if (!tty_mediumraw) {
+ return;
+ }
+ trace_fbdev_kbd_raw(0);
+
+ /* restore state */
+ tcsetattr(tty, TCSANOW, &tty_attributes);
+ ioctl(tty, KDSKBMODE, tty_mode);
+ fcntl(tty, F_SETFL, tty_flags);
+
+ tty_mediumraw = false;
+}
+
+static void send_scancode(int keycode, int up)
+{
+ int scancode = scancode_map[keycode];
+
+ if (!scancode) {
+ fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
+ __func__, keycode, keynames[keycode]);
+ return;
+ }
+ if (scancode & SCANCODE_GREY) {
+ kbd_put_keycode(SCANCODE_EMUL0);
+ }
+ if (up) {
+ kbd_put_keycode(scancode | SCANCODE_UP);
+ } else {
+ kbd_put_keycode(scancode & SCANCODE_KEYCODEMASK);
+ }
+}
+
+static void send_keysym(int keycode, int shift)
+{
+ const struct keysym_map *keysym_map = keysym_map_en_us;
+ int keysym;
+
+ if (shift && keysym_map[keycode].shifted) {
+ keysym = keysym_map[keycode].shifted;
+ } else if (keysym_map[keycode].normal) {
+ keysym = keysym_map[keycode].normal;
+ } else {
+ fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
+ __func__, keycode, keynames[keycode]);
+ return;
+ }
+ kbd_put_keysym(keysym);
+}
+
+static void reset_keys(void)
+{
+ int keycode;
+
+ for (keycode = 0; keycode < KEY_MAX; keycode++) {
+ if (key_down[keycode]) {
+ if (is_graphic_console()) {
+ send_scancode(keycode, 1);
+ }
+ key_down[keycode] = false;
+ }
+ }
+}
+
+static void read_mediumraw(void *opaque)
+{
+ uint8_t buf[32];
+ int i, rc, up, keycode;
+ bool ctrl, alt, shift;
+
+ rc = read(tty, buf, sizeof(buf));
+ switch (rc) {
+ case -1:
+ perror("read tty");
+ goto err;
+ case 0:
+ fprintf(stderr, "%s: eof\n", __func__);
+ goto err;
+ default:
+ for (i = 0; i < rc; i++) {
+ up = buf[i] & 0x80;
+ keycode = buf[i] & 0x7f;
+ if (keycode == 0) {
+ keycode = (buf[i+1] & 0x7f) << 7;
+ keycode |= buf[i+2] & 0x7f;
+ i += 2;
+ }
+ if (keycode > KEY_MAX) {
+ continue;
+ }
+
+ if (up) {
+ if (!key_down[keycode]) {
+ continue;
+ }
+ key_down[keycode] = false;
+ } else {
+ key_down[keycode] = true;
+ }
+
+ trace_fbdev_kbd_event(keycode, keynames[keycode], !up);
+
+ alt = key_down[KEY_LEFTALT] || key_down[KEY_RIGHTALT];
+ ctrl = key_down[KEY_LEFTCTRL] || key_down[KEY_RIGHTCTRL];
+ shift = key_down[KEY_LEFTSHIFT] || key_down[KEY_RIGHTSHIFT];
+
+ if (ctrl && alt && !up) {
+ if (keycode == KEY_ESC) {
+ fprintf(stderr, "=== fbdev emergency escape "
+ "(ctrl-alt-esc) ===\n");
+ exit(1);
+ }
+ if (keycode >= KEY_F1 && keycode <= KEY_F10) {
+ fbdev_activate_vt(tty, keycode+1-KEY_F1, false);
+ key_down[keycode] = false;
+ continue;
+ }
+ if (keycode >= KEY_1 && keycode <= KEY_9) {
+ console_select(keycode-KEY_1);
+ reset_keys();
+ continue;
+ }
+ }
+
+ if (is_graphic_console()) {
+ send_scancode(keycode, up);
+ } else if (!up) {
+ send_keysym(keycode, shift);
+ }
+ }
+ }
+ return;
+
+err:
+ exit(1);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void fbdev_cls(void)
+{
+ memset(fb_mem + fb_mem_offset, 0, fb_fix.line_length * fb_var.yres);
+}
+
+static int fbdev_activate_vt(int tty, int vtno, bool wait)
+{
+ trace_fbdev_vt_activate(vtno, wait);
+
+ if (ioctl(tty, VT_ACTIVATE, vtno) < 0) {
+ perror("ioctl VT_ACTIVATE");
+ return -1;
+ }
+
+ if (wait) {
+ if (ioctl(tty, VT_WAITACTIVE, vtno) < 0) {
+ perror("ioctl VT_WAITACTIVE");
+ return -1;
+ }
+ trace_fbdev_vt_activated();
+ }
+
+ return 0;
+}
+
+static void fbdev_cleanup(void)
+{
+ trace_fbdev_cleanup();
+
+ /* restore console */
+ if (fb_mem != NULL) {
+ munmap(fb_mem, fb_fix.smem_len+fb_mem_offset);
+ fb_mem = NULL;
+ }
+ if (fb != -1) {
+ if (ioctl(fb, FBIOPUT_VSCREENINFO, &fb_ovar) < 0) {
+ perror("ioctl FBIOPUT_VSCREENINFO");
+ }
+ close(fb);
+ fb = -1;
+ }
+
+ if (tty != -1) {
+ stop_mediumraw(tty);
+ if (ioctl(tty, KDSETMODE, kd_omode) < 0) {
+ perror("ioctl KDSETMODE");
+ }
+ if (ioctl(tty, VT_SETMODE, &vt_omode) < 0) {
+ perror("ioctl VT_SETMODE");
+ }
+ if (orig_vtno) {
+ fbdev_activate_vt(tty, orig_vtno, true);
+ }
+ qemu_set_fd_handler(tty, NULL, NULL, NULL);
+ close(tty);
+ tty = -1;
+ }
+}
+
+static int fbdev_init(const char *device, Error **err)
+{
+ struct vt_stat vts;
+ unsigned long page_mask;
+ char ttyname[32];
+
+ /* open framebuffer */
+ if (device == NULL) {
+ device = getenv("FRAMEBUFFER");
+ }
+ if (device == NULL) {
+ device = "/dev/fb0";
+ }
+ fb = open(device, O_RDWR);
+ if (fb == -1) {
+ error_setg(err, "open %s: %s\n", device, strerror(errno));
+ return -1;
+ }
+
+ /* open virtual console */
+ tty = 0;
+ if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
+ error_setg(err, "Not started from virtual terminal, "
+ "trying to open one.\n");
+
+ snprintf(ttyname, sizeof(ttyname), "/dev/tty0");
+ tty = open(ttyname, O_RDWR);
+ if (tty == -1) {
+ error_setg(err, "open %s: %s\n", ttyname, strerror(errno));
+ goto err_early;
+ }
+ if (ioctl(tty, VT_OPENQRY, &vtno) < 0) {
+ error_setg(err, "ioctl VT_OPENQRY: %s\n", strerror(errno));
+ goto err_early;
+ }
+ if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
+ error_setg(err, "ioctl VT_GETSTATE: %s\n", strerror(errno));
+ goto err_early;
+ }
+ close(tty);
+
+ snprintf(ttyname, sizeof(ttyname), "/dev/tty%d", vtno);
+ tty = open(ttyname, O_RDWR);
+ if (tty == -1) {
+ error_setg(err, "open %s: %s\n", ttyname, strerror(errno));
+ goto err_early;
+ }
+ orig_vtno = vts.v_active;
+ fprintf(stderr, "Switching to vt %d (current %d).\n", vtno, orig_vtno);
+ } else {
+ orig_vtno = 0;
+ vtno = vts.v_active;
+ fprintf(stderr, "Started at vt %d, using it.\n", vtno);
+ }
+ fbdev_activate_vt(tty, vtno, true);
+
+ /* get current settings (which we have to restore) */
+ if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) < 0) {
+ error_setg(err, "ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno));
+ goto err_early;
+ }
+ if (ioctl(tty, KDGETMODE, &kd_omode) < 0) {
+ error_setg(err, "ioctl KDGETMODE: %s\n", strerror(errno));
+ goto err_early;
+ }
+ if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) {
+ error_setg(err, "ioctl VT_GETMODE: %s\n", strerror(errno));
+ goto err_early;
+ }
+
+ /* checks & initialisation */
+ if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) < 0) {
+ error_setg(err, "ioctl : %s\n", strerror(errno));
+ perror("ioctl FBIOGET_FSCREENINFO");
+ goto err;
+ }
+ if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) < 0) {
+ error_setg(err, "ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno));
+ goto err;
+ }
+ if (fb_fix.type != FB_TYPE_PACKED_PIXELS) {
+ error_setg(err, "can handle only packed pixel frame buffers\n");
+ goto err;
+ }
+ switch (fb_var.bits_per_pixel) {
+ case 32:
+ break;
+ default:
+ error_setg(err, "can't handle %d bpp frame buffers\n",
+ fb_var.bits_per_pixel);
+ goto err;
+ }
+
+ page_mask = getpagesize()-1;
+ fb_switch_state = FB_ACTIVE;
+ fb_mem_offset = (unsigned long)(fb_fix.smem_start) & page_mask;
+ fb_mem = mmap(NULL, fb_fix.smem_len+fb_mem_offset,
+ PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);
+ if (fb_mem == MAP_FAILED) {
+ error_setg(err, "mmap: %s\n", strerror(errno));
+ goto err;
+ }
+ /* move viewport to upper left corner */
+ if (fb_var.xoffset != 0 || fb_var.yoffset != 0) {
+ fb_var.xoffset = 0;
+ fb_var.yoffset = 0;
+ if (ioctl(fb, FBIOPAN_DISPLAY, &fb_var) < 0) {
+ error_setg(err, "ioctl FBIOPAN_DISPLAY: %s\n", strerror(errno));
+ goto err;
+ }
+ }
+ if (ioctl(tty, KDSETMODE, KD_GRAPHICS) < 0) {
+ error_setg(err, "ioctl KDSETMODE: %s\n", strerror(errno));
+ goto err;
+ }
+ /* some fb drivers need this again after switching to graphics ... */
+ fbdev_activate_vt(tty, vtno, true);
+
+ fbdev_cls();
+
+ start_mediumraw(tty);
+ qemu_set_fd_handler(tty, read_mediumraw, NULL, NULL);
+
+ /* create PixelFormat from fbdev structs */
+ fbpf.bits_per_pixel = fb_var.bits_per_pixel;
+ fbpf.bytes_per_pixel = (fb_var.bits_per_pixel+7)/8;
+ fbpf.depth = fb_var.bits_per_pixel == 32
+ ? 24 : fb_var.bits_per_pixel;
+ fbpf.rshift = fb_var.red.offset;
+ fbpf.rbits = fb_var.red.length;
+ fbpf.gshift = fb_var.green.offset;
+ fbpf.gbits = fb_var.green.length;
+ fbpf.bshift = fb_var.blue.offset;
+ fbpf.bbits = fb_var.blue.length;
+ fbpf.ashift = fb_var.transp.offset;
+ fbpf.abits = fb_var.transp.length;
+
+ if (fbpf.rbits) {
+ fbpf.rmax = (1 << fbpf.rbits) - 1;
+ fbpf.rmask = fbpf.rmax << fbpf.rshift;
+ }
+ if (fbpf.gbits) {
+ fbpf.gmax = (1 << fbpf.gbits) - 1;
+ fbpf.gmask = fbpf.gmax << fbpf.gshift;
+ }
+ if (fbpf.bbits) {
+ fbpf.bmax = (1 << fbpf.bbits) - 1;
+ fbpf.bmask = fbpf.bmax << fbpf.bshift;
+ }
+ if (fbpf.abits) {
+ fbpf.amax = (1 << fbpf.abits) - 1;
+ fbpf.amask = fbpf.amax << fbpf.ashift;
+ }
+ return 0;
+
+err_early:
+ if (tty > 0) {
+ close(tty);
+ }
+ close(fb);
+ return -1;
+
+err:
+ fbdev_cleanup();
+ return -1;
+}
+
+static void
+fbdev_catch_fatal_signal(int signr)
+{
+ fprintf(stderr, "%s: %s, restoring linux console state ...\n",
+ __func__, strsignal(signr));
+ fbdev_cleanup();
+ signal(SIGABRT, SIG_DFL);
+ fprintf(stderr, "%s: ... done, going abort() now.\n", __func__);
+ abort();
+}
+
+static void fbdev_catch_exit_signals(void)
+{
+ static const int signals[] = {
+ SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGBUS
+ };
+ struct sigaction act, old;
+ int i;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = fbdev_catch_fatal_signal;
+ act.sa_flags = SA_RESETHAND;
+ sigemptyset(&act.sa_mask);
+ for (i = 0; i < ARRAY_SIZE(signals); i++) {
+ sigaction(signals[i], &act, &old);
+ }
+}
+
+/* -------------------------------------------------------------------- */
+/* console switching */
+
+static void fbdev_switch_signal(int signal)
+{
+ if (signal == SIG_REL) {
+ /* release */
+ trace_fbdev_vt_release_request();
+ fb_switch_state = FB_REL_REQ;
+ }
+ if (signal == SIG_ACQ) {
+ /* acquisition */
+ trace_fbdev_vt_aquire_request();
+ fb_switch_state = FB_ACQ_REQ;
+ }
+}
+
+static void fbdev_switch_release(void)
+{
+ stop_mediumraw(tty);
+ ioctl(tty, KDSETMODE, kd_omode);
+ ioctl(tty, VT_RELDISP, 1);
+ fb_switch_state = FB_INACTIVE;
+ trace_fbdev_vt_released();
+}
+
+static void fbdev_switch_acquire(void)
+{
+ ioctl(tty, VT_RELDISP, VT_ACKACQ);
+ start_mediumraw(tty);
+ reset_keys();
+ ioctl(tty, KDSETMODE, KD_GRAPHICS);
+ fb_switch_state = FB_ACTIVE;
+ trace_fbdev_vt_aquired();
+}
+
+static int fbdev_switch_init(void)
+{
+ struct sigaction act, old;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = fbdev_switch_signal;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIG_REL, &act, &old);
+ sigaction(SIG_ACQ, &act, &old);
+
+ if (ioctl(tty, VT_GETMODE, &vt_mode) < 0) {
+ perror("ioctl VT_GETMODE");
+ exit(1);
+ }
+ vt_mode.mode = VT_PROCESS;
+ vt_mode.waitv = 0;
+ vt_mode.relsig = SIG_REL;
+ vt_mode.acqsig = SIG_ACQ;
+
+ if (ioctl(tty, VT_SETMODE, &vt_mode) < 0) {
+ perror("ioctl VT_SETMODE");
+ exit(1);
+ }
+ return 0;
+}
+
+/* -------------------------------------------------------------------- */
+/* rendering */
+
+static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
+{
+ uint8_t *dst;
+ uint8_t *src;
+ int line;
+
+ if (!conv) {
+ return;
+ }
+
+ src = ds_get_data(ds) + y * ds_get_linesize(ds)
+ + x * ds_get_bytes_per_pixel(ds);
+ dst = fb_mem + y * fb_fix.line_length
+ + x * fbpf.bytes_per_pixel;
+
+ dst += cy * fb_fix.line_length;
+ dst += cx * fbpf.bytes_per_pixel;
+
+ if (h > fb_var.yres - y) {
+ h = fb_var.yres - y;
+ }
+ if (w > fb_var.xres - x) {
+ w = fb_var.xres - x;
+ }
+
+ for (line = y; line < y+h; line++) {
+ qemu_pf_conv_run(conv, dst, src, w);
+ dst += fb_fix.line_length;
+ src += ds_get_linesize(ds);
+ }
+}
+
+/* -------------------------------------------------------------------- */
+/* qemu interfaces */
+
+static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ if (fb_switch_state != FB_ACTIVE) {
+ return;
+ }
+
+ if (resize_screen) {
+ trace_fbdev_dpy_resize(ds_get_width(ds), ds_get_height(ds));
+ resize_screen = 0;
+ cx = 0; cy = 0;
+ cw = ds_get_width(ds);
+ ch = ds_get_height(ds);
+ if (ds_get_width(ds) < fb_var.xres) {
+ cx = (fb_var.xres - ds_get_width(ds)) / 2;
+ }
+ if (ds_get_height(ds) < fb_var.yres) {
+ cy = (fb_var.yres - ds_get_height(ds)) / 2;
+ }
+
+ if (conv) {
+ qemu_pf_conv_put(conv);
+ }
+ conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
+ if (conv == NULL) {
+ fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
+ }
+ }
+
+ if (redraw_screen) {
+ trace_fbdev_dpy_redraw();
+ redraw_screen = 0;
+ fbdev_cls();
+ x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
+ }
+
+ fbdev_render(ds, x, y, w, h);
+}
+
+static void fbdev_resize(DisplayState *ds)
+{
+ resize_screen++;
+ redraw_screen++;
+}
+
+static void fbdev_refresh(DisplayState *ds)
+{
+ switch (fb_switch_state) {
+ case FB_REL_REQ:
+ fbdev_switch_release();
+ case FB_INACTIVE:
+ return;
+ case FB_ACQ_REQ:
+ fbdev_switch_acquire();
+ redraw_screen++;
+ case FB_ACTIVE:
+ break;
+ }
+
+ vga_hw_update();
+ if (redraw_screen) {
+ fbdev_update(ds, 0, 0, 0, 0);
+ }
+}
+
+static void fbdev_exit_notifier(Notifier *notifier, void *data)
+{
+ fbdev_cleanup();
+}
+
+int fbdev_display_init(DisplayState *ds, const char *device, Error **err)
+{
+ if (dcl != NULL) {
+ return 0;
+ }
+
+ if (fbdev_init(device, err) != 0) {
+ return -1;
+ }
+ exit_notifier.notify = fbdev_exit_notifier;
+ qemu_add_exit_notifier(&exit_notifier);
+ fbdev_switch_init();
+ fbdev_catch_exit_signals();
+ init_mouse();
+
+ fbdev_resize(ds);
+ dcl = g_new0(DisplayChangeListener, 1);
+ dcl->dpy_update = fbdev_update;
+ dcl->dpy_resize = fbdev_resize;
+ dcl->dpy_refresh = fbdev_refresh;
+ register_displaychangelistener(ds, dcl);
+
+ trace_fbdev_enabled();
+ return 0;
+}
+
+void fbdev_display_uninit(DisplayState *ds)
+{
+ if (dcl == NULL) {
+ return;
+ }
+
+ unregister_displaychangelistener(ds, dcl);
+ g_free(dcl);
+ dcl = NULL;
+
+ fbdev_cleanup();
+ qemu_remove_exit_notifier(&exit_notifier);
+ uninit_mouse();
+}
diff --git a/ui/linux-keynames.h b/ui/linux-keynames.h
new file mode 100644
index 0000000..058af28
--- /dev/null
+++ b/ui/linux-keynames.h
@@ -0,0 +1,388 @@
+/*
+ * awk '/#define KEY_/ { printf(" [%s] = \"%s\",\n",$2,$2); }' \
+ * /usr/include/linux/input.h
+ */
+ [KEY_RESERVED] = "KEY_RESERVED",
+ [KEY_ESC] = "KEY_ESC",
+ [KEY_1] = "KEY_1",
+ [KEY_2] = "KEY_2",
+ [KEY_3] = "KEY_3",
+ [KEY_4] = "KEY_4",
+ [KEY_5] = "KEY_5",
+ [KEY_6] = "KEY_6",
+ [KEY_7] = "KEY_7",
+ [KEY_8] = "KEY_8",
+ [KEY_9] = "KEY_9",
+ [KEY_0] = "KEY_0",
+ [KEY_MINUS] = "KEY_MINUS",
+ [KEY_EQUAL] = "KEY_EQUAL",
+ [KEY_BACKSPACE] = "KEY_BACKSPACE",
+ [KEY_TAB] = "KEY_TAB",
+ [KEY_Q] = "KEY_Q",
+ [KEY_W] = "KEY_W",
+ [KEY_E] = "KEY_E",
+ [KEY_R] = "KEY_R",
+ [KEY_T] = "KEY_T",
+ [KEY_Y] = "KEY_Y",
+ [KEY_U] = "KEY_U",
+ [KEY_I] = "KEY_I",
+ [KEY_O] = "KEY_O",
+ [KEY_P] = "KEY_P",
+ [KEY_LEFTBRACE] = "KEY_LEFTBRACE",
+ [KEY_RIGHTBRACE] = "KEY_RIGHTBRACE",
+ [KEY_ENTER] = "KEY_ENTER",
+ [KEY_LEFTCTRL] = "KEY_LEFTCTRL",
+ [KEY_A] = "KEY_A",
+ [KEY_S] = "KEY_S",
+ [KEY_D] = "KEY_D",
+ [KEY_F] = "KEY_F",
+ [KEY_G] = "KEY_G",
+ [KEY_H] = "KEY_H",
+ [KEY_J] = "KEY_J",
+ [KEY_K] = "KEY_K",
+ [KEY_L] = "KEY_L",
+ [KEY_SEMICOLON] = "KEY_SEMICOLON",
+ [KEY_APOSTROPHE] = "KEY_APOSTROPHE",
+ [KEY_GRAVE] = "KEY_GRAVE",
+ [KEY_LEFTSHIFT] = "KEY_LEFTSHIFT",
+ [KEY_BACKSLASH] = "KEY_BACKSLASH",
+ [KEY_Z] = "KEY_Z",
+ [KEY_X] = "KEY_X",
+ [KEY_C] = "KEY_C",
+ [KEY_V] = "KEY_V",
+ [KEY_B] = "KEY_B",
+ [KEY_N] = "KEY_N",
+ [KEY_M] = "KEY_M",
+ [KEY_COMMA] = "KEY_COMMA",
+ [KEY_DOT] = "KEY_DOT",
+ [KEY_SLASH] = "KEY_SLASH",
+ [KEY_RIGHTSHIFT] = "KEY_RIGHTSHIFT",
+ [KEY_KPASTERISK] = "KEY_KPASTERISK",
+ [KEY_LEFTALT] = "KEY_LEFTALT",
+ [KEY_SPACE] = "KEY_SPACE",
+ [KEY_CAPSLOCK] = "KEY_CAPSLOCK",
+ [KEY_F1] = "KEY_F1",
+ [KEY_F2] = "KEY_F2",
+ [KEY_F3] = "KEY_F3",
+ [KEY_F4] = "KEY_F4",
+ [KEY_F5] = "KEY_F5",
+ [KEY_F6] = "KEY_F6",
+ [KEY_F7] = "KEY_F7",
+ [KEY_F8] = "KEY_F8",
+ [KEY_F9] = "KEY_F9",
+ [KEY_F10] = "KEY_F10",
+ [KEY_NUMLOCK] = "KEY_NUMLOCK",
+ [KEY_SCROLLLOCK] = "KEY_SCROLLLOCK",
+ [KEY_KP7] = "KEY_KP7",
+ [KEY_KP8] = "KEY_KP8",
+ [KEY_KP9] = "KEY_KP9",
+ [KEY_KPMINUS] = "KEY_KPMINUS",
+ [KEY_KP4] = "KEY_KP4",
+ [KEY_KP5] = "KEY_KP5",
+ [KEY_KP6] = "KEY_KP6",
+ [KEY_KPPLUS] = "KEY_KPPLUS",
+ [KEY_KP1] = "KEY_KP1",
+ [KEY_KP2] = "KEY_KP2",
+ [KEY_KP3] = "KEY_KP3",
+ [KEY_KP0] = "KEY_KP0",
+ [KEY_KPDOT] = "KEY_KPDOT",
+ [KEY_ZENKAKUHANKAKU] = "KEY_ZENKAKUHANKAKU",
+ [KEY_102ND] = "KEY_102ND",
+ [KEY_F11] = "KEY_F11",
+ [KEY_F12] = "KEY_F12",
+ [KEY_RO] = "KEY_RO",
+ [KEY_KATAKANA] = "KEY_KATAKANA",
+ [KEY_HIRAGANA] = "KEY_HIRAGANA",
+ [KEY_HENKAN] = "KEY_HENKAN",
+ [KEY_KATAKANAHIRAGANA] = "KEY_KATAKANAHIRAGANA",
+ [KEY_MUHENKAN] = "KEY_MUHENKAN",
+ [KEY_KPJPCOMMA] = "KEY_KPJPCOMMA",
+ [KEY_KPENTER] = "KEY_KPENTER",
+ [KEY_RIGHTCTRL] = "KEY_RIGHTCTRL",
+ [KEY_KPSLASH] = "KEY_KPSLASH",
+ [KEY_SYSRQ] = "KEY_SYSRQ",
+ [KEY_RIGHTALT] = "KEY_RIGHTALT",
+ [KEY_LINEFEED] = "KEY_LINEFEED",
+ [KEY_HOME] = "KEY_HOME",
+ [KEY_UP] = "KEY_UP",
+ [KEY_PAGEUP] = "KEY_PAGEUP",
+ [KEY_LEFT] = "KEY_LEFT",
+ [KEY_RIGHT] = "KEY_RIGHT",
+ [KEY_END] = "KEY_END",
+ [KEY_DOWN] = "KEY_DOWN",
+ [KEY_PAGEDOWN] = "KEY_PAGEDOWN",
+ [KEY_INSERT] = "KEY_INSERT",
+ [KEY_DELETE] = "KEY_DELETE",
+ [KEY_MACRO] = "KEY_MACRO",
+ [KEY_MUTE] = "KEY_MUTE",
+ [KEY_VOLUMEDOWN] = "KEY_VOLUMEDOWN",
+ [KEY_VOLUMEUP] = "KEY_VOLUMEUP",
+ [KEY_POWER] = "KEY_POWER",
+ [KEY_KPEQUAL] = "KEY_KPEQUAL",
+ [KEY_KPPLUSMINUS] = "KEY_KPPLUSMINUS",
+ [KEY_PAUSE] = "KEY_PAUSE",
+ [KEY_SCALE] = "KEY_SCALE",
+ [KEY_KPCOMMA] = "KEY_KPCOMMA",
+ [KEY_HANGEUL] = "KEY_HANGEUL",
+ [KEY_HANGUEL] = "KEY_HANGUEL",
+ [KEY_HANJA] = "KEY_HANJA",
+ [KEY_YEN] = "KEY_YEN",
+ [KEY_LEFTMETA] = "KEY_LEFTMETA",
+ [KEY_RIGHTMETA] = "KEY_RIGHTMETA",
+ [KEY_COMPOSE] = "KEY_COMPOSE",
+ [KEY_STOP] = "KEY_STOP",
+ [KEY_AGAIN] = "KEY_AGAIN",
+ [KEY_PROPS] = "KEY_PROPS",
+ [KEY_UNDO] = "KEY_UNDO",
+ [KEY_FRONT] = "KEY_FRONT",
+ [KEY_COPY] = "KEY_COPY",
+ [KEY_OPEN] = "KEY_OPEN",
+ [KEY_PASTE] = "KEY_PASTE",
+ [KEY_FIND] = "KEY_FIND",
+ [KEY_CUT] = "KEY_CUT",
+ [KEY_HELP] = "KEY_HELP",
+ [KEY_MENU] = "KEY_MENU",
+ [KEY_CALC] = "KEY_CALC",
+ [KEY_SETUP] = "KEY_SETUP",
+ [KEY_SLEEP] = "KEY_SLEEP",
+ [KEY_WAKEUP] = "KEY_WAKEUP",
+ [KEY_FILE] = "KEY_FILE",
+ [KEY_SENDFILE] = "KEY_SENDFILE",
+ [KEY_DELETEFILE] = "KEY_DELETEFILE",
+ [KEY_XFER] = "KEY_XFER",
+ [KEY_PROG1] = "KEY_PROG1",
+ [KEY_PROG2] = "KEY_PROG2",
+ [KEY_WWW] = "KEY_WWW",
+ [KEY_MSDOS] = "KEY_MSDOS",
+ [KEY_COFFEE] = "KEY_COFFEE",
+ [KEY_SCREENLOCK] = "KEY_SCREENLOCK",
+ [KEY_DIRECTION] = "KEY_DIRECTION",
+ [KEY_CYCLEWINDOWS] = "KEY_CYCLEWINDOWS",
+ [KEY_MAIL] = "KEY_MAIL",
+ [KEY_BOOKMARKS] = "KEY_BOOKMARKS",
+ [KEY_COMPUTER] = "KEY_COMPUTER",
+ [KEY_BACK] = "KEY_BACK",
+ [KEY_FORWARD] = "KEY_FORWARD",
+ [KEY_CLOSECD] = "KEY_CLOSECD",
+ [KEY_EJECTCD] = "KEY_EJECTCD",
+ [KEY_EJECTCLOSECD] = "KEY_EJECTCLOSECD",
+ [KEY_NEXTSONG] = "KEY_NEXTSONG",
+ [KEY_PLAYPAUSE] = "KEY_PLAYPAUSE",
+ [KEY_PREVIOUSSONG] = "KEY_PREVIOUSSONG",
+ [KEY_STOPCD] = "KEY_STOPCD",
+ [KEY_RECORD] = "KEY_RECORD",
+ [KEY_REWIND] = "KEY_REWIND",
+ [KEY_PHONE] = "KEY_PHONE",
+ [KEY_ISO] = "KEY_ISO",
+ [KEY_CONFIG] = "KEY_CONFIG",
+ [KEY_HOMEPAGE] = "KEY_HOMEPAGE",
+ [KEY_REFRESH] = "KEY_REFRESH",
+ [KEY_EXIT] = "KEY_EXIT",
+ [KEY_MOVE] = "KEY_MOVE",
+ [KEY_EDIT] = "KEY_EDIT",
+ [KEY_SCROLLUP] = "KEY_SCROLLUP",
+ [KEY_SCROLLDOWN] = "KEY_SCROLLDOWN",
+ [KEY_KPLEFTPAREN] = "KEY_KPLEFTPAREN",
+ [KEY_KPRIGHTPAREN] = "KEY_KPRIGHTPAREN",
+ [KEY_NEW] = "KEY_NEW",
+ [KEY_REDO] = "KEY_REDO",
+ [KEY_F13] = "KEY_F13",
+ [KEY_F14] = "KEY_F14",
+ [KEY_F15] = "KEY_F15",
+ [KEY_F16] = "KEY_F16",
+ [KEY_F17] = "KEY_F17",
+ [KEY_F18] = "KEY_F18",
+ [KEY_F19] = "KEY_F19",
+ [KEY_F20] = "KEY_F20",
+ [KEY_F21] = "KEY_F21",
+ [KEY_F22] = "KEY_F22",
+ [KEY_F23] = "KEY_F23",
+ [KEY_F24] = "KEY_F24",
+ [KEY_PLAYCD] = "KEY_PLAYCD",
+ [KEY_PAUSECD] = "KEY_PAUSECD",
+ [KEY_PROG3] = "KEY_PROG3",
+ [KEY_PROG4] = "KEY_PROG4",
+ [KEY_DASHBOARD] = "KEY_DASHBOARD",
+ [KEY_SUSPEND] = "KEY_SUSPEND",
+ [KEY_CLOSE] = "KEY_CLOSE",
+ [KEY_PLAY] = "KEY_PLAY",
+ [KEY_FASTFORWARD] = "KEY_FASTFORWARD",
+ [KEY_BASSBOOST] = "KEY_BASSBOOST",
+ [KEY_PRINT] = "KEY_PRINT",
+ [KEY_HP] = "KEY_HP",
+ [KEY_CAMERA] = "KEY_CAMERA",
+ [KEY_SOUND] = "KEY_SOUND",
+ [KEY_QUESTION] = "KEY_QUESTION",
+ [KEY_EMAIL] = "KEY_EMAIL",
+ [KEY_CHAT] = "KEY_CHAT",
+ [KEY_SEARCH] = "KEY_SEARCH",
+ [KEY_CONNECT] = "KEY_CONNECT",
+ [KEY_FINANCE] = "KEY_FINANCE",
+ [KEY_SPORT] = "KEY_SPORT",
+ [KEY_SHOP] = "KEY_SHOP",
+ [KEY_ALTERASE] = "KEY_ALTERASE",
+ [KEY_CANCEL] = "KEY_CANCEL",
+ [KEY_BRIGHTNESSDOWN] = "KEY_BRIGHTNESSDOWN",
+ [KEY_BRIGHTNESSUP] = "KEY_BRIGHTNESSUP",
+ [KEY_MEDIA] = "KEY_MEDIA",
+ [KEY_SWITCHVIDEOMODE] = "KEY_SWITCHVIDEOMODE",
+ [KEY_KBDILLUMTOGGLE] = "KEY_KBDILLUMTOGGLE",
+ [KEY_KBDILLUMDOWN] = "KEY_KBDILLUMDOWN",
+ [KEY_KBDILLUMUP] = "KEY_KBDILLUMUP",
+ [KEY_SEND] = "KEY_SEND",
+ [KEY_REPLY] = "KEY_REPLY",
+ [KEY_FORWARDMAIL] = "KEY_FORWARDMAIL",
+ [KEY_SAVE] = "KEY_SAVE",
+ [KEY_DOCUMENTS] = "KEY_DOCUMENTS",
+ [KEY_BATTERY] = "KEY_BATTERY",
+ [KEY_BLUETOOTH] = "KEY_BLUETOOTH",
+ [KEY_WLAN] = "KEY_WLAN",
+ [KEY_UWB] = "KEY_UWB",
+ [KEY_UNKNOWN] = "KEY_UNKNOWN",
+ [KEY_VIDEO_NEXT] = "KEY_VIDEO_NEXT",
+ [KEY_VIDEO_PREV] = "KEY_VIDEO_PREV",
+ [KEY_BRIGHTNESS_CYCLE] = "KEY_BRIGHTNESS_CYCLE",
+ [KEY_BRIGHTNESS_ZERO] = "KEY_BRIGHTNESS_ZERO",
+ [KEY_DISPLAY_OFF] = "KEY_DISPLAY_OFF",
+ [KEY_WIMAX] = "KEY_WIMAX",
+ [KEY_OK] = "KEY_OK",
+ [KEY_SELECT] = "KEY_SELECT",
+ [KEY_GOTO] = "KEY_GOTO",
+ [KEY_CLEAR] = "KEY_CLEAR",
+ [KEY_POWER2] = "KEY_POWER2",
+ [KEY_OPTION] = "KEY_OPTION",
+ [KEY_INFO] = "KEY_INFO",
+ [KEY_TIME] = "KEY_TIME",
+ [KEY_VENDOR] = "KEY_VENDOR",
+ [KEY_ARCHIVE] = "KEY_ARCHIVE",
+ [KEY_PROGRAM] = "KEY_PROGRAM",
+ [KEY_CHANNEL] = "KEY_CHANNEL",
+ [KEY_FAVORITES] = "KEY_FAVORITES",
+ [KEY_EPG] = "KEY_EPG",
+ [KEY_PVR] = "KEY_PVR",
+ [KEY_MHP] = "KEY_MHP",
+ [KEY_LANGUAGE] = "KEY_LANGUAGE",
+ [KEY_TITLE] = "KEY_TITLE",
+ [KEY_SUBTITLE] = "KEY_SUBTITLE",
+ [KEY_ANGLE] = "KEY_ANGLE",
+ [KEY_ZOOM] = "KEY_ZOOM",
+ [KEY_MODE] = "KEY_MODE",
+ [KEY_KEYBOARD] = "KEY_KEYBOARD",
+ [KEY_SCREEN] = "KEY_SCREEN",
+ [KEY_PC] = "KEY_PC",
+ [KEY_TV] = "KEY_TV",
+ [KEY_TV2] = "KEY_TV2",
+ [KEY_VCR] = "KEY_VCR",
+ [KEY_VCR2] = "KEY_VCR2",
+ [KEY_SAT] = "KEY_SAT",
+ [KEY_SAT2] = "KEY_SAT2",
+ [KEY_CD] = "KEY_CD",
+ [KEY_TAPE] = "KEY_TAPE",
+ [KEY_RADIO] = "KEY_RADIO",
+ [KEY_TUNER] = "KEY_TUNER",
+ [KEY_PLAYER] = "KEY_PLAYER",
+ [KEY_TEXT] = "KEY_TEXT",
+ [KEY_DVD] = "KEY_DVD",
+ [KEY_AUX] = "KEY_AUX",
+ [KEY_MP3] = "KEY_MP3",
+ [KEY_AUDIO] = "KEY_AUDIO",
+ [KEY_VIDEO] = "KEY_VIDEO",
+ [KEY_DIRECTORY] = "KEY_DIRECTORY",
+ [KEY_LIST] = "KEY_LIST",
+ [KEY_MEMO] = "KEY_MEMO",
+ [KEY_CALENDAR] = "KEY_CALENDAR",
+ [KEY_RED] = "KEY_RED",
+ [KEY_GREEN] = "KEY_GREEN",
+ [KEY_YELLOW] = "KEY_YELLOW",
+ [KEY_BLUE] = "KEY_BLUE",
+ [KEY_CHANNELUP] = "KEY_CHANNELUP",
+ [KEY_CHANNELDOWN] = "KEY_CHANNELDOWN",
+ [KEY_FIRST] = "KEY_FIRST",
+ [KEY_LAST] = "KEY_LAST",
+ [KEY_AB] = "KEY_AB",
+ [KEY_NEXT] = "KEY_NEXT",
+ [KEY_RESTART] = "KEY_RESTART",
+ [KEY_SLOW] = "KEY_SLOW",
+ [KEY_SHUFFLE] = "KEY_SHUFFLE",
+ [KEY_BREAK] = "KEY_BREAK",
+ [KEY_PREVIOUS] = "KEY_PREVIOUS",
+ [KEY_DIGITS] = "KEY_DIGITS",
+ [KEY_TEEN] = "KEY_TEEN",
+ [KEY_TWEN] = "KEY_TWEN",
+ [KEY_VIDEOPHONE] = "KEY_VIDEOPHONE",
+ [KEY_GAMES] = "KEY_GAMES",
+ [KEY_ZOOMIN] = "KEY_ZOOMIN",
+ [KEY_ZOOMOUT] = "KEY_ZOOMOUT",
+ [KEY_ZOOMRESET] = "KEY_ZOOMRESET",
+ [KEY_WORDPROCESSOR] = "KEY_WORDPROCESSOR",
+ [KEY_EDITOR] = "KEY_EDITOR",
+ [KEY_SPREADSHEET] = "KEY_SPREADSHEET",
+ [KEY_GRAPHICSEDITOR] = "KEY_GRAPHICSEDITOR",
+ [KEY_PRESENTATION] = "KEY_PRESENTATION",
+ [KEY_DATABASE] = "KEY_DATABASE",
+ [KEY_NEWS] = "KEY_NEWS",
+ [KEY_VOICEMAIL] = "KEY_VOICEMAIL",
+ [KEY_ADDRESSBOOK] = "KEY_ADDRESSBOOK",
+ [KEY_MESSENGER] = "KEY_MESSENGER",
+ [KEY_DISPLAYTOGGLE] = "KEY_DISPLAYTOGGLE",
+ [KEY_SPELLCHECK] = "KEY_SPELLCHECK",
+ [KEY_LOGOFF] = "KEY_LOGOFF",
+ [KEY_DOLLAR] = "KEY_DOLLAR",
+ [KEY_EURO] = "KEY_EURO",
+ [KEY_FRAMEBACK] = "KEY_FRAMEBACK",
+ [KEY_FRAMEFORWARD] = "KEY_FRAMEFORWARD",
+ [KEY_CONTEXT_MENU] = "KEY_CONTEXT_MENU",
+ [KEY_MEDIA_REPEAT] = "KEY_MEDIA_REPEAT",
+ [KEY_10CHANNELSUP] = "KEY_10CHANNELSUP",
+ [KEY_10CHANNELSDOWN] = "KEY_10CHANNELSDOWN",
+ [KEY_DEL_EOL] = "KEY_DEL_EOL",
+ [KEY_DEL_EOS] = "KEY_DEL_EOS",
+ [KEY_INS_LINE] = "KEY_INS_LINE",
+ [KEY_DEL_LINE] = "KEY_DEL_LINE",
+ [KEY_FN] = "KEY_FN",
+ [KEY_FN_ESC] = "KEY_FN_ESC",
+ [KEY_FN_F1] = "KEY_FN_F1",
+ [KEY_FN_F2] = "KEY_FN_F2",
+ [KEY_FN_F3] = "KEY_FN_F3",
+ [KEY_FN_F4] = "KEY_FN_F4",
+ [KEY_FN_F5] = "KEY_FN_F5",
+ [KEY_FN_F6] = "KEY_FN_F6",
+ [KEY_FN_F7] = "KEY_FN_F7",
+ [KEY_FN_F8] = "KEY_FN_F8",
+ [KEY_FN_F9] = "KEY_FN_F9",
+ [KEY_FN_F10] = "KEY_FN_F10",
+ [KEY_FN_F11] = "KEY_FN_F11",
+ [KEY_FN_F12] = "KEY_FN_F12",
+ [KEY_FN_1] = "KEY_FN_1",
+ [KEY_FN_2] = "KEY_FN_2",
+ [KEY_FN_D] = "KEY_FN_D",
+ [KEY_FN_E] = "KEY_FN_E",
+ [KEY_FN_F] = "KEY_FN_F",
+ [KEY_FN_S] = "KEY_FN_S",
+ [KEY_FN_B] = "KEY_FN_B",
+ [KEY_BRL_DOT1] = "KEY_BRL_DOT1",
+ [KEY_BRL_DOT2] = "KEY_BRL_DOT2",
+ [KEY_BRL_DOT3] = "KEY_BRL_DOT3",
+ [KEY_BRL_DOT4] = "KEY_BRL_DOT4",
+ [KEY_BRL_DOT5] = "KEY_BRL_DOT5",
+ [KEY_BRL_DOT6] = "KEY_BRL_DOT6",
+ [KEY_BRL_DOT7] = "KEY_BRL_DOT7",
+ [KEY_BRL_DOT8] = "KEY_BRL_DOT8",
+ [KEY_BRL_DOT9] = "KEY_BRL_DOT9",
+ [KEY_BRL_DOT10] = "KEY_BRL_DOT10",
+ [KEY_NUMERIC_0] = "KEY_NUMERIC_0",
+ [KEY_NUMERIC_1] = "KEY_NUMERIC_1",
+ [KEY_NUMERIC_2] = "KEY_NUMERIC_2",
+ [KEY_NUMERIC_3] = "KEY_NUMERIC_3",
+ [KEY_NUMERIC_4] = "KEY_NUMERIC_4",
+ [KEY_NUMERIC_5] = "KEY_NUMERIC_5",
+ [KEY_NUMERIC_6] = "KEY_NUMERIC_6",
+ [KEY_NUMERIC_7] = "KEY_NUMERIC_7",
+ [KEY_NUMERIC_8] = "KEY_NUMERIC_8",
+ [KEY_NUMERIC_9] = "KEY_NUMERIC_9",
+ [KEY_NUMERIC_STAR] = "KEY_NUMERIC_STAR",
+ [KEY_NUMERIC_POUND] = "KEY_NUMERIC_POUND",
+ [KEY_RFKILL] = "KEY_RFKILL",
+ [KEY_MIN_INTERESTING] = "KEY_MIN_INTERESTING",
+ [KEY_MAX] = "KEY_MAX",
+ [KEY_CNT] = "KEY_CNT",
diff --git a/vl.c b/vl.c
index fbb77fe..85f2d37 100644
--- a/vl.c
+++ b/vl.c
@@ -3041,6 +3041,11 @@ int main(int argc, char **argv, char **envp)
fprintf(stderr, "SDL support is disabled\n");
exit(1);
#endif
+#ifdef CONFIG_LINUX
+ case QEMU_OPTION_fbdev:
+ display_type = DT_FBDEV;
+ break;
+#endif
case QEMU_OPTION_pidfile:
pid_file = optarg;
break;
@@ -3681,6 +3686,20 @@ int main(int argc, char **argv, char **envp)
curses_display_init(ds, full_screen);
break;
#endif
+#if defined(CONFIG_LINUX)
+ case DT_FBDEV:
+ {
+ Error *errp = NULL;
+ if (fbdev_display_init(ds, NULL, &errp) != 0) {
+ if (error_is_set(&errp)) {
+ fprintf(stderr, "%s\n", error_get_pretty(errp));
+ error_free(errp);
+ }
+ exit(1);
+ }
+ break;
+ }
+#endif
#if defined(CONFIG_SDL)
case DT_SDL:
sdl_display_init(ds, full_screen, no_frame);
--
1.7.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
` (3 preceding siblings ...)
2012-09-19 11:15 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann
@ 2012-09-19 11:15 ` Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time Gerd Hoffmann
` (3 subsequent siblings)
8 siblings, 0 replies; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
This patch adds a fbdev monitor command to enable/disable
the fbdev display at runtime to both qmp and hmp.
qmp: framebuffer-display enable=on|off
hmp: framebuffer-display on|off
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
hmp-commands.hx | 15 +++++++++++++++
hmp.c | 9 +++++++++
hmp.h | 1 +
qapi-schema.json | 13 +++++++++++++
qmp-commands.hx | 6 ++++++
qmp.c | 19 +++++++++++++++++++
6 files changed, 63 insertions(+), 0 deletions(-)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index ed67e99..93e75ab 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1377,6 +1377,21 @@ passed since 1970, i.e. unix epoch.
ETEXI
{
+ .name = "framebuffer-display",
+ .args_type = "enable:b",
+ .params = "on|off",
+ .help = "enable/disable linux console framebuffer display",
+ .mhandler.cmd = hmp_framebuffer_display,
+ },
+
+STEXI
+@item framebuffer-display on | off
+@findex framebuffer-display
+
+enable/disable linux console framebuffer display.
+ETEXI
+
+ {
.name = "info",
.args_type = "item:s?",
.params = "[subcommand]",
diff --git a/hmp.c b/hmp.c
index ba6fbd3..9677ef6 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1168,3 +1168,12 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
qmp_screendump(filename, &err);
hmp_handle_error(mon, &err);
}
+
+void hmp_framebuffer_display(Monitor *mon, const QDict *qdict)
+{
+ int enable = qdict_get_bool(qdict, "enable");
+ Error *errp = NULL;
+
+ qmp_framebuffer_display(enable, &errp);
+ hmp_handle_error(mon, &errp);
+}
diff --git a/hmp.h b/hmp.h
index 48b9c59..20aa08c 100644
--- a/hmp.h
+++ b/hmp.h
@@ -73,5 +73,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict);
void hmp_closefd(Monitor *mon, const QDict *qdict);
void hmp_send_key(Monitor *mon, const QDict *qdict);
void hmp_screen_dump(Monitor *mon, const QDict *qdict);
+void hmp_framebuffer_display(Monitor *mon, const QDict *qdict);
#endif
diff --git a/qapi-schema.json b/qapi-schema.json
index 14e4419..87c77f7 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2619,3 +2619,16 @@
# Since: 0.14.0
##
{ 'command': 'screendump', 'data': {'filename': 'str'} }
+
+# @framebuffer-display:
+#
+# Enable/disable linux console framebuffer display.
+#
+# @enable: whenever the framebuffer display should be enabled or disabled.
+#
+# Returns: Nothing.
+#
+# Since: 1.3
+#
+##
+{ 'command': 'framebuffer-display', 'data': {'enable': 'bool'} }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 6e21ddb..e0a747a 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2539,3 +2539,9 @@ EQMP
.args_type = "",
.mhandler.cmd_new = qmp_marshal_input_query_target,
},
+
+ {
+ .name = "framebuffer-display",
+ .args_type = "enable:b",
+ .mhandler.cmd_new = qmp_marshal_input_framebuffer_display,
+ },
diff --git a/qmp.c b/qmp.c
index 8463922..56e007f 100644
--- a/qmp.c
+++ b/qmp.c
@@ -391,6 +391,25 @@ void qmp_change(const char *device, const char *target,
}
}
+void qmp_framebuffer_display(bool enable, Error **errp)
+{
+#if defined(CONFIG_LINUX)
+ DisplayState *ds = get_displaystate();
+
+ if (enable) {
+ if (fbdev_display_init(ds, NULL, errp) != 0) {
+ if (!error_is_set(errp)) {
+ error_setg(errp, "fbdev initialization failed");
+ }
+ }
+ } else {
+ fbdev_display_uninit(ds);
+ }
+#else
+ error_setg(errp, "fbdev support disabled at compile time");
+#endif
+}
+
static void qom_list_types_tramp(ObjectClass *klass, void *data)
{
ObjectTypeInfoList *e, **pret = data;
--
1.7.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time.
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
` (4 preceding siblings ...)
2012-09-19 11:15 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann
@ 2012-09-19 11:15 ` Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann
` (2 subsequent siblings)
8 siblings, 0 replies; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Add CONFIG_FBDEV, add --enable-fbdev and --disable-fbdev
configure switches so fbdev can be enabled/disabled at
compile time.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
configure | 12 ++++++++++++
qmp.c | 2 +-
ui/Makefile.objs | 2 +-
vl.c | 4 ++--
4 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/configure b/configure
index 8564142..c4ba338 100755
--- a/configure
+++ b/configure
@@ -148,6 +148,7 @@ docs=""
fdt=""
nptl=""
sdl=""
+fbdev="no"
virtfs=""
vnc="yes"
sparse="no"
@@ -527,6 +528,7 @@ Haiku)
usb="linux"
kvm="yes"
vhost_net="yes"
+ fbdev="yes"
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
audio_possible_drivers="$audio_possible_drivers fmod"
fi
@@ -658,6 +660,10 @@ for opt do
;;
--enable-sdl) sdl="yes"
;;
+ --disable-fbdev) fbdev="no"
+ ;;
+ --enable-fbdev) fbdev="yes"
+ ;;
--disable-virtfs) virtfs="no"
;;
--enable-virtfs) virtfs="yes"
@@ -1070,6 +1076,8 @@ echo " --disable-strip disable stripping binaries"
echo " --disable-werror disable compilation abort on warning"
echo " --disable-sdl disable SDL"
echo " --enable-sdl enable SDL"
+echo " --disable-fbdev disable linux framebuffer"
+echo " --enable-fbdev enable linux framebuffer"
echo " --disable-virtfs disable VirtFS"
echo " --enable-virtfs enable VirtFS"
echo " --disable-vnc disable VNC"
@@ -3159,6 +3167,7 @@ if test "$darwin" = "yes" ; then
echo "Cocoa support $cocoa"
fi
echo "SDL support $sdl"
+echo "fbdev support $fbdev"
echo "curses support $curses"
echo "curl support $curl"
echo "mingw32 support $mingw32"
@@ -3367,6 +3376,9 @@ if test "$sdl" = "yes" ; then
echo "CONFIG_SDL=y" >> $config_host_mak
echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak
fi
+if test "$fbdev" = "yes" ; then
+ echo "CONFIG_FBDEV=y" >> $config_host_mak
+fi
if test "$cocoa" = "yes" ; then
echo "CONFIG_COCOA=y" >> $config_host_mak
fi
diff --git a/qmp.c b/qmp.c
index 56e007f..712ab58 100644
--- a/qmp.c
+++ b/qmp.c
@@ -393,7 +393,7 @@ void qmp_change(const char *device, const char *target,
void qmp_framebuffer_display(bool enable, Error **errp)
{
-#if defined(CONFIG_LINUX)
+#if defined(CONFIG_FBDEV)
DisplayState *ds = get_displaystate();
if (enable) {
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 55ddcf2..479cd01 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -12,4 +12,4 @@ common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
common-obj-$(CONFIG_COCOA) += cocoa.o
common-obj-$(CONFIG_CURSES) += curses.o
common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
-common-obj-$(CONFIG_LINUX) += fbdev.o
+common-obj-$(CONFIG_FBDEV) += fbdev.o
diff --git a/vl.c b/vl.c
index 85f2d37..4159035 100644
--- a/vl.c
+++ b/vl.c
@@ -3041,7 +3041,7 @@ int main(int argc, char **argv, char **envp)
fprintf(stderr, "SDL support is disabled\n");
exit(1);
#endif
-#ifdef CONFIG_LINUX
+#ifdef CONFIG_FBDEV
case QEMU_OPTION_fbdev:
display_type = DT_FBDEV;
break;
@@ -3686,7 +3686,7 @@ int main(int argc, char **argv, char **envp)
curses_display_init(ds, full_screen);
break;
#endif
-#if defined(CONFIG_LINUX)
+#if defined(CONFIG_FBDEV)
case DT_FBDEV:
{
Error *errp = NULL;
--
1.7.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [Qemu-devel] [PATCH 7/9] fbdev: move to pixman
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
` (5 preceding siblings ...)
2012-09-19 11:15 ` [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time Gerd Hoffmann
@ 2012-09-19 11:15 ` Gerd Hoffmann
2012-09-19 18:10 ` Stefano Stabellini
2012-09-19 11:15 ` [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support Gerd Hoffmann
8 siblings, 1 reply; 26+ 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] 26+ messages in thread
* [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
` (6 preceding siblings ...)
2012-09-19 11:15 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann
@ 2012-09-19 11:15 ` Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support Gerd Hoffmann
8 siblings, 0 replies; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Add mouse_set and cursor_define DisplayChangeListener callbacks
and mouse pointer rendering support.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
ui/fbdev.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 95 insertions(+), 0 deletions(-)
diff --git a/ui/fbdev.c b/ui/fbdev.c
index d55850e..d859d84 100644
--- a/ui/fbdev.c
+++ b/ui/fbdev.c
@@ -82,6 +82,12 @@ static pixman_image_t *framebuffer;
static pixman_transform_t transform;
static pixman_region16_t dirty;
+static QEMUCursor *ptr_cursor;
+static pixman_image_t *ptr_image;
+static int ptr_refresh;
+static int px, py, pw, ph;
+static int mx, my, mon;
+
/* fwd decls */
static int fbdev_activate_vt(int tty, int vtno, bool wait);
@@ -877,6 +883,51 @@ static void fbdev_render(DisplayState *ds)
pixman_region_init(&dirty);
}
+static void fbdev_unrender_ptr(DisplayState *ds)
+{
+ if (!pw && !ph) {
+ return;
+ }
+ pixman_region_union_rect(&dirty, &dirty, px, py, pw, ph);
+ ph = pw = 0;
+}
+
+static void fbdev_render_ptr(DisplayState *ds)
+{
+ pixman_region16_t region;
+ pixman_transform_t transform;
+
+ if (!mon || !ptr_image) {
+ return;
+ }
+ if (mx < 0 || mx >= cw || my < 0 || my >= ch) {
+ return;
+ }
+
+ px = mx - ptr_cursor->hot_x;
+ py = my - ptr_cursor->hot_y;
+ pw = ptr_cursor->width;
+ ph = ptr_cursor->height;
+
+ pixman_transform_init_identity(&transform);
+ pixman_transform_translate(&transform, NULL,
+ pixman_int_to_fixed(-cx),
+ pixman_int_to_fixed(-cy));
+ pixman_transform_translate(&transform, NULL,
+ pixman_int_to_fixed(-px),
+ pixman_int_to_fixed(-py));
+ pixman_image_set_transform(ptr_image, &transform);
+
+ pixman_region_init_rect(®ion, 0, 0, pw, ph);
+ pixman_image_set_clip_region(ptr_image, ®ion);
+
+ pixman_image_composite(PIXMAN_OP_OVER, ptr_image, NULL, framebuffer,
+ 0, 0, 0, 0, 0, 0, fb_var.xres, fb_var.yres);
+
+ pixman_region_fini(®ion);
+ ptr_refresh = 0;
+}
+
/* -------------------------------------------------------------------- */
/* qemu interfaces */
@@ -918,6 +969,9 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
}
pixman_region_union_rect(&dirty, &dirty, x, y, w, h);
+ if (ptr_image && mon && pw && ph) {
+ ptr_refresh++;
+ }
}
static void fbdev_resize(DisplayState *ds)
@@ -954,9 +1008,48 @@ static void fbdev_refresh(DisplayState *ds)
fbdev_update(ds, 0, 0, 0, 0);
}
+ if (ptr_refresh) {
+ fbdev_unrender_ptr(ds);
+ }
if (pixman_region_not_empty(&dirty)) {
fbdev_render(ds);
}
+ if (ptr_refresh) {
+ fbdev_render_ptr(ds);
+ }
+}
+
+static void fbdev_mouse_set(DisplayState *ds, int x, int y, int on)
+{
+ ptr_refresh++;
+ mx = x;
+ my = y;
+ mon = on;
+}
+
+static void fbdev_cursor_define(DisplayState *ds, QEMUCursor *cursor)
+{
+ ptr_refresh++;
+
+ if (ptr_cursor) {
+ cursor_put(ptr_cursor);
+ ptr_cursor = NULL;
+ }
+ if (ptr_image) {
+ pixman_image_unref(ptr_image);
+ ptr_image = NULL;
+ }
+
+ if (!cursor) {
+ return;
+ }
+
+ ptr_cursor = cursor;
+ cursor_get(ptr_cursor);
+ ptr_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
+ cursor->width, cursor->height,
+ cursor->data,
+ cursor->width * 4);
}
static void fbdev_exit_notifier(Notifier *notifier, void *data)
@@ -985,6 +1078,8 @@ int fbdev_display_init(DisplayState *ds, const char *device, Error **err)
dcl->dpy_resize = fbdev_resize;
dcl->dpy_setdata = fbdev_setdata;
dcl->dpy_refresh = fbdev_refresh;
+ dcl->dpy_mouse_set = fbdev_mouse_set;
+ dcl->dpy_cursor_define = fbdev_cursor_define;
register_displaychangelistener(ds, dcl);
trace_fbdev_enabled();
--
1.7.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
` (7 preceding siblings ...)
2012-09-19 11:15 ` [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support Gerd Hoffmann
@ 2012-09-19 11:15 ` Gerd Hoffmann
8 siblings, 0 replies; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-19 11:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Add support for scaling the guest display.
Ctrl-Alt-S hotkey toggles scaling.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
ui/fbdev.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 51 insertions(+), 10 deletions(-)
diff --git a/ui/fbdev.c b/ui/fbdev.c
index d859d84..0f40259 100644
--- a/ui/fbdev.c
+++ b/ui/fbdev.c
@@ -81,6 +81,7 @@ static pixman_image_t *surface;
static pixman_image_t *framebuffer;
static pixman_transform_t transform;
static pixman_region16_t dirty;
+static double scale;
static QEMUCursor *ptr_cursor;
static pixman_image_t *ptr_image;
@@ -88,6 +89,10 @@ static int ptr_refresh;
static int px, py, pw, ph;
static int mx, my, mon;
+/* options */
+static int use_scale = 1;
+static pixman_filter_t pfilter = PIXMAN_FILTER_GOOD;
+
/* fwd decls */
static int fbdev_activate_vt(int tty, int vtno, bool wait);
@@ -182,13 +187,14 @@ static void read_mouse(void *opaque)
if (ay < 0) {
ay = 0;
}
- if (ax >= cw) {
- ax = cw-1;
+ if (ax >= cw*scale) {
+ ax = cw*scale-1;
}
- if (ay >= ch) {
- ay = ch-1;
+ if (ay >= ch*scale) {
+ ay = ch*scale-1;
}
- kbd_mouse_event(ax * 0x7FFF / cw, ay * 0x7FFF / ch, 0, b);
+ kbd_mouse_event(ax * 0x7FFF / (cw*scale),
+ ay * 0x7FFF / (ch*scale), 0, b);
} else {
kbd_mouse_event(x, y, 0, b);
}
@@ -543,6 +549,12 @@ static void read_mediumraw(void *opaque)
"(ctrl-alt-esc) ===\n");
exit(1);
}
+ if (keycode == KEY_S) {
+ use_scale = !use_scale;
+ resize_screen++;
+ redraw_screen++;
+ continue;
+ }
if (keycode >= KEY_F1 && keycode <= KEY_F10) {
fbdev_activate_vt(tty, keycode+1-KEY_F1, false);
key_down[keycode] = false;
@@ -913,6 +925,11 @@ static void fbdev_render_ptr(DisplayState *ds)
pixman_transform_translate(&transform, NULL,
pixman_int_to_fixed(-cx),
pixman_int_to_fixed(-cy));
+ if (use_scale) {
+ pixman_transform_scale(&transform, NULL,
+ pixman_double_to_fixed(1/scale),
+ pixman_double_to_fixed(1/scale));
+ }
pixman_transform_translate(&transform, NULL,
pixman_int_to_fixed(-px),
pixman_int_to_fixed(-py));
@@ -938,16 +955,32 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
}
if (resize_screen) {
+ double xs, ys;
+
trace_fbdev_dpy_resize(ds_get_width(ds), ds_get_height(ds));
resize_screen = 0;
cx = 0; cy = 0;
cw = ds_get_width(ds);
ch = ds_get_height(ds);
- if (ds_get_width(ds) < fb_var.xres) {
- cx = (fb_var.xres - ds_get_width(ds)) / 2;
- }
- if (ds_get_height(ds) < fb_var.yres) {
- cy = (fb_var.yres - ds_get_height(ds)) / 2;
+
+ if (use_scale) {
+ xs = (double)fb_var.xres / cw;
+ ys = (double)fb_var.yres / ch;
+ if (xs > ys) {
+ scale = ys;
+ cx = (fb_var.xres - ds_get_width(ds)*scale) / 2;
+ } else {
+ scale = xs;
+ cy = (fb_var.yres - ds_get_height(ds)*scale) / 2;
+ }
+ } else {
+ scale = 1;
+ if (ds_get_width(ds) < fb_var.xres) {
+ cx = (fb_var.xres - ds_get_width(ds)) / 2;
+ }
+ if (ds_get_height(ds) < fb_var.yres) {
+ cy = (fb_var.yres - ds_get_height(ds)) / 2;
+ }
}
if (surface) {
pixman_image_unref(surface);
@@ -958,7 +991,14 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
pixman_transform_translate(&transform, NULL,
pixman_int_to_fixed(-cx),
pixman_int_to_fixed(-cy));
+ if (use_scale) {
+ pixman_transform_scale(&transform, NULL,
+ pixman_double_to_fixed(1/scale),
+ pixman_double_to_fixed(1/scale));
+ }
pixman_image_set_transform(surface, &transform);
+
+ pixman_image_set_filter(surface, pfilter, NULL, 0);
}
if (redraw_screen) {
@@ -1050,6 +1090,7 @@ static void fbdev_cursor_define(DisplayState *ds, QEMUCursor *cursor)
cursor->width, cursor->height,
cursor->data,
cursor->width * 4);
+ pixman_image_set_filter(ptr_image, pfilter, NULL, 0);
}
static void fbdev_exit_notifier(Notifier *notifier, void *data)
--
1.7.1
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver.
2012-09-19 11:15 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann
@ 2012-09-19 18:09 ` Stefano Stabellini
2012-09-21 12:28 ` Gerd Hoffmann
0 siblings, 1 reply; 26+ messages in thread
From: Stefano Stabellini @ 2012-09-19 18:09 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org
On Wed, 19 Sep 2012, Gerd Hoffmann wrote:
> +/* -------------------------------------------------------------------- */
> +/* rendering */
> +
> +static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
> +{
> + uint8_t *dst;
> + uint8_t *src;
> + int line;
> +
> + if (!conv) {
> + return;
> + }
> +
> + src = ds_get_data(ds) + y * ds_get_linesize(ds)
> + + x * ds_get_bytes_per_pixel(ds);
> + dst = fb_mem + y * fb_fix.line_length
> + + x * fbpf.bytes_per_pixel;
> +
> + dst += cy * fb_fix.line_length;
> + dst += cx * fbpf.bytes_per_pixel;
> +
> + if (h > fb_var.yres - y) {
> + h = fb_var.yres - y;
> + }
> + if (w > fb_var.xres - x) {
> + w = fb_var.xres - x;
> + }
> +
> + for (line = y; line < y+h; line++) {
> + qemu_pf_conv_run(conv, dst, src, w);
> + dst += fb_fix.line_length;
> + src += ds_get_linesize(ds);
> + }
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* qemu interfaces */
> +
> +static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
> +{
> + if (fb_switch_state != FB_ACTIVE) {
> + return;
> + }
> +
> + if (resize_screen) {
> + trace_fbdev_dpy_resize(ds_get_width(ds), ds_get_height(ds));
> + resize_screen = 0;
> + cx = 0; cy = 0;
> + cw = ds_get_width(ds);
> + ch = ds_get_height(ds);
> + if (ds_get_width(ds) < fb_var.xres) {
> + cx = (fb_var.xres - ds_get_width(ds)) / 2;
> + }
> + if (ds_get_height(ds) < fb_var.yres) {
> + cy = (fb_var.yres - ds_get_height(ds)) / 2;
> + }
> +
> + if (conv) {
> + qemu_pf_conv_put(conv);
> + }
> + conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
> + if (conv == NULL) {
> + fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
> + }
> + }
> +
> + if (redraw_screen) {
> + trace_fbdev_dpy_redraw();
> + redraw_screen = 0;
> + fbdev_cls();
> + x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
> + }
> +
> + fbdev_render(ds, x, y, w, h);
> +}
> +
> +static void fbdev_resize(DisplayState *ds)
> +{
> + resize_screen++;
> + redraw_screen++;
> +}
> +
> +static void fbdev_refresh(DisplayState *ds)
> +{
> + switch (fb_switch_state) {
> + case FB_REL_REQ:
> + fbdev_switch_release();
> + case FB_INACTIVE:
> + return;
> + case FB_ACQ_REQ:
> + fbdev_switch_acquire();
> + redraw_screen++;
Rather than introducing redraw_screen, I would just call vga_hw_invalidate
instead here. It is always a win if we can avoid to introduce one more
state machine.
> + case FB_ACTIVE:
> + break;
> + }
> +
> + vga_hw_update();
> + if (redraw_screen) {
> + fbdev_update(ds, 0, 0, 0, 0);
> + }
> +}
^ permalink raw reply [flat|nested] 26+ 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; 26+ 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] 26+ 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; 26+ 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] 26+ 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; 26+ 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] 26+ 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; 26+ 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] 26+ 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; 26+ 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] 26+ 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; 26+ 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] 26+ 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; 26+ 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] 26+ 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; 26+ 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] 26+ 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; 26+ 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] 26+ 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; 26+ 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] 26+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver.
2012-09-19 18:09 ` Stefano Stabellini
@ 2012-09-21 12:28 ` Gerd Hoffmann
2012-09-24 11:06 ` Stefano Stabellini
0 siblings, 1 reply; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-21 12:28 UTC (permalink / raw)
To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org
Hi,
>> + case FB_ACQ_REQ:
>> + fbdev_switch_acquire();
>> + redraw_screen++;
>
> Rather than introducing redraw_screen, I would just call vga_hw_invalidate
> instead here. It is always a win if we can avoid to introduce one more
> state machine.
Wouldn't that make the vga emulation do additional work which is simply
not needed?
cheers,
Gerd
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver.
2012-09-21 12:28 ` Gerd Hoffmann
@ 2012-09-24 11:06 ` Stefano Stabellini
2012-09-24 13:09 ` Gerd Hoffmann
0 siblings, 1 reply; 26+ messages in thread
From: Stefano Stabellini @ 2012-09-24 11:06 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel@nongnu.org, Stefano Stabellini
On Fri, 21 Sep 2012, Gerd Hoffmann wrote:
> Hi,
>
> >> + case FB_ACQ_REQ:
> >> + fbdev_switch_acquire();
> >> + redraw_screen++;
> >
> > Rather than introducing redraw_screen, I would just call vga_hw_invalidate
> > instead here. It is always a win if we can avoid to introduce one more
> > state machine.
>
> Wouldn't that make the vga emulation do additional work which is simply
> not needed?
It causes full_update to be set and that causes vga_draw_line to be
called for every line on the screen.
However in the important cases (32bpp and 16bpp) the buffer is shared
and nothing happens. So no more work.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver.
2012-09-24 11:06 ` Stefano Stabellini
@ 2012-09-24 13:09 ` Gerd Hoffmann
0 siblings, 0 replies; 26+ messages in thread
From: Gerd Hoffmann @ 2012-09-24 13:09 UTC (permalink / raw)
To: Stefano Stabellini; +Cc: qemu-devel@nongnu.org
On 09/24/12 13:06, Stefano Stabellini wrote:
> On Fri, 21 Sep 2012, Gerd Hoffmann wrote:
>> Hi,
>>
>>>> + case FB_ACQ_REQ:
>>>> + fbdev_switch_acquire();
>>>> + redraw_screen++;
>>>
>>> Rather than introducing redraw_screen, I would just call vga_hw_invalidate
>>> instead here. It is always a win if we can avoid to introduce one more
>>> state machine.
>>
>> Wouldn't that make the vga emulation do additional work which is simply
>> not needed?
>
> It causes full_update to be set and that causes vga_draw_line to be
> called for every line on the screen.
> However in the important cases (32bpp and 16bpp) the buffer is shared
> and nothing happens. So no more work.
When looking at the code I feel again like this is one relict we should
just get rid of ...
The reason it exists in the first place seems to be console switching.
The qemu-internal one, via Ctrl-Alt-<nr>, to serial / monitor text
consoles. In that case the vga emulation must be able to restore the
display, and vga_hw_invalidate will handle it.
That logic predates the display surfaces though. I think today we could
just assign a displaysurface to each qemu console and be done with it.
Console switching will just switch display surfaces without any
re-rendering needed. Likewise screendumping can stop hoping through all
the loops it hops today.
Any other vga_hw_invalidate calls creped in over time (ab-)use it to
kick a redraw in the ui code. They kick the vga emulation for no
reason, there is nothing the vga emulation must redraw.
cheers,
Gerd
^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2012-09-24 13:09 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-19 11:15 [Qemu-devel] [PATCH v4 0/9] linux framebuffer display driver Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 1/9] QLIST-ify display change listeners Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 3/9] move set_mouse + cursor_define callbacks Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver Gerd Hoffmann
2012-09-19 18:09 ` Stefano Stabellini
2012-09-21 12:28 ` Gerd Hoffmann
2012-09-24 11:06 ` Stefano Stabellini
2012-09-24 13:09 ` Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 5/9] fbdev: add monitor command to enable/disable Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 6/9] fbdev: make configurable at compile time Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 7/9] fbdev: move to pixman Gerd Hoffmann
2012-09-19 18:10 ` Stefano Stabellini
2012-09-20 6:16 ` Gerd Hoffmann
2012-09-20 11:33 ` Stefano Stabellini
2012-09-20 13:51 ` Gerd Hoffmann
2012-09-20 15:20 ` Stefano Stabellini
2012-09-20 15:27 ` Gerd Hoffmann
2012-09-20 15:28 ` Stefano Stabellini
2012-09-20 15:33 ` Stefano Stabellini
2012-09-21 5:40 ` Gerd Hoffmann
2012-09-21 10:48 ` Stefano Stabellini
2012-09-19 11:15 ` [Qemu-devel] [PATCH 8/9] fbdev: add mouse pointer support Gerd Hoffmann
2012-09-19 11:15 ` [Qemu-devel] [PATCH 9/9] fbdev: add display scaling support Gerd Hoffmann
-- strict thread matches above, loose matches on Subject: below --
2012-09-18 7:17 [Qemu-devel] [PULL 0/9] linux framebuffer display driver Gerd Hoffmann
2012-09-18 7:17 ` [Qemu-devel] [PATCH 2/9] add unregister_displaychangelistener Gerd Hoffmann
2012-09-18 10:54 ` 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).