[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