diff options
Diffstat (limited to 'ui')
-rw-r--r-- | ui/clipboard.c | 66 | ||||
-rw-r--r-- | ui/egl-helpers.c | 10 | ||||
-rw-r--r-- | ui/gtk-clipboard.c | 13 | ||||
-rw-r--r-- | ui/gtk-egl.c | 58 | ||||
-rw-r--r-- | ui/gtk-gl-area.c | 53 | ||||
-rw-r--r-- | ui/gtk.c | 245 | ||||
-rw-r--r-- | ui/qemu-pixman.c | 15 | ||||
-rw-r--r-- | ui/sdl2-gl.c | 2 | ||||
-rw-r--r-- | ui/sdl2.c | 20 | ||||
-rw-r--r-- | ui/vdagent.c | 205 | ||||
-rw-r--r-- | ui/vnc-enc-tight.c | 20 | ||||
-rw-r--r-- | ui/vnc-enc-zrle.c | 2 | ||||
-rw-r--r-- | ui/vnc-jobs.c | 2 | ||||
-rw-r--r-- | ui/vnc.c | 9 | ||||
-rw-r--r-- | ui/vnc.h | 2 |
15 files changed, 562 insertions, 160 deletions
diff --git a/ui/clipboard.c b/ui/clipboard.c index 132086e..ec00a0b 100644 --- a/ui/clipboard.c +++ b/ui/clipboard.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "system/runstate.h" #include "ui/clipboard.h" #include "trace.h" @@ -7,8 +8,62 @@ static NotifierList clipboard_notifiers = static QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; +static VMChangeStateEntry *cb_change_state_entry = NULL; + +static bool cb_reset_serial_on_resume = false; + +static const VMStateDescription vmstate_cbcontent = { + .name = "clipboard/content", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(available, QemuClipboardContent), + VMSTATE_BOOL(requested, QemuClipboardContent), + VMSTATE_UINT32(size, QemuClipboardContent), + VMSTATE_VBUFFER_ALLOC_UINT32(data, QemuClipboardContent, 0, 0, size), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_cbinfo = { + .name = "clipboard", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_INT32(selection, QemuClipboardInfo), + VMSTATE_BOOL(has_serial, QemuClipboardInfo), + VMSTATE_UINT32(serial, QemuClipboardInfo), + VMSTATE_STRUCT_ARRAY(types, QemuClipboardInfo, QEMU_CLIPBOARD_TYPE__COUNT, 0, vmstate_cbcontent, QemuClipboardContent), + VMSTATE_END_OF_LIST() + } +}; + +static void qemu_clipboard_change_state(void *opaque, bool running, RunState state) +{ + int i; + + if (!running) { + return; + } + + if (cb_reset_serial_on_resume) { + qemu_clipboard_reset_serial(); + } + + for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { + if (cbinfo[i]) { + qemu_clipboard_update(cbinfo[i]); + } + } + +} + void qemu_clipboard_peer_register(QemuClipboardPeer *peer) { + if (cb_change_state_entry == NULL) { + cb_change_state_entry = qemu_add_vm_change_state_handler(qemu_clipboard_change_state, NULL); + } + notifier_list_add(&clipboard_notifiers, &peer->notifier); } @@ -83,7 +138,9 @@ void qemu_clipboard_update(QemuClipboardInfo *info) } } - notifier_list_notify(&clipboard_notifiers, ¬ify); + if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) { + notifier_list_notify(&clipboard_notifiers, ¬ify); + } if (cbinfo[info->selection] != info) { qemu_clipboard_info_unref(cbinfo[info->selection]); @@ -163,7 +220,12 @@ void qemu_clipboard_reset_serial(void) info->serial = 0; } } - notifier_list_notify(&clipboard_notifiers, ¬ify); + + if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) { + notifier_list_notify(&clipboard_notifiers, ¬ify); + } else { + cb_reset_serial_on_resume = true; + } } void qemu_clipboard_set_data(QemuClipboardPeer *peer, diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 9cda2bb..5503a79 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -93,14 +93,18 @@ void egl_fb_destroy(egl_fb *fb) fb->width = 0; fb->height = 0; + fb->x = 0; + fb->y = 0; fb->texture = 0; fb->framebuffer = 0; } -void egl_fb_setup_default(egl_fb *fb, int width, int height) +void egl_fb_setup_default(egl_fb *fb, int width, int height, int x, int y) { fb->width = width; fb->height = height; + fb->x = x; + fb->y = y; fb->framebuffer = 0; /* default framebuffer */ } @@ -145,6 +149,7 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer); glViewport(0, 0, dst->width, dst->height); + glClear(GL_COLOR_BUFFER_BIT); if (src->dmabuf) { x1 = qemu_dmabuf_get_x(src->dmabuf); @@ -161,7 +166,8 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) x2 = x1 + w; glBlitFramebuffer(x1, y1, x2, y2, - 0, 0, dst->width, dst->height, + dst->x, dst->y, + dst->x + dst->width, dst->y + dst->height, GL_COLOR_BUFFER_BIT, GL_LINEAR); } diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c index 8d8a636..65d89ec 100644 --- a/ui/gtk-clipboard.c +++ b/ui/gtk-clipboard.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "ui/gtk.h" @@ -95,11 +96,13 @@ static void gd_clipboard_update_info(GtkDisplayState *gd, gtk_clipboard_clear(gd->gtkcb[s]); if (targets) { gd->cbowner[s] = true; - gtk_clipboard_set_with_data(gd->gtkcb[s], - targets, n_targets, - gd_clipboard_get_data, - gd_clipboard_clear, - gd); + if (!gtk_clipboard_set_with_data(gd->gtkcb[s], + targets, n_targets, + gd_clipboard_get_data, + gd_clipboard_clear, + gd)) { + warn_report("Failed to set GTK clipboard"); + } gtk_target_table_free(targets, n_targets); } diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index f7a428c..0b787be 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -70,16 +70,18 @@ void gd_egl_draw(VirtualConsole *vc) QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; int fence_fd; #endif - int ww, wh, ws; + int ww, wh, pw, ph, gs; if (!vc->gfx.gls) { return; } window = gtk_widget_get_window(vc->gfx.drawing_area); - ws = gdk_window_get_scale_factor(window); - ww = gdk_window_get_width(window) * ws; - wh = gdk_window_get_height(window) * ws; + gs = gdk_window_get_scale_factor(window); + ww = gdk_window_get_width(window); + wh = gdk_window_get_height(window); + pw = ww * gs; + ph = wh * gs; if (vc->gfx.scanout_mode) { #ifdef CONFIG_GBM @@ -93,8 +95,9 @@ void gd_egl_draw(VirtualConsole *vc) #endif gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); - vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); - vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); + gd_update_scale(vc, ww, wh, + surface_width(vc->gfx.ds), + surface_height(vc->gfx.ds)); glFlush(); #ifdef CONFIG_GBM @@ -115,13 +118,14 @@ void gd_egl_draw(VirtualConsole *vc) eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, vc->gfx.esurface, vc->gfx.ectx); - surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); + surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph); surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); - vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); - vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); + gd_update_scale(vc, ww, wh, + surface_width(vc->gfx.ds), + surface_height(vc->gfx.ds)); glFlush(); } @@ -336,7 +340,11 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkWindow *window; - int ww, wh, ws; + int px_offset, py_offset; + int gs; + int pw_widget, ph_widget, pw_surface, ph_surface; + int ww_widget, wh_widget, ww_surface, wh_surface; + int fbw, fbh; if (!vc->gfx.scanout_mode) { return; @@ -349,10 +357,32 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, vc->gfx.esurface, vc->gfx.ectx); window = gtk_widget_get_window(vc->gfx.drawing_area); - ws = gdk_window_get_scale_factor(window); - ww = gdk_window_get_width(window) * ws; - wh = gdk_window_get_height(window) * ws; - egl_fb_setup_default(&vc->gfx.win_fb, ww, wh); + gs = gdk_window_get_scale_factor(window); + ww_widget = gdk_window_get_width(window); + wh_widget = gdk_window_get_height(window); + fbw = surface_width(vc->gfx.ds); + fbh = surface_height(vc->gfx.ds); + + gd_update_scale(vc, ww_widget, wh_widget, fbw, fbh); + + ww_surface = fbw * vc->gfx.scale_x; + wh_surface = fbh * vc->gfx.scale_y; + pw_widget = ww_widget * gs; + ph_widget = wh_widget * gs; + pw_surface = ww_surface * gs; + ph_surface = wh_surface * gs; + + px_offset = 0; + py_offset = 0; + if (pw_widget > pw_surface) { + px_offset = (pw_widget - pw_surface) / 2; + } + if (ph_widget > ph_surface) { + py_offset = (ph_widget - ph_surface) / 2; + } + + egl_fb_setup_default(&vc->gfx.win_fb, pw_surface, ph_surface, + px_offset, py_offset); if (vc->gfx.cursor_fb.texture) { egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb, vc->gfx.y0_top); diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 2c9a0db..8151cc4 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -42,16 +42,37 @@ void gd_gl_area_draw(VirtualConsole *vc) #ifdef CONFIG_GBM QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; #endif - int ww, wh, ws, y1, y2; + int pw, ph, gs, y1, y2; + int ww, wh; + int ww_surface, wh_surface; + int fbw, fbh; + int wx_offset, wy_offset; if (!vc->gfx.gls) { return; } gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); - ws = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area)); - ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * ws; - wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * ws; + gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area)); + fbw = surface_width(vc->gfx.ds); + fbh = surface_height(vc->gfx.ds); + ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area); + wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area); + pw = ww * gs; + ph = wh * gs; + + gd_update_scale(vc, ww, wh, fbw, fbh); + + ww_surface = fbw * vc->gfx.scale_x; + wh_surface = fbh * vc->gfx.scale_y; + + wx_offset = wy_offset = 0; + if (ww > ww_surface) { + wx_offset = (ww - ww_surface) / 2; + } + if (wh > wh_surface) { + wy_offset = (wh - wh_surface) / 2; + } if (vc->gfx.scanout_mode) { if (!vc->gfx.guest_fb.framebuffer) { @@ -71,11 +92,29 @@ void gd_gl_area_draw(VirtualConsole *vc) glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer); /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */ - glViewport(0, 0, ww, wh); + if (wx_offset > 0) { + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, wx_offset * gs, wh * gs); + glClear(GL_COLOR_BUFFER_BIT); + glScissor((ww - wx_offset) * gs, 0, wx_offset * gs, wh * gs); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } + if (wy_offset > 0) { + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, ww * gs, wy_offset * gs); + glClear(GL_COLOR_BUFFER_BIT); + glScissor(0, (wh - wy_offset) * gs, ww * gs, wy_offset * gs); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } + + glViewport(0, 0, pw, ph); y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; y2 = vc->gfx.y0_top ? vc->gfx.h : 0; glBlitFramebuffer(0, y1, vc->gfx.w, y2, - 0, 0, ww, wh, + wx_offset * gs, wy_offset * gs, + (ww - wx_offset) * gs, (wh - wy_offset) * gs, GL_COLOR_BUFFER_BIT, GL_NEAREST); #ifdef CONFIG_GBM if (dmabuf) { @@ -101,7 +140,7 @@ void gd_gl_area_draw(VirtualConsole *vc) } gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); - surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); + surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph); surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); } } @@ -387,16 +387,16 @@ static void *gd_win32_get_hwnd(VirtualConsole *vc) /** DisplayState Callbacks **/ static void gd_update(DisplayChangeListener *dcl, - int x, int y, int w, int h) + int fbx, int fby, int fbw, int fbh) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkWindow *win; - int x1, x2, y1, y2; - int mx, my; - int fbw, fbh; - int ww, wh; + int wx1, wx2, wy1, wy2; + int wx_offset, wy_offset; + int ww_surface, wh_surface; + int ww_widget, wh_widget; - trace_gd_update(vc->label, x, y, w, h); + trace_gd_update(vc->label, fbx, fby, fbw, fbh); if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { return; @@ -405,35 +405,36 @@ static void gd_update(DisplayChangeListener *dcl, if (vc->gfx.convert) { pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, NULL, vc->gfx.convert, - x, y, 0, 0, x, y, w, h); + fbx, fby, 0, 0, fbx, fby, fbw, fbh); } - x1 = floor(x * vc->gfx.scale_x); - y1 = floor(y * vc->gfx.scale_y); + wx1 = floor(fbx * vc->gfx.scale_x); + wy1 = floor(fby * vc->gfx.scale_y); - x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x); - y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y); + wx2 = ceil(fbx * vc->gfx.scale_x + fbw * vc->gfx.scale_x); + wy2 = ceil(fby * vc->gfx.scale_y + fbh * vc->gfx.scale_y); - fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; - fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; + ww_surface = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + wh_surface = surface_height(vc->gfx.ds) * vc->gfx.scale_y; win = gtk_widget_get_window(vc->gfx.drawing_area); if (!win) { return; } - ww = gdk_window_get_width(win); - wh = gdk_window_get_height(win); + ww_widget = gdk_window_get_width(win); + wh_widget = gdk_window_get_height(win); - mx = my = 0; - if (ww > fbw) { - mx = (ww - fbw) / 2; + wx_offset = wy_offset = 0; + if (ww_widget > ww_surface) { + wx_offset = (ww_widget - ww_surface) / 2; } - if (wh > fbh) { - my = (wh - fbh) / 2; + if (wh_widget > wh_surface) { + wy_offset = (wh_widget - wh_surface) / 2; } gtk_widget_queue_draw_area(vc->gfx.drawing_area, - mx + x1, my + y1, (x2 - x1), (y2 - y1)); + wx_offset + wx1, wy_offset + wy1, + (wx2 - wx1), (wy2 - wy1)); } static void gd_refresh(DisplayChangeListener *dcl) @@ -771,8 +772,21 @@ static void gd_resize_event(GtkGLArea *area, gint width, gint height, gpointer *opaque) { VirtualConsole *vc = (void *)opaque; + double pw = width, ph = height; + double sx = vc->gfx.scale_x, sy = vc->gfx.scale_y; + GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(area)); + const int gs = gdk_window_get_scale_factor(window); - gd_set_ui_size(vc, width, height); + if (!vc->s->free_scale && !vc->s->full_screen) { + pw /= sx; + ph /= sy; + } + + /** + * width and height here are in pixel coordinate, so we must divide it + * by global window scale (gs) + */ + gd_set_ui_size(vc, pw / gs, ph / gs); } #endif @@ -800,12 +814,95 @@ void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget) #endif } +void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh) +{ + if (!vc) { + return; + } + + if (vc->s->full_screen) { + vc->gfx.scale_x = (double)ww / fbw; + vc->gfx.scale_y = (double)wh / fbh; + } else if (vc->s->free_scale) { + double sx, sy; + + sx = (double)ww / fbw; + sy = (double)wh / fbh; + + vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); + } +} +/** + * DOC: Coordinate handling. + * + * We are coping with sizes and positions in various coordinates and the + * handling of these coordinates is somewhat confusing. It would benefit us + * all if we define these coordinates explicitly and clearly. Besides, it's + * also helpful to follow the same naming convention for variables + * representing values in different coordinates. + * + * I. Definitions + * + * - (guest) buffer coordinate: this is the coordinates that the guest will + * see. The x/y offsets and width/height specified in commands sent by + * guest is basically in buffer coordinate. + * + * - (host) pixel coordinate: this is the coordinate in pixel level on the + * host destop. A window/widget of width 300 in pixel coordinate means it + * occupies 300 pixels horizontally. + * + * - (host) logical window coordinate: the existence of global scaling + * factor in desktop level makes this kind of coordinate play a role. It + * always holds that (logical window size) * (global scale factor) = + * (pixel size). + * + * - global scale factor: this is specified in desktop level and is + * typically invariant during the life cycle of the process. Users with + * high-DPI monitors might set this scale, for example, to 2, in order to + * make the UI look larger. + * + * - zooming scale: this can be freely controlled by the QEMU user to zoom + * in/out the guest content. + * + * II. Representation + * + * We'd like to use consistent representation for variables in different + * coordinates: + * - buffer coordinate: prefix fb + * - pixel coordinate: prefix p + * - logical window coordinate: prefix w + * + * For scales: + * - global scale factor: prefix gs + * - zooming scale: prefix scale/s + * + * Example: fbw, pw, ww for width in different coordinates + * + * III. Equation + * + * - fbw * gs * scale_x = pw + * - pw = gs * ww + * + * Consequently we have + * + * - fbw * scale_x = ww + * + * Example: assuming we are running QEMU on a 3840x2160 screen and have set + * global scaling factor to 2, if the guest buffer size is 1920x1080 and the + * zooming scale is 0.5, then we have: + * - fbw = 1920, fbh = 1080 + * - pw = 1920, ph = 1080 + * - ww = 960, wh = 540 + * A bonus of this configuration is that we can achieve pixel to pixel + * presentation of the guest content. + */ + static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) { VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; - int mx, my; - int ww, wh; + int wx_offset, wy_offset; + int ww_widget, wh_widget, ww_surface, wh_surface; int fbw, fbh; #if defined(CONFIG_OPENGL) @@ -839,46 +936,37 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) fbw = surface_width(vc->gfx.ds); fbh = surface_height(vc->gfx.ds); - ww = gdk_window_get_width(gtk_widget_get_window(widget)); - wh = gdk_window_get_height(gtk_widget_get_window(widget)); + ww_widget = gdk_window_get_width(gtk_widget_get_window(widget)); + wh_widget = gdk_window_get_height(gtk_widget_get_window(widget)); - if (s->full_screen) { - vc->gfx.scale_x = (double)ww / fbw; - vc->gfx.scale_y = (double)wh / fbh; - } else if (s->free_scale) { - double sx, sy; + gd_update_scale(vc, ww_widget, wh_widget, fbw, fbh); - sx = (double)ww / fbw; - sy = (double)wh / fbh; + ww_surface = fbw * vc->gfx.scale_x; + wh_surface = fbh * vc->gfx.scale_y; - vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); + wx_offset = wy_offset = 0; + if (ww_widget > ww_surface) { + wx_offset = (ww_widget - ww_surface) / 2; } - - fbw *= vc->gfx.scale_x; - fbh *= vc->gfx.scale_y; - - mx = my = 0; - if (ww > fbw) { - mx = (ww - fbw) / 2; - } - if (wh > fbh) { - my = (wh - fbh) / 2; + if (wh_widget > wh_surface) { + wy_offset = (wh_widget - wh_surface) / 2; } - cairo_rectangle(cr, 0, 0, ww, wh); + cairo_rectangle(cr, 0, 0, ww_widget, wh_widget); /* Optionally cut out the inner area where the pixmap will be drawn. This avoids 'flashing' since we're not double-buffering. Note we're using the undocumented behaviour of drawing the rectangle from right to left to cut out the whole */ - cairo_rectangle(cr, mx + fbw, my, - -1 * fbw, fbh); + cairo_rectangle(cr, wx_offset + ww_surface, wy_offset, + -1 * ww_surface, wh_surface); cairo_fill(cr); cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y); cairo_set_source_surface(cr, vc->gfx.surface, - mx / vc->gfx.scale_x, my / vc->gfx.scale_y); + wx_offset / vc->gfx.scale_x, + wy_offset / vc->gfx.scale_y); cairo_paint(cr); return TRUE; @@ -889,19 +977,19 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, { VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; - int x, y; - int mx, my; - int fbh, fbw; - int ww, wh; + int fbx, fby; + int wx_offset, wy_offset; + int wh_surface, ww_surface; + int ww_widget, wh_widget; if (!vc->gfx.ds) { return TRUE; } - fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; - fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - ww = gtk_widget_get_allocated_width(widget); - wh = gtk_widget_get_allocated_height(widget); + ww_surface = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + wh_surface = surface_height(vc->gfx.ds) * vc->gfx.scale_y; + ww_widget = gtk_widget_get_allocated_width(widget); + wh_widget = gtk_widget_get_allocated_height(widget); /* * `widget` may not have the same size with the frame buffer. @@ -909,41 +997,42 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, * To achieve that, `vc` will be displayed at (mx, my) * so that it is displayed at the center of the widget. */ - mx = my = 0; - if (ww > fbw) { - mx = (ww - fbw) / 2; + wx_offset = wy_offset = 0; + if (ww_widget > ww_surface) { + wx_offset = (ww_widget - ww_surface) / 2; } - if (wh > fbh) { - my = (wh - fbh) / 2; + if (wh_widget > wh_surface) { + wy_offset = (wh_widget - wh_surface) / 2; } /* * `motion` is reported in `widget` coordinates * so translating it to the coordinates in `vc`. */ - x = (motion->x - mx) / vc->gfx.scale_x; - y = (motion->y - my) / vc->gfx.scale_y; + fbx = (motion->x - wx_offset) / vc->gfx.scale_x; + fby = (motion->y - wy_offset) / vc->gfx.scale_y; - trace_gd_motion_event(ww, wh, gtk_widget_get_scale_factor(widget), x, y); + trace_gd_motion_event(ww_widget, wh_widget, + gtk_widget_get_scale_factor(widget), fbx, fby); if (qemu_input_is_absolute(vc->gfx.dcl.con)) { - if (x < 0 || y < 0 || - x >= surface_width(vc->gfx.ds) || - y >= surface_height(vc->gfx.ds)) { + if (fbx < 0 || fby < 0 || + fbx >= surface_width(vc->gfx.ds) || + fby >= surface_height(vc->gfx.ds)) { return TRUE; } - qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x, + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, fbx, 0, surface_width(vc->gfx.ds)); - qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, fby, 0, surface_height(vc->gfx.ds)); qemu_input_event_sync(); } else if (s->last_set && s->ptr_owner == vc) { - qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x); - qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y); + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, fbx - s->last_x); + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, fby - s->last_y); qemu_input_event_sync(); } - s->last_x = x; - s->last_y = y; + s->last_x = fbx; + s->last_y = fby; s->last_set = TRUE; if (!qemu_input_is_absolute(vc->gfx.dcl.con) && s->ptr_owner == vc) { @@ -1760,8 +1849,16 @@ static gboolean gd_configure(GtkWidget *widget, GdkEventConfigure *cfg, gpointer opaque) { VirtualConsole *vc = opaque; + const double sx = vc->gfx.scale_x, sy = vc->gfx.scale_y; + double width = cfg->width, height = cfg->height; + + if (!vc->s->free_scale && !vc->s->full_screen) { + width /= sx; + height /= sy; + } + + gd_set_ui_size(vc, width, height); - gd_set_ui_size(vc, cfg->width, cfg->height); return FALSE; } diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 6ef4376..ef4e71d 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -126,33 +126,34 @@ uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman_format) return 0; } -int qemu_pixman_get_type(int rshift, int gshift, int bshift) +int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian) { int type = PIXMAN_TYPE_OTHER; + bool native_endian = (endian == G_BYTE_ORDER); if (rshift > gshift && gshift > bshift) { if (bshift == 0) { - type = PIXMAN_TYPE_ARGB; + type = native_endian ? PIXMAN_TYPE_ARGB : PIXMAN_TYPE_BGRA; } else { - type = PIXMAN_TYPE_RGBA; + type = native_endian ? PIXMAN_TYPE_RGBA : PIXMAN_TYPE_ABGR; } } else if (rshift < gshift && gshift < bshift) { if (rshift == 0) { - type = PIXMAN_TYPE_ABGR; + type = native_endian ? PIXMAN_TYPE_ABGR : PIXMAN_TYPE_RGBA; } else { - type = PIXMAN_TYPE_BGRA; + type = native_endian ? PIXMAN_TYPE_BGRA : PIXMAN_TYPE_ARGB; } } return type; } #ifdef CONFIG_PIXMAN -pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) +pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf, int endian) { pixman_format_code_t format; int type; - type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift); + type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift, endian); format = PIXMAN_FORMAT(pf->bits_per_pixel, type, pf->abits, pf->rbits, pf->gbits, pf->bbits); if (!pixman_format_supported_source(format)) { diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index e01d9ab..3be17d1 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -241,7 +241,7 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, SDL_GL_MakeCurrent(scon->real_window, scon->winctx); SDL_GetWindowSize(scon->real_window, &ww, &wh); - egl_fb_setup_default(&scon->win_fb, ww, wh); + egl_fb_setup_default(&scon->win_fb, ww, wh, 0, 0); egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top); SDL_GL_SwapWindow(scon->real_window); @@ -488,14 +488,14 @@ static void handle_mousemotion(SDL_Event *ev) { int max_x, max_y; struct sdl2_console *scon = get_scon_from_window(ev->motion.windowID); + int scr_w, scr_h, surf_w, surf_h, x, y, dx, dy; if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); if (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { - int scr_w, scr_h; - SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); max_x = scr_w - 1; max_y = scr_h - 1; if (gui_grab && !gui_fullscreen @@ -509,9 +509,14 @@ static void handle_mousemotion(SDL_Event *ev) sdl_grab_start(scon); } } + surf_w = surface_width(scon->surface); + surf_h = surface_height(scon->surface); + x = (int64_t)ev->motion.x * surf_w / scr_w; + y = (int64_t)ev->motion.y * surf_h / scr_h; + dx = (int64_t)ev->motion.xrel * surf_w / scr_w; + dy = (int64_t)ev->motion.yrel * surf_h / scr_h; if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { - sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, - ev->motion.x, ev->motion.y, ev->motion.state); + sdl_send_mouse_event(scon, dx, dy, x, y, ev->motion.state); } } @@ -520,12 +525,17 @@ static void handle_mousebutton(SDL_Event *ev) int buttonstate = SDL_GetMouseState(NULL, NULL); SDL_MouseButtonEvent *bev; struct sdl2_console *scon = get_scon_from_window(ev->button.windowID); + int scr_w, scr_h, x, y; if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } bev = &ev->button; + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + x = (int64_t)bev->x * surface_width(scon->surface) / scr_w; + y = (int64_t)bev->y * surface_height(scon->surface) / scr_h; + if (!gui_grab && !qemu_input_is_absolute(scon->dcl.con)) { if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { /* start grabbing all events */ @@ -537,7 +547,7 @@ static void handle_mousebutton(SDL_Event *ev) } else { buttonstate &= ~SDL_BUTTON(bev->button); } - sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate); + sdl_send_mouse_event(scon, 0, 0, x, y, buttonstate); } } diff --git a/ui/vdagent.c b/ui/vdagent.c index 04513de..c0746fe 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -6,10 +6,10 @@ #include "qemu/option.h" #include "qemu/units.h" #include "hw/qdev-core.h" -#include "migration/blocker.h" #include "ui/clipboard.h" #include "ui/console.h" #include "ui/input.h" +#include "migration/vmstate.h" #include "trace.h" #include "qapi/qapi-types-char.h" @@ -32,14 +32,12 @@ struct VDAgentChardev { Chardev parent; - /* TODO: migration isn't yet supported */ - Error *migration_blocker; - /* config */ bool mouse; bool clipboard; /* guest vdagent */ + bool connected; uint32_t caps; VDIChunkHeader chunk; uint32_t chunksize; @@ -47,7 +45,7 @@ struct VDAgentChardev { uint32_t msgsize; uint8_t *xbuf; uint32_t xoff, xsize; - Buffer outbuf; + GByteArray *outbuf; /* mouse */ DeviceState mouse_dev; @@ -142,16 +140,16 @@ static void vdagent_send_buf(VDAgentChardev *vd) { uint32_t len; - while (!buffer_empty(&vd->outbuf)) { + while (vd->outbuf->len) { len = qemu_chr_be_can_write(CHARDEV(vd)); if (len == 0) { return; } - if (len > vd->outbuf.offset) { - len = vd->outbuf.offset; + if (len > vd->outbuf->len) { + len = vd->outbuf->len; } - qemu_chr_be_write(CHARDEV(vd), vd->outbuf.buffer, len); - buffer_advance(&vd->outbuf, len); + qemu_chr_be_write(CHARDEV(vd), vd->outbuf->data, len); + g_byte_array_remove_range(vd->outbuf, 0, len); } } @@ -166,7 +164,7 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) msg->protocol = VD_AGENT_PROTOCOL; - if (vd->outbuf.offset + msgsize > VDAGENT_BUFFER_LIMIT) { + if (vd->outbuf->len + msgsize > VDAGENT_BUFFER_LIMIT) { error_report("buffer full, dropping message"); return; } @@ -177,9 +175,8 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) if (chunk.size > 1024) { chunk.size = 1024; } - buffer_reserve(&vd->outbuf, sizeof(chunk) + chunk.size); - buffer_append(&vd->outbuf, &chunk, sizeof(chunk)); - buffer_append(&vd->outbuf, msgbuf + msgoff, chunk.size); + g_byte_array_append(vd->outbuf, (void *)&chunk, sizeof(chunk)); + g_byte_array_append(vd->outbuf, msgbuf + msgoff, chunk.size); msgoff += chunk.size; } vdagent_send_buf(vd); @@ -672,10 +669,6 @@ static void vdagent_chr_open(Chardev *chr, return; #endif - if (migrate_add_blocker(&vd->migration_blocker, errp) != 0) { - return; - } - vd->mouse = VDAGENT_MOUSE_DEFAULT; if (cfg->has_mouse) { vd->mouse = cfg->mouse; @@ -694,6 +687,18 @@ static void vdagent_chr_open(Chardev *chr, *be_opened = true; } +static void vdagent_clipboard_peer_register(VDAgentChardev *vd) +{ + if (vd->cbpeer.notifier.notify != NULL) { + return; + } + + vd->cbpeer.name = "vdagent"; + vd->cbpeer.notifier.notify = vdagent_clipboard_notify; + vd->cbpeer.request = vdagent_clipboard_request; + qemu_clipboard_peer_register(&vd->cbpeer); +} + static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) { VDAgentAnnounceCapabilities *caps = (void *)msg->data; @@ -720,13 +725,9 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) memset(vd->last_serial, 0, sizeof(vd->last_serial)); - if (have_clipboard(vd) && vd->cbpeer.notifier.notify == NULL) { + if (have_clipboard(vd)) { qemu_clipboard_reset_serial(); - - vd->cbpeer.name = "vdagent"; - vd->cbpeer.notifier.notify = vdagent_clipboard_notify; - vd->cbpeer.request = vdagent_clipboard_request; - qemu_clipboard_peer_register(&vd->cbpeer); + vdagent_clipboard_peer_register(vd); } } @@ -859,7 +860,8 @@ static void vdagent_disconnect(VDAgentChardev *vd) { trace_vdagent_disconnect(); - buffer_reset(&vd->outbuf); + vd->connected = false; + g_byte_array_set_size(vd->outbuf, 0); vdagent_reset_bufs(vd); vd->caps = 0; if (vd->mouse_hs) { @@ -877,6 +879,10 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) trace_vdagent_fe_open(fe_open); + if (vd->connected == fe_open) { + return; + } + if (!fe_open) { trace_vdagent_close(); vdagent_disconnect(vd); @@ -886,6 +892,7 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) return; } + vd->connected = true; vdagent_send_caps(vd, true); } @@ -916,25 +923,163 @@ static void vdagent_chr_class_init(ObjectClass *oc, const void *data) cc->chr_accept_input = vdagent_chr_accept_input; } +static int post_load(void *opaque, int version_id) +{ + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(opaque); + + if (have_mouse(vd) && vd->mouse_hs) { + qemu_input_handler_activate(vd->mouse_hs); + } + + if (have_clipboard(vd)) { + vdagent_clipboard_peer_register(vd); + } + + return 0; +} + +static const VMStateDescription vmstate_chunk = { + .name = "vdagent/chunk", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(port, VDIChunkHeader), + VMSTATE_UINT32(size, VDIChunkHeader), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vdba = { + .name = "vdagent/bytearray", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(len, GByteArray), + VMSTATE_VBUFFER_ALLOC_UINT32(data, GByteArray, 0, 0, len), + VMSTATE_END_OF_LIST() + } +}; + +struct CBInfoArray { + uint32_t n; + QemuClipboardInfo cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; +}; + +static const VMStateDescription vmstate_cbinfo_array = { + .name = "cbinfoarray", + .fields = (const VMStateField[]) { + VMSTATE_UINT32(n, struct CBInfoArray), + VMSTATE_STRUCT_VARRAY_UINT32(cbinfo, struct CBInfoArray, n, + 0, vmstate_cbinfo, QemuClipboardInfo), + VMSTATE_END_OF_LIST() + } +}; + +static int put_cbinfo(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(pv); + struct CBInfoArray cbinfo = {}; + int i; + + if (!have_clipboard(vd)) { + return 0; + } + + for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { + if (qemu_clipboard_peer_owns(&vd->cbpeer, i)) { + cbinfo.cbinfo[cbinfo.n++] = *qemu_clipboard_info(i); + } + } + + return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc); +} + +static int get_cbinfo(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(pv); + struct CBInfoArray cbinfo = {}; + int i, ret; + + if (!have_clipboard(vd)) { + return 0; + } + + vdagent_clipboard_peer_register(vd); + + ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0); + if (ret) { + return ret; + } + + for (i = 0; i < cbinfo.n; i++) { + g_autoptr(QemuClipboardInfo) info = + qemu_clipboard_info_new(&vd->cbpeer, cbinfo.cbinfo[i].selection); + /* this will steal clipboard data pointer from cbinfo.types */ + memcpy(info->types, cbinfo.cbinfo[i].types, sizeof(cbinfo.cbinfo[i].types)); + qemu_clipboard_update(info); + } + + return 0; +} + +static const VMStateInfo vmstate_cbinfos = { + .name = "vdagent/cbinfos", + .get = get_cbinfo, + .put = put_cbinfo, +}; + +static const VMStateDescription vmstate_vdagent = { + .name = "vdagent", + .version_id = 0, + .minimum_version_id = 0, + .post_load = post_load, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(connected, VDAgentChardev), + VMSTATE_UINT32(caps, VDAgentChardev), + VMSTATE_STRUCT(chunk, VDAgentChardev, 0, vmstate_chunk, VDIChunkHeader), + VMSTATE_UINT32(chunksize, VDAgentChardev), + VMSTATE_UINT32(msgsize, VDAgentChardev), + VMSTATE_VBUFFER_ALLOC_UINT32(msgbuf, VDAgentChardev, 0, 0, msgsize), + VMSTATE_UINT32(xsize, VDAgentChardev), + VMSTATE_UINT32(xoff, VDAgentChardev), + VMSTATE_VBUFFER_ALLOC_UINT32(xbuf, VDAgentChardev, 0, 0, xsize), + VMSTATE_STRUCT_POINTER(outbuf, VDAgentChardev, vmstate_vdba, GByteArray), + VMSTATE_UINT32(mouse_x, VDAgentChardev), + VMSTATE_UINT32(mouse_y, VDAgentChardev), + VMSTATE_UINT32(mouse_btn, VDAgentChardev), + VMSTATE_UINT32(mouse_display, VDAgentChardev), + VMSTATE_UINT32_ARRAY(last_serial, VDAgentChardev, + QEMU_CLIPBOARD_SELECTION__COUNT), + VMSTATE_UINT32_ARRAY(cbpending, VDAgentChardev, + QEMU_CLIPBOARD_SELECTION__COUNT), + { + .name = "cbinfos", + .info = &vmstate_cbinfos, + .flags = VMS_SINGLE, + }, + VMSTATE_END_OF_LIST() + } +}; + static void vdagent_chr_init(Object *obj) { VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); - buffer_init(&vd->outbuf, "vdagent-outbuf"); - error_setg(&vd->migration_blocker, - "The vdagent chardev doesn't yet support migration"); + vd->outbuf = g_byte_array_new(); + vmstate_register_any(NULL, &vmstate_vdagent, vd); } static void vdagent_chr_fini(Object *obj) { VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); - migrate_del_blocker(&vd->migration_blocker); vdagent_disconnect(vd); if (vd->mouse_hs) { qemu_input_handler_unregister(vd->mouse_hs); } - buffer_free(&vd->outbuf); + g_clear_pointer(&vd->outbuf, g_byte_array_unref); } static const TypeInfo vdagent_chr_type_info = { diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index 41f559e..25c7b2c 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -150,7 +150,7 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) * If client is big-endian, color samples begin from the second * byte (offset 1) of a 32-bit pixel value. */ - off = vs->client_be; + off = vs->client_endian == G_BIG_ENDIAN ? 1 : 0; memset(stats, 0, sizeof (stats)); @@ -891,7 +891,7 @@ static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret) buf8 = buf; - if (1 /* FIXME */) { + if (vs->client_endian == G_BYTE_ORDER) { rshift = vs->client_pf.rshift; gshift = vs->client_pf.gshift; bshift = vs->client_pf.bshift; @@ -1001,16 +1001,24 @@ static int send_mono_rect(VncState *vs, int x, int y, break; } case 2: - vnc_write(vs, &bg, 2); - vnc_write(vs, &fg, 2); + { + uint16_t bg16 = bg; + uint16_t fg16 = fg; + vnc_write(vs, &bg16, 2); + vnc_write(vs, &fg16, 2); tight_encode_mono_rect16(vs->tight->tight.buffer, w, h, bg, fg); break; + } default: - vnc_write_u8(vs, bg); - vnc_write_u8(vs, fg); + { + uint8_t bg8 = bg; + uint8_t fg8 = fg; + vnc_write_u8(vs, bg8); + vnc_write_u8(vs, fg8); tight_encode_mono_rect8(vs->tight->tight.buffer, w, h, bg, fg); break; } + } vs->tight->tight.offset = bytes; bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY); diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c index bd33b89..97ec6c7 100644 --- a/ui/vnc-enc-zrle.c +++ b/ui/vnc-enc-zrle.c @@ -255,7 +255,7 @@ static void zrle_write_u8(VncState *vs, uint8_t value) static int zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) { - bool be = vs->client_be; + bool be = vs->client_endian == G_BIG_ENDIAN; size_t bytes; int zywrle_level; diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index fcca7ec..d3486af 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -188,7 +188,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->lossy_rect = orig->lossy_rect; local->write_pixels = orig->write_pixels; local->client_pf = orig->client_pf; - local->client_be = orig->client_be; + local->client_endian = orig->client_endian; local->tight = orig->tight; local->zlib = orig->zlib; local->hextile = orig->hextile; @@ -891,7 +891,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) buf[0] = v; break; case 2: - if (vs->client_be) { + if (vs->client_endian == G_BIG_ENDIAN) { buf[0] = v >> 8; buf[1] = v; } else { @@ -901,7 +901,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) break; default: case 4: - if (vs->client_be) { + if (vs->client_endian == G_BIG_ENDIAN) { buf[0] = v >> 24; buf[1] = v >> 16; buf[2] = v >> 8; @@ -2240,7 +2240,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) static void set_pixel_conversion(VncState *vs) { - pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf); + pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf, + vs->client_endian); if (fmt == VNC_SERVER_FB_FORMAT) { vs->write_pixels = vnc_write_pixels_copy; @@ -2312,7 +2313,7 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel, vs->client_pf.bits_per_pixel = bits_per_pixel; vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; - vs->client_be = big_endian_flag; + vs->client_endian = big_endian_flag ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; if (!true_color_flag) { send_color_map(vs); @@ -323,7 +323,7 @@ struct VncState VncWritePixels *write_pixels; PixelFormat client_pf; pixman_format_code_t client_format; - bool client_be; + int client_endian; /* G_LITTLE_ENDIAN or G_BIG_ENDIAN */ CaptureVoiceOut *audio_cap; struct audsettings as; |