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

Marc-André Lureau mlureau at redhat.com
Wed Oct 30 15:43:06 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.

Looks good,

> ---
>  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++;
> +        }

You couldn't call app_window_try_fullscreen() ?

It could be nice if the virt_viewer_app_get_initial_monitor_for_display() could be done there, in one place.

> +    }
> +}
> +
>  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) {

What was the reason for this change?

We wanted to disable kiosk mode, now that won't update window kiosk state.

>          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) {

Is this related? Why is it needed?

> +        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 at redhat.com
> https://www.redhat.com/mailman/listinfo/virt-tools-list
> 




More information about the virt-tools-list mailing list