Fullscreen mode generally just assigns display 1 to monitor 1, display 2 to monitor 2, etc. For custom setups, you can define a monitor mapping in the settings keyfile per-vm. This requires a vm uuid (so only works in virt-viewer or on versions of spice-server that send the uuid over the wire). The format is pretty basic: [6485b20f-e9da-614c-72b0-60a7857e7886] monitor-mapping=2;3 The group name ("6485b20f-e9da-614c-72b0-60a7857e7886") is the uuid id of the vm. This group has a single key: monitor-mapping. This key is an array of integers describing the order in which to assign the monitors to a guest display. Any monitors that are not listed in this array will not be configured at startup. For instance: monitor-mapping=2;1 will attempt to configure 2 displays on the guest and assign the first display to monitor 2 and the second display to monitor 1. monitor-mapping=2 will only configure a single display on the guest and place it on the second monitor. Any monitor numbers listed in the keyfile are greater than the number of monitors that are physically present, they will be ignored. --- src/virt-viewer-app.c | 101 ++++++++++++++++++++++++++++++++++++---- src/virt-viewer-app.h | 3 ++ src/virt-viewer-session-spice.c | 41 +++++++++++++--- src/virt-viewer-window.c | 9 +++- src/virt-viewer.c | 7 +++ 5 files changed, 144 insertions(+), 17 deletions(-) diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c index d071568..da25863 100644 --- a/src/virt-viewer-app.c +++ b/src/virt-viewer-app.c @@ -108,6 +108,7 @@ struct _VirtViewerAppPrivate { VirtViewerWindow *main_window; GtkWidget *main_notebook; GHashTable *windows; + GArray *initial_display_map; gchar *clipboard; gboolean direct; @@ -271,6 +272,82 @@ virt_viewer_app_quit(VirtViewerApp *self) gtk_main_quit(); } +gint virt_viewer_app_get_n_initial_displays(VirtViewerApp* self) +{ + if (self->priv->initial_display_map) + return self->priv->initial_display_map->len; + + return gdk_screen_get_n_monitors(gdk_screen_get_default()); +} + +gint virt_viewer_app_get_initial_monitor_for_display(VirtViewerApp* self, gint display) +{ + gint monitor = -1; + + if (self->priv->initial_display_map) { + if (display < self->priv->initial_display_map->len) + monitor = g_array_index(self->priv->initial_display_map, gint, display); + } else { + monitor = display; + } + + return monitor; +} + + +void virt_viewer_app_set_uuid_string(VirtViewerApp*self, const gchar* uuid_string) +{ + GArray* mapping = NULL; + GError* error = NULL; + gsize ndisplays = 0; + gint* displays = NULL; + gint nmonitors = gdk_screen_get_n_monitors(gdk_screen_get_default()); + + DEBUG_LOG("%s: UUID changed to %s", G_STRFUNC, uuid_string); + + displays = g_key_file_get_integer_list(self->priv->config, + uuid_string, "monitor-mapping", &ndisplays, &error); + if (error) { + if (error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) + g_warning("Error reading monitor assignments: %s", error->message); + g_clear_error(&error); + } else { + int i = 0; + mapping = g_array_sized_new(FALSE, FALSE, sizeof(displays[0]), ndisplays); + // config file format is 1-based, not 0-based + for (i = 0; i < ndisplays; i++) { + gint val = displays[i] - 1; + + // sanity check + if (val >= nmonitors) + g_warning("Initial monitor #%i for display #%i does not exist, skipping...", val, i); + else + g_array_append_val(mapping, val); + } + g_free(displays); + } + + if (self->priv->initial_display_map) + g_array_unref(self->priv->initial_display_map); + + self->priv->initial_display_map = mapping; + + // if we're changing our initial display map, move any existing windows to + // the appropriate monitors according to the per-vm configuration + if (mapping && self->priv->fullscreen) { + GHashTableIter iter; + gpointer value; + gint i = 0; + + g_hash_table_iter_init(&iter, self->priv->windows); + while (g_hash_table_iter_next(&iter, NULL, &value)) { + gint monitor = virt_viewer_app_get_initial_monitor_for_display(self, i); + virt_viewer_window_enter_fullscreen(VIRT_VIEWER_WINDOW(value), monitor); + i++; + } + } +} + void virt_viewer_app_maybe_quit(VirtViewerApp *self, VirtViewerWindow *window) { @@ -674,7 +751,8 @@ virt_viewer_app_window_new(VirtViewerApp *self, gint nth) virt_viewer_window_set_zoom_level(window, virt_viewer_window_get_zoom_level(self->priv->main_window)); virt_viewer_app_set_nth_window(self, nth, window); if (self->priv->fullscreen) - app_window_try_fullscreen(self, window, nth); + app_window_try_fullscreen(self, window, + virt_viewer_app_get_initial_monitor_for_display(self, nth)); w = virt_viewer_window_get_window(window); g_signal_connect(w, "hide", G_CALLBACK(viewer_window_visible_cb), self); @@ -745,6 +823,7 @@ virt_viewer_app_display_added(VirtViewerSession *session G_GNUC_UNUSED, } window = virt_viewer_app_window_new(self, nth); + } } @@ -1283,19 +1362,20 @@ virt_viewer_app_set_kiosk(VirtViewerApp *self, gboolean enabled) int i; self->priv->kiosk = enabled; - if (enabled) + if (enabled) { virt_viewer_app_set_fullscreen(self, enabled); - for (i = 0; i < gdk_screen_get_n_monitors(gdk_screen_get_default()); i++) { - VirtViewerWindow *win = virt_viewer_app_get_nth_window(self, i); + for (i = 0; i < gdk_screen_get_n_monitors(gdk_screen_get_default()); i++) { + VirtViewerWindow *win = virt_viewer_app_get_nth_window(self, i); - if (win == NULL) - win = virt_viewer_app_window_new(self, i); + if (win == NULL) + win = virt_viewer_app_window_new(self, i); - if (enabled) - virt_viewer_window_show(win); + if (enabled) + virt_viewer_window_show(win); - virt_viewer_window_set_kiosk(win, enabled); + virt_viewer_window_set_kiosk(win, enabled); + } } } @@ -1433,6 +1513,7 @@ virt_viewer_app_dispose (GObject *object) g_free(priv->config_file); priv->config_file = NULL; g_clear_pointer(&priv->config, g_key_file_free); + g_clear_pointer(&priv->initial_display_map, g_array_unref); virt_viewer_app_free_connect_info(self); @@ -1854,8 +1935,8 @@ static void fullscreen_cb(gpointer key, gpointer value, gpointer user_data) { - gint nth = *(gint*)key; FullscreenOptions *options = (FullscreenOptions *)user_data; + gint nth = virt_viewer_app_get_initial_monitor_for_display(options->app, *(gint*)key); VirtViewerWindow *vwin = VIRT_VIEWER_WINDOW(value); DEBUG_LOG("fullscreen display %d: %d", nth, options->fullscreen); diff --git a/src/virt-viewer-app.h b/src/virt-viewer-app.h index 4721fe3..6c9443a 100644 --- a/src/virt-viewer-app.h +++ b/src/virt-viewer-app.h @@ -99,6 +99,9 @@ VirtViewerSession* virt_viewer_app_get_session(VirtViewerApp *self); gboolean virt_viewer_app_get_fullscreen(VirtViewerApp *app); gboolean virt_viewer_app_get_fullscreen_auto_conf(VirtViewerApp *app); const GOptionEntry* virt_viewer_app_get_options(void); +gint virt_viewer_app_get_n_initial_displays(VirtViewerApp* self); +gint virt_viewer_app_get_initial_monitor_for_display(VirtViewerApp* self, gint display); +void virt_viewer_app_set_uuid_string(VirtViewerApp* self, const gchar* uuid_string); G_END_DECLS diff --git a/src/virt-viewer-session-spice.c b/src/virt-viewer-session-spice.c index 4ed4aff..229d01e 100644 --- a/src/virt-viewer-session-spice.c +++ b/src/virt-viewer-session-spice.c @@ -28,6 +28,7 @@ #include <glib/gi18n.h> #include <spice-option.h> +#include <spice-util.h> #include <usb-device-widget.h> #include "virt-viewer-file.h" #include "virt-viewer-util.h" @@ -51,6 +52,7 @@ struct _VirtViewerSessionSpicePrivate { SpiceMainChannel *main_channel; const SpiceAudio *audio; int channel_count; + int display_channel_count; int usbredir_channel_count; gboolean has_sw_smartcard_reader; guint pass_try; @@ -677,10 +679,11 @@ virt_viewer_session_spice_channel_new(SpiceSession *s, self->priv->main_channel = SPICE_MAIN_CHANNEL(channel); g_signal_connect(channel, "notify::agent-connected", G_CALLBACK(agent_connected_changed), self); - virt_viewer_session_spice_fullscreen_auto_conf(self); } if (SPICE_IS_DISPLAY_CHANNEL(channel)) { + self->priv->display_channel_count++; + virt_viewer_session_spice_fullscreen_auto_conf(self); g_signal_emit_by_name(session, "session-initialized"); g_signal_connect(channel, "notify::monitors", @@ -717,7 +720,8 @@ virt_viewer_session_spice_fullscreen_auto_conf(VirtViewerSessionSpice *self) VirtViewerApp *app = NULL; GdkRectangle dest; gboolean agent_connected; - gint i; + gint i, j; + gsize ndisplays = 0; app = virt_viewer_session_get_app(VIRT_VIEWER_SESSION(self)); g_return_val_if_fail(VIRT_VIEWER_IS_APP(app), TRUE); @@ -730,21 +734,28 @@ virt_viewer_session_spice_fullscreen_auto_conf(VirtViewerSessionSpice *self) DEBUG_LOG("no main channel yet"); return FALSE; } + if (self->priv->display_channel_count == 0) { + DEBUG_LOG("no display channel yet"); + return FALSE; + } g_object_get(cmain, "agent-connected", &agent_connected, NULL); if (!agent_connected) { DEBUG_LOG("Agent not connected, skipping autoconf"); return FALSE; } - DEBUG_LOG("Performing full screen auto-conf, %d host monitors", - gdk_screen_get_n_monitors(screen)); g_object_set(G_OBJECT(cmain), "disable-display-position", FALSE, "disable-display-align", TRUE, NULL); spice_main_set_display_enabled(cmain, -1, FALSE); - for (i = 0; i < gdk_screen_get_n_monitors(screen); i++) { - gdk_screen_get_monitor_geometry(screen, i, &dest); + + ndisplays = virt_viewer_app_get_n_initial_displays(app); + DEBUG_LOG("Performing full screen auto-conf, %zd host monitors", ndisplays); + + for (i = 0; i < ndisplays; i++) { + j = virt_viewer_app_get_initial_monitor_for_display(app, i); + gdk_screen_get_monitor_geometry(screen, j, &dest); DEBUG_LOG("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); @@ -777,6 +788,7 @@ virt_viewer_session_spice_channel_destroy(G_GNUC_UNUSED SpiceSession *s, if (SPICE_IS_DISPLAY_CHANNEL(channel)) { VirtViewerDisplay *display = g_object_get_data(G_OBJECT(channel), "virt-viewer-display"); DEBUG_LOG("zap display channel (#%d, %p)", id, display); + self->priv->display_channel_count--; } if (SPICE_IS_PLAYBACK_CHANNEL(channel) && self->priv->audio) { @@ -804,6 +816,22 @@ fullscreen_changed(GObject *gobject G_GNUC_UNUSED, virt_viewer_session_spice_fullscreen_auto_conf(self); } +static void +uuid_changed(GObject *gobject G_GNUC_UNUSED, + GParamSpec *pspec G_GNUC_UNUSED, + VirtViewerSessionSpice *self) +{ + guint8* uuid = NULL; + gchar* uuid_str = NULL; + VirtViewerApp* app = virt_viewer_session_get_app(VIRT_VIEWER_SESSION(self)); + + g_object_get(self->priv->session, "uuid", &uuid, NULL); + uuid_str = spice_uuid_to_string(uuid); + virt_viewer_app_set_uuid_string(app, uuid_str); + g_free(uuid_str); + +} + VirtViewerSession * virt_viewer_session_spice_new(VirtViewerApp *app, GtkWindow *main_window) { @@ -815,6 +843,7 @@ virt_viewer_session_spice_new(VirtViewerApp *app, GtkWindow *main_window) self->priv->main_window = g_object_ref(main_window); g_signal_connect(app, "notify::fullscreen", G_CALLBACK(fullscreen_changed), self); + g_signal_connect(self->priv->session, "notify::uuid", G_CALLBACK(uuid_changed), self); return VIRT_VIEWER_SESSION(self); } diff --git a/src/virt-viewer-window.c b/src/virt-viewer-window.c index 5ce1d98..ac2ec3e 100644 --- a/src/virt-viewer-window.c +++ b/src/virt-viewer-window.c @@ -496,7 +496,9 @@ virt_viewer_window_leave_fullscreen(VirtViewerWindow *self) if (!priv->fullscreen) return; + g_signal_handlers_block_by_func(check, virt_viewer_window_menu_view_fullscreen, self); gtk_check_menu_item_set_active(check, FALSE); + g_signal_handlers_unblock_by_func(check, virt_viewer_window_menu_view_fullscreen, self); priv->fullscreen = FALSE; priv->fullscreen_monitor = -1; if (priv->display) { @@ -528,18 +530,23 @@ virt_viewer_window_enter_fullscreen(VirtViewerWindow *self, gint monitor) GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "top-menu")); GtkCheckMenuItem *check = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(priv->builder, "menu-view-fullscreen")); + if (priv->fullscreen && priv->fullscreen_monitor != monitor) + virt_viewer_window_leave_fullscreen(self); + if (priv->fullscreen) return; - priv->fullscreen_monitor = monitor; priv->fullscreen = TRUE; + priv->fullscreen_monitor = monitor; if (!gtk_widget_get_mapped(priv->window)) { g_signal_connect(priv->window, "map-event", G_CALLBACK(mapped), self); return; } + g_signal_handlers_block_by_func(check, virt_viewer_window_menu_view_fullscreen, self); gtk_check_menu_item_set_active(check, TRUE); + g_signal_handlers_unblock_by_func(check, virt_viewer_window_menu_view_fullscreen, self); gtk_widget_hide(menu); gtk_widget_show(priv->toolbar); ViewAutoDrawer_SetActive(VIEW_AUTODRAWER(priv->layout), TRUE); diff --git a/src/virt-viewer.c b/src/virt-viewer.c index ae25fc6..e1553fd 100644 --- a/src/virt-viewer.c +++ b/src/virt-viewer.c @@ -530,6 +530,7 @@ virt_viewer_initial_connect(VirtViewerApp *app, GError **error) gboolean ret = FALSE; VirtViewer *self = VIRT_VIEWER(app); VirtViewerPrivate *priv = self->priv; + char uuid_string[VIR_UUID_STRING_BUFLEN]; DEBUG_LOG("initial connect"); @@ -555,6 +556,12 @@ virt_viewer_initial_connect(VirtViewerApp *app, GError **error) } } + if (virDomainGetUUIDString(dom, uuid_string) < 0) { + DEBUG_LOG("Couldn't get uuid from libvirt"); + } else { + virt_viewer_app_set_uuid_string(app, uuid_string); + } + virt_viewer_app_show_status(app, _("Checking guest domain status")); if (virDomainGetInfo(dom, &info) < 0) { DEBUG_LOG("Cannot get guest state"); -- 1.8.3.1 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list