When using a custom fullscreen display configuration, it's possible to specify that e.g. a single screen should be fullscreen on client monitor #4. Since we send down absolute positions and disable alignment when all windows are in fullscreen, we can send configurations with a very large offset to the top-left corner. This could result in the guest trying to create a screen that was much larger than necessary. For example when sending a configuration of 1280x1024+4240+0, the guest would need to allocate a screen of size 5520x1024, which might fail if video memory was too low. To avoid this issue, we shift all displays down so that the minimum X coordinate for all screens is at x=0, and the minimum y coordinate is at y=0. --- src/virt-viewer-session-spice.c | 22 +++++++--- src/virt-viewer-session.c | 53 ++---------------------- src/virt-viewer-util.c | 91 +++++++++++++++++++++++++++++++++++++++++ src/virt-viewer-util.h | 6 +++ 4 files changed, 117 insertions(+), 55 deletions(-) diff --git a/src/virt-viewer-session-spice.c b/src/virt-viewer-session-spice.c index 885399c..37c083e 100644 --- a/src/virt-viewer-session-spice.c +++ b/src/virt-viewer-session-spice.c @@ -770,7 +770,7 @@ virt_viewer_session_spice_fullscreen_auto_conf(VirtViewerSessionSpice *self) GdkScreen *screen = gdk_screen_get_default(); SpiceMainChannel* cmain = virt_viewer_session_spice_get_main_channel(self); VirtViewerApp *app = NULL; - GdkRectangle dest; + GdkRectangle *displays; gboolean agent_connected; gint i; gsize ndisplays = 0; @@ -804,18 +804,30 @@ virt_viewer_session_spice_fullscreen_auto_conf(VirtViewerSessionSpice *self) ndisplays = virt_viewer_app_get_n_initial_displays(app); g_debug("Performing full screen auto-conf, %" G_GSIZE_FORMAT " host monitors", ndisplays); + displays = g_new0(GdkRectangle, ndisplays); for (i = 0; i < ndisplays; i++) { + GdkRectangle* rect = &displays[i]; gint j = virt_viewer_app_get_initial_monitor_for_display(app, i); if (j == -1) continue; - gdk_screen_get_monitor_geometry(screen, j, &dest); - g_debug("Set SPICE display %d to (%d,%d)-(%dx%d)", - i, dest.x, dest.y, dest.width, dest.height); - spice_main_set_display(cmain, i, dest.x, dest.y, dest.width, dest.height); + gdk_screen_get_monitor_geometry(screen, j, rect); + } + + virt_viewer_shift_monitors_to_origin(displays, ndisplays); + + for (i = 0; i < ndisplays; i++) { + GdkRectangle *rect = &displays[i]; + if (rect->width == 0 || rect->height == 0) + continue; + + spice_main_set_display(cmain, i, rect->x, rect->y, rect->width, rect->height); spice_main_set_display_enabled(cmain, i, TRUE); + g_debug("Set SPICE display %d to (%d,%d)-(%dx%d)", + i, rect->x, rect->y, rect->width, rect->height); } + g_free(displays); spice_main_send_monitor_config(cmain); self->priv->did_auto_conf = TRUE; diff --git a/src/virt-viewer-session.c b/src/virt-viewer-session.c index d9c84a6..4262eda 100644 --- a/src/virt-viewer-session.c +++ b/src/virt-viewer-session.c @@ -339,55 +339,6 @@ virt_viewer_session_init(VirtViewerSession *session) session->priv = VIRT_VIEWER_SESSION_GET_PRIVATE(session); } -/* simple sorting of monitors. Primary sort left-to-right, secondary sort from - * top-to-bottom, finally by monitor id */ -static int -displays_cmp(const void *p1, const void *p2, gpointer user_data) -{ - guint diff; - GdkRectangle *displays = user_data; - guint i = *(guint*)p1; - guint j = *(guint*)p2; - GdkRectangle *m1 = &displays[i]; - GdkRectangle *m2 = &displays[j]; - diff = m1->x - m2->x; - if (diff == 0) - diff = m1->y - m2->y; - if (diff == 0) - diff = i - j; - - return diff; -} - -static void -virt_viewer_session_align_monitors_linear(GdkRectangle *displays, guint ndisplays) -{ - gint i, x = 0; - guint *sorted_displays; - - g_return_if_fail(displays != NULL); - - if (ndisplays == 0) - return; - - sorted_displays = g_new0(guint, ndisplays); - for (i = 0; i < ndisplays; i++) - sorted_displays[i] = i; - g_qsort_with_data(sorted_displays, ndisplays, sizeof(guint), displays_cmp, displays); - - /* adjust monitor positions so that there's no gaps or overlap between - * monitors */ - for (i = 0; i < ndisplays; i++) { - guint nth = sorted_displays[i]; - g_assert(nth < ndisplays); - GdkRectangle *rect = &displays[nth]; - rect->x = x; - rect->y = 0; - x += rect->width; - } - g_free(sorted_displays); -} - static void virt_viewer_session_on_monitor_geometry_changed(VirtViewerSession* self, VirtViewerDisplay* display G_GNUC_UNUSED) @@ -428,7 +379,9 @@ virt_viewer_session_on_monitor_geometry_changed(VirtViewerSession* self, } if (!all_fullscreen) - virt_viewer_session_align_monitors_linear(monitors, nmonitors); + virt_viewer_align_monitors_linear(monitors, nmonitors); + + virt_viewer_shift_monitors_to_origin(monitors, nmonitors); klass->apply_monitor_geometry(self, monitors, nmonitors); g_free(monitors); diff --git a/src/virt-viewer-util.c b/src/virt-viewer-util.c index 655f489..401b220 100644 --- a/src/virt-viewer-util.c +++ b/src/virt-viewer-util.c @@ -488,6 +488,97 @@ end: g_strfreev(v2); return retval; } + +/* simple sorting of monitors. Primary sort left-to-right, secondary sort from + * top-to-bottom, finally by monitor id */ +static int +displays_cmp(const void *p1, const void *p2, gpointer user_data) +{ + guint diff; + GdkRectangle *displays = user_data; + guint i = *(guint*)p1; + guint j = *(guint*)p2; + GdkRectangle *m1 = &displays[i]; + GdkRectangle *m2 = &displays[j]; + diff = m1->x - m2->x; + if (diff == 0) + diff = m1->y - m2->y; + if (diff == 0) + diff = i - j; + + return diff; +} + +void +virt_viewer_align_monitors_linear(GdkRectangle *displays, guint ndisplays) +{ + gint i, x = 0; + guint *sorted_displays; + + g_return_if_fail(displays != NULL); + + if (ndisplays == 0) + return; + + sorted_displays = g_new0(guint, ndisplays); + for (i = 0; i < ndisplays; i++) + sorted_displays[i] = i; + g_qsort_with_data(sorted_displays, ndisplays, sizeof(guint), displays_cmp, displays); + + /* adjust monitor positions so that there's no gaps or overlap between + * monitors */ + for (i = 0; i < ndisplays; i++) { + guint nth = sorted_displays[i]; + g_assert(nth < ndisplays); + GdkRectangle *rect = &displays[nth]; + rect->x = x; + rect->y = 0; + x += rect->width; + } + g_free(sorted_displays); +} + +/* Shift all displays down so that the monitor origin is at (0,0). This reduces + * the size of the screen that will be required on the guest in the case where + * there is a fullscreen window on a non-primary client monitor. For example, + * instead of sending down the following configuration: + * 1280x1024+4240+0 + * which implies that the guest screen must be at least 5520x1024, we'd send + * 1280x1024+0+0 + * which implies the guest screen is only 1280x1024. The first version might + * fail if the guest video memory is not large enough to handle a screen of + * that size. + */ +void +virt_viewer_shift_monitors_to_origin(GdkRectangle *displays, guint ndisplays) +{ + gint xmin = G_MAXINT; + gint ymin = G_MAXINT; + gint i; + + g_return_if_fail(ndisplays > 0); + + for (i = 0; i < ndisplays; i++) { + GdkRectangle *display = &displays[i]; + if (display->width > 0 && display->height > 0) { + xmin = MIN(xmin, display->x); + ymin = MIN(ymin, display->y); + } + } + + if (xmin > 0 || ymin > 0) { + g_debug("%s: Shifting all monitors down by (%i, %i)", G_STRFUNC, xmin, ymin); + for (i = 0; i < ndisplays; i++) { + GdkRectangle *display = &displays[i]; + if (display->width > 0 && display->height > 0) { + display->x -= xmin; + display->y -= ymin; + } + } + } +} + + /* * Local variables: * c-indent-level: 4 diff --git a/src/virt-viewer-util.h b/src/virt-viewer-util.h index 287f9bd..8c38739 100644 --- a/src/virt-viewer-util.h +++ b/src/virt-viewer-util.h @@ -55,6 +55,12 @@ gulong virt_viewer_signal_connect_object(gpointer instance, gchar* spice_hotkey_to_gtk_accelerator(const gchar *key); gint virt_viewer_compare_version(const gchar *s1, const gchar *s2); +/* monitor alignment */ +void virt_viewer_align_monitors_linear(GdkRectangle *displays, guint ndisplays); +void virt_viewer_shift_monitors_to_origin(GdkRectangle *displays, guint ndisplays); + + + #endif /* -- 1.9.3 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list