In real-world conditions, messages with seamless changes might get delayed
in comparison to display data. While the client has outdated positions of
windows, spice_cairo_draw_event() draws parts of the display that should not
be visible any more at the moment. Once the data with new positions arrive,
there might be no need to redraw the old position because it has been redrawn
earlier and hence the cairo is clipped in such a way that does not allow
to draw the appropriate areas transparent.

This issue can be reproduced by moving/resizing windows very quickly or
by manually delaying window changes events on the agent side.

Opposite issue happens when a window is brought from minimalized state.
Outdated seamless-mode list prevents the window from drawing properly.
The border of the window is drawn first, the inner (client) area later.
If the client receives updated coordinates after the border is drawn and
cairo is then clipped on the client area, the border stays transparent.

Becuase of these reasons, the old and new seamless mode region should be
invalidated after we receive new seamless mode list/change.
---
 src/channel-main.c      | 45 ++++++++++++++++++++++++++++++++++++++++++---
 src/channel-main.h      |  1 +
 src/map-file            |  1 +
 src/spice-glib-sym-file |  1 +
 src/spice-widget.c      | 15 ++++++++++++++-
 5 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/src/channel-main.c b/src/channel-main.c
index b0b0c9d..2981ac3 100644
--- a/src/channel-main.c
+++ b/src/channel-main.c
@@ -123,6 +123,7 @@ struct _SpiceMainChannelPrivate  {
 
     GMutex                      seamless_mode_lock;
     GList                       *seamless_mode_list;
+    GList                       *seamless_mode_list_old;
     gboolean                    seamless_mode;
 };
 
@@ -402,7 +403,9 @@ static void spice_main_channel_dispose(GObject *obj)
 
     g_mutex_clear(&c->seamless_mode_lock);
     g_list_free_full(c->seamless_mode_list, g_free);
+    g_list_free_full(c->seamless_mode_list_old, g_free);
     c->seamless_mode_list = NULL;
+    c->seamless_mode_list_old = NULL;
 
     if (G_OBJECT_CLASS(spice_main_channel_parent_class)->dispose)
         G_OBJECT_CLASS(spice_main_channel_parent_class)->dispose(obj);
@@ -2100,7 +2103,8 @@ static void main_agent_handle_msg(SpiceChannel *channel,
 
         g_mutex_lock(&self->priv->seamless_mode_lock);
 
-        g_list_free_full(c->seamless_mode_list, g_free);
+        c->seamless_mode_list_old = g_list_concat(c->seamless_mode_list_old,
+                                                  c->seamless_mode_list);
         c->seamless_mode_list = NULL;
 
         for (i = 0; i < list->num_of_windows; i++) {
@@ -2126,11 +2130,19 @@ static void main_agent_handle_msg(SpiceChannel *channel,
             win_cpy = list->data;
             if (win_cpy->id == win->id) {
                 g_mutex_lock(&self->priv->seamless_mode_lock);
+
                 if (win->w == 0) {
-                    c->seamless_mode_list = 
g_list_remove(c->seamless_mode_list, win_cpy);
+                    c->seamless_mode_list = 
g_list_remove_link(c->seamless_mode_list, list);
+                    c->seamless_mode_list_old = 
g_list_prepend(c->seamless_mode_list_old, win_cpy);
+                    g_list_free(list);
                     goto seamless_notify;
                 }
-                memcpy(win_cpy, win, sizeof(VDAgentSeamlessModeWindow));
+
+                win = g_new0(VDAgentSeamlessModeWindow, 1);
+                memcpy(win, win_cpy, sizeof(VDAgentSeamlessModeWindow));
+                c->seamless_mode_list_old = 
g_list_prepend(c->seamless_mode_list_old, win);
+
+                memcpy(win_cpy, payload, sizeof(VDAgentSeamlessModeWindow));
                 goto seamless_notify;
             }
         }
@@ -3343,3 +3355,30 @@ GList 
*spice_main_get_seamless_mode_list(SpiceMainChannel *channel)
 
     return list;
 }
+
+/**
+ * spice_main_get_seamless_mode_list_old:
+ * @channel: a #SpiceMainChannel
+ *
+ * Returned list contains old coordinates of windows
+ * that have changed their position since the last call
+ * to this function().
+ *
+ * Returns: (transfer full) (element-type GdkRectangle): a newly allocated
+ * list of GdkRectangle structs.
+ **/
+GList *spice_main_get_seamless_mode_list_old(SpiceMainChannel *channel)
+{
+    GList *list;
+
+    g_mutex_lock(&channel->priv->seamless_mode_lock);
+    list = g_list_copy_deep(channel->priv->seamless_mode_list_old,
+                            (GCopyFunc)g_memdup,
+                            (gpointer)sizeof(GdkRectangle));
+
+    g_list_free_full(channel->priv->seamless_mode_list_old, g_free);
+    channel->priv->seamless_mode_list_old = NULL;
+    g_mutex_unlock(&channel->priv->seamless_mode_lock);
+
+    return list;
+}
diff --git a/src/channel-main.h b/src/channel-main.h
index 50d7615..baa2a19 100644
--- a/src/channel-main.h
+++ b/src/channel-main.h
@@ -114,6 +114,7 @@ void spice_main_clipboard_request(SpiceMainChannel 
*channel, guint32 type);
 void spice_main_set_seamless_mode(SpiceMainChannel *channel, gboolean enabled);
 gboolean spice_main_get_seamless_mode_supported(SpiceMainChannel *channel);
 GList *spice_main_get_seamless_mode_list(SpiceMainChannel *channel);
+GList *spice_main_get_seamless_mode_list_old(SpiceMainChannel *channel);
 
 G_END_DECLS
 
diff --git a/src/map-file b/src/map-file
index dcfd16d..b07e3cc 100644
--- a/src/map-file
+++ b/src/map-file
@@ -81,6 +81,7 @@ spice_main_file_copy_async;
 spice_main_file_copy_finish;
 spice_main_request_mouse_mode;
 spice_main_get_seamless_mode_list;
+spice_main_get_seamless_mode_list_old;
 spice_main_get_seamless_mode_supported;
 spice_main_send_monitor_config;
 spice_main_set_display;
diff --git a/src/spice-glib-sym-file b/src/spice-glib-sym-file
index 08c8c60..2992478 100644
--- a/src/spice-glib-sym-file
+++ b/src/spice-glib-sym-file
@@ -59,6 +59,7 @@ spice_main_file_copy_async
 spice_main_file_copy_finish
 spice_main_request_mouse_mode
 spice_main_get_seamless_mode_list
+spice_main_get_seamless_mode_list_old
 spice_main_get_seamless_mode_supported
 spice_main_send_monitor_config
 spice_main_set_display
diff --git a/src/spice-widget.c b/src/spice-widget.c
index fe8117f..9eb8da7 100644
--- a/src/spice-widget.c
+++ b/src/spice-widget.c
@@ -3146,7 +3146,7 @@ void spice_display_update_seamless_mode(SpiceDisplay 
*display)
 {
     SpiceDisplayPrivate *d = display->priv;
     GtkWidget *toplevel = NULL;
-    GList *l = NULL, *list = NULL;
+    GList *l = NULL, *list = NULL, *list_old = NULL;
     cairo_region_t *region = NULL;
     gboolean enabled;
 
@@ -3160,6 +3160,18 @@ void spice_display_update_seamless_mode(SpiceDisplay 
*display)
     }
 
     list = spice_main_get_seamless_mode_list(d->main);
+    list_old = spice_main_get_seamless_mode_list_old(d->main);
+
+    if (list != NULL || list_old != NULL) {
+        region = cairo_region_create();
+        for (l = list_old; l != NULL; l = l->next)
+            cairo_region_union_rectangle(region, (GdkRectangle *)(l->data));
+        for (l = list; l != NULL; l = l->next)
+            cairo_region_union_rectangle(region, (GdkRectangle *)(l->data));
+
+        
gdk_window_invalidate_region(gtk_widget_get_window(GTK_WIDGET(display)), 
region, FALSE);
+        cairo_region_destroy(region);
+    }
 
     if (list != NULL) {
         GdkRectangle *window;
@@ -3190,6 +3202,7 @@ void spice_display_update_seamless_mode(SpiceDisplay 
*display)
     }
 
     g_list_free_full(list, g_free);
+    g_list_free_full(list_old, g_free);
 }
 
 /**
-- 
2.13.4

_______________________________________________
Spice-devel mailing list
Spice-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/spice-devel

Reply via email to