[Spice-devel] [RFC PATCH spice-gtk v2 13/20] Rework the handling of monitors_config

Lukáš Hrázký lhrazky at redhat.com
Thu Aug 16 16:26:42 UTC 2018


Before this commit, the code relied on
spice_main_channel_update_display() called from the client application
to add items to the list of monitor_configs to be sent to the server
(stored in SpiceMainChannelPrivate.display). One of the disadvantages
this approach has is that all data that you need in the list have to go
through the client application. Thus adding any attribute to a
monitor_config requires an API change.

This commit changes this by creating and maintaining our own list of
monitor_configs under the session object. The methods that are exposed
to the client app no longer create new items in this list.

Now the spice_main_channel_update_display{,_enabled}() functions only
update existing items in the spice-gtk-maintained list. The identifier
for these functions was unfortunately the index in the array on the
spice-gtk side and channel_id + monitor_id in e.g. virt-viewer. This
worked under the assumption that there is either only one display
channel or multiple display channels each with only one monitor.
Presumably other clients can use whatever they like as the id, but doing
something else most probably wouldn't work.

So, we use the channel_id + monitor_id in the now-deprecated
spice_main_channel_update_display{,_enabled}() to match against their
single 'id' argument. This works for as long as the assumption mentioned
above holds true, which doesn't for the guest streaming use case
anymore. So for that use case a new client is needed using the new
consistent API.

An unsorted list is used and the items are looked up by simple
iteration. No more than 5 items are really expected in the list. The
order of the items in the list is determined by their creation timing
and therefore is undeterministic. The order should no longer matter for
anything though.

Signed-off-by: Lukáš Hrázký <lhrazky at redhat.com>
---
 src/channel-display.c    |  23 +++++-
 src/channel-main.c       | 156 +++++++++++++++++++++++++--------------
 src/channel-main.h       |   3 +
 src/map-file             |   4 +
 src/spice-glib-sym-file  |   4 +
 src/spice-session-priv.h |  10 +++
 src/spice-session.c      | 152 ++++++++++++++++++++++++++++++++++++++
 src/spice-session.h      |  31 ++++++++
 src/spice-widget.c       |   7 +-
 tools/spicy.c            |  15 ++--
 10 files changed, 334 insertions(+), 71 deletions(-)

diff --git a/src/channel-display.c b/src/channel-display.c
index 7c663cb..868aa83 100644
--- a/src/channel-display.c
+++ b/src/channel-display.c
@@ -1049,8 +1049,10 @@ static void spice_display_channel_up(SpiceChannel *channel)
     out->marshallers->msgc_display_init(out->marshaller, &init);
     spice_msg_out_send_internal(out);
 
-    /* notify of existence of this monitor */
-    g_coroutine_object_notify(G_OBJECT(channel), "monitors");
+    // if we don't have MONITORS_CONFIG_CAPABILITY, notify of existence of this monitor
+    if (!spice_channel_test_capability(channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG)) {
+        g_coroutine_object_notify(G_OBJECT(channel), "monitors");
+    }
 
     if (preferred_compression != SPICE_IMAGE_COMPRESSION_INVALID) {
         spice_display_channel_change_preferred_compression(channel, preferred_compression);
@@ -1869,6 +1871,7 @@ static void display_handle_surface_destroy(SpiceChannel *channel, SpiceMsgIn *in
 /* coroutine context */
 static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in)
 {
+    SpiceSession *session = spice_channel_get_session(channel);
     SpiceMsgDisplayMonitorsConfig *config = spice_msg_in_parsed(in);
     SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
     guint i;
@@ -1907,6 +1910,22 @@ static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in
         mc->y = head->y;
         mc->width = head->width;
         mc->height = head->height;
+
+        if (spice_session_get_monitor_config(session, channel->priv->channel_id, i)) {
+            spice_session_update_monitor_config_dimensions(session, channel->priv->channel_id, i,
+                                                           head->x, head->y, head->width, head->height, FALSE);
+        } else {
+            spice_session_add_monitor_config(session, channel->priv->channel_id, i,
+                                             head->x, head->y, head->width, head->height);
+        }
+    }
+
+    // On top of the above, create disabled configs for all monitors up to max_allowed
+    // if they don't exist.
+    for (i = config->count; i < config->max_allowed; i++) {
+        if (!spice_session_get_monitor_config(session, channel->priv->channel_id, i)) {
+            spice_session_add_monitor_config(session, channel->priv->channel_id, i, 0, 0, 0, 0);
+        }
     }
 
     g_coroutine_object_notify(G_OBJECT(channel), "monitors");
diff --git a/src/channel-main.c b/src/channel-main.c
index 4c6bc70..a23bc64 100644
--- a/src/channel-main.c
+++ b/src/channel-main.c
@@ -52,20 +52,6 @@
 
 typedef struct spice_migrate spice_migrate;
 
-typedef enum {
-    DISPLAY_UNDEFINED,
-    DISPLAY_DISABLED,
-    DISPLAY_ENABLED,
-} SpiceDisplayState;
-
-typedef struct {
-    int                     x;
-    int                     y;
-    int                     width;
-    int                     height;
-    SpiceDisplayState       display_state;
-} SpiceDisplayConfig;
-
 typedef struct {
     GHashTable                 *xfer_task;
     SpiceMainChannel           *channel;
@@ -102,7 +88,6 @@ struct _SpiceMainChannelPrivate  {
     guint                       agent_msg_pos;
     uint8_t                     agent_msg_size;
     uint32_t                    agent_caps[VD_AGENT_CAPS_SIZE];
-    SpiceDisplayConfig          display[MAX_DISPLAY];
     gint                        timer_id;
     GQueue                      *agent_msg_queue;
     GHashTable                  *file_xfer_tasks;
@@ -1111,13 +1096,17 @@ gboolean spice_main_channel_send_monitor_config(SpiceMainChannel *channel)
     c = channel->priv;
     g_return_val_if_fail(c->agent_connected, FALSE);
 
+    GArray *monitor_configs = spice_session_get_monitor_configs(
+        spice_channel_get_session(SPICE_CHANNEL(channel)));
+
     if (spice_main_channel_agent_test_capability(channel, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG)) {
-        monitors = SPICE_N_ELEMENTS(c->display);
+        monitors = monitor_configs->len;
     } else {
         monitors = 0;
-        for (i = 0; i < SPICE_N_ELEMENTS(c->display); i++) {
-            if (c->display[i].display_state == DISPLAY_ENABLED)
+        for (i = 0; i < monitor_configs->len; i++) {
+            if (g_array_index(monitor_configs, SpiceMonitorConfig, i).display_state == DISPLAY_ENABLED) {
                 monitors += 1;
+            }
         }
     }
 
@@ -1131,18 +1120,19 @@ gboolean spice_main_channel_send_monitor_config(SpiceMainChannel *channel)
 
     CHANNEL_DEBUG(channel, "sending new monitors config to guest");
     j = 0;
-    for (i = 0; i < SPICE_N_ELEMENTS(c->display); i++) {
-        if (c->display[i].display_state != DISPLAY_ENABLED) {
+    for (i = 0; i < monitor_configs->len; i++) {
+        SpiceMonitorConfig *mc = &g_array_index(monitor_configs, SpiceMonitorConfig, i);
+        if (mc->display_state != DISPLAY_ENABLED) {
             if (spice_main_channel_agent_test_capability(channel,
                                                          VD_AGENT_CAP_SPARSE_MONITORS_CONFIG))
                 j++;
             continue;
         }
         mon->monitors[j].depth  = c->display_color_depth ? c->display_color_depth : 32;
-        mon->monitors[j].width  = c->display[i].width;
-        mon->monitors[j].height = c->display[i].height;
-        mon->monitors[j].x = c->display[i].x;
-        mon->monitors[j].y = c->display[i].y;
+        mon->monitors[j].width  = mc->width;
+        mon->monitors[j].height = mc->height;
+        mon->monitors[j].x = mc->x;
+        mon->monitors[j].y = mc->y;
         CHANNEL_DEBUG(channel, "monitor #%d: %ux%u+%d+%d @ %u bpp", j,
                       mon->monitors[j].width, mon->monitors[j].height,
                       mon->monitors[j].x, mon->monitors[j].y,
@@ -1477,15 +1467,18 @@ static void agent_clipboard_release(SpiceMainChannel *channel, guint selection)
 
 static gboolean any_display_has_dimensions(SpiceMainChannel *channel)
 {
-    SpiceMainChannelPrivate *c;
     guint i;
 
     g_return_val_if_fail(SPICE_IS_MAIN_CHANNEL(channel), FALSE);
-    c = channel->priv;
 
-    for (i = 0; i < MAX_DISPLAY; i++) {
-        if (c->display[i].width > 0 && c->display[i].height > 0)
+    GArray *monitor_configs = spice_session_get_monitor_configs(
+        spice_channel_get_session(SPICE_CHANNEL(channel)));
+
+    for (i = 0; i < monitor_configs->len; i++) {
+        SpiceMonitorConfig *mc = &g_array_index(monitor_configs, SpiceMonitorConfig, i);
+        if (mc->width > 0 && mc->height > 0) {
             return TRUE;
+        }
     }
 
     return FALSE;
@@ -1509,12 +1502,19 @@ static gboolean timer_set_display(gpointer data)
     }
 
     session = spice_channel_get_session(SPICE_CHANNEL(channel));
+    GArray *monitor_configs = spice_session_get_monitor_configs(session);
 
     if (!spice_main_channel_agent_test_capability(channel, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG)) {
         /* ensure we have an explicit monitor configuration at least for
            number of display channels */
-        for (i = 0; i < spice_session_get_n_display_channels(session); i++)
-            if (c->display[i].display_state == DISPLAY_UNDEFINED) {
+
+        guint n_display_channels = spice_session_get_n_display_channels(session);
+        if (n_display_channels < monitor_configs->len) {
+            SPICE_DEBUG("Not sending monitors config, missing monitors");
+            return FALSE;
+        }
+        for (i = 0; i < n_display_channels; i++)
+            if (g_array_index(monitor_configs, SpiceMonitorConfig, i).display_state == DISPLAY_UNDEFINED) {
                 SPICE_DEBUG("Not sending monitors config, missing monitors");
                 return FALSE;
             }
@@ -1525,7 +1525,7 @@ static gboolean timer_set_display(gpointer data)
 }
 
 /* any context  */
-static void update_display_timer(SpiceMainChannel *channel, guint seconds)
+void spice_main_channel_update_display_timer(SpiceMainChannel *channel, guint seconds)
 {
     SpiceMainChannelPrivate *c = channel->priv;
 
@@ -2010,7 +2010,7 @@ static void main_agent_handle_msg(SpiceChannel *channel,
         }
         c->agent_caps_received = true;
         g_coroutine_signal_emit(self, signals[SPICE_MAIN_AGENT_UPDATE], 0);
-        update_display_timer(SPICE_MAIN_CHANNEL(channel), 0);
+        spice_main_channel_update_display_timer(SPICE_MAIN_CHANNEL(channel), 0);
 
         if (caps->request)
             agent_announce_caps(self);
@@ -2626,7 +2626,7 @@ gboolean spice_main_channel_agent_test_capability(SpiceMainChannel *channel, gui
 /**
  * spice_main_update_display:
  * @channel: a #SpiceMainChannel
- * @id: display ID
+ * @id: the display ID - assumed to be channel_id + monitor_id
  * @x: x position
  * @y: y position
  * @width: display width
@@ -2652,7 +2652,7 @@ void spice_main_update_display(SpiceMainChannel *channel, int id,
 /**
  * spice_main_channel_update_display:
  * @channel: a #SpiceMainChannel
- * @id: display ID
+ * @id: the display ID - assumed to be channel_id + monitor_id
  * @x: x position
  * @y: y position
  * @width: display width
@@ -2671,8 +2671,6 @@ void spice_main_update_display(SpiceMainChannel *channel, int id,
 void spice_main_channel_update_display(SpiceMainChannel *channel, int id, int x, int y, int width,
                                int height, gboolean update)
 {
-    SpiceMainChannelPrivate *c;
-
     g_return_if_fail(channel != NULL);
     g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel));
     g_return_if_fail(x >= 0);
@@ -2680,28 +2678,48 @@ void spice_main_channel_update_display(SpiceMainChannel *channel, int id, int x,
     g_return_if_fail(width >= 0);
     g_return_if_fail(height >= 0);
 
-    c = SPICE_MAIN_CHANNEL(channel)->priv;
+    GArray *monitor_configs = spice_session_get_monitor_configs(
+        spice_channel_get_session(SPICE_CHANNEL(channel)));
 
-    g_return_if_fail(id < SPICE_N_ELEMENTS(c->display));
+    bool found = false;
+    SpiceMonitorConfig *mc = NULL;
+    for (size_t i = 0; i < monitor_configs->len; ++i) {
+        mc = &g_array_index(monitor_configs, SpiceMonitorConfig, i);
+        if (mc->channel_id + mc->monitor_id == id) {
+            found = true;
+            break;
+        }
+    }
 
-    SpiceDisplayConfig display = {
-        .x = x, .y = y, .width = width, .height = height,
-        .display_state = c->display[id].display_state
-    };
+    if (!found) {
+        g_warning("Display ID %d not found while trying to update the display (monitor config).", id);
+        return;
+    }
 
-    if (memcmp(&display, &c->display[id], sizeof(SpiceDisplayConfig)) == 0)
+    if (mc->x == x &&
+        mc->y == y &&
+        mc->width == width &&
+        mc->height == height) {
         return;
+    }
+
+    CHANNEL_DEBUG(channel,
+        "Update display (monitor config) id: %d (channel_id: %u, monitor_id: %u), +%d+%d-%dx%d",
+        id, mc->channel_id, mc->monitor_id, x, y, width, height);
 
-    c->display[id] = display;
+    mc->x = x;
+    mc->y = y;
+    mc->width = width;
+    mc->height = height;
 
     if (update)
-        update_display_timer(channel, 1);
+        spice_main_channel_update_display_timer(channel, 1);
 }
 
 /**
  * spice_main_set_display:
  * @channel: a #SpiceMainChannel
- * @id: display ID
+ * @id: the display ID - assumed to be channel_id + monitor_id
  * @x: x position
  * @y: y position
  * @width: display width
@@ -2943,7 +2961,7 @@ void spice_main_channel_clipboard_selection_request(SpiceMainChannel *channel, g
 /**
  * spice_main_update_display_enabled:
  * @channel: a #SpiceMainChannel
- * @id: display ID (if -1: set all displays)
+ * @id: the display ID - assumed to be channel_id + monitor_id
  * @enabled: wether display @id is enabled
  * @update: if %TRUE, update guest display state after 1sec.
  *
@@ -2968,7 +2986,7 @@ void spice_main_update_display_enabled(SpiceMainChannel *channel, int id, gboole
 /**
  * spice_main_channel_update_display_enabled:
  * @channel: a #SpiceMainChannel
- * @id: display ID (if -1: set all displays)
+ * @id: the display ID - assumed to be channel_id + monitor_id
  * @enabled: wether display @id is enabled
  * @update: if %TRUE, update guest display state after 1sec.
  *
@@ -2986,33 +3004,57 @@ void spice_main_update_display_enabled(SpiceMainChannel *channel, int id, gboole
 void spice_main_channel_update_display_enabled(SpiceMainChannel *channel, int id, gboolean enabled,
                                                gboolean update)
 {
-    SpiceDisplayState display_state = enabled ? DISPLAY_ENABLED : DISPLAY_DISABLED;
     g_return_if_fail(channel != NULL);
     g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel));
     g_return_if_fail(id >= -1);
 
-    SpiceMainChannelPrivate *c = channel->priv;
+    GArray *monitor_configs = spice_session_get_monitor_configs(
+        spice_channel_get_session(SPICE_CHANNEL(channel)));
+
+    SpiceDisplayState display_state = enabled ? DISPLAY_ENABLED : DISPLAY_DISABLED;
 
     if (id == -1) {
+        SPICE_DEBUG("Updating all monitor configs' state to %s", enabled ? "enabled" : "disabled");
+
         gint i;
-        for (i = 0; i < G_N_ELEMENTS(c->display); i++) {
-            c->display[i].display_state = display_state;
+        for (i = 0; i < monitor_configs->len; i++) {
+            g_array_index(monitor_configs, SpiceMonitorConfig, i).display_state = display_state;
         }
     } else {
-        g_return_if_fail(id < G_N_ELEMENTS(c->display));
-        if (c->display[id].display_state == display_state)
+        bool found = false;
+        SpiceMonitorConfig *mc = NULL;
+        for (size_t i = 0; i < monitor_configs->len; ++i) {
+            mc = &g_array_index(monitor_configs, SpiceMonitorConfig, i);
+            if (mc->channel_id + mc->monitor_id == id) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) {
+            g_warning("Display ID %d not found while trying to %s the display (monitor config).",
+                      id, enabled ? "enable" : "disable");
+            return;
+        }
+
+        if (mc->display_state == display_state) {
             return;
-        c->display[id].display_state = display_state;
+        }
+
+        SPICE_DEBUG("Updating monitor config id: %d (channel_id: %u, monitor_id: %u) state to %s",
+                    id, mc->channel_id, mc->monitor_id, enabled ? "enabled" : "disabled");
+
+        mc->display_state = display_state;
     }
 
     if (update)
-        update_display_timer(channel, 1);
+        spice_main_channel_update_display_timer(channel, 1);
 }
 
 /**
  * spice_main_set_display_enabled:
  * @channel: a #SpiceMainChannel
- * @id: display ID (if -1: set all displays)
+ * @id: the display ID - assumed to be channel_id + monitor_id
  * @enabled: wether display @id is enabled
  *
  * When sending monitor configuration to agent guest, don't set
diff --git a/src/channel-main.h b/src/channel-main.h
index fd7a3bb..0495bb2 100644
--- a/src/channel-main.h
+++ b/src/channel-main.h
@@ -101,6 +101,9 @@ gboolean spice_main_channel_file_copy_finish(SpiceMainChannel *channel,
 
 void spice_main_channel_request_mouse_mode(SpiceMainChannel *channel, int mode);
 
+// TODO had to add this to the interface to access it from spice-session, what to do with it?
+void spice_main_channel_update_display_timer(SpiceMainChannel *channel, guint seconds);
+
 #ifndef SPICE_DISABLE_DEPRECATED
 G_DEPRECATED_FOR(spice_main_channel_clipboard_selection_grab)
 void spice_main_clipboard_grab(SpiceMainChannel *channel, guint32 *types, int ntypes);
diff --git a/src/map-file b/src/map-file
index cdb81c3..0e4c375 100644
--- a/src/map-file
+++ b/src/map-file
@@ -122,6 +122,8 @@ spice_record_channel_send_data;
 spice_record_send_data;
 spice_session_connect;
 spice_session_disconnect;
+spice_display_state_get_type;
+spice_session_get_monitor_config;
 spice_session_get_channels;
 spice_session_get_proxy_uri;
 spice_session_get_read_only;
@@ -131,6 +133,8 @@ spice_session_is_for_migration;
 spice_session_migration_get_type;
 spice_session_new;
 spice_session_open_fd;
+spice_session_update_monitor_config_dimensions;
+spice_session_update_monitor_config_enabled;
 spice_session_verify_get_type;
 spice_set_session_option;
 spice_smartcard_channel_get_type;
diff --git a/src/spice-glib-sym-file b/src/spice-glib-sym-file
index b19844c..897a303 100644
--- a/src/spice-glib-sym-file
+++ b/src/spice-glib-sym-file
@@ -101,6 +101,8 @@ spice_record_channel_send_data
 spice_record_send_data
 spice_session_connect
 spice_session_disconnect
+spice_display_state_get_type
+spice_session_get_monitor_config
 spice_session_get_channels
 spice_session_get_proxy_uri
 spice_session_get_read_only
@@ -110,6 +112,8 @@ spice_session_is_for_migration
 spice_session_migration_get_type
 spice_session_new
 spice_session_open_fd
+spice_session_update_monitor_config_dimensions
+spice_session_update_monitor_config_enabled
 spice_session_verify_get_type
 spice_set_session_option
 spice_smartcard_channel_get_type
diff --git a/src/spice-session-priv.h b/src/spice-session-priv.h
index 03005aa..77cb8c6 100644
--- a/src/spice-session-priv.h
+++ b/src/spice-session-priv.h
@@ -100,6 +100,16 @@ void spice_session_set_main_channel(SpiceSession *session, SpiceChannel *channel
 gboolean spice_session_set_migration_session(SpiceSession *session, SpiceSession *mig_session);
 SpiceAudio *spice_audio_get(SpiceSession *session, GMainContext *context);
 const gchar* spice_audio_data_mode_to_string(gint mode);
+
+GArray *spice_session_get_monitor_configs(SpiceSession *session);
+
+SpiceMonitorConfig *spice_session_get_monitor_config_mutable(SpiceSession *session,
+                                                             uint32_t channel_id,
+                                                             uint32_t monitor_id);
+
+void spice_session_add_monitor_config(SpiceSession *session, uint32_t channel_id, uint32_t display_id,
+                                      uint32_t x, uint32_t y, uint32_t width, uint32_t height);
+
 G_END_DECLS
 
 #endif /* __SPICE_CLIENT_SESSION_PRIV_H__ */
diff --git a/src/spice-session.c b/src/spice-session.c
index b1aeb84..29a4c69 100644
--- a/src/spice-session.c
+++ b/src/spice-session.c
@@ -90,6 +90,11 @@ struct _SpiceSessionPrivate {
     GStrv             secure_channels;
     gint              color_depth;
 
+    /* A list of monitor configs for all channels and monitors. It is filled up
+     * with monitors_config messages as they arrive on display channels.
+     */
+    GArray            *monitor_configs;
+
     int               connection_id;
     int               protocol;
     SpiceChannel      *cmain; /* weak reference */
@@ -288,6 +293,8 @@ static void spice_session_init(SpiceSession *session)
     s->images = cache_image_new((GDestroyNotify)pixman_image_unref);
     s->glz_window = glz_decoder_window_new();
     update_proxy(session, NULL);
+
+    s->monitor_configs = g_array_new(FALSE, TRUE, sizeof(SpiceMonitorConfig));
 }
 
 static void
@@ -340,6 +347,8 @@ spice_session_dispose(GObject *gobject)
     g_clear_object(&s->proxy);
     g_clear_object(&s->webdav);
 
+    g_array_free(s->monitor_configs, TRUE);
+
     /* Chain up to the parent class */
     if (G_OBJECT_CLASS(spice_session_parent_class)->dispose)
         G_OBJECT_CLASS(spice_session_parent_class)->dispose(gobject);
@@ -2823,6 +2832,149 @@ gboolean spice_session_is_for_migration(SpiceSession *session)
     return session->priv->for_migration;
 }
 
+void spice_session_add_monitor_config(SpiceSession *session, uint32_t channel_id, uint32_t monitor_id,
+                                      uint32_t x, uint32_t y, uint32_t width, uint32_t height)
+{
+    GArray *monitor_configs = spice_session_get_monitor_configs(session);
+    g_assert(monitor_configs != NULL);
+
+    SpiceMonitorConfig mc = {
+        .channel_id = channel_id,
+        .monitor_id = monitor_id,
+        .x = x,
+        .y = y,
+        .width = width,
+        .height = height,
+        .display_state = DISPLAY_DISABLED
+    };
+
+    SPICE_DEBUG("Adding new monitor_config channel_id: %u, monitor_id: %u, +%u+%u:%ux%u",
+                channel_id, monitor_id, x, y, width, height);
+
+    g_array_append_val(monitor_configs, mc);
+}
+
+GArray *spice_session_get_monitor_configs(SpiceSession *session)
+{
+    g_assert(session != NULL);
+
+    return session->priv->monitor_configs;
+}
+
+SpiceMonitorConfig *spice_session_get_monitor_config_mutable(SpiceSession *session,
+                                                             uint32_t channel_id,
+                                                             uint32_t monitor_id)
+{
+    GArray *monitor_configs = spice_session_get_monitor_configs(session);
+    g_assert(monitor_configs != NULL);
+
+    for (size_t i = 0; i < monitor_configs->len; ++i) {
+        SpiceMonitorConfig *d = &g_array_index(monitor_configs, SpiceMonitorConfig, i);
+        if (d->channel_id == channel_id && d->monitor_id == monitor_id) {
+            return d;
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * spice_session_get_monitor_config:
+ * @session: a #SpiceSession
+ * @channel_id: a channel ID
+ * @monitor_id: a monitor ID
+ *
+ * Returns a #SpiceMonitorConfig identified by the @channel_id and @monitor_id.
+ *
+ * Returns: a pointer to SpiceMonitorConfig if found, %NULL otherwise
+ *
+ * Since: 0.36
+ **/
+const SpiceMonitorConfig *spice_session_get_monitor_config(SpiceSession *session,
+                                                           uint32_t channel_id,
+                                                           uint32_t monitor_id)
+{
+    return spice_session_get_monitor_config_mutable(session, channel_id, monitor_id);
+}
+
+/**
+ * spice_session_update_monitor_config_dimensions:
+ * @session: a #SpiceSession
+ * @channel_id: a channel ID
+ * @monitor_id: a monitor ID
+ * @x: X coordinate of the monitor
+ * @y: Y coordinate of the monitor
+ * @width: width of the monitor
+ * @height: height of the monitor
+ *
+ * Sets the dimensions (@x, @y, @width, @height) of a #SpiceMonitorConfig
+ * identified with @channel_id and @monitor_id.
+ *
+ * Since: 0.36
+ **/
+void spice_session_update_monitor_config_dimensions(SpiceSession *session,
+                                                    uint32_t  channel_id, uint32_t monitor_id,
+                                                    uint32_t x, uint32_t y,
+                                                    uint32_t width, uint32_t height, gboolean update)
+{
+    SpiceMonitorConfig *mc = spice_session_get_monitor_config_mutable(session, channel_id, monitor_id);
+
+    g_return_if_fail(mc != NULL);
+
+    if (mc->x == x &&
+        mc->y == y &&
+        mc->width == width &&
+        mc->height == height) {
+        return;
+    }
+
+    SPICE_DEBUG("Updating monitor config channel_id: %u, monitor_id: %u, +%u+%u-%ux%u",
+                channel_id, monitor_id, x, y, width, height);
+
+    mc->x = x;
+    mc->y = y;
+    mc->width = width;
+    mc->height = height;
+
+    if (update) {
+        spice_main_channel_update_display_timer((SpiceMainChannel *) session->priv->cmain, 1);
+    }
+}
+
+/**
+ * spice_session_update_monitor_config_enabled:
+ * @session: a #SpiceSession
+ * @channel_id: a channel ID
+ * @monitor_id: a monitor ID
+ * @enabled: The enabled flag to set
+ *
+ * Sets the @enabled flag of a #SpiceMonitorConfig
+ * identified with @channel_id and @monitor_id.
+ *
+ * Since: 0.36
+ **/
+void spice_session_update_monitor_config_enabled(SpiceSession *session,
+                                                 uint32_t  channel_id, uint32_t monitor_id,
+                                                 gboolean enabled, gboolean update)
+{
+    SpiceMonitorConfig *mc = spice_session_get_monitor_config_mutable(session, channel_id, monitor_id);
+
+    g_return_if_fail(mc != NULL);
+
+    if (mc->display_state == (enabled ? DISPLAY_ENABLED : DISPLAY_DISABLED)) {
+        return;
+    }
+
+    SPICE_DEBUG("%s monitor config channel_id: %u, monitor_id: %u",
+                enabled ? "Enabling" : "Disabling", channel_id, monitor_id);
+
+    mc->display_state = enabled ? DISPLAY_ENABLED : DISPLAY_DISABLED;
+
+    if (update) {
+        spice_main_channel_update_display_timer((SpiceMainChannel *) session->priv->cmain, 1);
+    }
+}
+
 G_GNUC_INTERNAL
 void spice_session_set_main_channel(SpiceSession *session, SpiceChannel *channel)
 {
diff --git a/src/spice-session.h b/src/spice-session.h
index cfe02b1..16523bb 100644
--- a/src/spice-session.h
+++ b/src/spice-session.h
@@ -23,6 +23,8 @@
 #endif
 
 #include <glib-object.h>
+#include <stdint.h>
+
 #include "spice-types.h"
 #include "spice-uri.h"
 #include "spice-glib-enums.h"
@@ -103,6 +105,22 @@ struct _SpiceSessionClass
     gchar _spice_reserved[SPICE_RESERVED_PADDING];
 };
 
+typedef enum {
+    DISPLAY_UNDEFINED,
+    DISPLAY_DISABLED,
+    DISPLAY_ENABLED,
+} SpiceDisplayState;
+
+typedef struct {
+    uint32_t                channel_id;
+    uint32_t                monitor_id;
+    uint32_t                x;
+    uint32_t                y;
+    uint32_t                width;
+    uint32_t                height;
+    SpiceDisplayState       display_state;
+} SpiceMonitorConfig;
+
 GType spice_session_get_type(void);
 
 SpiceSession *spice_session_new(void);
@@ -115,6 +133,19 @@ gboolean spice_session_get_read_only(SpiceSession *session);
 SpiceURI *spice_session_get_proxy_uri(SpiceSession *session);
 gboolean spice_session_is_for_migration(SpiceSession *session);
 
+const SpiceMonitorConfig *spice_session_get_monitor_config(SpiceSession *session,
+                                                           uint32_t channel_id,
+                                                           uint32_t monitor_id);
+
+void spice_session_update_monitor_config_dimensions(SpiceSession *session,
+                                                    uint32_t channel_id, uint32_t monitor_id,
+                                                    uint32_t x, uint32_t y,
+                                                    uint32_t width, uint32_t height, gboolean update);
+
+void spice_session_update_monitor_config_enabled(SpiceSession *session,
+                                                 uint32_t channel_id, uint32_t monitor_id,
+                                                 gboolean enabled, gboolean update);
+
 G_END_DECLS
 
 #endif /* __SPICE_CLIENT_SESSION_H__ */
diff --git a/src/spice-widget.c b/src/spice-widget.c
index 853c9df..e361c91 100644
--- a/src/spice-widget.c
+++ b/src/spice-widget.c
@@ -256,7 +256,7 @@ static void update_ready(SpiceDisplay *display)
      * application will manage the state of the displays.
      */
     if (d->resize_guest_enable) {
-        spice_main_channel_update_display_enabled(d->main, get_display_id(display), ready, TRUE);
+        spice_session_update_monitor_config_enabled(d->session, d->channel_id, d->monitor_id, ready, TRUE);
     }
 
     if (d->ready == ready)
@@ -1204,8 +1204,9 @@ static void recalc_geometry(GtkWidget *widget)
                   d->ww, d->wh, zoom);
 
     if (d->resize_guest_enable)
-        spice_main_channel_update_display(d->main, get_display_id(display),
-                                          d->area.x, d->area.y, d->ww / zoom, d->wh / zoom, TRUE);
+        spice_session_update_monitor_config_dimensions(d->session, d->channel_id, d->monitor_id,
+                                                       d->area.x, d->area.y,
+                                                       d->ww / zoom, d->wh / zoom, TRUE);
 }
 
 /* ---------------------------------------------------------------- */
diff --git a/tools/spicy.c b/tools/spicy.c
index 263c15f..fc2b88a 100644
--- a/tools/spicy.c
+++ b/tools/spicy.c
@@ -614,11 +614,11 @@ static void menu_cb_resize_to(GtkAction *action G_GNUC_UNUSED,
 
     gtk_widget_show_all(dialog);
     if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY) {
-        spice_main_channel_update_display_enabled(win->conn->main, win->id + win->monitor_id, TRUE,
-                                                  FALSE);
-        spice_main_channel_update_display(
-            win->conn->main,
-            win->id + win->monitor_id,
+        spice_session_update_monitor_config_enabled(win->conn->session, win->id, win->monitor_id,
+                                                    TRUE, FALSE);
+        spice_session_update_monitor_config_dimensions(
+            win->conn->session,
+            win->id, win->monitor_id,
             gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_x)),
             gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_y)),
             gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_width)),
@@ -1439,10 +1439,7 @@ static void del_window(spice_connection *conn, SpiceWindow *win)
 
     g_debug("del display monitor %d:%d", win->id, win->monitor_id);
     conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = NULL;
-    if (win->id > 0)
-        spice_main_channel_update_display_enabled(conn->main, win->id, FALSE, TRUE);
-    else
-        spice_main_channel_update_display_enabled(conn->main, win->monitor_id, FALSE, TRUE);
+    spice_session_update_monitor_config_enabled(conn->session, win->id, win->monitor_id, FALSE, TRUE);
     spice_main_channel_send_monitor_config(conn->main);
 
     destroy_spice_window(win);
-- 
2.18.0



More information about the Spice-devel mailing list