@@ -1376,6 +1376,16 @@ DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *
return ds->allocator;
}
+void unregister_displayallocator(DisplayState *ds)
+{
+ if (ds->allocator != &default_allocator) {
+ ds->allocator->free_displaysurface(ds->surface);
+ ds->surface = defaultallocator_create_displaysurface(ds_get_width(ds),
+ ds_get_height(ds));
+ ds->allocator = &default_allocator;
+ }
+}
+
DisplayState *graphic_console_init(vga_hw_update_ptr update,
vga_hw_invalidate_ptr invalidate,
vga_hw_screen_dump_ptr screen_dump,
@@ -192,6 +192,7 @@ PixelFormat qemu_different_endianness_pixelformat(int bpp);
PixelFormat qemu_default_pixelformat(int bpp);
DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da);
+void unregister_displayallocator(DisplayState *ds);
static inline DisplaySurface* qemu_create_displaysurface(DisplayState *ds, int width, int height)
{
@@ -371,7 +372,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
/* fbdev.c */
void fbdev_display_init(DisplayState *ds, const char *device);
-void fbdev_display_uninit(void);
+void fbdev_display_uninit(DisplayState *ds);
/* cocoa.m */
void cocoa_display_init(DisplayState *ds, int full_screen);
@@ -67,13 +67,13 @@ static int fb_switch_state = FB_ACTIVE;
/* qdev windup */
static DisplayChangeListener *dcl;
+static DisplayAllocator *da;
static QemuPfConv *conv;
static PixelFormat fbpf;
-static int resize_screen;
-static int redraw_screen;
static int cx, cy, cw, ch;
static int debug = 0;
static Notifier exit_notifier;
+uint8_t *guest_surface;
/* fwd decls */
static int fbdev_activate_vt(int tty, int vtno, bool wait);
@@ -519,6 +519,10 @@ static void fbdev_cleanup(void)
fprintf(stderr, "%s\n", __FUNCTION__);
/* 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");
@@ -786,10 +790,10 @@ static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
uint8_t *src;
int line;
- if (!conv)
+ if (!conv || !guest_surface)
return;
- src = ds_get_data(ds) + y * ds_get_linesize(ds)
+ src = guest_surface + 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;
@@ -819,46 +823,50 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
if (fb_switch_state != FB_ACTIVE)
return;
- if (resize_screen) {
- if (debug)
- fprintf(stderr, "%s: handle resize\n", __FUNCTION__);
- 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 (guest_surface != NULL) {
+ fbdev_render(ds, x, y, w, h);
+ }
+}
- 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");
- }
+static void fbdev_setdata(DisplayState *ds)
+{
+ if (conv) {
+ qemu_pf_conv_put(conv);
}
- if (redraw_screen) {
- if (debug)
- fprintf(stderr, "%s: handle redraw\n", __FUNCTION__);
- redraw_screen = 0;
- fbdev_cls();
- x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
+ conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
+ if (conv == NULL) {
+ fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
}
- fbdev_render(ds, x, y, w, h);
+ guest_surface = ds_get_data(ds);
}
static void fbdev_resize(DisplayState *ds)
{
- if (debug)
- fprintf(stderr, "%s: request resize+redraw\n", __FUNCTION__);
- resize_screen++;
- redraw_screen++;
+ int is_video_ptr;
+
+ if (fb_switch_state == FB_ACTIVE) {
+ fbdev_cls();
+ }
+
+ is_video_ptr = ds_get_data(ds) >= fb_mem + fb_mem_offset &&
+ ds_get_data(ds) < fb_mem + fb_fix.smem_len + fb_mem_offset;
+
+ if (ds_get_bits_per_pixel(ds) != fbpf.bits_per_pixel ||
+ ds_get_linesize(ds) != fb_fix.line_length ||
+ !is_video_ptr) {
+ cx = 0; cy = 0;
+ 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;
+ }
+ fbdev_setdata(ds);
+ } else {
+ guest_surface = NULL;
+ }
}
static void fbdev_refresh(DisplayState *ds)
@@ -866,21 +874,17 @@ static void fbdev_refresh(DisplayState *ds)
switch (fb_switch_state) {
case FB_REL_REQ:
fbdev_switch_release();
+ vga_hw_invalidate();
case FB_INACTIVE:
return;
case FB_ACQ_REQ:
fbdev_switch_acquire();
- redraw_screen++;
- if (debug)
- fprintf(stderr, "%s: request redraw\n", __FUNCTION__);
+ vga_hw_invalidate();
case FB_ACTIVE:
break;
}
vga_hw_update();
- if (redraw_screen) {
- fbdev_update(ds, 0, 0, 0, 0);
- }
}
static void fbdev_exit_notifier(Notifier *notifier)
@@ -888,6 +892,52 @@ static void fbdev_exit_notifier(Notifier *notifier)
fbdev_cleanup();
}
+static DisplaySurface *fbdev_create_displaysurface(int width, int height)
+{
+ DisplaySurface *surface = qemu_mallocz(sizeof (DisplaySurface));
+
+ surface->width = width;
+ surface->height = height;
+
+ surface->pf = fbpf;
+ surface->linesize = fb_fix.line_length;
+
+ if (fb_switch_state == FB_INACTIVE) {
+ surface->flags = QEMU_ALLOCATED_FLAG;
+ surface->data = qemu_mallocz(surface->linesize * surface->height);
+ } else {
+ surface->flags = QEMU_REALPIXELS_FLAG;
+ surface->data = fb_mem;
+
+ if (width < fb_var.xres)
+ surface->data += ((fb_var.xres - width) / 2) * fbpf.bytes_per_pixel;
+ if (height < fb_var.yres)
+ surface->data += ((fb_var.yres - height) / 2) * fb_fix.line_length;
+ }
+
+ return surface;
+}
+
+static void fbdev_free_displaysurface(DisplaySurface *surface)
+{
+ if (surface == NULL)
+ return;
+
+ if (surface->flags & QEMU_ALLOCATED_FLAG) {
+ qemu_free(surface->data);
+ }
+
+ qemu_free(surface);
+}
+
+static DisplaySurface *fbdev_resize_displaysurface(DisplaySurface *surface,
+ int width,
+ int height)
+{
+ fbdev_free_displaysurface(surface);
+ return fbdev_create_displaysurface(width, height);
+}
+
void fbdev_display_init(DisplayState *ds, const char *device)
{
if (dcl != NULL) {
@@ -905,15 +955,24 @@ void fbdev_display_init(DisplayState *ds, const char *device)
fbdev_catch_exit_signals();
init_mouse();
- fbdev_resize(ds);
dcl = qemu_mallocz(sizeof(DisplayChangeListener));
dcl->dpy_update = fbdev_update;
dcl->dpy_resize = fbdev_resize;
dcl->dpy_refresh = fbdev_refresh;
+ dcl->dpy_setdata = fbdev_setdata;
register_displaychangelistener(ds, dcl);
+
+ da = qemu_mallocz(sizeof (DisplayAllocator));
+ da->create_displaysurface = fbdev_create_displaysurface;
+ da->resize_displaysurface = fbdev_resize_displaysurface;
+ da->free_displaysurface = fbdev_free_displaysurface;
+
+ if (register_displayallocator(ds, da) == da) {
+ vga_hw_invalidate();
+ }
}
-void fbdev_display_uninit(void)
+void fbdev_display_uninit(DisplayState *ds)
{
if (dcl == NULL) {
if (debug)
@@ -921,6 +980,9 @@ void fbdev_display_uninit(void)
return;
}
+ unregister_displayallocator(ds);
+ qemu_free(da);
+
unregister_displaychangelistener(dcl);
qemu_free(dcl);
dcl = NULL;
@@ -965,7 +965,7 @@ static int do_change_fbdev(Monitor *mon, const char *target)
if (strcmp(target, "on") == 0) {
fbdev_display_init(get_displaystate(), NULL);
} else {
- fbdev_display_uninit();
+ fbdev_display_uninit(get_displaystate());
}
return 0;
#endif