[Spice-commits] 26 commits - doc/reference gtk/channel-display.c gtk/channel-display.h gtk/channel-display-priv.h gtk/channel-main.c gtk/glib-compat.h gtk/map-file gtk/spice-channel.c gtk/spice-widget.c gtk/spice-widget-cairo.c gtk/spice-widget.h gtk/spice-widget-priv.h gtk/spice-widget-x11.c gtk/spicy.c spice-common

Marc-André Lureau elmarco at kemper.freedesktop.org
Mon Jul 16 09:00:56 PDT 2012


 doc/reference/spice-gtk-sections.txt |    1 
 gtk/channel-display-priv.h           |    2 
 gtk/channel-display.c                |  141 ++++++++++
 gtk/channel-display.h                |   24 +
 gtk/channel-main.c                   |   95 ++++++-
 gtk/glib-compat.h                    |   20 +
 gtk/map-file                         |    2 
 gtk/spice-channel.c                  |    2 
 gtk/spice-widget-cairo.c             |   37 +-
 gtk/spice-widget-priv.h              |    5 
 gtk/spice-widget-x11.c               |   22 -
 gtk/spice-widget.c                   |  470 ++++++++++++++++++++++-------------
 gtk/spice-widget.h                   |    4 
 gtk/spicy.c                          |  115 +++++++-
 spice-common                         |    2 
 15 files changed, 720 insertions(+), 222 deletions(-)

New commits:
commit fdb8813a438d801368f9521253f44cc12061d4dd
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Jul 5 01:29:44 2012 +0200

    Enable the display early when the widget is created
    
    The  spice_main_set_display_enabled() function  is  used  to mark  the
    display/monitor  config  as  enabled.  In  order  to  simplify  client
    implementation, the widget enables  the matching display automatically
    when the channel mark is received. This is only for legacy reason, and
    my lack  of understanding at  that time. It  could as well  be enabled
    earlier, when the widget is created.  It wasn't really a good decision
    to disable monitor when the mark is off, which can be toggled when the
    primary surface is resize for example, and can cause some races..

diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 5c12db9..02bb089 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -1790,7 +1790,6 @@ static void mark(SpiceDisplay *display, gint mark)
 
     SPICE_DEBUG("widget mark: %d, %d:%d %p", mark, d->channel_id, d->monitor_id, display);
     d->mark = mark;
-    spice_main_set_display_enabled(d->main, get_display_id(display), d->mark != 0);
     update_ready(display);
 }
 
@@ -1963,6 +1962,7 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
             mark(display, primary.marked);
         }
         spice_channel_connect(channel);
+        spice_main_set_display_enabled(d->main, get_display_id(display), TRUE);
         return;
     }
 
commit 2a2900267f4f1b94213e856c793509d897cb50cd
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Jul 5 01:24:44 2012 +0200

    Add SpiceDisplay:ready property
    
    There are several condition to meet in order to have a widget ready to
    be displayed: the monitor must exist, the area must intersect, and the
    display mark must be reached. This  property will help clients to know
    when the  widget display is  ready. Until now,  it was relying  on the
    channel  mark signal  only,  and had  to  deal with  the  rest of  the
    conditions without much help.

diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index a44e5fe..87cf34e 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -52,6 +52,8 @@ struct _SpiceDisplayPrivate {
     bool                    resize_guest_enable;
 
     /* state */
+    gboolean                ready;
+    gboolean                monitor_ready;
     enum SpiceSurfaceFmt    format;
     gint                    width, height, stride;
     gint                    shmid;
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 048770c..5c12db9 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -102,7 +102,8 @@ enum {
     PROP_SCALING,
     PROP_DISABLE_INPUTS,
     PROP_ZOOM_LEVEL,
-    PROP_MONITOR_ID
+    PROP_MONITOR_ID,
+    PROP_READY
 };
 
 /* Signals */
@@ -175,6 +176,9 @@ static void spice_display_get_property(GObject    *object,
     case PROP_ZOOM_LEVEL:
         g_value_set_int(value, d->zoom_level);
         break;
+    case PROP_READY:
+        g_value_set_boolean(value, d->ready);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -227,6 +231,31 @@ static void update_keyboard_focus(SpiceDisplay *display, gboolean state)
     spice_gtk_session_request_auto_usbredir(d->gtk_session, state);
 }
 
+static void update_ready(SpiceDisplay *display)
+{
+    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+    gboolean ready;
+
+    ready = d->mark != 0 && d->monitor_ready;
+
+    if (d->ready == ready)
+        return;
+
+    if (ready && gtk_widget_get_window(GTK_WIDGET(display)))
+        gtk_widget_queue_draw(GTK_WIDGET(display));
+
+    d->ready = ready;
+    g_object_notify(G_OBJECT(display), "ready");
+}
+
+static void set_monitor_ready(SpiceDisplay *self, gboolean ready)
+{
+    SpiceDisplayPrivate *d = self->priv;
+
+    d->monitor_ready = ready;
+    update_ready(self);
+}
+
 static void update_monitor_area(SpiceDisplay *display)
 {
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
@@ -240,11 +269,11 @@ static void update_monitor_area(SpiceDisplay *display)
     g_object_get(d->display, "monitors", &monitors, NULL);
     if (monitors == NULL || d->monitor_id >= monitors->len) {
         SPICE_DEBUG("update monitor: no monitor %d", d->monitor_id);
+        set_monitor_ready(display, false);
         if (spice_channel_test_capability(d->display, SPICE_DISPLAY_CAP_MONITORS_CONFIG)) {
             SPICE_DEBUG("waiting until MonitorsConfig is received");
             return;
         }
-        /* FIXME: mark false */
         goto whole;
     }
 
@@ -263,6 +292,7 @@ static void update_monitor_area(SpiceDisplay *display)
 whole:
     /* by display whole surface */
     update_area(display, 0, 0, d->width, d->height);
+    set_monitor_ready(display, true);
 }
 
 static void spice_display_set_property(GObject      *object,
@@ -1477,6 +1507,24 @@ static void spice_display_class_init(SpiceDisplayClass *klass)
                               G_PARAM_STATIC_STRINGS));
 
     /**
+     * SpiceDisplay:ready:
+     *
+     * Indicate whether the display is ready to be shown. It takes
+     * into account several conditions, such as the channel display
+     * "mark" state, whether the monitor area is visible..
+     *
+     * Since: 0.13
+     **/
+    g_object_class_install_property
+        (gobject_class, PROP_READY,
+         g_param_spec_boolean("ready",
+                              "Ready",
+                              "Ready to display",
+                              FALSE,
+                              G_PARAM_READABLE |
+                              G_PARAM_STATIC_STRINGS));
+
+    /**
      * SpiceDisplay:auto-clipboard:
      *
      * When this is true the clipboard gets automatically shared between host
@@ -1660,7 +1708,7 @@ static void update_area(SpiceDisplay *display,
     if (!gdk_rectangle_intersect(&primary, &area, &area)) {
         SPICE_DEBUG("The monitor area is not intersecting primary surface");
         memset(&d->area, '\0', sizeof(d->area));
-        /* FIXME mark false? */
+        set_monitor_ready(display, false);
         return;
     }
 
@@ -1669,7 +1717,7 @@ static void update_area(SpiceDisplay *display,
     spicex_image_create(display);
     update_size_request(display);
 
-    gtk_widget_queue_draw(GTK_WIDGET(display));
+    set_monitor_ready(display, true);
 }
 
 static void primary_create(SpiceChannel *channel, gint format,
@@ -1701,6 +1749,7 @@ static void primary_destroy(SpiceChannel *channel, gpointer data)
     d->shmid  = 0;
     d->data = NULL;
     d->data_origin = NULL;
+    set_monitor_ready(display, false);
 }
 
 static void invalidate(SpiceChannel *channel,
@@ -1742,8 +1791,7 @@ static void mark(SpiceDisplay *display, gint mark)
     SPICE_DEBUG("widget mark: %d, %d:%d %p", mark, d->channel_id, d->monitor_id, display);
     d->mark = mark;
     spice_main_set_display_enabled(d->main, get_display_id(display), d->mark != 0);
-    if (mark != 0 && gtk_widget_get_window(GTK_WIDGET(display)))
-        gtk_widget_queue_draw(GTK_WIDGET(display));
+    update_ready(display);
 }
 
 static void cursor_set(SpiceCursorChannel *channel,
commit fe842771b2e2400fbc5ba8efd74550cde44276ee
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jun 27 17:06:34 2012 +0200

    Handle MonitorsConfig::max_allowed

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 637a6cf..d77447a 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -72,6 +72,7 @@ struct _SpiceDisplayChannelPrivate {
     gboolean                    mark;
     guint                       mark_false_event_id;
     GArray                      *monitors;
+    guint                       monitors_max;
 #ifdef WIN32
     HDC dc;
 #endif
@@ -84,7 +85,8 @@ enum {
     PROP_0,
     PROP_WIDTH,
     PROP_HEIGHT,
-    PROP_MONITORS
+    PROP_MONITORS,
+    PROP_MONITORS_MAX
 };
 
 enum {
@@ -177,6 +179,10 @@ static void spice_display_get_property(GObject    *object,
         g_value_set_boxed(value, c->monitors);
         break;
     }
+    case PROP_MONITORS_MAX: {
+        g_value_set_uint(value, c->monitors_max);
+        break;
+    }
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -256,6 +262,24 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass)
                             G_PARAM_STATIC_STRINGS));
 
     /**
+     * SpiceDisplayChannel:monitors-max:
+     *
+     * The maximum number of monitors the server or guest supports.
+     * May change during client lifetime, for instance guest may
+     * reboot or dynamically adjust this.
+     *
+     * Since: 0.13
+     */
+    g_object_class_install_property
+        (gobject_class, PROP_MONITORS_MAX,
+         g_param_spec_uint("monitors-max",
+                           "Max display monitors",
+                           "The current maximum number of monitors",
+                           1, G_MAXINT16, 1,
+                           G_PARAM_READABLE |
+                           G_PARAM_STATIC_STRINGS));
+
+    /**
      * SpiceDisplayChannel::display-primary-create:
      * @display: the #SpiceDisplayChannel that emitted the signal
      * @format: %SPICE_SURFACE_FMT_32_xRGB or %SPICE_SURFACE_FMT_16_555;
@@ -1479,8 +1503,9 @@ static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in
     g_return_if_fail(config != NULL);
     g_return_if_fail(config->count > 0);
 
-    SPICE_DEBUG("monitors config: n: %d", config->count);
+    SPICE_DEBUG("monitors config: n: %d/%d", config->count, config->max_allowed);
 
+    c->monitors_max = config->max_allowed;
     c->monitors = g_array_set_size(c->monitors, config->count);
 
     for (i = 0; i < config->count; i++) {
commit 4ea133feb82c83a0ed0daaad381101b0afd5f3a5
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Jun 26 21:49:37 2012 +0200

    main: send monitor config immediately
    
    The only way this can be called currently is via the main/system
    coroutine. Remove display timer if any.

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index d7830ae..0c15dfa 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -916,8 +916,6 @@ static void monitors_align(VDAgentMonConfig *monitors, int nmonitors)
  *
  * Returns: %TRUE on success.
  **/
-/* any context: the message is not flushed immediately,
-   you can wakeup() the channel coroutine or send_msg_queue() */
 gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
 {
     SpiceMainChannelPrivate *c;
@@ -966,6 +964,12 @@ gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
     agent_msg_queue(channel, VD_AGENT_MONITORS_CONFIG, size, mon);
     free(mon);
 
+    spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE);
+    if (c->timer_id != 0) {
+        g_source_remove(c->timer_id);
+        c->timer_id = 0;
+    }
+
     return TRUE;
 }
 
@@ -1862,7 +1866,6 @@ static gboolean timer_set_display(gpointer data)
     c->timer_id = 0;
     if (c->agent_connected)
         spice_main_send_monitor_config(SPICE_MAIN_CHANNEL(channel));
-    spice_channel_wakeup(channel, FALSE);
 
     return false;
 }
commit be8ff99571478deb5c8d116134f65ed2b788dbd3
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jun 20 18:47:05 2012 +0200

    Implement simple monitors alignment

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 3c173c6..d7830ae 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -15,6 +15,10 @@
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
+#include <math.h>
+#include <spice/vd_agent.h>
+#include <common/rect.h>
+
 #include "glib-compat.h"
 #include "spice-client.h"
 #include "spice-common.h"
@@ -24,8 +28,6 @@
 #include "spice-channel-priv.h"
 #include "spice-session-priv.h"
 
-#include <spice/vd_agent.h>
-
 /**
  * SECTION:channel-main
  * @short_description: the main Spice channel
@@ -58,6 +60,7 @@ struct _SpiceMainChannelPrivate  {
     gboolean                    display_disable_font_smooth:1;
     gboolean                    display_disable_animation:1;
     gboolean                    disable_display_position:1;
+    gboolean                    disable_display_align:1;
 
     int                         agent_tokens;
     VDAgentMessage              agent_msg; /* partial msg reconstruction */
@@ -102,6 +105,7 @@ enum {
     PROP_DISPLAY_DISABLE_ANIMATION,
     PROP_DISPLAY_COLOR_DEPTH,
     PROP_DISABLE_DISPLAY_POSITION,
+    PROP_DISABLE_DISPLAY_ALIGN,
 };
 
 /* Signals */
@@ -203,6 +207,9 @@ static void spice_main_get_property(GObject    *object,
     case PROP_DISABLE_DISPLAY_POSITION:
         g_value_set_boolean(value, c->disable_display_position);
         break;
+    case PROP_DISABLE_DISPLAY_ALIGN:
+        g_value_set_boolean(value, c->disable_display_align);
+        break;
     default:
 	G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 	break;
@@ -233,6 +240,9 @@ static void spice_main_set_property(GObject *gobject, guint prop_id,
     case PROP_DISABLE_DISPLAY_POSITION:
         c->disable_display_position = g_value_get_boolean(value);
         break;
+    case PROP_DISABLE_DISPLAY_ALIGN:
+        c->disable_display_align = g_value_get_boolean(value);
+        break;
     default:
 	G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
 	break;
@@ -410,6 +420,23 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass)
                            G_PARAM_CONSTRUCT |
                            G_PARAM_STATIC_STRINGS));
 
+    /**
+     * SpiceMainChannel:disable-display-align:
+     *
+     * Disable automatic horizontal display position alignment.
+     *
+     * Since: 0.13
+     */
+    g_object_class_install_property
+        (gobject_class, PROP_DISABLE_DISPLAY_ALIGN,
+         g_param_spec_boolean("disable-display-align",
+                              "Disable display align",
+                              "Disable display position alignment",
+                              FALSE,
+                              G_PARAM_READWRITE |
+                              G_PARAM_CONSTRUCT |
+                              G_PARAM_STATIC_STRINGS));
+
     /* TODO use notify instead */
     /**
      * SpiceMainChannel::main-mouse-update:
@@ -846,6 +873,37 @@ static void agent_msg_queue_many(SpiceMainChannel *channel, int type, const void
     g_warn_if_fail(out == NULL);
 }
 
+static int monitors_cmp(const void *p1, const void *p2)
+{
+    const VDAgentMonConfig *m1 = p1;
+    const VDAgentMonConfig *m2 = p2;
+    double d1 = sqrt(m1->x * m1->x + m1->y * m1->y);
+    double d2 = sqrt(m2->x * m2->x + m2->y * m2->y);
+
+    return d1 - d2;
+}
+
+static void monitors_align(VDAgentMonConfig *monitors, int nmonitors)
+{
+    gint i, x = 0;
+
+    if (nmonitors == 0)
+        return;
+
+    /* sort by distance from origin */
+    qsort(monitors, nmonitors, sizeof(VDAgentMonConfig), monitors_cmp);
+
+    /* super-KISS ltr alignment, feel free to improve */
+    for (i = 0; i < nmonitors; i++) {
+        monitors[i].x = x;
+        monitors[i].y = 0;
+        x += monitors[i].width;
+        g_debug("#%d +%d+%d-%dx%d", i, monitors[i].x, monitors[i].y,
+                monitors[i].width, monitors[i].height);
+    }
+}
+
+
 #define agent_msg_queue(Channel, Type, Size, Data) \
     agent_msg_queue_many((Channel), (Type), (Data), (Size), NULL)
 
@@ -882,7 +940,8 @@ gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
     mon = spice_malloc0(size);
 
     mon->num_of_monitors = monitors;
-    if (c->disable_display_position == FALSE)
+    if (c->disable_display_position == FALSE ||
+        c->disable_display_align == FALSE)
         mon->flags |= VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS;
 
     j = 0;
@@ -901,6 +960,9 @@ gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
         j++;
     }
 
+    if (c->disable_display_align == FALSE)
+        monitors_align(mon->monitors, mon->num_of_monitors);
+
     agent_msg_queue(channel, VD_AGENT_MONITORS_CONFIG, size, mon);
     free(mon);
 
@@ -1845,6 +1907,10 @@ void spice_main_set_display(SpiceMainChannel *channel, int id,
 
     g_return_if_fail(channel != NULL);
     g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel));
+    g_return_if_fail(x >= 0);
+    g_return_if_fail(y >= 0);
+    g_return_if_fail(width >= 0);
+    g_return_if_fail(height >= 0);
 
     c = SPICE_MAIN_CHANNEL(channel)->priv;
 
commit 547597a687065b70b9065c38ba1f393351bb92b1
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jun 20 15:16:43 2012 +0200

    widget: use display monitors configuration
    
    Use display::monitors property to manage display area. Call
    update_area_monitor() to update the widget area depending on monitor
    configuration

diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index d33e79e..048770c 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -130,6 +130,7 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data);
 static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data);
 static void sync_keyboard_lock_modifiers(SpiceDisplay *display);
 static void cursor_invalidate(SpiceDisplay *display);
+static void update_area(SpiceDisplay *display, gint x, gint y, gint width, gint height);
 
 /* ---------------------------------------------------------------- */
 
@@ -226,6 +227,44 @@ static void update_keyboard_focus(SpiceDisplay *display, gboolean state)
     spice_gtk_session_request_auto_usbredir(d->gtk_session, state);
 }
 
+static void update_monitor_area(SpiceDisplay *display)
+{
+    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+    SpiceDisplayMonitorConfig *c;
+    GArray *monitors = NULL;
+
+    SPICE_DEBUG("update monitor area %d:%d", d->channel_id, d->monitor_id);
+    if (d->monitor_id < 0)
+        goto whole;
+
+    g_object_get(d->display, "monitors", &monitors, NULL);
+    if (monitors == NULL || d->monitor_id >= monitors->len) {
+        SPICE_DEBUG("update monitor: no monitor %d", d->monitor_id);
+        if (spice_channel_test_capability(d->display, SPICE_DISPLAY_CAP_MONITORS_CONFIG)) {
+            SPICE_DEBUG("waiting until MonitorsConfig is received");
+            return;
+        }
+        /* FIXME: mark false */
+        goto whole;
+    }
+
+    c = &g_array_index(monitors, SpiceDisplayMonitorConfig, d->monitor_id);
+    g_return_if_fail(c != NULL);
+    if (c->surface_id != 0) {
+        g_warning("FIXME: only support monitor config with primary surface 0, "
+                  "but given config surface %d", c->surface_id);
+        goto whole;
+    }
+
+    update_area(display, c->x, c->y, c->width, c->height);
+    g_clear_pointer(&monitors, g_array_unref);
+    return;
+
+whole:
+    /* by display whole surface */
+    update_area(display, 0, 0, d->width, d->height);
+}
+
 static void spice_display_set_property(GObject      *object,
                                        guint         prop_id,
                                        const GValue *value,
@@ -245,6 +284,8 @@ static void spice_display_set_property(GObject      *object,
         break;
     case PROP_MONITOR_ID:
         d->monitor_id = g_value_get_int(value);
+        if (d->display) /* if constructed */
+            update_monitor_area(display);
         break;
     case PROP_KEYBOARD_GRAB:
         d->keyboard_grab_enable = g_value_get_boolean(value);
@@ -808,13 +849,13 @@ static void recalc_geometry(GtkWidget *widget)
     } else
         zoom = (gdouble)d->zoom_level / 100;
 
-    SPICE_DEBUG("monitors: id %d, guest +%d+%d:%dx%d, window %dx%d, zoom %g, offset +%d+%d",
-                d->channel_id, d->area.x, d->area.y, d->area.width, d->area.height,
+    SPICE_DEBUG("recalc geom monitor: %d:%d, guest +%d+%d:%dx%d, window %dx%d, zoom %g, offset +%d+%d",
+                d->channel_id, d->monitor_id, d->area.x, d->area.y, d->area.width, d->area.height,
                 d->ww, d->wh, zoom, d->mx, d->my);
 
     if (d->resize_guest_enable)
         spice_main_set_display(d->main, get_display_id(display),
-                               0, 0, d->ww / zoom, d->wh / zoom);
+                               d->area.x, d->area.y, d->ww / zoom, d->wh / zoom);
 }
 
 /* ---------------------------------------------------------------- */
@@ -1645,8 +1686,7 @@ static void primary_create(SpiceChannel *channel, gint format,
     d->height = height;
     d->data_origin = d->data = imgdata;
 
-    /* by default, display whole surface */
-    update_area(display, 0, 0, width, height);
+    update_monitor_area(display);
 }
 
 static void primary_destroy(SpiceChannel *channel, gpointer data)
@@ -1694,13 +1734,12 @@ static void invalidate(SpiceChannel *channel,
                                x, y, w, h);
 }
 
-static void mark(SpiceChannel *channel, gint mark, gpointer data)
+static void mark(SpiceDisplay *display, gint mark)
 {
-    SpiceDisplay *display = data;
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
     g_return_if_fail(d != NULL);
 
-    SPICE_DEBUG("widget mark: %d, channel %d", mark, d->channel_id);
+    SPICE_DEBUG("widget mark: %d, %d:%d %p", mark, d->channel_id, d->monitor_id, display);
     d->mark = mark;
     spice_main_set_display_enabled(d->main, get_display_id(display), d->mark != 0);
     if (mark != 0 && gtk_widget_get_window(GTK_WIDGET(display)))
@@ -1856,6 +1895,7 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
     }
 
     if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
+        SpiceDisplayPrimary primary;
         if (id != d->channel_id)
             return;
         d->display = channel;
@@ -1866,7 +1906,14 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
         spice_g_signal_connect_object(channel, "display-invalidate",
                                       G_CALLBACK(invalidate), display, 0);
         spice_g_signal_connect_object(channel, "display-mark",
-                                      G_CALLBACK(mark), display, 0);
+                                      G_CALLBACK(mark), display, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+        spice_g_signal_connect_object(channel, "notify::monitors",
+                                      G_CALLBACK(update_monitor_area), display, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+        if (spice_display_get_primary(channel, 0, &primary)) {
+            primary_create(channel, primary.format, primary.width, primary.height,
+                           primary.stride, primary.shmid, primary.data, display);
+            mark(display, primary.marked);
+        }
         spice_channel_connect(channel);
         return;
     }
commit 98287e581330dbdd81c3ef73d46c41f6658a3638
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Jun 19 15:46:36 2012 +0200

    spicy: disable display when deleting window

diff --git a/gtk/spicy.c b/gtk/spicy.c
index 0571cf7..5510f0c 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -97,6 +97,7 @@ G_DEFINE_TYPE (SpiceWindow, spice_window, G_TYPE_OBJECT);
 struct spice_connection {
     SpiceSession     *session;
     SpiceGtkSession  *gtk_session;
+    SpiceMainChannel *main;
     SpiceWindow     *wins[CHANNELID_MAX * MONITORID_MAX];
     SpiceAudio       *audio;
     const char       *mouse_state;
@@ -117,6 +118,7 @@ static void usb_connect_failed(GObject               *object,
                                GError                *error,
                                gpointer               data);
 static gboolean is_gtk_session_property(const gchar *property);
+static void del_window(spice_connection *conn, SpiceWindow *win);
 
 /* options */
 static gboolean fullscreen = false;
@@ -561,7 +563,11 @@ static gboolean delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
 {
     SpiceWindow *win = data;
 
-    connection_disconnect(win->conn);
+    if (win->monitor_id == 0)
+        connection_disconnect(win->conn);
+    else
+        del_window(win->conn, win);
+
     return true;
 }
 
@@ -1530,6 +1536,12 @@ 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_set_display_enabled(conn->main, win->id, FALSE);
+    else
+        spice_main_set_display_enabled(conn->main, win->monitor_id, FALSE);
+    spice_main_send_monitor_config(conn->main);
+
     destroy_spice_window(win);
 }
 
@@ -1576,6 +1588,7 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
 
     if (SPICE_IS_MAIN_CHANNEL(channel)) {
         SPICE_DEBUG("new main channel");
+        conn->main = SPICE_MAIN_CHANNEL(channel);
         g_signal_connect(channel, "channel-event",
                          G_CALLBACK(main_channel_event), conn);
         g_signal_connect(channel, "main-mouse-update",
@@ -1621,6 +1634,7 @@ static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer dat
     g_object_get(channel, "channel-id", &id, NULL);
     if (SPICE_IS_MAIN_CHANNEL(channel)) {
         SPICE_DEBUG("zap main channel");
+        conn->main = NULL;
     }
 
     if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
commit 569b15e1618e8740da5d89c73779854ae620227d
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jun 20 14:42:13 2012 +0200

    spicy: learn to deal with monitors

diff --git a/gtk/spicy.c b/gtk/spicy.c
index 5ffe3b7..0571cf7 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -32,6 +32,7 @@
 #include "smartcard-manager.h"
 #endif
 
+#include "glib-compat.h"
 #include "spice-widget.h"
 #include "spice-gtk-session.h"
 #include "spice-audio.h"
@@ -62,7 +63,8 @@ typedef struct _SpiceWindowClass SpiceWindowClass;
 struct _SpiceWindow {
     GObject          object;
     spice_connection *conn;
-    int              id;
+    gint             id;
+    gint             monitor_id;
     GtkWidget        *toplevel, *spice;
     GtkWidget        *menubar, *toolbar;
     GtkWidget        *ritem, *rmenu;
@@ -87,10 +89,15 @@ struct _SpiceWindowClass
 
 G_DEFINE_TYPE (SpiceWindow, spice_window, G_TYPE_OBJECT);
 
+#define CHANNELID_MAX 4
+#define MONITORID_MAX 4
+
+// FIXME: turn this into an object, get rid of fixed wins array, use
+// signals to replace the various callback that iterate over wins array
 struct spice_connection {
     SpiceSession     *session;
     SpiceGtkSession  *gtk_session;
-    SpiceWindow     *wins[4];
+    SpiceWindow     *wins[CHANNELID_MAX * MONITORID_MAX];
     SpiceAudio       *audio;
     const char       *mouse_state;
     const char       *agent_state;
@@ -1141,7 +1148,7 @@ spice_window_init (SpiceWindow *self)
 {
 }
 
-static SpiceWindow *create_spice_window(spice_connection *conn, int id, SpiceChannel *channel)
+static SpiceWindow *create_spice_window(spice_connection *conn, SpiceChannel *channel, int id, gint monitor_id)
 {
     char title[32];
     SpiceWindow *win;
@@ -1154,12 +1161,13 @@ static SpiceWindow *create_spice_window(spice_connection *conn, int id, SpiceCha
 
     win = g_object_new(SPICE_TYPE_WINDOW, NULL);
     win->id = id;
+    win->monitor_id = monitor_id;
     win->conn = conn;
     win->display_channel = channel;
 
     /* toplevel */
     win->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-    snprintf(title, sizeof(title), _("spice display %d"), id);
+    snprintf(title, sizeof(title), _("spice display %d:%d"), id, monitor_id);
     gtk_window_set_title(GTK_WINDOW(win->toplevel), title);
     g_signal_connect(G_OBJECT(win->toplevel), "window-state-event",
                      G_CALLBACK(window_state_cb), win);
@@ -1204,7 +1212,7 @@ static SpiceWindow *create_spice_window(spice_connection *conn, int id, SpiceCha
 #endif
 
     /* spice display */
-    win->spice = GTK_WIDGET(spice_display_new(conn->session, id));
+    win->spice = GTK_WIDGET(spice_display_new_with_monitor(conn->session, id, monitor_id));
     g_signal_connect(win->spice, "configure-event", G_CALLBACK(configure_event_cb), win);
     seq = spice_grab_sequence_new_from_string("Shift_L+F12");
     spice_display_set_grab_keys(SPICE_DISPLAY(win->spice), seq);
@@ -1324,7 +1332,10 @@ static SpiceWindow *create_spice_window(spice_connection *conn, int id, SpiceCha
 
 static void destroy_spice_window(SpiceWindow *win)
 {
-    SPICE_DEBUG("destroy window (#%d)", win->id);
+    if (win == NULL)
+        return;
+
+    SPICE_DEBUG("destroy window (#%d:%d)", win->id, win->monitor_id);
     g_object_unref(win->ag);
     g_object_unref(win->ui);
     gtk_widget_destroy(win->toplevel);
@@ -1490,6 +1501,70 @@ static void update_auto_usbredir_sensitive(spice_connection *conn)
 #endif
 }
 
+static SpiceWindow* get_window(spice_connection *conn, int channel_id, int monitor_id)
+{
+    g_return_val_if_fail(channel_id < CHANNELID_MAX, NULL);
+    g_return_val_if_fail(monitor_id < MONITORID_MAX, NULL);
+
+    return conn->wins[channel_id * CHANNELID_MAX + monitor_id];
+}
+
+static void add_window(spice_connection *conn, SpiceWindow *win)
+{
+    g_return_if_fail(win != NULL);
+    g_return_if_fail(win->id < CHANNELID_MAX);
+    g_return_if_fail(win->monitor_id < MONITORID_MAX);
+    g_return_if_fail(conn->wins[win->id * CHANNELID_MAX + win->monitor_id] == NULL);
+
+    SPICE_DEBUG("add display monitor %d:%d", win->id, win->monitor_id);
+    conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = win;
+}
+
+static void del_window(spice_connection *conn, SpiceWindow *win)
+{
+    if (win == NULL)
+        return;
+
+    g_return_if_fail(win->id < CHANNELID_MAX);
+    g_return_if_fail(win->monitor_id < MONITORID_MAX);
+
+    g_debug("del display monitor %d:%d", win->id, win->monitor_id);
+    conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = NULL;
+    destroy_spice_window(win);
+}
+
+static void display_monitors(SpiceChannel *display, GParamSpec *pspec,
+                             spice_connection *conn)
+{
+    GArray *monitors = NULL;
+    int id;
+    guint i;
+
+    g_object_get(display,
+                 "channel-id", &id,
+                 "monitors", &monitors,
+                 NULL);
+    g_return_if_fail(monitors != NULL);
+
+    for (i = 0; i < monitors->len; i++) {
+        SpiceWindow *w;
+
+        if (!get_window(conn, id, i)) {
+            w = create_spice_window(conn, display, id, i);
+            add_window(conn, w);
+            spice_g_signal_connect_object(display, "display-mark",
+                                          G_CALLBACK(display_mark), w, 0);
+            gtk_widget_show(w->toplevel);
+            update_auto_usbredir_sensitive(conn);
+        }
+    }
+
+    for (; i < MONITORID_MAX; i++)
+        del_window(conn, get_window(conn, id, i));
+
+    g_clear_pointer(&monitors, g_array_unref);
+}
+
 static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
 {
     spice_connection *conn = data;
@@ -1517,10 +1592,9 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
         if (conn->wins[id] != NULL)
             return;
         SPICE_DEBUG("new display channel (#%d)", id);
-        conn->wins[id] = create_spice_window(conn, id, channel);
-        g_signal_connect(channel, "display-mark",
-                         G_CALLBACK(display_mark), conn->wins[id]);
-        update_auto_usbredir_sensitive(conn);
+        g_signal_connect(channel, "notify::monitors",
+                         G_CALLBACK(display_monitors), conn);
+        spice_channel_connect(channel);
     }
 
     if (SPICE_IS_INPUTS_CHANNEL(channel)) {
@@ -1552,11 +1626,8 @@ static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer dat
     if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
         if (id >= SPICE_N_ELEMENTS(conn->wins))
             return;
-        if (conn->wins[id] == NULL)
-            return;
         SPICE_DEBUG("zap display channel (#%d)", id);
-        destroy_spice_window(conn->wins[id]);
-        conn->wins[id] = NULL;
+        /* FIXME destroy widget only */
     }
 
     if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
commit 0b0ec738a9779dc3c37cf0573e3846be0729a5ce
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jun 20 14:49:07 2012 +0200

    Make-up a MonitorConfig if the server doesn't provide one
    
    This allows easier compatibility for clients that don't have to
    check and interact differently depending on channel capabilities.

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 3cd133e..637a6cf 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -744,10 +744,21 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
     g_return_val_if_fail(surface->canvas != NULL, 0);
     ring_add(&c->surfaces, &surface->link);
 
-    if (surface->primary)
+    if (surface->primary) {
         emit_main_context(channel, SPICE_DISPLAY_PRIMARY_CREATE,
                           surface->format, surface->width, surface->height,
                           surface->stride, surface->shmid, surface->data);
+
+        if (!spice_channel_test_capability(channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG)) {
+            g_array_set_size(c->monitors, 1);
+            SpiceDisplayMonitorConfig *config = &g_array_index(c->monitors, SpiceDisplayMonitorConfig, 0);
+            config->x = config->y = 0;
+            config->width = surface->width;
+            config->height = surface->height;
+            g_object_notify_main_context(G_OBJECT(channel), "monitors");
+        }
+    }
+
     return 0;
 }
 
commit 6106d1aaa4c4f18ae29865a70aca9212b088f756
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jun 20 14:47:26 2012 +0200

    Handle SPICE_MSG_DISPLAY_MONITORS_CONFIG

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 675ea14..3cd133e 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -1458,6 +1458,37 @@ static void display_handle_surface_destroy(SpiceChannel *channel, SpiceMsgIn *in
     free(surface);
 }
 
+/* coroutine context */
+static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in)
+{
+    SpiceMsgDisplayMonitorsConfig *config = spice_msg_in_parsed(in);
+    SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
+    guint i;
+
+    g_return_if_fail(config != NULL);
+    g_return_if_fail(config->count > 0);
+
+    SPICE_DEBUG("monitors config: n: %d", config->count);
+
+    c->monitors = g_array_set_size(c->monitors, config->count);
+
+    for (i = 0; i < config->count; i++) {
+        SpiceDisplayMonitorConfig *mc = &g_array_index(c->monitors, SpiceDisplayMonitorConfig, i);
+        SpiceHead *head = &config->heads[i];
+        SPICE_DEBUG("monitor id: %u, surface id: %u, +%u+%u-%ux%u",
+                    head->id, head->surface_id,
+                    head->x, head->y, head->width, head->height);
+        mc->id = head->id;
+        mc->surface_id = head->surface_id;
+        mc->x = head->x;
+        mc->y = head->y;
+        mc->width = head->width;
+        mc->height = head->height;
+    }
+
+    g_object_notify_main_context(G_OBJECT(channel), "monitors");
+}
+
 static const spice_msg_handler display_handlers[] = {
     [ SPICE_MSG_DISPLAY_MODE ]               = display_handle_mode,
     [ SPICE_MSG_DISPLAY_MARK ]               = display_handle_mark,
@@ -1490,6 +1521,8 @@ static const spice_msg_handler display_handlers[] = {
 
     [ SPICE_MSG_DISPLAY_SURFACE_CREATE ]     = display_handle_surface_create,
     [ SPICE_MSG_DISPLAY_SURFACE_DESTROY ]    = display_handle_surface_destroy,
+
+    [ SPICE_MSG_DISPLAY_MONITORS_CONFIG ]    = display_handle_monitors_config,
 };
 
 /* coroutine context */
commit 8ee26756499604c329af98cf8c19daff3d1caa7d
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Jun 19 17:26:32 2012 +0200

    Don't attempt to draw an invalid area
    
    If we don't intersect, the area is invalid or of size 0.

diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 6635e31..d33e79e 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -878,7 +878,8 @@ static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
     g_return_val_if_fail(d != NULL, false);
 
-    if (d->mark == 0 || d->data == NULL)
+    if (d->mark == 0 || d->data == NULL ||
+        d->area.width == 0 || d->area.height == 0)
         return false;
     g_return_val_if_fail(d->ximage != NULL, false);
 
@@ -894,7 +895,8 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *expose)
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
     g_return_val_if_fail(d != NULL, false);
 
-    if (d->mark == 0 || d->data == NULL)
+    if (d->mark == 0 || d->data == NULL ||
+        d->area.width == 0 || d->area.height == 0)
         return false;
     g_return_val_if_fail(d->ximage != NULL, false);
 
commit b79f3e148b5c9952a63de80c0a6959dc129c9cc0
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Mon Jun 18 19:27:06 2012 +0200

    Use monitor_id to compute display_id

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 9e97b8b..3c173c6 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -1829,7 +1829,7 @@ gboolean spice_main_agent_test_capability(SpiceMainChannel *channel, guint32 cap
 /**
  * spice_main_set_display:
  * @channel:
- * @id: display channel ID
+ * @id: display ID
  * @x: x position
  * @y: y position
  * @width: display width
@@ -2013,7 +2013,7 @@ void spice_main_clipboard_selection_request(SpiceMainChannel *channel, guint sel
 /**
  * spice_main_set_display_enabled:
  * @channel: a #SpiceMainChannel
- * @id: display channel ID (if -1: set all displays)
+ * @id: display ID (if -1: set all displays)
  * @enabled: wether display @id is enabled
  *
  * When sending monitor configuration to agent guest, don't set
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 3febb66..6635e31 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -779,6 +779,19 @@ static void update_mouse_grab(SpiceDisplay *display)
         try_mouse_ungrab(display);
 }
 
+static gint get_display_id(SpiceDisplay *display)
+{
+    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+
+    /* supported monitor_id only with display channel #0 */
+    if (d->channel_id == 0 && d->monitor_id >= 0)
+        return d->monitor_id;
+
+    g_return_val_if_fail(d->monitor_id <= 0, -1);
+
+    return d->channel_id;
+}
+
 static void recalc_geometry(GtkWidget *widget)
 {
     SpiceDisplay *display = SPICE_DISPLAY(widget);
@@ -800,7 +813,7 @@ static void recalc_geometry(GtkWidget *widget)
                 d->ww, d->wh, zoom, d->mx, d->my);
 
     if (d->resize_guest_enable)
-        spice_main_set_display(d->main, d->channel_id,
+        spice_main_set_display(d->main, get_display_id(display),
                                0, 0, d->ww / zoom, d->wh / zoom);
 }
 
@@ -1687,7 +1700,7 @@ static void mark(SpiceChannel *channel, gint mark, gpointer data)
 
     SPICE_DEBUG("widget mark: %d, channel %d", mark, d->channel_id);
     d->mark = mark;
-    spice_main_set_display_enabled(d->main, d->channel_id, d->mark != 0);
+    spice_main_set_display_enabled(d->main, get_display_id(display), d->mark != 0);
     if (mark != 0 && gtk_widget_get_window(GTK_WIDGET(display)))
         gtk_widget_queue_draw(GTK_WIDGET(display));
 }
commit b1aaa08be76605d68ee1b4a7feb93078c1dff73e
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Mon Jun 18 19:25:48 2012 +0200

    Document spice_main_send_monitor_config()

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 1d83bfa..9e97b8b 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -849,6 +849,15 @@ static void agent_msg_queue_many(SpiceMainChannel *channel, int type, const void
 #define agent_msg_queue(Channel, Type, Size, Data) \
     agent_msg_queue_many((Channel), (Type), (Data), (Size), NULL)
 
+/**
+ * spice_main_send_monitor_config:
+ * @channel:
+ *
+ * Send monitors configuration previously set with
+ * spice_main_set_display() and spice_main_set_display_enabled()
+ *
+ * Returns: %TRUE on success.
+ **/
 /* any context: the message is not flushed immediately,
    you can wakeup() the channel coroutine or send_msg_queue() */
 gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
commit 8aacc276c5246a21ad64191e5f8fe47ae0f0f6e3
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Jun 16 16:40:04 2012 +0200

    widget: don't forget to disconnect all signals handlers
    
    We forgot about display-mark. Use spice_g_signal_connect_object()
    helper, which will disconnect properly in all circunstances.

diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 7f7f17a..3febb66 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -126,9 +126,6 @@ static void update_mouse_grab(SpiceDisplay *display);
 static void try_mouse_grab(SpiceDisplay *display);
 static void try_mouse_ungrab(SpiceDisplay *display);
 static void recalc_geometry(GtkWidget *widget);
-static void disconnect_main(SpiceDisplay *display);
-static void disconnect_cursor(SpiceDisplay *display);
-static void disconnect_display(SpiceDisplay *display);
 static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data);
 static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data);
 static void sync_keyboard_lock_modifiers(SpiceDisplay *display);
@@ -313,21 +310,9 @@ static void spice_display_dispose(GObject *obj)
 
     SPICE_DEBUG("spice display dispose");
 
-    disconnect_main(display);
-    disconnect_display(display);
-    disconnect_cursor(display);
-
-    if (d->session) {
-        g_signal_handlers_disconnect_by_func(d->session, G_CALLBACK(channel_new),
-                                             display);
-        g_signal_handlers_disconnect_by_func(d->session, G_CALLBACK(channel_destroy),
-                                             display);
-        g_signal_handlers_disconnect_by_func(d->session, G_CALLBACK(session_inhibit_keyboard_grab_changed),
-                                             display);
-        g_object_unref(d->session);
-        d->session = NULL;
-        d->gtk_session = NULL;
-    }
+    spicex_image_destroy(display);
+    g_clear_object(&d->session);
+    d->gtk_session = NULL;
 
     G_OBJECT_CLASS(spice_display_parent_class)->dispose(obj);
 }
@@ -442,10 +427,10 @@ spice_display_constructor(GType                  gtype,
     if (!d->session)
         g_error("SpiceDisplay constructed without a session");
 
-    g_signal_connect(d->session, "channel-new",
-                     G_CALLBACK(channel_new), display);
-    g_signal_connect(d->session, "channel-destroy",
-                     G_CALLBACK(channel_destroy), display);
+    spice_g_signal_connect_object(d->session, "channel-new",
+                                  G_CALLBACK(channel_new), display, 0);
+    spice_g_signal_connect_object(d->session, "channel-destroy",
+                                  G_CALLBACK(channel_destroy), display, 0);
     list = spice_session_get_channels(d->session);
     for (it = g_list_first(list); it != NULL; it = g_list_next(it)) {
         if (SPICE_IS_MAIN_CHANNEL(it->data)) {
@@ -462,9 +447,9 @@ spice_display_constructor(GType                  gtype,
     spice_g_signal_connect_object(d->gtk_session, "notify::auto-clipboard",
                                   G_CALLBACK(gtk_session_property_changed), display, 0);
 
-    g_signal_connect(d->session, "notify::inhibit-keyboard-grab",
-                     G_CALLBACK(session_inhibit_keyboard_grab_changed),
-                     display);
+    spice_g_signal_connect_object(d->session, "notify::inhibit-keyboard-grab",
+                                  G_CALLBACK(session_inhibit_keyboard_grab_changed),
+                                  display, 0);
 
     return obj;
 }
@@ -1840,50 +1825,6 @@ static void cursor_reset(SpiceCursorChannel *channel, gpointer data)
     gdk_window_set_cursor(window, NULL);
 }
 
-static void disconnect_main(SpiceDisplay *display)
-{
-    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-
-    if (d->main == NULL)
-        return;
-    g_signal_handlers_disconnect_by_func(d->main, G_CALLBACK(update_mouse_mode),
-                                         display);
-    d->main = NULL;
-}
-
-static void disconnect_display(SpiceDisplay *display)
-{
-    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-
-    if (d->display == NULL)
-        return;
-    g_signal_handlers_disconnect_by_func(d->display, G_CALLBACK(primary_create),
-                                         display);
-    g_signal_handlers_disconnect_by_func(d->display, G_CALLBACK(primary_destroy),
-                                         display);
-    g_signal_handlers_disconnect_by_func(d->display, G_CALLBACK(invalidate),
-                                         display);
-    primary_destroy(d->display, display);
-    d->display = NULL;
-}
-
-static void disconnect_cursor(SpiceDisplay *display)
-{
-    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-
-    if (d->cursor == NULL)
-        return;
-    g_signal_handlers_disconnect_by_func(d->cursor, G_CALLBACK(cursor_set),
-                                         display);
-    g_signal_handlers_disconnect_by_func(d->cursor, G_CALLBACK(cursor_move),
-                                         display);
-    g_signal_handlers_disconnect_by_func(d->cursor, G_CALLBACK(cursor_hide),
-                                         display);
-    g_signal_handlers_disconnect_by_func(d->cursor, G_CALLBACK(cursor_reset),
-                                         display);
-    d->cursor = NULL;
-}
-
 static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
 {
     SpiceDisplay *display = data;
@@ -1893,8 +1834,8 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
     g_object_get(channel, "channel-id", &id, NULL);
     if (SPICE_IS_MAIN_CHANNEL(channel)) {
         d->main = SPICE_MAIN_CHANNEL(channel);
-        g_signal_connect(channel, "main-mouse-update",
-                         G_CALLBACK(update_mouse_mode), display);
+        spice_g_signal_connect_object(channel, "main-mouse-update",
+                                      G_CALLBACK(update_mouse_mode), display, 0);
         update_mouse_mode(channel, display);
         return;
     }
@@ -1903,14 +1844,14 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
         if (id != d->channel_id)
             return;
         d->display = channel;
-        g_signal_connect(channel, "display-primary-create",
-                         G_CALLBACK(primary_create), display);
-        g_signal_connect(channel, "display-primary-destroy",
-                         G_CALLBACK(primary_destroy), display);
-        g_signal_connect(channel, "display-invalidate",
-                         G_CALLBACK(invalidate), display);
-        g_signal_connect(channel, "display-mark",
-                         G_CALLBACK(mark), display);
+        spice_g_signal_connect_object(channel, "display-primary-create",
+                                      G_CALLBACK(primary_create), display, 0);
+        spice_g_signal_connect_object(channel, "display-primary-destroy",
+                                      G_CALLBACK(primary_destroy), display, 0);
+        spice_g_signal_connect_object(channel, "display-invalidate",
+                                      G_CALLBACK(invalidate), display, 0);
+        spice_g_signal_connect_object(channel, "display-mark",
+                                      G_CALLBACK(mark), display, 0);
         spice_channel_connect(channel);
         return;
     }
@@ -1919,14 +1860,14 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
         if (id != d->channel_id)
             return;
         d->cursor = SPICE_CURSOR_CHANNEL(channel);
-        g_signal_connect(channel, "cursor-set",
-                         G_CALLBACK(cursor_set), display);
-        g_signal_connect(channel, "cursor-move",
-                         G_CALLBACK(cursor_move), display);
-        g_signal_connect(channel, "cursor-hide",
-                         G_CALLBACK(cursor_hide), display);
-        g_signal_connect(channel, "cursor-reset",
-                         G_CALLBACK(cursor_reset), display);
+        spice_g_signal_connect_object(channel, "cursor-set",
+                                      G_CALLBACK(cursor_set), display, 0);
+        spice_g_signal_connect_object(channel, "cursor-move",
+                                      G_CALLBACK(cursor_move), display, 0);
+        spice_g_signal_connect_object(channel, "cursor-hide",
+                                      G_CALLBACK(cursor_hide), display, 0);
+        spice_g_signal_connect_object(channel, "cursor-reset",
+                                      G_CALLBACK(cursor_reset), display, 0);
         spice_channel_connect(channel);
         return;
     }
@@ -1959,21 +1900,22 @@ static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer dat
     SPICE_DEBUG("channel_destroy %d", id);
 
     if (SPICE_IS_MAIN_CHANNEL(channel)) {
-        disconnect_main(display);
+        d->main = NULL;
         return;
     }
 
     if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
         if (id != d->channel_id)
             return;
-        disconnect_display(display);
+        primary_destroy(d->display, display);
+        d->display = NULL;
         return;
     }
 
     if (SPICE_IS_CURSOR_CHANNEL(channel)) {
         if (id != d->channel_id)
             return;
-        disconnect_cursor(display);
+        d->cursor = NULL;
         return;
     }
 
commit 69df4d4133234e5c09ffc15d015845e586785979
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Jun 16 15:53:59 2012 +0200

    spice_channel_connect() success if state >= CONNECTING
    
    We may have several widget trying to re-connect the channels now.
    It is fine to return successfully if we are already connecting or
    connected.

diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 793e36f..54d406d 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2314,7 +2314,7 @@ gboolean spice_channel_connect(SpiceChannel *channel)
     g_return_val_if_fail(SPICE_IS_CHANNEL(channel), FALSE);
     SpiceChannelPrivate *c = channel->priv;
 
-    if (c->state == SPICE_CHANNEL_STATE_CONNECTING)
+    if (c->state >= SPICE_CHANNEL_STATE_CONNECTING)
         return TRUE;
 
     return channel_connect(channel);
commit 66deab923d168509988f472c9275d27ae6f2530d
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Jun 16 15:39:21 2012 +0200

    widget: add monitor property with ctor

diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt
index a339d32..daf575c 100644
--- a/doc/reference/spice-gtk-sections.txt
+++ b/doc/reference/spice-gtk-sections.txt
@@ -336,6 +336,7 @@ SpiceDisplay
 SpiceDisplayClass
 SpiceDisplayKeyEvent
 spice_display_new
+spice_display_new_with_monitor
 spice_display_mouse_ungrab
 spice_display_copy_to_guest
 spice_display_paste_from_guest
diff --git a/gtk/map-file b/gtk/map-file
index c58bab5..0d48bb3 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -25,6 +25,7 @@ spice_display_get_type;
 spice_display_key_event_get_type;
 spice_display_mouse_ungrab;
 spice_display_new;
+spice_display_new_with_monitor;
 spice_display_paste_from_guest;
 spice_display_send_keys;
 spice_display_set_grab_keys;
diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index a94db68..a44e5fe 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -43,6 +43,7 @@ G_BEGIN_DECLS
 
 struct _SpiceDisplayPrivate {
     gint                    channel_id;
+    gint                    monitor_id;
 
     /* options */
     bool                    keyboard_grab_enable;
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 4d0f9be..7f7f17a 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -101,7 +101,8 @@ enum {
     PROP_AUTO_CLIPBOARD,
     PROP_SCALING,
     PROP_DISABLE_INPUTS,
-    PROP_ZOOM_LEVEL
+    PROP_ZOOM_LEVEL,
+    PROP_MONITOR_ID
 };
 
 /* Signals */
@@ -151,6 +152,9 @@ static void spice_display_get_property(GObject    *object,
     case PROP_CHANNEL_ID:
         g_value_set_int(value, d->channel_id);
         break;
+    case PROP_MONITOR_ID:
+        g_value_set_int(value, d->monitor_id);
+        break;
     case PROP_KEYBOARD_GRAB:
         g_value_set_boolean(value, d->keyboard_grab_enable);
         break;
@@ -242,6 +246,9 @@ static void spice_display_set_property(GObject      *object,
     case PROP_CHANNEL_ID:
         d->channel_id = g_value_get_int(value);
         break;
+    case PROP_MONITOR_ID:
+        d->monitor_id = g_value_get_int(value);
+        break;
     case PROP_KEYBOARD_GRAB:
         d->keyboard_grab_enable = g_value_get_boolean(value);
         update_keyboard_grab(display);
@@ -1492,6 +1499,25 @@ static void spice_display_class_init(SpiceDisplayClass *klass)
                           G_PARAM_STATIC_STRINGS));
 
     /**
+     * SpiceDisplay:monitor-id:
+     *
+     * Select monitor from #SpiceDisplay to show.
+     * The value -1 means the whole display is shown.
+     * By default, the monitor 0 is selected.
+     *
+     * Since: 0.13
+     **/
+    g_object_class_install_property
+        (gobject_class, PROP_MONITOR_ID,
+         g_param_spec_int("monitor-id",
+                          "Monitor ID",
+                          "Select monitor ID",
+                          -1, G_MAXINT, 0,
+                          G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT |
+                          G_PARAM_STATIC_STRINGS));
+
+    /**
      * SpiceDisplay::mouse-grab:
      * @display: the #SpiceDisplay that emitted the signal
      * @status: 1 if grabbed, 0 otherwise.
@@ -1969,7 +1995,7 @@ static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer dat
 /**
  * spice_display_new:
  * @session: a #SpiceSession
- * @id: the display channel ID to associate with #SpiceDisplay
+ * @channel_id: the display channel ID to associate with #SpiceDisplay
  *
  * Returns: a new #SpiceDisplay widget.
  **/
@@ -1980,6 +2006,24 @@ SpiceDisplay *spice_display_new(SpiceSession *session, int id)
 }
 
 /**
+ * spice_display_new_with_monitor:
+ * @session: a #SpiceSession
+ * @channel_id: the display channel ID to associate with #SpiceDisplay
+ * @monitor_id: the monitor id within the display channel
+ *
+ * Since: 0.13
+ * Returns: a new #SpiceDisplay widget.
+ **/
+SpiceDisplay* spice_display_new_with_monitor(SpiceSession *session, gint channel_id, gint monitor_id)
+{
+    return g_object_new(SPICE_TYPE_DISPLAY,
+                        "session", session,
+                        "channel-id", channel_id,
+                        "monitor-id", monitor_id,
+                        NULL);
+}
+
+/**
  * spice_display_mouse_ungrab:
  * @display:
  *
diff --git a/gtk/spice-widget.h b/gtk/spice-widget.h
index 3f6a785..d239ed2 100644
--- a/gtk/spice-widget.h
+++ b/gtk/spice-widget.h
@@ -70,7 +70,9 @@ typedef enum
 
 GType	        spice_display_get_type(void);
 
-SpiceDisplay* spice_display_new(SpiceSession *session, int id);
+SpiceDisplay* spice_display_new(SpiceSession *session, int channel_id);
+SpiceDisplay* spice_display_new_with_monitor(SpiceSession *session, gint channel_id, gint monitor_id);
+
 void spice_display_mouse_ungrab(SpiceDisplay *display);
 void spice_display_set_grab_keys(SpiceDisplay *display, SpiceGrabSequence *seq);
 SpiceGrabSequence *spice_display_get_grab_keys(SpiceDisplay *display);
commit 230125032d848878ff2ee85d2829b9b62133a8c4
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Jun 16 15:36:40 2012 +0200

    Claim SPICE_DISPLAY_CAP_MONITORS_CONFIG

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 5ae6e34..675ea14 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -654,6 +654,7 @@ static HDC create_compatible_dc(void)
 static void spice_display_channel_reset_capabilities(SpiceChannel *channel)
 {
     spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_SIZED_STREAM);
+    spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_MONITORS_CONFIG);
 }
 
 static void spice_display_channel_init(SpiceDisplayChannel *channel)
commit 9381bc293cd0a7e344aa16f410034dc3099ed954
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Jun 16 15:35:46 2012 +0200

    Add spice_display_get_primary()

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index f82df4c..5ae6e34 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -352,6 +352,43 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass)
     rop3_init();
 }
 
+/**
+ * spice_display_get_primary:
+ * @channel:
+ * @surface_id:
+ * @primary:
+ *
+ * Retrieve primary display surface @surface_id.
+ *
+ * Returns: %TRUE if the primary surface was found and its details
+ * collected in @primary.
+ */
+gboolean spice_display_get_primary(SpiceChannel *channel, guint32 surface_id,
+                                   SpiceDisplayPrimary *primary)
+{
+    g_return_val_if_fail(SPICE_IS_DISPLAY_CHANNEL(channel), FALSE);
+    g_return_val_if_fail(primary != NULL, FALSE);
+
+    SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
+    display_surface *surface = find_surface(c, surface_id);
+
+    if (surface == NULL)
+        return FALSE;
+
+    g_return_val_if_fail(surface->primary, FALSE);
+
+    primary->format = surface->format;
+    primary->width = surface->width;
+    primary->height = surface->height;
+    primary->stride = surface->stride;
+    primary->shmid = surface->shmid;
+    primary->data = surface->data;
+    primary->marked = c->mark;
+    SPICE_DEBUG("get primary %p", primary->data);
+
+    return TRUE;
+}
+
 /* signal trampoline---------------------------------------------------------- */
 
 struct SPICE_DISPLAY_PRIMARY_CREATE {
diff --git a/gtk/channel-display.h b/gtk/channel-display.h
index a31aa54..88e60d9 100644
--- a/gtk/channel-display.h
+++ b/gtk/channel-display.h
@@ -43,6 +43,17 @@ struct _SpiceDisplayMonitorConfig {
     guint height;
 };
 
+typedef struct _SpiceDisplayPrimary SpiceDisplayPrimary;
+struct _SpiceDisplayPrimary {
+    enum SpiceSurfaceFmt format;
+    gint width;
+    gint height;
+    gint stride;
+    gint shmid;
+    guint8 *data;
+    gboolean marked;
+};
+
 /**
  * SpiceDisplayChannel:
  *
@@ -80,10 +91,11 @@ struct _SpiceDisplayChannelClass {
                          gboolean mark);
 
     /*< private >*/
-    /* Do not add fields to this struct */
 };
 
 GType	        spice_display_channel_get_type(void);
+gboolean        spice_display_get_primary(SpiceChannel *channel, guint32 surface_id,
+                                          SpiceDisplayPrimary *primary);
 
 G_END_DECLS
 
diff --git a/gtk/map-file b/gtk/map-file
index 32ead37..c58bab5 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -17,6 +17,7 @@ spice_channel_type_to_string;
 spice_client_error_quark;
 spice_cursor_channel_get_type;
 spice_display_channel_get_type;
+spice_display_get_primary;
 spice_display_copy_to_guest;
 spice_display_get_grab_keys;
 spice_display_get_pixbuf;
commit d2e6f651da995ac09b6ba0c03e8b8a347a9a3e51
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Jun 16 12:54:27 2012 +0200

    display: add readonly monitors property

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 952628c..f82df4c 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -27,6 +27,7 @@
 #include <sys/ipc.h>
 #endif
 
+#include "glib-compat.h"
 #include "spice-client.h"
 #include "spice-common.h"
 
@@ -70,6 +71,7 @@ struct _SpiceDisplayChannelPrivate {
     int                         nstreams;
     gboolean                    mark;
     guint                       mark_false_event_id;
+    GArray                      *monitors;
 #ifdef WIN32
     HDC dc;
 #endif
@@ -82,6 +84,7 @@ enum {
     PROP_0,
     PROP_WIDTH,
     PROP_HEIGHT,
+    PROP_MONITORS
 };
 
 enum {
@@ -123,6 +126,9 @@ static void spice_display_channel_dispose(GObject *object)
 
 static void spice_display_channel_finalize(GObject *object)
 {
+    SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(object)->priv;
+
+    g_clear_pointer(&c->monitors, g_array_unref);
     clear_surfaces(SPICE_CHANNEL(object), FALSE);
     clear_streams(SPICE_CHANNEL(object));
 
@@ -142,6 +148,8 @@ static void spice_display_channel_constructed(GObject *object)
     g_return_if_fail(c->images != NULL);
     g_return_if_fail(c->palettes != NULL);
 
+    c->monitors = g_array_new(FALSE, TRUE, sizeof(SpiceDisplayMonitorConfig));
+
     if (G_OBJECT_CLASS(spice_display_channel_parent_class)->constructed)
         G_OBJECT_CLASS(spice_display_channel_parent_class)->constructed(object);
 }
@@ -165,6 +173,10 @@ static void spice_display_get_property(GObject    *object,
         g_value_set_uint(value, surface ? surface->height : 0);
         break;
     }
+    case PROP_MONITORS: {
+        g_value_set_boxed(value, c->monitors);
+        break;
+    }
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -228,6 +240,22 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass)
                            G_PARAM_STATIC_STRINGS));
 
     /**
+     * SpiceDisplayChannel:monitors:
+     *
+     * Current monitors configuration.
+     *
+     * Since: 0.13
+     */
+    g_object_class_install_property
+        (gobject_class, PROP_MONITORS,
+         g_param_spec_boxed("monitors",
+                            "Display monitors",
+                            "The monitors configuration",
+                            G_TYPE_ARRAY,
+                            G_PARAM_READABLE |
+                            G_PARAM_STATIC_STRINGS));
+
+    /**
      * SpiceDisplayChannel::display-primary-create:
      * @display: the #SpiceDisplayChannel that emitted the signal
      * @format: %SPICE_SURFACE_FMT_32_xRGB or %SPICE_SURFACE_FMT_16_555;
diff --git a/gtk/channel-display.h b/gtk/channel-display.h
index b8d7679..a31aa54 100644
--- a/gtk/channel-display.h
+++ b/gtk/channel-display.h
@@ -33,6 +33,16 @@ typedef struct _SpiceDisplayChannel SpiceDisplayChannel;
 typedef struct _SpiceDisplayChannelClass SpiceDisplayChannelClass;
 typedef struct _SpiceDisplayChannelPrivate SpiceDisplayChannelPrivate;
 
+typedef struct _SpiceDisplayMonitorConfig SpiceDisplayMonitorConfig;
+struct _SpiceDisplayMonitorConfig {
+    guint id;
+    guint surface_id;
+    guint x;
+    guint y;
+    guint width;
+    guint height;
+};
+
 /**
  * SpiceDisplayChannel:
  *
commit d809b2ce54fb61081bc6d3d1f67068093f5532ec
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Jun 16 12:51:02 2012 +0200

    glib-compat: add g_clear_pointer
    
    A helpful macro from glib 2.34

diff --git a/gtk/glib-compat.h b/gtk/glib-compat.h
index bd8f3de..aa750b2 100644
--- a/gtk/glib-compat.h
+++ b/gtk/glib-compat.h
@@ -106,4 +106,24 @@ GType spice_main_context_get_type (void) G_GNUC_CONST;
 # define G_SIGNAL_DEPRECATED (1 << 9)
 #endif
 
+#ifndef g_clear_pointer
+#define g_clear_pointer(pp, destroy) \
+  G_STMT_START {                                                               \
+    G_STATIC_ASSERT (sizeof *(pp) == sizeof (gpointer));                       \
+    /* Only one access, please */                                              \
+    gpointer *_pp = (gpointer *) (pp);                                         \
+    gpointer _p;                                                               \
+    /* This assignment is needed to avoid a gcc warning */                     \
+    GDestroyNotify _destroy = (GDestroyNotify) (destroy);                      \
+                                                                               \
+    (void) (0 ? (gpointer) *(pp) : 0);                                         \
+    do                                                                         \
+      _p = g_atomic_pointer_get (_pp);                                         \
+    while G_UNLIKELY (!g_atomic_pointer_compare_and_exchange (_pp, _p, NULL)); \
+                                                                               \
+    if (_p)                                                                    \
+      _destroy (_p);                                                           \
+  } G_STMT_END
+#endif
+
 #endif /* GLIB_COMPAT_H */
commit 6bea13927e6c1833e2c12b604c51bb265cede305
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Jun 16 12:48:40 2012 +0200

    Change surface_id to a guint32
    
    That's the correct type used by the protocol.

diff --git a/gtk/channel-display-priv.h b/gtk/channel-display-priv.h
index d327c28..ff0f484 100644
--- a/gtk/channel-display-priv.h
+++ b/gtk/channel-display-priv.h
@@ -38,7 +38,7 @@ G_BEGIN_DECLS
 
 typedef struct display_surface {
     RingItem                    link;
-    int                         surface_id;
+    guint32                     surface_id;
     bool                        primary;
     enum SpiceSurfaceFmt        format;
     int                         width, height, stride, size;
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 801d867..952628c 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -100,7 +100,7 @@ static void spice_display_channel_up(SpiceChannel *channel);
 
 static void clear_surfaces(SpiceChannel *channel, gboolean keep_primary);
 static void clear_streams(SpiceChannel *channel);
-static display_surface *find_surface(SpiceDisplayChannelPrivate *c, int surface_id);
+static display_surface *find_surface(SpiceDisplayChannelPrivate *c, guint32 surface_id);
 static gboolean display_stream_render(display_stream *st);
 static void spice_display_channel_reset(SpiceChannel *channel, gboolean migrating);
 static void spice_display_channel_reset_capabilities(SpiceChannel *channel);
@@ -710,7 +710,7 @@ static void destroy_canvas(display_surface *surface)
     surface->canvas = NULL;
 }
 
-static display_surface *find_surface(SpiceDisplayChannelPrivate *c, int surface_id)
+static display_surface *find_surface(SpiceDisplayChannelPrivate *c, guint32 surface_id)
 {
     display_surface *surface;
     RingItem *item;
commit ac9e55b64ff00803530557d2cad15287076a51b1
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jun 20 14:13:01 2012 +0200

    widget: add main channel before other channels
    
    Make sure that the d->main channel member can be referenced when
    adding further channels, when we perform their setup.

diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 237d832..4d0f9be 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -441,7 +441,14 @@ spice_display_constructor(GType                  gtype,
                      G_CALLBACK(channel_destroy), display);
     list = spice_session_get_channels(d->session);
     for (it = g_list_first(list); it != NULL; it = g_list_next(it)) {
-        channel_new(d->session, it->data, (gpointer*)display);
+        if (SPICE_IS_MAIN_CHANNEL(it->data)) {
+            channel_new(d->session, it->data, (gpointer*)display);
+            break;
+        }
+    }
+    for (it = g_list_first(list); it != NULL; it = g_list_next(it)) {
+        if (!SPICE_IS_MAIN_CHANNEL(it->data))
+            channel_new(d->session, it->data, (gpointer*)display);
     }
     g_list_free(list);
 
commit 53127e30561ed4d66c772a57428b352a3b92127b
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Fri Jun 15 16:34:54 2012 +0200

    Add missing agent cap description

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 70861e6..1d83bfa 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -149,6 +149,7 @@ static const char *agent_caps[] = {
     [ VD_AGENT_CAP_CLIPBOARD           ] = "clipboard (old)",
     [ VD_AGENT_CAP_DISPLAY_CONFIG      ] = "display config",
     [ VD_AGENT_CAP_CLIPBOARD_BY_DEMAND ] = "clipboard",
+    [ VD_AGENT_CAP_CLIPBOARD_SELECTION ] = "clipboard selection",
 };
 #define NAME(_a, _i) ((_i) < SPICE_N_ELEMENTS(_a) ? (_a[(_i)] ?: "?") : "?")
 
commit e3bb7b1cfd162fcb8943e9d582dab43eeec6ce41
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Jun 12 19:24:47 2012 +0200

    display: learn to restrict display to an area
    
    Each spice widget can now restrict the area of the primary
    surface they show and interact with by setting the private
    area member.
    
    A nice clean-up would be to seperate an area object that
    would deal with clipping, input translation and color
    conversion, but the resulting code would be similar anyway

diff --git a/gtk/spice-widget-cairo.c b/gtk/spice-widget-cairo.c
index 2f1ef75..1144308 100644
--- a/gtk/spice-widget-cairo.c
+++ b/gtk/spice-widget-cairo.c
@@ -37,16 +37,22 @@ int spicex_image_create(SpiceDisplay *display)
 {
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
 
+    g_return_val_if_fail(d->ximage == NULL, 1);
+
     if (d->format == SPICE_SURFACE_FMT_16_555 ||
         d->format == SPICE_SURFACE_FMT_16_565) {
         d->convert = TRUE;
-        d->data = g_malloc0(d->height * d->stride); /* pixels are 32 bits */
+        d->data = g_malloc0(d->area.width * d->area.height * 4);
+
+        d->ximage = cairo_image_surface_create_for_data
+            (d->data, CAIRO_FORMAT_RGB24, d->area.width, d->area.height, d->area.width * 4);
+
     } else {
         d->convert = FALSE;
-    }
 
-    d->ximage = cairo_image_surface_create_for_data
-        (d->data, CAIRO_FORMAT_RGB24, d->width, d->height, d->stride);
+        d->ximage = cairo_image_surface_create_for_data
+            (d->data, CAIRO_FORMAT_RGB24, d->width, d->height, d->stride);
+    }
 
     return 0;
 }
@@ -64,13 +70,13 @@ void spicex_image_destroy(SpiceDisplay *display)
         g_free(d->data);
         d->data = NULL;
     }
+    d->convert = FALSE;
 }
 
 G_GNUC_INTERNAL
 void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
 {
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-    int fbw = d->width, fbh = d->height;
     int ww, wh;
 
     gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(display)), &ww, &wh);
@@ -86,8 +92,8 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
            behaviour of drawing the rectangle from right to left
            to cut out the whole */
         if (d->ximage)
-            cairo_rectangle(cr, d->mx + fbw, d->my,
-                            -1 * fbw, fbh);
+            cairo_rectangle(cr, d->mx + d->area.width, d->my,
+                            -d->area.width, d->area.height);
         cairo_fill(cr);
     }
 
@@ -97,9 +103,16 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
             double sx, sy;
             spice_display_get_scaling(display, &sx, &sy);
             cairo_scale(cr, sx, sy);
+            if (!d->convert)
+                cairo_translate(cr, -d->area.x, -d->area.y);
             cairo_set_source_surface(cr, d->ximage, 0, 0);
         } else {
-            cairo_set_source_surface(cr, d->ximage, d->mx, d->my);
+            cairo_translate(cr, d->mx, d->my);
+            cairo_rectangle(cr, 0, 0, d->area.width, d->area.height);
+            cairo_clip(cr);
+            if (!d->convert)
+                cairo_translate(cr, -d->area.x, -d->area.y);
+            cairo_set_source_surface(cr, d->ximage, 0, 0);
         }
         cairo_paint(cr);
 
@@ -108,8 +121,8 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
             GdkPixbuf *image = d->mouse_pixbuf;
             if (image != NULL) {
                 gdk_cairo_set_source_pixbuf(cr, image,
-                                            d->mx + d->mouse_guest_x - d->mouse_hotspot.x,
-                                            d->my + d->mouse_guest_y - d->mouse_hotspot.y);
+                                            d->mouse_guest_x - d->mouse_hotspot.x,
+                                            d->mouse_guest_y - d->mouse_hotspot.y);
                 cairo_paint(cr);
             }
         }
@@ -149,8 +162,8 @@ void spicex_image_invalidate(SpiceDisplay *display,
         double sx, sy;
 
         /* Scale the exposed region */
-        sx = (double)ww / (double)d->width;
-        sy = (double)wh / (double)d->height;
+        sx = (double)ww / (double)d->area.width;
+        sy = (double)wh / (double)d->area.height;
 
         *x *= sx;
         *y *= sy;
diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index 898d86c..a94db68 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -57,6 +57,8 @@ struct _SpiceDisplayPrivate {
     gpointer                data_origin; /* the original display image data */
     gpointer                data; /* converted if necessary to 32 bits */
 
+    GdkRectangle            area;
+    /* window border */
     gint                    ww, wh, mx, my;
 
     bool                    convert;
diff --git a/gtk/spice-widget-x11.c b/gtk/spice-widget-x11.c
index be2b6e7..7fc99ff 100644
--- a/gtk/spice-widget-x11.c
+++ b/gtk/spice-widget-x11.c
@@ -222,30 +222,30 @@ void spicex_expose_event(SpiceDisplay *display, GdkEventExpose *expose)
 
     if (expose->area.x >= d->mx &&
         expose->area.y >= d->my &&
-        expose->area.x + expose->area.width  <= d->mx + d->width &&
-        expose->area.y + expose->area.height <= d->my + d->height) {
+        expose->area.x + expose->area.width  <= d->mx + d->area.width &&
+        expose->area.y + expose->area.height <= d->my + d->area.height) {
         /* area is completely inside the guest screen -- blit it */
         if (d->have_mitshm && d->shminfo) {
             XShmPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
                          d->gc, d->ximage,
-                         expose->area.x - d->mx, expose->area.y - d->my,
-                         expose->area.x,         expose->area.y,
+                         d->area.x + expose->area.x - d->mx, d->area.y + expose->area.y - d->my,
+                         expose->area.x, expose->area.y,
                          expose->area.width, expose->area.height,
                          true);
         } else {
             XPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
                       d->gc, d->ximage,
-                      expose->area.x - d->mx, expose->area.y - d->my,
-                      expose->area.x,         expose->area.y,
+                      d->area.x + expose->area.x - d->mx, d->area.y + expose->area.y - d->my,
+                      expose->area.x expose->area.y,
                       expose->area.width, expose->area.height);
         }
     } else {
         /* complete window update */
-        if (d->ww > d->width || d->wh > d->height) {
+        if (d->ww > d->area.width || d->wh > d->area.height) {
             int x1 = d->mx;
-            int x2 = d->mx + d->width;
+            int x2 = d->mx + d->area.width;
             int y1 = d->my;
-            int y2 = d->my + d->height;
+            int y2 = d->my + d->area.height;
             XFillRectangle(d->dpy, gdk_x11_drawable_get_xid(window),
                            d->gc, 0, 0, x1, d->wh);
             XFillRectangle(d->dpy, gdk_x11_drawable_get_xid(window),
@@ -258,12 +258,12 @@ void spicex_expose_event(SpiceDisplay *display, GdkEventExpose *expose)
         if (d->have_mitshm && d->shminfo) {
             XShmPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
                          d->gc, d->ximage,
-                         0, 0, d->mx, d->my, d->width, d->height,
+                         d->area.x, d->area.y, d->mx, d->my, d->area.width, d->area.height,
                          true);
         } else {
             XPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
                       d->gc, d->ximage,
-                      0, 0, d->mx, d->my, d->width, d->height);
+                      d->area.x, d->area.y, d->mx, d->my, d->area.width, d->area.height);
         }
     }
 }
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 095224c..237d832 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -201,8 +201,8 @@ static void update_size_request(SpiceDisplay *display)
         reqwidth = 640;
         reqheight = 480;
     } else {
-        reqwidth = d->width;
-        reqheight = d->height;
+        reqwidth = d->area.width;
+        reqheight = d->area.height;
     }
 
     gtk_widget_set_size_request(GTK_WIDGET(display), reqwidth, reqheight);
@@ -789,15 +789,16 @@ static void recalc_geometry(GtkWidget *widget)
     d->mx = 0;
     d->my = 0;
     if (!spicex_is_scaled(display)) {
-        if (d->ww > d->width)
-            d->mx = (d->ww - d->width) / 2;
-        if (d->wh > d->height)
-            d->my = (d->wh - d->height) / 2;
+        if (d->ww > d->area.width)
+            d->mx = (d->ww - d->area.width) / 2;
+        if (d->wh > d->area.height)
+            d->my = (d->wh - d->area.height) / 2;
     } else
         zoom = (gdouble)d->zoom_level / 100;
 
-    SPICE_DEBUG("monitors: id %d, guest %dx%d, window %dx%d, zoom %g, offset +%d+%d",
-                d->channel_id, d->width, d->height, d->ww, d->wh, zoom, d->mx, d->my);
+    SPICE_DEBUG("monitors: id %d, guest +%d+%d:%dx%d, window %dx%d, zoom %g, offset +%d+%d",
+                d->channel_id, d->area.x, d->area.y, d->area.width, d->area.height,
+                d->ww, d->wh, zoom, d->mx, d->my);
 
     if (d->resize_guest_enable)
         spice_main_set_display(d->main, d->channel_id,
@@ -820,44 +821,36 @@ static void recalc_geometry(GtkWidget *widget)
 
 #define CONVERT_0555_TO_8888(s) (CONVERT_0555_TO_0888(s) | 0xff000000)
 
-static gboolean do_color_convert(SpiceDisplay *display,
-                                 gint x, gint y, gint w, gint h)
+static gboolean do_color_convert(SpiceDisplay *display, GdkRectangle *r)
 {
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-    int i, j, maxy, maxx, miny, minx;
     guint32 *dest = d->data;
     guint16 *src = d->data_origin;
+    gint x, y;
 
-    if (!d->convert)
-        return true;
-
+    g_return_val_if_fail(r != NULL, false);
     g_return_val_if_fail(d->format == SPICE_SURFACE_FMT_16_555 ||
                          d->format == SPICE_SURFACE_FMT_16_565, false);
 
-    miny = MAX(y, 0);
-    minx = MAX(x, 0);
-    maxy = MIN(y + h, d->height);
-    maxx = MIN(x + w, d->width);
-
-    dest +=  (d->stride / 4) * miny;
-    src += (d->stride / 2) * miny;
+    src += (d->stride / 2) * r->y + r->x;
+    dest += d->area.width * (r->y - d->area.y) + (r->x - d->area.x);
 
     if (d->format == SPICE_SURFACE_FMT_16_555) {
-        for (j = miny; j < maxy; j++) {
-            for (i = minx; i < maxx; i++) {
-                dest[i] = CONVERT_0555_TO_0888(src[i]);
+        for (y = 0; y < r->height; y++) {
+            for (x = 0; x < r->width; x++) {
+                dest[x] = CONVERT_0555_TO_0888(src[x]);
             }
 
-            dest += d->stride / 4;
+            dest += d->area.width;
             src += d->stride / 2;
         }
     } else if (d->format == SPICE_SURFACE_FMT_16_565) {
-        for (j = miny; j < maxy; j++) {
-            for (i = minx; i < maxx; i++) {
-                dest[i] = CONVERT_0565_TO_0888(src[i]);
+        for (y = 0; y < r->height; y++) {
+            for (x = 0; x < r->width; x++) {
+                dest[x] = CONVERT_0565_TO_0888(src[x]);
             }
 
-            dest += d->stride / 4;
+            dest += d->area.width;
             src += d->stride / 2;
         }
     }
@@ -875,10 +868,7 @@ static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
 
     if (d->mark == 0 || d->data == NULL)
         return false;
-
-    if (!d->ximage) {
-        spicex_image_create(display);
-    }
+    g_return_val_if_fail(d->ximage != NULL, false);
 
     spicex_draw_event(display, cr);
     update_mouse_pointer(display);
@@ -894,10 +884,7 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *expose)
 
     if (d->mark == 0 || d->data == NULL)
         return false;
-
-    if (!d->ximage) {
-        spicex_image_create(display);
-    }
+    g_return_val_if_fail(d->ximage != NULL, false);
 
     spicex_expose_event(display, expose);
     update_mouse_pointer(display);
@@ -1181,10 +1168,10 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
         return true;
 
     gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
-    if (spicex_is_scaled(display) && (d->width != ww || d->height != wh)) {
+    if (spicex_is_scaled(display) && (d->area.width != ww || d->area.height != wh)) {
         double sx, sy;
-        sx = (double)d->width / (double)ww;
-        sy = (double)d->height / (double)wh;
+        sx = (double)d->area.width / (double)ww;
+        sy = (double)d->area.height / (double)wh;
 
         /* Scaling the desktop, so scale the mouse coords by the same.
          * Ratio for the rounding used:
@@ -1208,10 +1195,10 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
 
     switch (d->mouse_mode) {
     case SPICE_MOUSE_MODE_CLIENT:
-        if (motion->x >= 0 && motion->x < d->width &&
-            motion->y >= 0 && motion->y < d->height) {
+        if (motion->x >= 0 && motion->x < d->area.width &&
+            motion->y >= 0 && motion->y < d->area.height) {
             spice_inputs_position(d->inputs,
-                                  motion->x, motion->y,
+                                  motion->x + d->area.x, motion->y + d->area.y,
                                   d->channel_id,
                                   button_mask_gdk_to_spice(motion->state));
         }
@@ -1285,7 +1272,7 @@ static gboolean button_event(GtkWidget *widget, GdkEventButton *button)
         gtk_widget_get_pointer (widget, &x, &y);
         x -= d->mx;
         y -= d->my;
-        if (!(x >= 0 && x < d->width && y >= 0 && y < d->height))
+        if (!(x >= 0 && x < d->area.width && y >= 0 && y < d->area.height))
             return true;
     }
 
@@ -1577,6 +1564,40 @@ static void update_mouse_mode(SpiceChannel *channel, gpointer data)
     cursor_invalidate(display);
 }
 
+static void update_area(SpiceDisplay *display,
+                        gint x, gint y, gint width, gint height)
+{
+    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+    GdkRectangle primary = {
+        .x = 0,
+        .y = 0,
+        .width = d->width,
+        .height = d->height
+    };
+    GdkRectangle area = {
+        .x = x,
+        .y = y,
+        .width = width,
+        .height = height
+    };
+
+    SPICE_DEBUG("update area, primary: %dx%d, area: +%d+%d %dx%d", d->width, d->height, area.x, area.y, area.width, area.height);
+
+    if (!gdk_rectangle_intersect(&primary, &area, &area)) {
+        SPICE_DEBUG("The monitor area is not intersecting primary surface");
+        memset(&d->area, '\0', sizeof(d->area));
+        /* FIXME mark false? */
+        return;
+    }
+
+    spicex_image_destroy(display);
+    d->area = area;
+    spicex_image_create(display);
+    update_size_request(display);
+
+    gtk_widget_queue_draw(GTK_WIDGET(display));
+}
+
 static void primary_create(SpiceChannel *channel, gint format,
                            gint width, gint height, gint stride,
                            gint shmid, gpointer imgdata, gpointer data)
@@ -1586,14 +1607,13 @@ static void primary_create(SpiceChannel *channel, gint format,
 
     d->format = format;
     d->stride = stride;
-    d->shmid  = shmid;
+    d->shmid = shmid;
+    d->width = width;
+    d->height = height;
     d->data_origin = d->data = imgdata;
 
-    if (d->width != width || d->height != height) {
-        d->width  = width;
-        d->height = height;
-        update_size_request(display);
-    }
+    /* by default, display whole surface */
+    update_area(display, 0, 0, width, height);
 }
 
 static void primary_destroy(SpiceChannel *channel, gpointer data)
@@ -1602,26 +1622,40 @@ static void primary_destroy(SpiceChannel *channel, gpointer data)
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
 
     spicex_image_destroy(display);
-    d->format = 0;
     d->width  = 0;
     d->height = 0;
     d->stride = 0;
     d->shmid  = 0;
-    d->data   = 0;
-    d->data_origin = 0;
+    d->data = NULL;
+    d->data_origin = NULL;
 }
 
 static void invalidate(SpiceChannel *channel,
                        gint x, gint y, gint w, gint h, gpointer data)
 {
     SpiceDisplay *display = data;
+    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+    GdkRectangle rect = {
+        .x = x,
+        .y = y,
+        .width = w,
+        .height = h
+    };
 
     if (!gtk_widget_get_window(GTK_WIDGET(display)))
         return;
 
-    if (!do_color_convert(display, x, y, w, h))
+    if (!gdk_rectangle_intersect(&rect, &d->area, &rect))
         return;
 
+    if (d->convert)
+        do_color_convert(display, &rect);
+
+    x = rect.x - d->area.x;
+    y = rect.y - d->area.y;
+    w = rect.width;
+    h = rect.height;
+
     spicex_image_invalidate(display, &x, &y, &w, &h);
     gtk_widget_queue_draw_area(GTK_WIDGET(display),
                                x, y, w, h);
@@ -1706,7 +1740,7 @@ G_GNUC_INTERNAL
 void spice_display_get_scaling(SpiceDisplay *display, double *sx, double *sy)
 {
     SpiceDisplayPrivate *d = display->priv;
-    int fbw = d->width, fbh = d->height;
+    int fbw = d->area.width, fbh = d->area.height;
     int ww, wh;
 
     if (!spicex_is_scaled(display) || !gtk_widget_get_window(GTK_WIDGET(display))) {
@@ -1732,8 +1766,8 @@ static void cursor_invalidate(SpiceDisplay *display)
     spice_display_get_scaling(display, &sx, &sy);
 
     gtk_widget_queue_draw_area(GTK_WIDGET(display),
-                               (d->mouse_guest_x + d->mx - d->mouse_hotspot.x) * sx,
-                               (d->mouse_guest_y + d->my - d->mouse_hotspot.y) * sy,
+                               (d->mouse_guest_x + d->mx - d->mouse_hotspot.x - d->area.x) * sx,
+                               (d->mouse_guest_y + d->my - d->mouse_hotspot.y - d->area.y) * sy,
                                gdk_pixbuf_get_width(d->mouse_pixbuf) * sx,
                                gdk_pixbuf_get_height(d->mouse_pixbuf) * sy);
 }
@@ -2000,16 +2034,14 @@ GdkPixbuf *spice_display_get_pixbuf(SpiceDisplay *display)
 
     g_return_val_if_fail(d != NULL, NULL);
     /* TODO: ensure d->data has been exposed? */
+    g_return_val_if_fail(d->data != NULL, NULL);
 
-    data = g_malloc(d->width * d->height * 3);
+    data = g_malloc(d->area.width * d->area.height * 3);
     src = d->data;
     dest = data;
 
-    if (src == NULL || dest == NULL)
-        return NULL;
-
-    for (y = 0; y < d->height; ++y) {
-        for (x = 0; x < d->width; ++x) {
+    for (y = d->area.y; y < d->area.height; ++y) {
+        for (x = d->area.x; x < d->area.width; ++x) {
           dest[0] = src[x * 4 + 2];
           dest[1] = src[x * 4 + 1];
           dest[2] = src[x * 4 + 0];
@@ -2019,7 +2051,7 @@ GdkPixbuf *spice_display_get_pixbuf(SpiceDisplay *display)
     }
 
     pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, false,
-                                      8, d->width, d->height, d->width * 3,
+                                      8, d->area.width, d->area.height, d->area.width * 3,
                                       (GdkPixbufDestroyNotify)g_free, NULL);
     return pixbuf;
 }
commit 59b7c8a0053f4facb43b16313906353237a5e3db
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jun 20 15:14:38 2012 +0200

    widget: simplify redraw call

diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index e892591..095224c 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -1637,8 +1637,7 @@ static void mark(SpiceChannel *channel, gint mark, gpointer data)
     d->mark = mark;
     spice_main_set_display_enabled(d->main, d->channel_id, d->mark != 0);
     if (mark != 0 && gtk_widget_get_window(GTK_WIDGET(display)))
-        gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(display)),
-                                   NULL, FALSE);
+        gtk_widget_queue_draw(GTK_WIDGET(display));
 }
 
 static void cursor_set(SpiceCursorChannel *channel,
commit 175f80a876b29541c2341b48d1c57796e4186b9b
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Mon Jul 16 17:46:37 2012 +0200

    Update spice-common

diff --git a/spice-common b/spice-common
index 5f44094..735e431 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit 5f4409494066b5f59df58d6207fdbb0441aa9e90
+Subproject commit 735e43167195267e70559101a1b35196b59b84ca


More information about the Spice-commits mailing list