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

Marc-André Lureau mlureau at redhat.com
Fri Nov 8 22:24:16 UTC 2013



----- Original Message -----
> 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           | 111
>  ++++++++++++++++++++++++++++++++++------
>  src/virt-viewer-app.h           |   3 ++
>  src/virt-viewer-session-spice.c |  63 ++++++++++++++++++-----
>  src/virt-viewer-window.c        |   7 +++
>  src/virt-viewer.c               |   7 +++
>  5 files changed, 163 insertions(+), 28 deletions(-)
> 
> diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c
> index cdf6104..7962dc5 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;
> @@ -273,6 +274,96 @@ 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;
> +}
> +
> +static void
> +app_window_try_fullscreen(VirtViewerApp *self G_GNUC_UNUSED,
> +                          VirtViewerWindow *win, gint nth)
> +{
> +    GdkScreen *screen = gdk_screen_get_default();
> +
> +    if (nth >= gdk_screen_get_n_monitors(screen)) {
> +        DEBUG_LOG("skipping display %d", nth);
> +        return;
> +    }
> +
> +    virt_viewer_window_enter_fullscreen(win, nth);
> +}
> +
> +
> +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);
> +            app_window_try_fullscreen(self, VIRT_VIEWER_WINDOW(value),
> monitor);
> +            i++;
> +        }
> +    }
> +}
> +
>  void
>  virt_viewer_app_maybe_quit(VirtViewerApp *self, VirtViewerWindow *window)
>  {
> @@ -648,20 +739,6 @@ viewer_window_focus_out_cb(GtkWindow *window
> G_GNUC_UNUSED,
>      return FALSE;
>  }
>  
> -static void
> -app_window_try_fullscreen(VirtViewerApp *self G_GNUC_UNUSED,
> -                          VirtViewerWindow *win, gint nth)
> -{
> -    GdkScreen *screen = gdk_screen_get_default();
> -
> -    if (nth >= gdk_screen_get_n_monitors(screen)) {
> -        DEBUG_LOG("skipping display %d", nth);
> -        return;
> -    }
> -
> -    virt_viewer_window_enter_fullscreen(win, nth);
> -}
> -
>  static VirtViewerWindow*
>  virt_viewer_app_window_new(VirtViewerApp *self, gint nth)
>  {
> @@ -678,7 +755,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);
> @@ -1445,6 +1523,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);
>  
> @@ -1883,8 +1962,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 f72f5b3..737b0af 100644
> --- a/src/virt-viewer-app.h
> +++ b/src/virt-viewer-app.h
> @@ -100,6 +100,9 @@ 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);
>  void virt_viewer_app_clear_hotkeys(VirtViewerApp *app);
> +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 b42d48e..41467cf 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"
> @@ -579,8 +580,6 @@ agent_connected_changed(SpiceChannel *cmain
> G_GNUC_UNUSED,
>  {
>      // this will force refresh of application menu
>      g_signal_emit_by_name(self, "session-display-updated");
> -
> -    virt_viewer_session_spice_fullscreen_auto_conf(self);
>  }
>  
>  static void
> @@ -677,7 +676,6 @@ 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)) {
> @@ -709,6 +707,14 @@ virt_viewer_session_spice_channel_new(SpiceSession *s,
>      self->priv->channel_count++;
>  }
>  
> +static void
> +property_notify_do_auto_conf(GObject *gobject G_GNUC_UNUSED,
> +                             GParamSpec *pspec G_GNUC_UNUSED,
> +                             VirtViewerSessionSpice *self)
> +{
> +    virt_viewer_session_spice_fullscreen_auto_conf(self);
> +}
> +
>  static gboolean
>  virt_viewer_session_spice_fullscreen_auto_conf(VirtViewerSessionSpice *self)
>  {
> @@ -718,6 +724,7 @@
> virt_viewer_session_spice_fullscreen_auto_conf(VirtViewerSessionSpice *self)
>      GdkRectangle dest;
>      gboolean auto_conf, agent_connected;
>      gint i;
> +    gsize ndisplays = 0;
>  
>      app = virt_viewer_session_get_app(VIRT_VIEWER_SESSION(self));
>      g_return_val_if_fail(VIRT_VIEWER_IS_APP(app), TRUE);
> @@ -739,18 +746,23 @@
> virt_viewer_session_spice_fullscreen_auto_conf(VirtViewerSessionSpice *self)
>      g_object_get(cmain, "agent-connected", &agent_connected, NULL);
>      if (!agent_connected) {
>          DEBUG_LOG("Agent not connected, skipping autoconf");
> +        g_signal_connect(cmain, "notify::agent-connected",
> G_CALLBACK(property_notify_do_auto_conf), self);
>          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++) {
> +        gint 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);
> @@ -802,11 +814,34 @@ virt_viewer_session_spice_channel_destroy(G_GNUC_UNUSED
> SpiceSession *s,
>          g_signal_emit_by_name(self, "session-disconnected");
>  }
>  
> +#define UUID_LEN 16
>  static void
> -fullscreen_changed(GObject *gobject G_GNUC_UNUSED,
> -                   GParamSpec *pspec G_GNUC_UNUSED,
> -                   VirtViewerSessionSpice *self)
> -{
> +uuid_changed(GObject *gobject G_GNUC_UNUSED,
> +             GParamSpec *pspec G_GNUC_UNUSED,
> +             VirtViewerSessionSpice *self)
> +{
> +    guint8* uuid = NULL;
> +    VirtViewerApp* app =
> virt_viewer_session_get_app(VIRT_VIEWER_SESSION(self));
> +
> +    g_object_get(self->priv->session, "uuid", &uuid, NULL);
> +    if (uuid) {
> +        int i;
> +        gboolean uuid_empty = TRUE;
> +
> +        for (i = 0; i < UUID_LEN; i++) {
> +            if (uuid[i] != 0) {
> +                uuid_empty = FALSE;
> +                break;
> +            }
> +        }
> +
> +        if (!uuid_empty) {
> +            gchar* uuid_str = spice_uuid_to_string(uuid);
> +            virt_viewer_app_set_uuid_string(app, uuid_str);
> +            g_free(uuid_str);
> +        }
> +    }
> +
>      virt_viewer_session_spice_fullscreen_auto_conf(self);
>  }
>  
> @@ -820,7 +855,11 @@ virt_viewer_session_spice_new(VirtViewerApp *app,
> GtkWindow *main_window)
>      create_spice_session(self);
>      self->priv->main_window = g_object_ref(main_window);
>  
> -    g_signal_connect(app, "notify::fullscreen",
> G_CALLBACK(fullscreen_changed),  self);
> +    g_signal_connect(app, "notify::fullscreen",
> G_CALLBACK(property_notify_do_auto_conf), self);
> +
> +    /* notify::uuid is guaranteed to be emitted during connection startup
> even
> +     * if the server is too old to support sending uuid */
> +    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..468e61c 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);

Is this block/unblock addition related? a comment could help. could you make it a seperate fullscreen_menu_set_active() function instead?

ack otherwise

>      priv->fullscreen = FALSE;
>      priv->fullscreen_monitor = -1;
>      if (priv->display) {
> @@ -528,6 +530,9 @@ 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;
>  
> @@ -539,7 +544,9 @@ virt_viewer_window_enter_fullscreen(VirtViewerWindow
> *self, gint monitor)
>          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 at redhat.com
> https://www.redhat.com/mailman/listinfo/virt-tools-list
> 




More information about the virt-tools-list mailing list