[virt-tools-list] [PATCH] Add ability to define custom display->monitor mapping per vm

Jonathon Jongsma jjongsma at redhat.com
Wed Oct 30 14:38:03 UTC 2013


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




More information about the virt-tools-list mailing list