From 979f7ef8966bc4495a710ed9e4af42098f92ee79 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 19 Sep 2018 12:30:57 +0200 Subject: qxl: use guest_monitor_config for local renderer. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When processing monitor config from guest store head0 width and height for single-head configurations. Use these when creating the DisplaySurface in the local renderer. This fixes a rendering issue with wayland. Wayland rounds up the framebuffer width and height to a multiple of 64, so with odd resolutions (800x600 for example) the framebuffer is larger than the actual screen. The monitor config has the actual screen size though. This fixes guest display for anything using the local renderer (non-spice UI, screendump monitor command). Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Message-id: 20180919103057.9666-1-kraxel@redhat.com --- hw/display/qxl-render.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'hw/display/qxl-render.c') diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index c62b9a5e75..6debe8fc11 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -98,6 +98,8 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; DisplaySurface *surface; + int width = qxl->guest_head0_width ?: qxl->guest_primary.surface.width; + int height = qxl->guest_head0_height ?: qxl->guest_primary.surface.height; int i; if (qxl->guest_primary.resized) { @@ -111,8 +113,8 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); qxl->num_dirty_rects = 1; trace_qxl_render_guest_primary_resized( - qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height, + width, + height, qxl->guest_primary.qxl_stride, qxl->guest_primary.bytes_pp, qxl->guest_primary.bits_pp); @@ -120,15 +122,15 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) pixman_format_code_t format = qemu_default_pixman_format(qxl->guest_primary.bits_pp, true); surface = qemu_create_displaysurface_from - (qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height, + (width, + height, format, qxl->guest_primary.abs_stride, qxl->guest_primary.data); } else { surface = qemu_create_displaysurface - (qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height); + (width, + height); } dpy_gfx_replace_surface(vga->con, surface); } @@ -144,8 +146,8 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->dirty[i].top < 0 || qxl->dirty[i].left > qxl->dirty[i].right || qxl->dirty[i].top > qxl->dirty[i].bottom || - qxl->dirty[i].right > qxl->guest_primary.surface.width || - qxl->dirty[i].bottom > qxl->guest_primary.surface.height) { + qxl->dirty[i].right > width || + qxl->dirty[i].bottom > height) { continue; } qxl_blit(qxl, qxl->dirty+i); -- cgit 1.4.1 From 36ffc122dcd69ab66db4afab3a13cfca46bfc323 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Mon, 3 Sep 2018 16:54:47 +0200 Subject: qxl: support mono cursors with inverted colors Monochrome cursors are still used by Windows guests with the QXL-WDDM-DOD driver. Such cursor types have one odd feature, inversion of colors. GDK does not seem to support it, so implement an alternative solution: fill the inverted pixels and add an outline to make the cursor more visible. Tested with the text cursor in Notepad and Windows 10. cursor_set_mono is also used by the vmware GPU, so add a special check to avoid breaking its 32bpp format (tested with Kubuntu 14.04.4). I was unable to find a guest which supports the 1bpp format with a vmware GPU. The old implementation was buggy and removed in v2.10.0-108-g79c5a10cdd ("qxl: drop mono cursor support"), this version improves upon that by adding bounds validation, clarifying the semantics of the two masks and adds a workaround for inverted colors support. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1611984 Signed-off-by: Peter Wu Message-id: 20180903145447.17142-1-peter@lekensteyn.nl [ kraxel: minor codestyle fix ] Signed-off-by: Gerd Hoffmann --- hw/display/qxl-render.c | 16 ++++++++++++++++ ui/cursor.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) (limited to 'hw/display/qxl-render.c') diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 6debe8fc11..14ad2b352d 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -236,12 +236,28 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, uint32_t group_id) { QEMUCursor *c; + uint8_t *and_mask, *xor_mask; size_t size; c = cursor_alloc(cursor->header.width, cursor->header.height); c->hot_x = cursor->header.hot_spot_x; c->hot_y = cursor->header.hot_spot_y; switch (cursor->header.type) { + case SPICE_CURSOR_TYPE_MONO: + /* Assume that the full cursor is available in a single chunk. */ + size = 2 * cursor_get_mono_bpl(c) * c->height; + if (size != cursor->data_size) { + fprintf(stderr, "%s: bad monochrome cursor %ux%u with size %u\n", + __func__, c->width, c->height, cursor->data_size); + goto fail; + } + and_mask = cursor->chunk.data; + xor_mask = and_mask + cursor_get_mono_bpl(c) * c->height; + cursor_set_mono(c, 0xffffff, 0x000000, xor_mask, 1, and_mask); + if (qxl->debug > 2) { + cursor_print_ascii_art(c, "qxl/mono"); + } + break; case SPICE_CURSOR_TYPE_ALPHA: size = sizeof(uint32_t) * cursor->header.width * cursor->header.height; qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id); diff --git a/ui/cursor.c b/ui/cursor.c index f3da0cee79..26ce69fe5e 100644 --- a/ui/cursor.c +++ b/ui/cursor.c @@ -128,13 +128,25 @@ void cursor_set_mono(QEMUCursor *c, uint32_t *data = c->data; uint8_t bit; int x,y,bpl; - + bool expand_bitmap_only = image == mask; + bool has_inverted_colors = false; + const uint32_t inverted = 0x80000000; + + /* + * Converts a monochrome bitmap with XOR mask 'image' and AND mask 'mask': + * https://docs.microsoft.com/en-us/windows-hardware/drivers/display/drawing-monochrome-pointers + */ bpl = cursor_get_mono_bpl(c); for (y = 0; y < c->height; y++) { bit = 0x80; for (x = 0; x < c->width; x++, data++) { if (transparent && mask[x/8] & bit) { - *data = 0x00000000; + if (!expand_bitmap_only && image[x / 8] & bit) { + *data = inverted; + has_inverted_colors = true; + } else { + *data = 0x00000000; + } } else if (!transparent && !(mask[x/8] & bit)) { *data = 0x00000000; } else if (image[x/8] & bit) { @@ -150,6 +162,32 @@ void cursor_set_mono(QEMUCursor *c, mask += bpl; image += bpl; } + + /* + * If there are any pixels with inverted colors, create an outline (fill + * transparent neighbors with the background color) and use the foreground + * color as "inverted" color. + */ + if (has_inverted_colors) { + data = c->data; + for (y = 0; y < c->height; y++) { + for (x = 0; x < c->width; x++, data++) { + if (*data == 0 /* transparent */ && + ((x > 0 && data[-1] == inverted) || + (x + 1 < c->width && data[1] == inverted) || + (y > 0 && data[-c->width] == inverted) || + (y + 1 < c->height && data[c->width] == inverted))) { + *data = 0xff000000 | background; + } + } + } + data = c->data; + for (x = 0; x < c->width * c->height; x++, data++) { + if (*data == inverted) { + *data = 0xff000000 | foreground; + } + } + } } void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *image) -- cgit 1.4.1