[Spice-commits] 15 commits - gtk/channel-base.c gtk/channel-cursor.c gtk/channel-display.c gtk/channel-display-priv.h gtk/channel-inputs.c gtk/channel-main.c gtk/channel-playback.c gtk/channel-port.c gtk/channel-record.c gtk/channel-smartcard.c gtk/channel-usbredir.c gtk/spice-channel.c gtk/spice-channel-cache.h gtk/spice-channel.h gtk/spice-channel-priv.h gtk/spice-session.c gtk/spice-session-priv.h gtk/spice-util.c spice-common

Marc-André Lureau elmarco at kemper.freedesktop.org
Fri Sep 13 06:15:33 PDT 2013


 gtk/channel-base.c         |   47 +++++++
 gtk/channel-cursor.c       |   99 +++++-----------
 gtk/channel-display-priv.h |    1 
 gtk/channel-display.c      |  272 +++++++++++++++++----------------------------
 gtk/channel-inputs.c       |   31 +----
 gtk/channel-main.c         |   60 ++++-----
 gtk/channel-playback.c     |   42 ++----
 gtk/channel-port.c         |   32 +----
 gtk/channel-record.c       |   36 +----
 gtk/channel-smartcard.c    |   34 +----
 gtk/channel-usbredir.c     |   32 +----
 gtk/spice-channel-cache.h  |  111 ++++++------------
 gtk/spice-channel-priv.h   |   13 --
 gtk/spice-channel.c        |  142 +++++++++--------------
 gtk/spice-channel.h        |    3 
 gtk/spice-session-priv.h   |    5 
 gtk/spice-session.c        |   56 ++-------
 gtk/spice-util.c           |   13 +-
 spice-common               |    2 
 19 files changed, 410 insertions(+), 621 deletions(-)

New commits:
commit c02150d2adb077cb17e790b416600de44435857a
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Sep 10 16:19:32 2013 +0200

    display: unshare the palette cache
    
    As pointed out by Yonit on the ML:
    > (1) the palette cache shouldn't be shared among the display channels. I.e.,
    > there should be one instance per channel, and not one instance in
    > spice-session.

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 0af3859..794f4eb 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -144,6 +144,7 @@ static void spice_display_channel_finalize(GObject *object)
     clear_surfaces(SPICE_CHANNEL(object), FALSE);
     g_hash_table_unref(c->surfaces);
     clear_streams(SPICE_CHANNEL(object));
+    g_clear_pointer(&c->palettes, cache_unref);
 
     if (G_OBJECT_CLASS(spice_display_channel_parent_class)->finalize)
         G_OBJECT_CLASS(spice_display_channel_parent_class)->finalize(object);
@@ -155,7 +156,8 @@ static void spice_display_channel_constructed(GObject *object)
     SpiceSession *s = spice_channel_get_session(SPICE_CHANNEL(object));
 
     g_return_if_fail(s != NULL);
-    spice_session_get_caches(s, &c->images, &c->palettes, &c->glz_window);
+    spice_session_get_caches(s, &c->images, &c->glz_window);
+    c->palettes = cache_new(g_free);
 
     g_return_if_fail(c->glz_window != NULL);
     g_return_if_fail(c->images != NULL);
diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h
index e175281..55fee47 100644
--- a/gtk/spice-session-priv.h
+++ b/gtk/spice-session-priv.h
@@ -147,7 +147,6 @@ void spice_session_set_caches_hints(SpiceSession *session,
                                     uint32_t display_channels_count);
 void spice_session_get_caches(SpiceSession *session,
                               display_cache **images,
-                              display_cache **palettes,
                               SpiceGlzDecoderWindow **glz_window);
 void spice_session_palettes_clear(SpiceSession *session);
 void spice_session_images_clear(SpiceSession *session);
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index c050266..79a13de 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -177,7 +177,6 @@ static void spice_session_init(SpiceSession *session)
 
     ring_init(&s->channels);
     s->images = cache_new((GDestroyNotify)pixman_image_unref);
-    s->palettes = cache_new(g_free);
     s->glz_window = glz_decoder_window_new();
     update_proxy(session, NULL);
 }
@@ -239,7 +238,6 @@ spice_session_finalize(GObject *gobject)
     g_strfreev(s->secure_channels);
 
     g_clear_pointer(&s->images, cache_unref);
-    g_clear_pointer(&s->palettes, cache_unref);
     glz_decoder_window_destroy(s->glz_window);
 
     g_clear_pointer(&s->pubkey, g_byte_array_unref);
@@ -1308,7 +1306,6 @@ static void cache_clear_all(SpiceSession *self)
     SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(self);
 
     cache_clear(s->images);
-    cache_clear(s->palettes);
     glz_decoder_window_clear(s->glz_window);
 }
 
@@ -2087,7 +2084,6 @@ const gchar* spice_session_get_ca_file(SpiceSession *session)
 G_GNUC_INTERNAL
 void spice_session_get_caches(SpiceSession *session,
                               display_cache **images,
-                              display_cache **palettes,
                               SpiceGlzDecoderWindow **glz_window)
 {
     SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(session);
@@ -2096,8 +2092,6 @@ void spice_session_get_caches(SpiceSession *session,
 
     if (images)
         *images = s->images;
-    if (palettes)
-        *palettes = s->palettes;
     if (glz_window)
         *glz_window = s->glz_window;
 }
commit f61cb366af252c20c47f15089ff5ad62080539bb
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Sep 10 16:09:59 2013 +0200

    channel: unref incoming migrate data

diff --git a/gtk/channel-base.c b/gtk/channel-base.c
index abcf9d9..646042d 100644
--- a/gtk/channel-base.c
+++ b/gtk/channel-base.c
@@ -169,12 +169,12 @@ void spice_channel_handle_migrate(SpiceChannel *channel, SpiceMsgIn *in)
         spice_channel_recv_msg(channel, get_msg_handler, &data);
         if (!data) {
             g_critical("expected SPICE_MSG_MIGRATE_DATA, got empty message");
-            return;
+            goto end;
         } else if (spice_header_get_msg_type(data->header, c->use_mini_header) !=
                    SPICE_MSG_MIGRATE_DATA) {
             g_critical("expected SPICE_MSG_MIGRATE_DATA, got %d",
                       spice_header_get_msg_type(data->header, c->use_mini_header));
-            return;
+            goto end;
         }
     }
 
@@ -187,8 +187,11 @@ void spice_channel_handle_migrate(SpiceChannel *channel, SpiceMsgIn *in)
         spice_marshaller_add(out->marshaller, data->data,
                              spice_header_get_msg_size(data->header, c->use_mini_header));
         spice_msg_out_send_internal(out);
-        /* FIXME: who unref in? */
     }
+
+end:
+    if (data)
+        spice_msg_in_unref(data);
 }
 
 
commit 159c6ebf2cf3034e3ffb85b9b5fc1a4cec27dc99
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Sun Sep 8 20:25:40 2013 +0200

    gtk: simplify spice_channel_recv_msg
    
    Use of coroutines allow to simplify spice_channel_recv_msg(), it doesn't
    need to keep current reading state, it can rely on the coroutine stack
    for that.

diff --git a/gtk/channel-base.c b/gtk/channel-base.c
index 76d681a..abcf9d9 100644
--- a/gtk/channel-base.c
+++ b/gtk/channel-base.c
@@ -187,6 +187,7 @@ void spice_channel_handle_migrate(SpiceChannel *channel, SpiceMsgIn *in)
         spice_marshaller_add(out->marshaller, data->data,
                              spice_header_get_msg_size(data->header, c->use_mini_header));
         spice_msg_out_send_internal(out);
+        /* FIXME: who unref in? */
     }
 }
 
diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index 1f29c23..80a6563 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -59,7 +59,7 @@ struct _SpiceMsgIn {
     SpiceChannel          *channel;
     uint8_t               header[MAX_SPICE_DATA_HEADER_SIZE];
     uint8_t               *data;
-    int                   hpos,dpos;
+    int                   dpos;
     uint8_t               *parsed;
     size_t                psize;
     message_destructor_t  pfree;
@@ -122,7 +122,6 @@ struct _SpiceChannelPrivate {
     SpiceLinkReply*             peer_msg;
     int                         peer_pos;
 
-    SpiceMsgIn                  *msg_in;
     int                         message_ack_window;
     int                         message_ack_count;
 
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 14c526c..b01b820 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -1770,43 +1770,26 @@ void spice_channel_recv_msg(SpiceChannel *channel,
 {
     SpiceChannelPrivate *c = channel->priv;
     SpiceMsgIn *in;
-    int header_size;
     int msg_size;
     int msg_type;
     int sub_list_offset = 0;
-    int rc;
 
-    if (!c->msg_in) {
-        c->msg_in = spice_msg_in_new(channel);
-    }
-    in = c->msg_in;
-    header_size = spice_header_get_header_size(c->use_mini_header);
+    in = spice_msg_in_new(channel);
 
     /* receive message */
-    if (in->hpos < header_size) {
-        rc = spice_channel_read(channel, in->header + in->hpos,
-                                header_size - in->hpos);
-        if (rc < 0) {
-            g_critical("recv hdr: %s", strerror(errno));
-            return;
-        }
-        in->hpos += rc;
-        if (in->hpos < header_size)
-            return;
-        in->data = spice_malloc(spice_header_get_msg_size(in->header, c->use_mini_header));
-    }
+    spice_channel_read(channel, in->header,
+                       spice_header_get_header_size(c->use_mini_header));
+    if (c->has_error)
+        goto end;
+
     msg_size = spice_header_get_msg_size(in->header, c->use_mini_header);
-    if (in->dpos < msg_size) {
-        rc = spice_channel_read(channel, in->data + in->dpos,
-                                msg_size - in->dpos);
-        if (rc < 0) {
-            g_critical("recv msg: %s", strerror(errno));
-            return;
-        }
-        in->dpos += rc;
-        if (in->dpos < msg_size)
-            return;
-    }
+    /* FIXME: do not allow others to take ref on in, and use realloc here?
+     * this would avoid malloc/free on each message?
+     */
+    in->data = spice_malloc(msg_size);
+    spice_channel_read(channel, in->data, msg_size);
+    if (c->has_error)
+        goto end;
 
     msg_type = spice_header_get_msg_type(in->header, c->use_mini_header);
     sub_list_offset = spice_header_get_msg_sub_list(in->header, c->use_mini_header);
@@ -1829,7 +1812,7 @@ void spice_channel_recv_msg(SpiceChannel *channel,
             if (sub_in->parsed == NULL) {
                 g_critical("failed to parse sub-message: %s type %d",
                            c->name, spice_header_get_msg_type(sub_in->header, c->use_mini_header));
-                return;
+                goto end;
             }
             msg_handler(channel, sub_in, data);
             spice_msg_in_unref(sub_in);
@@ -1851,7 +1834,7 @@ void spice_channel_recv_msg(SpiceChannel *channel,
     }
 
     /* parse message */
-    in->parsed = c->parser(in->data, in->data + in->dpos, msg_type,
+    in->parsed = c->parser(in->data, in->data + msg_size, msg_type,
                            c->peer_hdr.minor_version, &in->psize, &in->pfree);
     if (in->parsed == NULL) {
         g_critical("failed to parse message: %s type %d",
@@ -1860,7 +1843,6 @@ void spice_channel_recv_msg(SpiceChannel *channel,
     }
 
     /* process message */
-    c->msg_in = NULL; /* the function is reentrant, reset state */
     /* spice_msg_in_hexdump(in); */
     msg_handler(channel, in, data);
 
@@ -1869,8 +1851,6 @@ end:
      * to c->in_serial (the server can sometimes skip serials) */
     c->last_message_serial = spice_header_get_in_msg_serial(in);
     c->in_serial++;
-    /* release message */
-    c->msg_in = NULL;
     spice_msg_in_unref(in);
 }
 
commit 1fcaaa15f8aca362f9e6afc87fb43cfbccf6ff62
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Sun Sep 8 19:48:09 2013 +0200

    gtk: use slices for frequently allocated objects

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 45d31c5..0af3859 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -657,7 +657,7 @@ static void destroy_surface(gpointer data)
     display_surface *surface = data;
 
     destroy_canvas(surface);
-    free(surface);
+    g_slice_free(display_surface, surface);
 }
 
 static void spice_display_channel_init(SpiceDisplayChannel *channel)
@@ -1687,7 +1687,7 @@ static void display_handle_surface_create(SpiceChannel *channel, SpiceMsgIn *in)
 {
     SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
     SpiceMsgSurfaceCreate *create = spice_msg_in_parsed(in);
-    display_surface *surface = spice_new0(display_surface, 1);
+    display_surface *surface = g_slice_new0(display_surface);
 
     surface->surface_id = create->surface_id;
     surface->format = create->format;
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index f6a691e..14c526c 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -462,7 +462,7 @@ SpiceMsgIn *spice_msg_in_new(SpiceChannel *channel)
 
     g_return_val_if_fail(channel != NULL, NULL);
 
-    in = spice_new0(SpiceMsgIn, 1);
+    in = g_slice_new0(SpiceMsgIn);
     in->refcount = 1;
     in->channel  = channel;
 
@@ -510,7 +510,7 @@ void spice_msg_in_unref(SpiceMsgIn *in)
     } else {
         free(in->data);
     }
-    free(in);
+    g_slice_free(SpiceMsgIn, in);
 }
 
 G_GNUC_INTERNAL
@@ -615,7 +615,7 @@ SpiceMsgOut *spice_msg_out_new(SpiceChannel *channel, int type)
 
     g_return_val_if_fail(c != NULL, NULL);
 
-    out = spice_new0(SpiceMsgOut, 1);
+    out = g_slice_new0(SpiceMsgOut);
     out->refcount = 1;
     out->channel  = channel;
     out->ro_check = msg_check_read_only(c->channel_type, type);
@@ -651,7 +651,7 @@ void spice_msg_out_unref(SpiceMsgOut *out)
     if (out->refcount > 0)
         return;
     spice_marshaller_destroy(out->marshaller);
-    free(out);
+    g_slice_free(SpiceMsgOut, out);
 }
 
 /* system context */
commit ed877341a11bca8ebea7e46080f48ec4c4f5c638
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Sun Sep 8 18:23:40 2013 +0200

    gtk: use GHashTable in display_cache
    
    The cache code isn't very quick, it shows up in profilers.  Using
    GHashTable allows to simplify the code while making it faster.

diff --git a/gtk/channel-cursor.c b/gtk/channel-cursor.c
index 5d2db84..0443b9f 100644
--- a/gtk/channel-cursor.c
+++ b/gtk/channel-cursor.c
@@ -51,7 +51,7 @@ struct display_cursor {
 };
 
 struct _SpiceCursorChannelPrivate {
-    display_cache               cursors;
+    display_cache               *cursors;
     gboolean                    init_done;
 };
 
@@ -66,7 +66,6 @@ enum {
 
 static guint signals[SPICE_CURSOR_LAST_SIGNAL];
 
-static void delete_cursor_all(SpiceChannel *channel);
 static display_cursor * display_cursor_ref(display_cursor *cursor);
 static void display_cursor_unref(display_cursor *cursor);
 static void channel_set_handlers(SpiceChannelClass *klass);
@@ -81,12 +80,14 @@ static void spice_cursor_channel_init(SpiceCursorChannel *channel)
 
     c = channel->priv = SPICE_CURSOR_CHANNEL_GET_PRIVATE(channel);
 
-    cache_init(&c->cursors, "cursor");
+    c->cursors = cache_new((GDestroyNotify)display_cursor_unref);
 }
 
 static void spice_cursor_channel_finalize(GObject *obj)
 {
-    delete_cursor_all(SPICE_CHANNEL(obj));
+    SpiceCursorChannelPrivate *c = SPICE_CURSOR_CHANNEL_GET_PRIVATE(obj);
+
+    g_clear_pointer(&c->cursors, cache_unref);
 
     if (G_OBJECT_CLASS(spice_cursor_channel_parent_class)->finalize)
         G_OBJECT_CLASS(spice_cursor_channel_parent_class)->finalize(obj);
@@ -97,7 +98,7 @@ static void spice_cursor_channel_reset(SpiceChannel *channel, gboolean migrating
 {
     SpiceCursorChannelPrivate *c = SPICE_CURSOR_CHANNEL(channel)->priv;
 
-    delete_cursor_all(channel);
+    cache_clear(c->cursors);
     c->init_done = FALSE;
 
     SPICE_CHANNEL_CLASS(spice_cursor_channel_parent_class)->channel_reset(channel, migrating);
@@ -359,7 +360,6 @@ static display_cursor *set_cursor(SpiceChannel *channel, SpiceCursor *scursor)
 {
     SpiceCursorChannelPrivate *c = SPICE_CURSOR_CHANNEL(channel)->priv;
     SpiceCursorHeader *hdr = &scursor->header;
-    display_cache_item *item;
     display_cursor *cursor;
     size_t size;
     gint i, pix_mask, pix;
@@ -378,9 +378,9 @@ static display_cursor *set_cursor(SpiceChannel *channel, SpiceCursor *scursor)
                   hdr->width, hdr->height);
 
     if (scursor->flags & SPICE_CURSOR_FLAGS_FROM_CACHE) {
-        item = cache_find(&c->cursors, hdr->unique);
-        g_return_val_if_fail(item != NULL, NULL);
-        return display_cursor_ref(item->ptr);
+        cursor = cache_find(c->cursors, hdr->unique);
+        g_return_val_if_fail(cursor != NULL, NULL);
+        return display_cursor_ref(cursor);
     }
 
     g_return_val_if_fail(scursor->data_size != 0, NULL);
@@ -453,36 +453,12 @@ static display_cursor *set_cursor(SpiceChannel *channel, SpiceCursor *scursor)
 
 cache_add:
     if (cursor && (scursor->flags & SPICE_CURSOR_FLAGS_CACHE_ME)) {
-        display_cursor_ref(cursor);
-        item = cache_add(&c->cursors, hdr->unique);
-        item->ptr = cursor;
+        cache_add(c->cursors, hdr->unique, display_cursor_ref(cursor));
     }
 
     return cursor;
 }
 
-static void delete_cursor_one(SpiceChannel *channel, display_cache_item *item)
-{
-    SpiceCursorChannelPrivate *c = SPICE_CURSOR_CHANNEL(channel)->priv;
-
-    display_cursor_unref((display_cursor*)item->ptr);
-    cache_del(&c->cursors, item);
-}
-
-static void delete_cursor_all(SpiceChannel *channel)
-{
-    SpiceCursorChannelPrivate *c = SPICE_CURSOR_CHANNEL(channel)->priv;
-    display_cache_item *item;
-
-    for (;;) {
-        item = cache_get_lru(&c->cursors);
-        if (item == NULL) {
-            return;
-        }
-        delete_cursor_one(channel, item);
-    }
-}
-
 /* coroutine context */
 static void emit_cursor_set(SpiceChannel *channel, display_cursor *cursor)
 {
@@ -502,7 +478,7 @@ static void cursor_handle_init(SpiceChannel *channel, SpiceMsgIn *in)
 
     g_return_if_fail(c->init_done == FALSE);
 
-    delete_cursor_all(channel);
+    cache_clear(c->cursors);
     cursor = set_cursor(channel, &init->cursor);
     c->init_done = TRUE;
     if (cursor)
@@ -520,7 +496,7 @@ static void cursor_handle_reset(SpiceChannel *channel, SpiceMsgIn *in)
 
     CHANNEL_DEBUG(channel, "%s, init_done: %d", __FUNCTION__, c->init_done);
 
-    delete_cursor_all(channel);
+    cache_clear(c->cursors);
     emit_main_context(channel, SPICE_CURSOR_RESET);
     c->init_done = FALSE;
 }
@@ -584,18 +560,18 @@ static void cursor_handle_inval_one(SpiceChannel *channel, SpiceMsgIn *in)
 {
     SpiceCursorChannelPrivate *c = SPICE_CURSOR_CHANNEL(channel)->priv;
     SpiceMsgDisplayInvalOne *zap = spice_msg_in_parsed(in);
-    display_cache_item *item;
 
     g_return_if_fail(c->init_done == TRUE);
 
-    item = cache_find(&c->cursors, zap->id);
-    delete_cursor_one(channel, item);
+    cache_remove(c->cursors, zap->id);
 }
 
 /* coroutine context */
 static void cursor_handle_inval_all(SpiceChannel *channel, SpiceMsgIn *in)
 {
-    delete_cursor_all(channel);
+    SpiceCursorChannelPrivate *c = SPICE_CURSOR_CHANNEL(channel)->priv;
+
+    cache_clear(c->cursors);
 }
 
 static void channel_set_handlers(SpiceChannelClass *klass)
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index c8061ff..45d31c5 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -486,16 +486,8 @@ static void image_put(SpiceImageCache *cache, uint64_t id, pixman_image_t *image
 {
     SpiceDisplayChannelPrivate *c =
         SPICE_CONTAINEROF(cache, SpiceDisplayChannelPrivate, image_cache);
-    display_cache_item *item;
 
-    item = cache_find(c->images, id);
-    if (item) {
-        cache_ref(item);
-        return;
-    }
-
-    item = cache_add(c->images, id);
-    item->ptr = pixman_image_ref(image);
+    cache_add(c->images, id, pixman_image_ref(image));
 }
 
 typedef struct _WaitImageData
@@ -508,18 +500,16 @@ typedef struct _WaitImageData
 
 static gboolean wait_image(gpointer data)
 {
-    display_cache_item *item;
+    gboolean lossy;
     WaitImageData *wait = data;
     SpiceDisplayChannelPrivate *c =
         SPICE_CONTAINEROF(wait->cache, SpiceDisplayChannelPrivate, image_cache);
+    pixman_image_t *image = cache_find_lossy(c->images, wait->id, &lossy);
 
-    item = cache_find(c->images, wait->id);
-    if (item == NULL ||
-        (item->lossy && !wait->lossy))
+    if (!image || (lossy && !wait->lossy))
         return FALSE;
 
-    cache_used(c->images, item);
-    wait->image = pixman_image_ref(item->ptr);
+    wait->image = pixman_image_ref(image);
 
     return TRUE;
 }
@@ -538,58 +528,33 @@ static pixman_image_t *image_get(SpiceImageCache *cache, uint64_t id)
     return wait.image;
 }
 
-static void image_remove(SpiceImageCache *cache, uint64_t id)
-{
-    SpiceDisplayChannelPrivate *c =
-        SPICE_CONTAINEROF(cache, SpiceDisplayChannelPrivate, image_cache);
-    display_cache_item *item;
-
-    item = cache_find(c->images, id);
-    g_return_if_fail(item != NULL);
-    if (cache_unref(item)) {
-        pixman_image_unref(item->ptr);
-        cache_del(c->images, item);
-    }
-}
-
 static void palette_put(SpicePaletteCache *cache, SpicePalette *palette)
 {
     SpiceDisplayChannelPrivate *c =
         SPICE_CONTAINEROF(cache, SpiceDisplayChannelPrivate, palette_cache);
-    display_cache_item *item;
 
-    item = cache_add(c->palettes, palette->unique);
-    item->ptr = g_memdup(palette, sizeof(SpicePalette) +
-                         palette->num_ents * sizeof(palette->ents[0]));
+    cache_add(c->palettes, palette->unique,
+              g_memdup(palette, sizeof(SpicePalette) +
+                       palette->num_ents * sizeof(palette->ents[0])));
 }
 
 static SpicePalette *palette_get(SpicePaletteCache *cache, uint64_t id)
 {
     SpiceDisplayChannelPrivate *c =
         SPICE_CONTAINEROF(cache, SpiceDisplayChannelPrivate, palette_cache);
-    display_cache_item *item;
 
-    item = cache_find(c->palettes, id);
-    if (item) {
-        cache_ref(item);
-        return item->ptr;
-    }
-    return NULL;
+    /* here the returned pointer is weak, no ref given to caller.  it
+     * seems spice canvas usage is exclusively temporary, so it's ok
+     * (for now) */
+    return cache_find(c->palettes, id);
 }
 
 static void palette_remove(SpicePaletteCache *cache, uint32_t id)
 {
     SpiceDisplayChannelPrivate *c =
         SPICE_CONTAINEROF(cache, SpiceDisplayChannelPrivate, palette_cache);
-    display_cache_item *item;
 
-    item = cache_find(c->palettes, id);
-    if (item) {
-        if (cache_unref(item)) {
-            g_free(item->ptr);
-            cache_del(c->palettes, item);
-        }
-    }
+    cache_remove(c->palettes, id);
 }
 
 static void palette_release(SpicePaletteCache *cache, SpicePalette *palette)
@@ -603,30 +568,18 @@ static void image_put_lossy(SpiceImageCache *cache, uint64_t id,
 {
     SpiceDisplayChannelPrivate *c =
         SPICE_CONTAINEROF(cache, SpiceDisplayChannelPrivate, image_cache);
-    display_cache_item *item;
 
 #ifndef NDEBUG
     g_warn_if_fail(cache_find(c->images, id) == NULL);
 #endif
 
-    item = cache_add(c->images, id);
-    item->ptr = pixman_image_ref(surface);
-    item->lossy = TRUE;
+    cache_add_lossy(c->images, id, pixman_image_ref(surface), TRUE);
 }
 
 static void image_replace_lossy(SpiceImageCache *cache, uint64_t id,
                                 pixman_image_t *surface)
 {
-    SpiceDisplayChannelPrivate *c =
-        SPICE_CONTAINEROF(cache, SpiceDisplayChannelPrivate, image_cache);
-    display_cache_item *item;
-
-    item = cache_find(c->images, id);
-    g_return_if_fail(item != NULL);
-
-    pixman_image_unref(item->ptr);
-    item->ptr = pixman_image_ref(surface);
-    item->lossy = FALSE;
+    image_put(cache, id, surface);
 }
 
 static pixman_image_t* image_get_lossless(SpiceImageCache *cache, uint64_t id)
@@ -968,7 +921,7 @@ static void display_handle_reset(SpiceChannel *channel, SpiceMsgIn *in)
     if (surface != NULL)
         surface->canvas->ops->clear(surface->canvas);
 
-    spice_session_palettes_clear(spice_channel_get_session(channel));
+    cache_clear(c->palettes);
 
     c->mark = FALSE;
     emit_main_context(channel, SPICE_DISPLAY_MARK, FALSE);
@@ -997,9 +950,12 @@ static void display_handle_inv_list(SpiceChannel *channel, SpiceMsgIn *in)
     int i;
 
     for (i = 0; i < list->count; i++) {
+        guint64 id = list->resources[i].id;
+
         switch (list->resources[i].type) {
         case SPICE_RES_TYPE_PIXMAP:
-            image_remove(&c->image_cache, list->resources[i].id);
+            if (!cache_remove(c->images, id))
+                SPICE_DEBUG("fail to remove image %" G_GUINT64_FORMAT, id);
             break;
         default:
             g_return_if_reached();
@@ -1011,9 +967,10 @@ static void display_handle_inv_list(SpiceChannel *channel, SpiceMsgIn *in)
 /* coroutine context */
 static void display_handle_inv_pixmap_all(SpiceChannel *channel, SpiceMsgIn *in)
 {
-    spice_channel_handle_wait_for_channels(channel, in);
+    SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
 
-    spice_session_images_clear(spice_channel_get_session(channel));
+    spice_channel_handle_wait_for_channels(channel, in);
+    cache_clear(c->images);
 }
 
 /* coroutine context */
@@ -1028,7 +985,9 @@ static void display_handle_inv_palette(SpiceChannel *channel, SpiceMsgIn *in)
 /* coroutine context */
 static void display_handle_inv_palette_all(SpiceChannel *channel, SpiceMsgIn *in)
 {
-    spice_session_palettes_clear(spice_channel_get_session(channel));
+    SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
+
+    cache_clear(c->palettes);
 }
 
 /* ------------------------------------------------------------------ */
diff --git a/gtk/spice-channel-cache.h b/gtk/spice-channel-cache.h
index 3f39877..17775e6 100644
--- a/gtk/spice-channel-cache.h
+++ b/gtk/spice-channel-cache.h
@@ -25,109 +25,80 @@
 G_BEGIN_DECLS
 
 typedef struct display_cache_item {
-    RingItem                    hash_link;
-    RingItem                    lru_link;
-    uint64_t                    id;
-    uint32_t                    refcount;
-    void                        *ptr;
+    guint64                     id;
     gboolean                    lossy;
 } display_cache_item;
 
-typedef struct display_cache {
-    const char                  *name;
-    Ring                        hash[64];
-    Ring                        lru;
-    int                         nitems;
-} display_cache;
+typedef GHashTable display_cache;
 
-static inline void cache_init(display_cache *cache, const char *name)
+static inline display_cache_item* cache_item_new(guint64 id, gboolean lossy)
 {
-    int i;
+    display_cache_item *self = g_slice_new(display_cache_item);
+    self->id = id;
+    self->lossy = lossy;
+    return self;
+}
 
-    cache->name = name;
-    ring_init(&cache->lru);
-    for (i = 0; i < SPICE_N_ELEMENTS(cache->hash); i++) {
-        ring_init(&cache->hash[i]);
-    }
+static inline void cache_item_free(display_cache_item *self)
+{
+    g_slice_free(display_cache_item, self);
 }
 
-static inline Ring *cache_head(display_cache *cache, uint64_t id)
+static inline display_cache* cache_new(GDestroyNotify value_destroy)
 {
-    return &cache->hash[id % SPICE_N_ELEMENTS(cache->hash)];
+    GHashTable* self;
+
+    self = g_hash_table_new_full(g_int64_hash, g_int64_equal,
+                                 (GDestroyNotify)cache_item_free,
+                                 value_destroy);
+
+    return self;
 }
 
-static inline void cache_used(display_cache *cache, display_cache_item *item)
+static inline gpointer cache_find(display_cache *cache, uint64_t id)
 {
-    ring_remove(&item->lru_link);
-    ring_add(&cache->lru, &item->lru_link);
+    return g_hash_table_lookup(cache, &id);
 }
 
-static inline display_cache_item *cache_get_lru(display_cache *cache)
+static inline gpointer cache_find_lossy(display_cache *cache, uint64_t id, gboolean *lossy)
 {
+    gpointer value;
     display_cache_item *item;
-    RingItem *ring;
 
-    if (ring_is_empty(&cache->lru))
+    if (!g_hash_table_lookup_extended(cache, &id, (gpointer*)&item, &value))
         return NULL;
-    ring = ring_get_tail(&cache->lru);
-    item = SPICE_CONTAINEROF(ring, display_cache_item, lru_link);
-    return item;
-}
 
-static inline display_cache_item *cache_find(display_cache *cache, uint64_t id)
-{
-    display_cache_item *item;
-    RingItem *ring;
-    Ring *head;
-
-    head = cache_head(cache, id);
-    for (ring = ring_get_head(head); ring != NULL; ring = ring_next(head, ring)) {
-        item = SPICE_CONTAINEROF(ring, display_cache_item, hash_link);
-        if (item->id == id) {
-            return item;
-        }
-    }
-
-    SPICE_DEBUG("%s: %s %" PRIx64 " [not found]", __FUNCTION__,
-            cache->name, id);
-    return NULL;
+    *lossy = item->lossy;
+
+    return value;
 }
 
-static inline display_cache_item *cache_add(display_cache *cache, uint64_t id)
+static inline void cache_add_lossy(display_cache *cache, uint64_t id,
+                                   gpointer value, gboolean lossy)
 {
-    display_cache_item *item;
+    display_cache_item *item = cache_item_new(id, lossy);
 
-    item = spice_new0(display_cache_item, 1);
-    item->id = id;
-    item->refcount = 1;
-    ring_add(cache_head(cache, item->id), &item->hash_link);
-    ring_add(&cache->lru, &item->lru_link);
-    cache->nitems++;
+    g_hash_table_replace(cache, item, value);
+}
 
-    SPICE_DEBUG("%s: %s %" PRIx64 " (%d)", __FUNCTION__,
-            cache->name, id, cache->nitems);
-    return item;
+static inline void cache_add(display_cache *cache, uint64_t id, gpointer value)
+{
+    cache_add_lossy(cache, id, value, FALSE);
 }
 
-static inline void cache_del(display_cache *cache, display_cache_item *item)
+static inline gboolean cache_remove(display_cache *cache, uint64_t id)
 {
-    SPICE_DEBUG("%s: %s %" PRIx64, __FUNCTION__,
-            cache->name, item->id);
-    ring_remove(&item->hash_link);
-    ring_remove(&item->lru_link);
-    free(item);
-    cache->nitems--;
+    return g_hash_table_remove(cache, &id);
 }
 
-static inline void cache_ref(display_cache_item *item)
+static inline void cache_clear(display_cache *cache)
 {
-    item->refcount++;
+    g_hash_table_remove_all(cache);
 }
 
-static inline int cache_unref(display_cache_item *item)
+static inline void cache_unref(display_cache *cache)
 {
-    item->refcount--;
-    return item->refcount == 0;
+    g_hash_table_unref(cache);
 }
 
 G_END_DECLS
diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h
index 5ed48dd..e175281 100644
--- a/gtk/spice-session-priv.h
+++ b/gtk/spice-session-priv.h
@@ -92,8 +92,8 @@ struct _SpiceSessionPrivate {
     guint             after_main_init;
     gboolean          migration_copy;
 
-    display_cache     images;
-    display_cache     palettes;
+    display_cache     *images;
+    display_cache     *palettes;
     SpiceGlzDecoderWindow *glz_window;
     int               images_cache_size;
     int               glz_window_size;
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index a9435f4..c050266 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -176,8 +176,8 @@ static void spice_session_init(SpiceSession *session)
     g_free(channels);
 
     ring_init(&s->channels);
-    cache_init(&s->images, "image");
-    cache_init(&s->palettes, "palette");
+    s->images = cache_new((GDestroyNotify)pixman_image_unref);
+    s->palettes = cache_new(g_free);
     s->glz_window = glz_decoder_window_new();
     update_proxy(session, NULL);
 }
@@ -219,35 +219,6 @@ spice_session_dispose(GObject *gobject)
         G_OBJECT_CLASS(spice_session_parent_class)->dispose(gobject);
 }
 
-G_GNUC_INTERNAL
-void spice_session_palettes_clear(SpiceSession *session)
-{
-    SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(session);
-    g_return_if_fail(s != NULL);
-
-    for (;;) {
-        display_cache_item *item = cache_get_lru(&s->palettes);
-        if (item == NULL)
-            break;
-        cache_del(&s->palettes, item);
-    }
-}
-
-G_GNUC_INTERNAL
-void spice_session_images_clear(SpiceSession *session)
-{
-    SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(session);
-    g_return_if_fail(s != NULL);
-
-    for (;;) {
-        display_cache_item *item = cache_get_lru(&s->images);
-        if (item == NULL)
-            break;
-        pixman_image_unref(item->ptr);
-        cache_del(&s->images, item);
-    }
-}
-
 static void
 spice_session_finalize(GObject *gobject)
 {
@@ -267,8 +238,8 @@ spice_session_finalize(GObject *gobject)
     g_strfreev(s->disable_effects);
     g_strfreev(s->secure_channels);
 
-    spice_session_palettes_clear(session);
-    spice_session_images_clear(session);
+    g_clear_pointer(&s->images, cache_unref);
+    g_clear_pointer(&s->palettes, cache_unref);
     glz_decoder_window_destroy(s->glz_window);
 
     g_clear_pointer(&s->pubkey, g_byte_array_unref);
@@ -1332,6 +1303,15 @@ gboolean spice_session_get_client_provided_socket(SpiceSession *session)
     return s->client_provided_sockets;
 }
 
+static void cache_clear_all(SpiceSession *self)
+{
+    SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(self);
+
+    cache_clear(s->images);
+    cache_clear(s->palettes);
+    glz_decoder_window_clear(s->glz_window);
+}
+
 G_GNUC_INTERNAL
 void spice_session_switching_disconnect(SpiceSession *self)
 {
@@ -1353,9 +1333,7 @@ void spice_session_switching_disconnect(SpiceSession *self)
 
     g_warn_if_fail(!ring_is_empty(&s->channels)); /* ring_get_length() == 1 */
 
-    spice_session_palettes_clear(self);
-    spice_session_images_clear(self);
-    glz_decoder_window_clear(s->glz_window);
+    cache_clear_all(self);
 }
 
 G_GNUC_INTERNAL
@@ -1561,9 +1539,7 @@ void spice_session_migrate_end(SpiceSession *self)
         }
     }
 
-    spice_session_palettes_clear(self);
-    spice_session_images_clear(self);
-    glz_decoder_window_clear(s->glz_window);
+    cache_clear_all(self);
 
     /* send MIGRATE_END to target */
     out = spice_msg_out_new(s->cmain, SPICE_MSGC_MAIN_MIGRATE_END);
@@ -2119,9 +2095,9 @@ void spice_session_get_caches(SpiceSession *session,
     g_return_if_fail(s != NULL);
 
     if (images)
-        *images = &s->images;
+        *images = s->images;
     if (palettes)
-        *palettes = &s->palettes;
+        *palettes = s->palettes;
     if (glz_window)
         *glz_window = s->glz_window;
 }
commit 07a7ef001686c626ab9400876cf7117ab74abb62
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Fri Sep 6 20:52:45 2013 +0200

    channel: do not reenter the mainloop at every iteration
    
    The current coroutine channel_iterate() creates a GSource for the socket
    reenters the mainloop waiting for IO condition. This is really heavy
    usage of mainloop showing up in system time and user space
    profiling (10% of CPU time spent in mainloop overhead). Instead flush
    all pending input messages before switching context and reentering main
    loop.
    
    This is the kind of results I get with a replay:
    
    before:
    
           2179,440455 task-clock                #    0,629 CPUs utilized
                 3 580 context-switches          #    0,002 M/sec
                   207 cpu-migrations            #    0,095 K/sec
                48 909 page-faults               #    0,022 M/sec
         7 362 442 301 cycles                    #    3,378 GHz
         5 256 957 520 stalled-cycles-frontend   #   71,40% frontend cycles
                 idle
       <not supported> stalled-cycles-backend
         5 460 266 981 instructions              #    0,74  insns per cycle
                                                 #    0,96  stalled cycles
                 per insn
           982 749 523 branches                  #  450,918 M/sec
            20 745 453 branch-misses             #    2,11% of all branches
    
           3,463422087 seconds time elapsed
    
    after:
    
           1741,651383 task-clock                #    0,522 CPUs utilized
                 5 093 context-switches          #    0,003 M/sec
                   137 cpu-migrations            #    0,079 K/sec
                49 033 page-faults               #    0,028 M/sec
         5 874 567 974 cycles                    #    3,373 GHz
         4 247 059 288 stalled-cycles-frontend   #   72,30% frontend cycles
                 idle
       <not supported> stalled-cycles-backend
         4 419 666 346 instructions              #    0,75  insns per cycle
                                                 #    0,96  stalled cycles
                 per insn
           776 972 366 branches                  #  446,112 M/sec
            17 862 170 branch-misses             #    2,30% of all branches
    
           3,337472053 seconds time elapsed

diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 55c8325..f6a691e 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2080,10 +2080,24 @@ static void spice_channel_iterate_read(SpiceChannel *channel)
 {
     SpiceChannelPrivate *c = channel->priv;
 
-    g_return_if_fail(c->state != SPICE_CHANNEL_STATE_MIGRATING);
+    g_coroutine_socket_wait(&c->coroutine, c->sock, G_IO_IN);
+
+    /* treat all incoming data (block on message completion) */
+    while (!c->has_error &&
+           c->state != SPICE_CHANNEL_STATE_MIGRATING &&
+           g_socket_condition_check(c->sock, G_IO_IN) & G_IO_IN) {
+
+        do
+            spice_channel_recv_msg(channel,
+                                   (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL);
+#if HAVE_SASL
+            /* flush the sasl buffer too */
+        while (c->sasl_decoded != NULL);
+#else
+        while (FALSE);
+#endif
+    }
 
-    spice_channel_recv_msg(channel,
-        (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL);
 }
 
 static gboolean wait_migration(gpointer data)
@@ -2105,37 +2119,20 @@ static gboolean spice_channel_iterate(SpiceChannel *channel)
     SpiceChannelPrivate *c = channel->priv;
     GIOCondition ret;
 
-    do {
-        if (c->state == SPICE_CHANNEL_STATE_MIGRATING &&
-            !g_coroutine_condition_wait(&c->coroutine, wait_migration, channel))
-                CHANNEL_DEBUG(channel, "migration wait cancelled");
-
-        if (c->has_error) {
-            CHANNEL_DEBUG(channel, "channel has error, breaking loop");
-            return FALSE;
-        }
-
-        SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);
-
-        ret = g_coroutine_socket_wait(&c->coroutine, c->sock,
-            c->state != SPICE_CHANNEL_STATE_MIGRATING ? G_IO_IN : 0);
-
-#ifdef WIN32
-        /* FIXME: windows gsocket is buggy, it doesn't return correct condition... */
-        ret = g_socket_condition_check(c->sock, G_IO_IN);
-#endif
-    } while (ret == 0); /* ret == 0 means no IO condition, but woken up */
+    if (c->state == SPICE_CHANNEL_STATE_MIGRATING &&
+        !g_coroutine_condition_wait(&c->coroutine, wait_migration, channel))
+        CHANNEL_DEBUG(channel, "migration wait cancelled");
 
-    if (ret & G_IO_IN) {
-        do
-            SPICE_CHANNEL_GET_CLASS(channel)->iterate_read(channel);
-#if HAVE_SASL
-        while (c->sasl_decoded != NULL);
-#else
-        while (FALSE);
-#endif
+    if (c->has_error) {
+        CHANNEL_DEBUG(channel, "channel has error, breaking loop");
+        return FALSE;
     }
 
+    /* flush any pending write and read */
+    SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);
+    SPICE_CHANNEL_GET_CLASS(channel)->iterate_read(channel);
+
+    ret = g_socket_condition_check(c->sock, G_IO_IN | G_IO_ERR | G_IO_HUP);
     if (c->state > SPICE_CHANNEL_STATE_CONNECTING &&
         ret & (G_IO_ERR|G_IO_HUP)) {
         SPICE_DEBUG("got socket error: %d", ret);
commit 94ed29c5cf903d265b7db04a3ec0968ec55ffff1
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Sat Aug 31 18:46:07 2013 +0200

    util: avoid calling getenv for every SPICE_DEBUG
    
    That doesn't seem to really improve performance so much,
    but that' s what we should do probably.

diff --git a/gtk/spice-util.c b/gtk/spice-util.c
index e02fc4d..4372f28 100644
--- a/gtk/spice-util.c
+++ b/gtk/spice-util.c
@@ -39,7 +39,7 @@
  * Various functions for debugging and informational purposes.
  */
 
-static gboolean debugFlag = FALSE;
+static GOnce debug_once = G_ONCE_INIT;
 
 /**
  * spice_util_set_debug:
@@ -61,12 +61,19 @@ void spice_util_set_debug(gboolean enabled)
         }
     }
 #endif
-    debugFlag = enabled;
+    debug_once.retval = GINT_TO_POINTER(enabled);
+}
+
+static gpointer getenv_debug(gpointer data)
+{
+    return GINT_TO_POINTER(g_getenv("SPICE_DEBUG") != NULL);
 }
 
 gboolean spice_util_get_debug(void)
 {
-    return debugFlag || g_getenv("SPICE_DEBUG") != NULL;
+    g_once(&debug_once, getenv_debug, NULL);
+
+    return GPOINTER_TO_INT(debug_once.retval);
 }
 
 /**
commit 28a8b5bd781038e452890a69fd92a92ea74870ac
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Aug 21 20:35:07 2013 +0200

    display: keep a reference to the primary surface
    
    Avoid hard-coding surface 0 as being primary, although in practice it
    always is so far. Also a lot of lookups are primary, so add a shortcut
    for this special case (~30% apparently), it shows some small lookup
    speedup.
    
    before:
    real     0m5.008s
    user     0m3.253s
    sys      0m2.015s
    
    after:
    real    0m4.930s
    user    0m3.133s
    sys     0m2.027s

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 8734721..c8061ff 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -67,6 +67,7 @@
 
 struct _SpiceDisplayChannelPrivate {
     GHashTable                  *surfaces;
+    display_surface             *primary;
     display_cache               *images;
     display_cache               *palettes;
     SpiceImageCache             image_cache;
@@ -180,13 +181,11 @@ static void spice_display_get_property(GObject    *object,
 
     switch (prop_id) {
     case PROP_WIDTH: {
-        display_surface *surface = find_surface(c, 0);
-        g_value_set_uint(value, surface ? surface->width : 0);
+        g_value_set_uint(value, c->primary ? c->primary->width : 0);
         break;
     }
     case PROP_HEIGHT: {
-        display_surface *surface = find_surface(c, 0);
-        g_value_set_uint(value, surface ? surface->height : 0);
+        g_value_set_uint(value, c->primary ? c->primary->height : 0);
         break;
     }
     case PROP_MONITORS: {
@@ -739,18 +738,16 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
     SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
 
     if (surface->primary) {
-        display_surface *primary = find_surface(c, 0);
-
-        if (primary) {
-            if (primary->width == surface->width &&
-                primary->height == surface->height) {
+        if (c->primary) {
+            if (c->primary->width == surface->width &&
+                c->primary->height == surface->height) {
                 CHANNEL_DEBUG(channel, "Reusing existing primary surface");
                 return 0;
             }
 
             emit_main_context(channel, SPICE_DISPLAY_PRIMARY_DESTROY);
 
-            g_hash_table_remove(c->surfaces, GINT_TO_POINTER(0));
+            g_hash_table_remove(c->surfaces, GINT_TO_POINTER(c->primary->surface_id));
         }
 
         CHANNEL_DEBUG(channel, "Create primary canvas");
@@ -803,6 +800,8 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
     g_hash_table_insert(c->surfaces, GINT_TO_POINTER(surface->surface_id), surface);
 
     if (surface->primary) {
+        g_warn_if_fail(c->primary == NULL);
+        c->primary = surface;
         emit_main_context(channel, SPICE_DISPLAY_PRIMARY_CREATE,
                           surface->format, surface->width, surface->height,
                           surface->stride, surface->shmid, surface->data);
@@ -847,6 +846,9 @@ static void destroy_canvas(display_surface *surface)
 
 static display_surface *find_surface(SpiceDisplayChannelPrivate *c, guint32 surface_id)
 {
+    if (c->primary && c->primary->surface_id == surface_id)
+        return c->primary;
+
     return g_hash_table_lookup(c->surfaces, GINT_TO_POINTER(surface_id));
 }
 
@@ -944,10 +946,9 @@ static void display_handle_mode(SpiceChannel *channel, SpiceMsgIn *in)
 static void display_handle_mark(SpiceChannel *channel, SpiceMsgIn *in)
 {
     SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
-    display_surface *surface = find_surface(c, 0);
 
     CHANNEL_DEBUG(channel, "%s", __FUNCTION__);
-    g_return_if_fail(surface != NULL);
+    g_return_if_fail(c->primary != NULL);
 #ifdef EXTRA_CHECKS
     g_warn_if_fail(c->mark == FALSE);
 #endif
@@ -960,7 +961,7 @@ static void display_handle_mark(SpiceChannel *channel, SpiceMsgIn *in)
 static void display_handle_reset(SpiceChannel *channel, SpiceMsgIn *in)
 {
     SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
-    display_surface *surface = find_surface(c, 0);
+    display_surface *surface = c->primary;
 
     CHANNEL_DEBUG(channel, "%s: TODO detach_from_screen", __FUNCTION__);
 
@@ -1784,6 +1785,7 @@ static void display_handle_surface_destroy(SpiceChannel *channel, SpiceMsgIn *in
         if (id != 0 && c->mark_false_event_id == 0) {
             c->mark_false_event_id = g_timeout_add_seconds(1, display_mark_false, channel);
         }
+        c->primary = NULL;
         emit_main_context(channel, SPICE_DISPLAY_PRIMARY_DESTROY);
     }
 
commit c7ceb7752bf1bbb75b53d9ad552efd77c5e28413
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Aug 21 20:20:27 2013 +0200

    display: make the hashtable to destroy the surface
    
    Improve a bit the code by using hashtable ownership.

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index a0bdec1..8734721 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -700,13 +700,21 @@ static void spice_display_channel_reset_capabilities(SpiceChannel *channel)
     }
 }
 
+static void destroy_surface(gpointer data)
+{
+    display_surface *surface = data;
+
+    destroy_canvas(surface);
+    free(surface);
+}
+
 static void spice_display_channel_init(SpiceDisplayChannel *channel)
 {
     SpiceDisplayChannelPrivate *c;
 
     c = channel->priv = SPICE_DISPLAY_CHANNEL_GET_PRIVATE(channel);
 
-    c->surfaces = g_hash_table_new(NULL, NULL);
+    c->surfaces = g_hash_table_new_full(NULL, NULL, NULL, destroy_surface);
     c->image_cache.ops = &image_cache_ops;
     c->palette_cache.ops = &palette_cache_ops;
     c->image_surfaces.ops = &image_surfaces_ops;
@@ -743,8 +751,6 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
             emit_main_context(channel, SPICE_DISPLAY_PRIMARY_DESTROY);
 
             g_hash_table_remove(c->surfaces, GINT_TO_POINTER(0));
-            destroy_canvas(primary);
-            free(primary);
         }
 
         CHANNEL_DEBUG(channel, "Create primary canvas");
@@ -859,8 +865,6 @@ static void clear_surfaces(SpiceChannel *channel, gboolean keep_primary)
         }
 
         g_hash_table_iter_remove(&iter);
-        destroy_canvas(surface);
-        free(surface);
     }
 }
 
@@ -1784,8 +1788,6 @@ static void display_handle_surface_destroy(SpiceChannel *channel, SpiceMsgIn *in
     }
 
     g_hash_table_remove(c->surfaces, GINT_TO_POINTER(surface->surface_id));
-    destroy_canvas(surface);
-    free(surface);
 }
 
 #define CLAMP_CHECK(x, low, high)  (((x) > (high)) ? TRUE : (((x) < (low)) ? TRUE : FALSE))
commit 383347f53597058cfea4581e5d70b886a6a55dbd
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Aug 21 20:17:12 2013 +0200

    display: replace ring with hashtable
    
    With a Spice replay (a tool not yet merged, but available in dev
    branches), the following commit improves a little bit performance by not
    spending so much CPU time in looking up surfaces. I found initially
    hotspot with "perf", and get a consistant ~200ms speedup with "time
    spicy-stats" after replacing the ring.
    
    before:
    real     0m5.147s
    user     0m3.506s
    sys      0m1.949s
    
    after:
    real     0m5.008s
    user     0m3.253s
    sys      0m2.015s

diff --git a/gtk/channel-display-priv.h b/gtk/channel-display-priv.h
index 09683be..92cd231 100644
--- a/gtk/channel-display-priv.h
+++ b/gtk/channel-display-priv.h
@@ -37,7 +37,6 @@ G_BEGIN_DECLS
 
 
 typedef struct display_surface {
-    RingItem                    link;
     guint32                     surface_id;
     bool                        primary;
     enum SpiceSurfaceFmt        format;
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 08d5dcb..a0bdec1 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -66,7 +66,7 @@
 #define MONITORS_MAX 256
 
 struct _SpiceDisplayChannelPrivate {
-    Ring                        surfaces;
+    GHashTable                  *surfaces;
     display_cache               *images;
     display_cache               *palettes;
     SpiceImageCache             image_cache;
@@ -141,6 +141,7 @@ static void spice_display_channel_finalize(GObject *object)
 
     g_clear_pointer(&c->monitors, g_array_unref);
     clear_surfaces(SPICE_CHANNEL(object), FALSE);
+    g_hash_table_unref(c->surfaces);
     clear_streams(SPICE_CHANNEL(object));
 
     if (G_OBJECT_CLASS(spice_display_channel_parent_class)->finalize)
@@ -705,7 +706,7 @@ static void spice_display_channel_init(SpiceDisplayChannel *channel)
 
     c = channel->priv = SPICE_DISPLAY_CHANNEL_GET_PRIVATE(channel);
 
-    ring_init(&c->surfaces);
+    c->surfaces = g_hash_table_new(NULL, NULL);
     c->image_cache.ops = &image_cache_ops;
     c->palette_cache.ops = &palette_cache_ops;
     c->image_surfaces.ops = &image_surfaces_ops;
@@ -740,7 +741,8 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
             }
 
             emit_main_context(channel, SPICE_DISPLAY_PRIMARY_DESTROY);
-            ring_remove(&primary->link);
+
+            g_hash_table_remove(c->surfaces, GINT_TO_POINTER(0));
             destroy_canvas(primary);
             free(primary);
         }
@@ -792,7 +794,7 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
                                              surface->zlib_decoder);
 
     g_return_val_if_fail(surface->canvas != NULL, 0);
-    ring_add(&c->surfaces, &surface->link);
+    g_hash_table_insert(c->surfaces, GINT_TO_POINTER(surface->surface_id), surface);
 
     if (surface->primary) {
         emit_main_context(channel, SPICE_DISPLAY_PRIMARY_CREATE,
@@ -839,35 +841,24 @@ static void destroy_canvas(display_surface *surface)
 
 static display_surface *find_surface(SpiceDisplayChannelPrivate *c, guint32 surface_id)
 {
-    display_surface *surface;
-    RingItem *item;
-
-    for (item = ring_get_head(&c->surfaces);
-         item != NULL;
-         item = ring_next(&c->surfaces, item)) {
-        surface = SPICE_CONTAINEROF(item, display_surface, link);
-        if (surface->surface_id == surface_id)
-            return surface;
-    }
-    return NULL;
+    return g_hash_table_lookup(c->surfaces, GINT_TO_POINTER(surface_id));
 }
 
 static void clear_surfaces(SpiceChannel *channel, gboolean keep_primary)
 {
     SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
+    GHashTableIter iter;
     display_surface *surface;
-    RingItem *item;
 
-    for (item = ring_get_head(&c->surfaces); item != NULL; ) {
-        surface = SPICE_CONTAINEROF(item, display_surface, link);
-        item = ring_next(&c->surfaces, item);
+    g_hash_table_iter_init(&iter, c->surfaces);
+    while (g_hash_table_iter_next(&iter, NULL, (gpointer*)&surface)) {
 
         if (keep_primary && surface->primary) {
             CHANNEL_DEBUG(channel, "keeping exisiting primary surface, migration or reset");
             continue;
         }
 
-        ring_remove(&surface->link);
+        g_hash_table_iter_remove(&iter);
         destroy_canvas(surface);
         free(surface);
     }
@@ -1792,7 +1783,7 @@ static void display_handle_surface_destroy(SpiceChannel *channel, SpiceMsgIn *in
         emit_main_context(channel, SPICE_DISPLAY_PRIMARY_DESTROY);
     }
 
-    ring_remove(&surface->link);
+    g_hash_table_remove(c->surfaces, GINT_TO_POINTER(surface->surface_id));
     destroy_canvas(surface);
     free(surface);
 }
commit e969b04f4585d274aa7fc40b6aeaaec3e7084b62
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Aug 21 18:50:28 2013 +0200

    channel: add SPICE_DISABLE_CHANNELS
    
    Allow to disable selectively channels, mainly used for testing,
    ex: SPICE_DISABLE_CHANNELS=display spicy-stats -p 12345

diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index 92c9315..1f29c23 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -134,6 +134,8 @@ struct _SpiceChannelPrivate {
     gsize                       total_read_bytes;
     uint64_t                    last_message_serial;
     GSList                      *flushing;
+
+    gboolean                    disable_channel_msg;
 };
 
 SpiceMsgIn *spice_msg_in_new(SpiceChannel *channel);
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index a0d9c15..55c8325 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -126,6 +126,10 @@ static void spice_channel_constructed(GObject *gobject)
              desc ? desc : "unknown", c->channel_type, c->channel_id);
     CHANNEL_DEBUG(channel, "%s", __FUNCTION__);
 
+    const char *disabled  = g_getenv("SPICE_DISABLE_CHANNELS");
+    if (disabled && strstr(disabled, desc))
+        c->disable_channel_msg = TRUE;
+
     c->connection_id = spice_session_get_connection_id(c->session);
     spice_session_channel_new(c->session, channel);
 
@@ -2075,6 +2079,7 @@ static void spice_channel_iterate_write(SpiceChannel *channel)
 static void spice_channel_iterate_read(SpiceChannel *channel)
 {
     SpiceChannelPrivate *c = channel->priv;
+
     g_return_if_fail(c->state != SPICE_CHANNEL_STATE_MIGRATING);
 
     spice_channel_recv_msg(channel,
@@ -2771,6 +2776,8 @@ static void spice_channel_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
     spice_msg_handler handler;
 
     g_return_if_fail(type < klass->handlers->len);
+    if (type > SPICE_MSG_BASE_LAST && channel->priv->disable_channel_msg)
+        return;
 
     handler = g_array_index(klass->handlers, spice_msg_handler, type);
     g_return_if_fail(handler != NULL);
commit 276792c72d88461d044a207592a4202ff66b7a1b
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Sep 10 14:59:35 2013 +0200

    channels: use spice_channel_set_handlers()
    
    This allows to simplify a little bit derived class (no need to override
    handle_msg), and allows the base class more flexibility (for example for
    filtering messages, as in the following patch)

diff --git a/gtk/channel-cursor.c b/gtk/channel-cursor.c
index e4a996b..5d2db84 100644
--- a/gtk/channel-cursor.c
+++ b/gtk/channel-cursor.c
@@ -66,10 +66,10 @@ enum {
 
 static guint signals[SPICE_CURSOR_LAST_SIGNAL];
 
-static void spice_cursor_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
 static void delete_cursor_all(SpiceChannel *channel);
 static display_cursor * display_cursor_ref(display_cursor *cursor);
 static void display_cursor_unref(display_cursor *cursor);
+static void channel_set_handlers(SpiceChannelClass *klass);
 
 G_DEFINE_TYPE(SpiceCursorChannel, spice_cursor_channel, SPICE_TYPE_CHANNEL)
 
@@ -109,7 +109,6 @@ static void spice_cursor_channel_class_init(SpiceCursorChannelClass *klass)
     SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass);
 
     gobject_class->finalize     = spice_cursor_channel_finalize;
-    channel_class->handle_msg   = spice_cursor_handle_msg;
     channel_class->channel_reset = spice_cursor_channel_reset;
 
     /**
@@ -194,6 +193,7 @@ static void spice_cursor_channel_class_init(SpiceCursorChannelClass *klass)
                      0);
 
     g_type_class_add_private(klass, sizeof(SpiceCursorChannelPrivate));
+    channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
 }
 
 /* signal trampoline---------------------------------------------------------- */
@@ -598,31 +598,18 @@ static void cursor_handle_inval_all(SpiceChannel *channel, SpiceMsgIn *in)
     delete_cursor_all(channel);
 }
 
-static const spice_msg_handler cursor_handlers[] = {
-    [ SPICE_MSG_CURSOR_INIT ]              = cursor_handle_init,
-    [ SPICE_MSG_CURSOR_RESET ]             = cursor_handle_reset,
-    [ SPICE_MSG_CURSOR_SET ]               = cursor_handle_set,
-    [ SPICE_MSG_CURSOR_MOVE ]              = cursor_handle_move,
-    [ SPICE_MSG_CURSOR_HIDE ]              = cursor_handle_hide,
-    [ SPICE_MSG_CURSOR_TRAIL ]             = cursor_handle_trail,
-    [ SPICE_MSG_CURSOR_INVAL_ONE ]         = cursor_handle_inval_one,
-    [ SPICE_MSG_CURSOR_INVAL_ALL ]         = cursor_handle_inval_all,
-};
-
-/* coroutine context */
-static void spice_cursor_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
+static void channel_set_handlers(SpiceChannelClass *klass)
 {
-    int type = spice_msg_in_type(msg);
-    SpiceChannelClass *parent_class;
-
-    g_return_if_fail(type < SPICE_N_ELEMENTS(cursor_handlers));
-
-    parent_class = SPICE_CHANNEL_CLASS(spice_cursor_channel_parent_class);
-
-    if (cursor_handlers[type] != NULL)
-        cursor_handlers[type](channel, msg);
-    else if (parent_class->handle_msg)
-        parent_class->handle_msg(channel, msg);
-    else
-        g_return_if_reached();
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_CURSOR_INIT ]              = cursor_handle_init,
+        [ SPICE_MSG_CURSOR_RESET ]             = cursor_handle_reset,
+        [ SPICE_MSG_CURSOR_SET ]               = cursor_handle_set,
+        [ SPICE_MSG_CURSOR_MOVE ]              = cursor_handle_move,
+        [ SPICE_MSG_CURSOR_HIDE ]              = cursor_handle_hide,
+        [ SPICE_MSG_CURSOR_TRAIL ]             = cursor_handle_trail,
+        [ SPICE_MSG_CURSOR_INVAL_ONE ]         = cursor_handle_inval_one,
+        [ SPICE_MSG_CURSOR_INVAL_ALL ]         = cursor_handle_inval_all,
+    };
+
+    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
 }
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 704d5a7..08d5dcb 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -107,8 +107,8 @@ enum {
 
 static guint signals[SPICE_DISPLAY_LAST_SIGNAL];
 
-static void spice_display_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
 static void spice_display_channel_up(SpiceChannel *channel);
+static void channel_set_handlers(SpiceChannelClass *klass);
 
 static void clear_surfaces(SpiceChannel *channel, gboolean keep_primary);
 static void clear_streams(SpiceChannel *channel);
@@ -235,7 +235,6 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass)
     gobject_class->set_property = spice_display_set_property;
     gobject_class->constructed = spice_display_channel_constructed;
 
-    channel_class->handle_msg   = spice_display_handle_msg;
     channel_class->channel_up   = spice_display_channel_up;
     channel_class->channel_reset = spice_display_channel_reset;
     channel_class->channel_reset_capabilities = spice_display_channel_reset_capabilities;
@@ -387,6 +386,7 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass)
     sw_canvas_init();
     quic_init();
     rop3_init();
+    channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
 }
 
 /**
@@ -1841,58 +1841,45 @@ static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in
     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,
-    [ SPICE_MSG_DISPLAY_RESET ]              = display_handle_reset,
-    [ SPICE_MSG_DISPLAY_COPY_BITS ]          = display_handle_copy_bits,
-    [ SPICE_MSG_DISPLAY_INVAL_LIST ]         = display_handle_inv_list,
-    [ SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS ]  = display_handle_inv_pixmap_all,
-    [ SPICE_MSG_DISPLAY_INVAL_PALETTE ]      = display_handle_inv_palette,
-    [ SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES ] = display_handle_inv_palette_all,
-
-    [ SPICE_MSG_DISPLAY_STREAM_CREATE ]      = display_handle_stream_create,
-    [ SPICE_MSG_DISPLAY_STREAM_DATA ]        = display_handle_stream_data,
-    [ SPICE_MSG_DISPLAY_STREAM_CLIP ]        = display_handle_stream_clip,
-    [ SPICE_MSG_DISPLAY_STREAM_DESTROY ]     = display_handle_stream_destroy,
-    [ SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL ] = display_handle_stream_destroy_all,
-    [ SPICE_MSG_DISPLAY_STREAM_DATA_SIZED ]  = display_handle_stream_data,
-    [ SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT ] = display_handle_stream_activate_report,
-
-    [ SPICE_MSG_DISPLAY_DRAW_FILL ]          = display_handle_draw_fill,
-    [ SPICE_MSG_DISPLAY_DRAW_OPAQUE ]        = display_handle_draw_opaque,
-    [ SPICE_MSG_DISPLAY_DRAW_COPY ]          = display_handle_draw_copy,
-    [ SPICE_MSG_DISPLAY_DRAW_BLEND ]         = display_handle_draw_blend,
-    [ SPICE_MSG_DISPLAY_DRAW_BLACKNESS ]     = display_handle_draw_blackness,
-    [ SPICE_MSG_DISPLAY_DRAW_WHITENESS ]     = display_handle_draw_whiteness,
-    [ SPICE_MSG_DISPLAY_DRAW_INVERS ]        = display_handle_draw_invers,
-    [ SPICE_MSG_DISPLAY_DRAW_ROP3 ]          = display_handle_draw_rop3,
-    [ SPICE_MSG_DISPLAY_DRAW_STROKE ]        = display_handle_draw_stroke,
-    [ SPICE_MSG_DISPLAY_DRAW_TEXT ]          = display_handle_draw_text,
-    [ SPICE_MSG_DISPLAY_DRAW_TRANSPARENT ]   = display_handle_draw_transparent,
-    [ SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND ]   = display_handle_draw_alpha_blend,
-    [ SPICE_MSG_DISPLAY_DRAW_COMPOSITE ]     = display_handle_draw_composite,
-
-    [ 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 */
-static void spice_display_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
-{
-    int type = spice_msg_in_type(msg);
-    SpiceChannelClass *parent_class;
-
-    g_return_if_fail(type < SPICE_N_ELEMENTS(display_handlers));
-
-    parent_class = SPICE_CHANNEL_CLASS(spice_display_channel_parent_class);
+static void channel_set_handlers(SpiceChannelClass *klass)
+{
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_DISPLAY_MODE ]               = display_handle_mode,
+        [ SPICE_MSG_DISPLAY_MARK ]               = display_handle_mark,
+        [ SPICE_MSG_DISPLAY_RESET ]              = display_handle_reset,
+        [ SPICE_MSG_DISPLAY_COPY_BITS ]          = display_handle_copy_bits,
+        [ SPICE_MSG_DISPLAY_INVAL_LIST ]         = display_handle_inv_list,
+        [ SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS ]  = display_handle_inv_pixmap_all,
+        [ SPICE_MSG_DISPLAY_INVAL_PALETTE ]      = display_handle_inv_palette,
+        [ SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES ] = display_handle_inv_palette_all,
+
+        [ SPICE_MSG_DISPLAY_STREAM_CREATE ]      = display_handle_stream_create,
+        [ SPICE_MSG_DISPLAY_STREAM_DATA ]        = display_handle_stream_data,
+        [ SPICE_MSG_DISPLAY_STREAM_CLIP ]        = display_handle_stream_clip,
+        [ SPICE_MSG_DISPLAY_STREAM_DESTROY ]     = display_handle_stream_destroy,
+        [ SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL ] = display_handle_stream_destroy_all,
+        [ SPICE_MSG_DISPLAY_STREAM_DATA_SIZED ]  = display_handle_stream_data,
+        [ SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT ] = display_handle_stream_activate_report,
+
+        [ SPICE_MSG_DISPLAY_DRAW_FILL ]          = display_handle_draw_fill,
+        [ SPICE_MSG_DISPLAY_DRAW_OPAQUE ]        = display_handle_draw_opaque,
+        [ SPICE_MSG_DISPLAY_DRAW_COPY ]          = display_handle_draw_copy,
+        [ SPICE_MSG_DISPLAY_DRAW_BLEND ]         = display_handle_draw_blend,
+        [ SPICE_MSG_DISPLAY_DRAW_BLACKNESS ]     = display_handle_draw_blackness,
+        [ SPICE_MSG_DISPLAY_DRAW_WHITENESS ]     = display_handle_draw_whiteness,
+        [ SPICE_MSG_DISPLAY_DRAW_INVERS ]        = display_handle_draw_invers,
+        [ SPICE_MSG_DISPLAY_DRAW_ROP3 ]          = display_handle_draw_rop3,
+        [ SPICE_MSG_DISPLAY_DRAW_STROKE ]        = display_handle_draw_stroke,
+        [ SPICE_MSG_DISPLAY_DRAW_TEXT ]          = display_handle_draw_text,
+        [ SPICE_MSG_DISPLAY_DRAW_TRANSPARENT ]   = display_handle_draw_transparent,
+        [ SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND ]   = display_handle_draw_alpha_blend,
+        [ SPICE_MSG_DISPLAY_DRAW_COMPOSITE ]     = display_handle_draw_composite,
+
+        [ 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,
+    };
 
-    if (display_handlers[type] != NULL)
-        display_handlers[type](channel, msg);
-    else if (parent_class->handle_msg)
-        parent_class->handle_msg(channel, msg);
-    else
-        g_return_if_reached();
+    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
 }
diff --git a/gtk/channel-inputs.c b/gtk/channel-inputs.c
index ee86dc2..a69dbe6 100644
--- a/gtk/channel-inputs.c
+++ b/gtk/channel-inputs.c
@@ -67,9 +67,9 @@ enum {
 
 static guint signals[SPICE_INPUTS_LAST_SIGNAL];
 
-static void spice_inputs_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
 static void spice_inputs_channel_up(SpiceChannel *channel);
 static void spice_inputs_channel_reset(SpiceChannel *channel, gboolean migrating);
+static void channel_set_handlers(SpiceChannelClass *klass);
 
 /* ------------------------------------------------------------------ */
 
@@ -108,7 +108,6 @@ static void spice_inputs_channel_class_init(SpiceInputsChannelClass *klass)
 
     gobject_class->finalize     = spice_inputs_channel_finalize;
     gobject_class->get_property = spice_inputs_get_property;
-    channel_class->handle_msg   = spice_inputs_handle_msg;
     channel_class->channel_up   = spice_inputs_channel_up;
     channel_class->channel_reset = spice_inputs_channel_reset;
 
@@ -143,6 +142,7 @@ static void spice_inputs_channel_class_init(SpiceInputsChannelClass *klass)
                      0);
 
     g_type_class_add_private(klass, sizeof(SpiceInputsChannelPrivate));
+    channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
 }
 
 /* signal trampoline---------------------------------------------------------- */
@@ -281,28 +281,15 @@ static void inputs_handle_ack(SpiceChannel *channel, SpiceMsgIn *in)
     }
 }
 
-static const spice_msg_handler inputs_handlers[] = {
-    [ SPICE_MSG_INPUTS_INIT ]              = inputs_handle_init,
-    [ SPICE_MSG_INPUTS_KEY_MODIFIERS ]     = inputs_handle_modifiers,
-    [ SPICE_MSG_INPUTS_MOUSE_MOTION_ACK ]  = inputs_handle_ack,
-};
-
-/* coroutine context */
-static void spice_inputs_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
+static void channel_set_handlers(SpiceChannelClass *klass)
 {
-    int type = spice_msg_in_type(msg);
-    SpiceChannelClass *parent_class;
-
-    g_return_if_fail(type < SPICE_N_ELEMENTS(inputs_handlers));
-
-    parent_class = SPICE_CHANNEL_CLASS(spice_inputs_channel_parent_class);
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_INPUTS_INIT ]              = inputs_handle_init,
+        [ SPICE_MSG_INPUTS_KEY_MODIFIERS ]     = inputs_handle_modifiers,
+        [ SPICE_MSG_INPUTS_MOUSE_MOTION_ACK ]  = inputs_handle_ack,
+    };
 
-    if (inputs_handlers[type] != NULL)
-        inputs_handlers[type](channel, msg);
-    else if (parent_class->handle_msg)
-        parent_class->handle_msg(channel, msg);
-    else
-        g_return_if_reached();
+    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
 }
 
 /**
diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 93995f1..b342e97 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -158,6 +158,7 @@ enum {
 static guint signals[SPICE_MAIN_LAST_SIGNAL];
 
 static void spice_main_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
+static void channel_set_handlers(SpiceChannelClass *klass);
 static void agent_send_msg_queue(SpiceMainChannel *channel);
 static void agent_free_msg_queue(SpiceMainChannel *channel);
 static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event,
@@ -728,6 +729,7 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass)
                      G_TYPE_OBJECT);
 
     g_type_class_add_private(klass, sizeof(SpiceMainChannelPrivate));
+    channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
 }
 
 /* signal trampoline---------------------------------------------------------- */
@@ -2316,28 +2318,33 @@ static void main_handle_migrate_cancel(SpiceChannel *channel,
     spice_session_abort_migration(session);
 }
 
-static const spice_msg_handler main_handlers[] = {
-    [ SPICE_MSG_MAIN_INIT ]                = main_handle_init,
-    [ SPICE_MSG_MAIN_NAME ]                = main_handle_name,
-    [ SPICE_MSG_MAIN_UUID ]                = main_handle_uuid,
-    [ SPICE_MSG_MAIN_CHANNELS_LIST ]       = main_handle_channels_list,
-    [ SPICE_MSG_MAIN_MOUSE_MODE ]          = main_handle_mouse_mode,
-    [ SPICE_MSG_MAIN_MULTI_MEDIA_TIME ]    = main_handle_mm_time,
-
-    [ SPICE_MSG_MAIN_AGENT_CONNECTED ]     = main_handle_agent_connected,
-    [ SPICE_MSG_MAIN_AGENT_DISCONNECTED ]  = main_handle_agent_disconnected,
-    [ SPICE_MSG_MAIN_AGENT_DATA ]          = main_handle_agent_data,
-    [ SPICE_MSG_MAIN_AGENT_TOKEN ]         = main_handle_agent_token,
-
-    [ SPICE_MSG_MAIN_MIGRATE_BEGIN ]       = main_handle_migrate_begin,
-    [ SPICE_MSG_MAIN_MIGRATE_END ]         = main_handle_migrate_end,
-    [ SPICE_MSG_MAIN_MIGRATE_CANCEL ]      = main_handle_migrate_cancel,
-    [ SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST ] = main_handle_migrate_switch_host,
-    [ SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS ]   = main_handle_agent_connected_tokens,
-    [ SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS ]   = main_handle_migrate_begin_seamless,
-    [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK]  = main_handle_migrate_dst_seamless_ack,
-    [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK] = main_handle_migrate_dst_seamless_nack,
-};
+static void channel_set_handlers(SpiceChannelClass *klass)
+{
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_MAIN_INIT ]                = main_handle_init,
+        [ SPICE_MSG_MAIN_NAME ]                = main_handle_name,
+        [ SPICE_MSG_MAIN_UUID ]                = main_handle_uuid,
+        [ SPICE_MSG_MAIN_CHANNELS_LIST ]       = main_handle_channels_list,
+        [ SPICE_MSG_MAIN_MOUSE_MODE ]          = main_handle_mouse_mode,
+        [ SPICE_MSG_MAIN_MULTI_MEDIA_TIME ]    = main_handle_mm_time,
+
+        [ SPICE_MSG_MAIN_AGENT_CONNECTED ]     = main_handle_agent_connected,
+        [ SPICE_MSG_MAIN_AGENT_DISCONNECTED ]  = main_handle_agent_disconnected,
+        [ SPICE_MSG_MAIN_AGENT_DATA ]          = main_handle_agent_data,
+        [ SPICE_MSG_MAIN_AGENT_TOKEN ]         = main_handle_agent_token,
+
+        [ SPICE_MSG_MAIN_MIGRATE_BEGIN ]       = main_handle_migrate_begin,
+        [ SPICE_MSG_MAIN_MIGRATE_END ]         = main_handle_migrate_end,
+        [ SPICE_MSG_MAIN_MIGRATE_CANCEL ]      = main_handle_migrate_cancel,
+        [ SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST ] = main_handle_migrate_switch_host,
+        [ SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS ]   = main_handle_agent_connected_tokens,
+        [ SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS ]   = main_handle_migrate_begin_seamless,
+        [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK]  = main_handle_migrate_dst_seamless_ack,
+        [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK] = main_handle_migrate_dst_seamless_nack,
+    };
+
+    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
+}
 
 /* coroutine context */
 static void spice_main_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
@@ -2346,8 +2353,6 @@ static void spice_main_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
     SpiceChannelClass *parent_class;
     SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv;
 
-    g_return_if_fail(type < SPICE_N_ELEMENTS(main_handlers));
-
     parent_class = SPICE_CHANNEL_CLASS(spice_main_channel_parent_class);
 
     if (c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE) {
@@ -2359,12 +2364,7 @@ static void spice_main_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
         }
     }
 
-    if (main_handlers[type] != NULL)
-        main_handlers[type](channel, msg);
-    else if (parent_class->handle_msg)
-        parent_class->handle_msg(channel, msg);
-    else
-        g_return_if_reached();
+    parent_class->handle_msg(channel, msg);
 }
 
 /**
diff --git a/gtk/channel-playback.c b/gtk/channel-playback.c
index f246a80..60fc113 100644
--- a/gtk/channel-playback.c
+++ b/gtk/channel-playback.c
@@ -82,8 +82,7 @@ enum {
 };
 
 static guint signals[SPICE_PLAYBACK_LAST_SIGNAL];
-
-static void spice_playback_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
+static void channel_set_handlers(SpiceChannelClass *klass);
 
 /* ------------------------------------------------------------------ */
 
@@ -197,7 +196,6 @@ static void spice_playback_channel_class_init(SpicePlaybackChannelClass *klass)
     gobject_class->get_property = spice_playback_channel_get_property;
     gobject_class->set_property = spice_playback_channel_set_property;
 
-    channel_class->handle_msg   = spice_playback_handle_msg;
     channel_class->channel_reset = spice_playback_channel_reset;
     channel_class->channel_reset_capabilities = spice_playback_channel_reset_capabilities;
 
@@ -308,6 +306,7 @@ static void spice_playback_channel_class_init(SpicePlaybackChannelClass *klass)
                      0);
 
     g_type_class_add_private(klass, sizeof(SpicePlaybackChannelPrivate));
+    channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
 }
 
 /* signal trampoline---------------------------------------------------------- */
@@ -517,32 +516,19 @@ static void playback_handle_set_latency(SpiceChannel *channel, SpiceMsgIn *in)
     g_object_notify_main_context(G_OBJECT(channel), "min-latency");
 }
 
-static const spice_msg_handler playback_handlers[] = {
-    [ SPICE_MSG_PLAYBACK_DATA ]            = playback_handle_data,
-    [ SPICE_MSG_PLAYBACK_MODE ]            = playback_handle_mode,
-    [ SPICE_MSG_PLAYBACK_START ]           = playback_handle_start,
-    [ SPICE_MSG_PLAYBACK_STOP ]            = playback_handle_stop,
-    [ SPICE_MSG_PLAYBACK_VOLUME ]          = playback_handle_set_volume,
-    [ SPICE_MSG_PLAYBACK_MUTE ]            = playback_handle_set_mute,
-    [ SPICE_MSG_PLAYBACK_LATENCY ]         = playback_handle_set_latency,
-};
-
-/* coroutine context */
-static void spice_playback_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
+static void channel_set_handlers(SpiceChannelClass *klass)
 {
-    int type = spice_msg_in_type(msg);
-    SpiceChannelClass *parent_class;
-
-    g_return_if_fail(type < SPICE_N_ELEMENTS(playback_handlers));
-
-    parent_class = SPICE_CHANNEL_CLASS(spice_playback_channel_parent_class);
-
-    if (playback_handlers[type] != NULL)
-        playback_handlers[type](channel, msg);
-    else if (parent_class->handle_msg)
-        parent_class->handle_msg(channel, msg);
-    else
-        g_return_if_reached();
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_PLAYBACK_DATA ]            = playback_handle_data,
+        [ SPICE_MSG_PLAYBACK_MODE ]            = playback_handle_mode,
+        [ SPICE_MSG_PLAYBACK_START ]           = playback_handle_start,
+        [ SPICE_MSG_PLAYBACK_STOP ]            = playback_handle_stop,
+        [ SPICE_MSG_PLAYBACK_VOLUME ]          = playback_handle_set_volume,
+        [ SPICE_MSG_PLAYBACK_MUTE ]            = playback_handle_set_mute,
+        [ SPICE_MSG_PLAYBACK_LATENCY ]         = playback_handle_set_latency,
+    };
+
+    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
 }
 
 void spice_playback_channel_set_delay(SpicePlaybackChannel *channel, guint32 delay_ms)
diff --git a/gtk/channel-port.c b/gtk/channel-port.c
index 1d6eef2..11948bb 100644
--- a/gtk/channel-port.c
+++ b/gtk/channel-port.c
@@ -76,8 +76,7 @@ enum {
 };
 
 static guint signals[LAST_SIGNAL];
-
-static void spice_port_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
+static void channel_set_handlers(SpiceChannelClass *klass);
 
 static void spice_port_channel_init(SpicePortChannel *channel)
 {
@@ -131,7 +130,6 @@ static void spice_port_channel_class_init(SpicePortChannelClass *klass)
 
     gobject_class->finalize     = spice_port_channel_finalize;
     gobject_class->get_property = spice_port_get_property;
-    channel_class->handle_msg   = spice_port_handle_msg;
     channel_class->channel_reset = spice_port_channel_reset;
 
     g_object_class_install_property
@@ -194,6 +192,7 @@ static void spice_port_channel_class_init(SpicePortChannelClass *klass)
                      G_TYPE_INT);
 
     g_type_class_add_private(klass, sizeof(SpicePortChannelPrivate));
+    channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
 }
 
 /* signal trampoline---------------------------------------------------------- */
@@ -401,26 +400,13 @@ void spice_port_event(SpicePortChannel *self, guint8 event)
     spice_msg_out_send(msg);
 }
 
-static const spice_msg_handler port_handlers[] = {
-    [ SPICE_MSG_PORT_INIT ]              = port_handle_init,
-    [ SPICE_MSG_PORT_EVENT ]             = port_handle_event,
-    [ SPICE_MSG_SPICEVMC_DATA ]          = port_handle_msg,
-};
-
-/* coroutine context */
-static void spice_port_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
+static void channel_set_handlers(SpiceChannelClass *klass)
 {
-    int type = spice_msg_in_type(msg);
-    SpiceChannelClass *parent_class;
-
-    g_return_if_fail(type < SPICE_N_ELEMENTS(port_handlers));
-
-    parent_class = SPICE_CHANNEL_CLASS(spice_port_channel_parent_class);
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_PORT_INIT ]              = port_handle_init,
+        [ SPICE_MSG_PORT_EVENT ]             = port_handle_event,
+        [ SPICE_MSG_SPICEVMC_DATA ]          = port_handle_msg,
+    };
 
-    if (port_handlers[type] != NULL)
-        port_handlers[type](channel, msg);
-    else if (parent_class->handle_msg)
-        parent_class->handle_msg(channel, msg);
-    else
-        g_return_if_reached();
+    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
 }
diff --git a/gtk/channel-record.c b/gtk/channel-record.c
index 6345569..e1f3ec7 100644
--- a/gtk/channel-record.c
+++ b/gtk/channel-record.c
@@ -81,7 +81,7 @@ enum {
 
 static guint signals[SPICE_RECORD_LAST_SIGNAL];
 
-static void spice_record_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
+static void channel_set_handlers(SpiceChannelClass *klass);
 static void channel_up(SpiceChannel *channel);
 
 #define FRAME_SIZE 256
@@ -197,7 +197,6 @@ static void spice_record_channel_class_init(SpiceRecordChannelClass *klass)
     gobject_class->finalize     = spice_record_channel_finalize;
     gobject_class->get_property = spice_record_channel_get_property;
     gobject_class->set_property = spice_record_channel_set_property;
-    channel_class->handle_msg   = spice_record_handle_msg;
     channel_class->channel_up   = channel_up;
     channel_class->channel_reset = channel_reset;
     channel_class->channel_reset_capabilities = spice_record_channel_reset_capabilities;
@@ -265,6 +264,7 @@ static void spice_record_channel_class_init(SpiceRecordChannelClass *klass)
                      0);
 
     g_type_class_add_private(klass, sizeof(SpiceRecordChannelPrivate));
+    channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
 }
 
 /* signal trampoline---------------------------------------------------------- */
@@ -521,28 +521,14 @@ static void record_handle_set_mute(SpiceChannel *channel, SpiceMsgIn *in)
     g_object_notify_main_context(G_OBJECT(channel), "mute");
 }
 
-static const spice_msg_handler record_handlers[] = {
-    [ SPICE_MSG_RECORD_START ]             = record_handle_start,
-    [ SPICE_MSG_RECORD_STOP ]              = record_handle_stop,
-    [ SPICE_MSG_RECORD_VOLUME ]            = record_handle_set_volume,
-    [ SPICE_MSG_RECORD_MUTE ]              = record_handle_set_mute,
-};
-
-/* coroutine context */
-static void spice_record_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
+static void channel_set_handlers(SpiceChannelClass *klass)
 {
-    int type = spice_msg_in_type(msg);
-
-    SpiceChannelClass *parent_class;
-
-    g_return_if_fail(type < SPICE_N_ELEMENTS(record_handlers));
-
-    parent_class = SPICE_CHANNEL_CLASS(spice_record_channel_parent_class);
-
-    if (record_handlers[type] != NULL)
-        record_handlers[type](channel, msg);
-    else if (parent_class->handle_msg)
-        parent_class->handle_msg(channel, msg);
-    else
-        g_return_if_reached();
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_RECORD_START ]             = record_handle_start,
+        [ SPICE_MSG_RECORD_STOP ]              = record_handle_stop,
+        [ SPICE_MSG_RECORD_VOLUME ]            = record_handle_set_volume,
+        [ SPICE_MSG_RECORD_MUTE ]              = record_handle_set_mute,
+    };
+
+    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
 }
diff --git a/gtk/channel-smartcard.c b/gtk/channel-smartcard.c
index 21b8343..88887a7 100644
--- a/gtk/channel-smartcard.c
+++ b/gtk/channel-smartcard.c
@@ -95,7 +95,6 @@ enum {
     SPICE_SMARTCARD_LAST_SIGNAL,
 };
 
-static void spice_smartcard_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
 static void spice_smartcard_channel_up(SpiceChannel *channel);
 static void handle_smartcard_msg(SpiceChannel *channel, SpiceMsgIn *in);
 static void smartcard_message_free(SpiceSmartcardChannelMessage *message);
@@ -209,6 +208,14 @@ static void spice_smartcard_channel_reset(SpiceChannel *channel, gboolean migrat
     SPICE_CHANNEL_CLASS(spice_smartcard_channel_parent_class)->channel_reset(channel, migrating);
 }
 
+static void channel_set_handlers(SpiceChannelClass *klass)
+{
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_SMARTCARD_DATA ] = handle_smartcard_msg,
+    };
+    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
+}
+
 static void spice_smartcard_channel_class_init(SpiceSmartcardChannelClass *klass)
 {
     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
@@ -217,17 +224,13 @@ static void spice_smartcard_channel_class_init(SpiceSmartcardChannelClass *klass
     gobject_class->finalize     = spice_smartcard_channel_finalize;
     gobject_class->constructed  = spice_smartcard_channel_constructed;
 
-    channel_class->handle_msg   = spice_smartcard_handle_msg;
     channel_class->channel_up   = spice_smartcard_channel_up;
     channel_class->channel_reset = spice_smartcard_channel_reset;
 
     g_type_class_add_private(klass, sizeof(SpiceSmartcardChannelPrivate));
+    channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
 }
 
-static const spice_msg_handler smartcard_handlers[] = {
-    [ SPICE_MSG_SMARTCARD_DATA ]           = handle_smartcard_msg,
-};
-
 /* ------------------------------------------------------------------ */
 /* private api                                                        */
 
@@ -442,24 +445,6 @@ static void card_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
 }
 #endif /* USE_SMARTCARD */
 
-/* coroutine context */
-static void spice_smartcard_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
-{
-    int type = spice_msg_in_type(msg);
-    SpiceChannelClass *parent_class;
-
-    g_return_if_fail(type < SPICE_N_ELEMENTS(smartcard_handlers));
-
-    parent_class = SPICE_CHANNEL_CLASS(spice_smartcard_channel_parent_class);
-
-    if (smartcard_handlers[type] != NULL)
-        smartcard_handlers[type](channel, msg);
-    else if (parent_class->handle_msg)
-        parent_class->handle_msg(channel, msg);
-    else
-        g_return_if_reached();
-}
-
 static void spice_smartcard_channel_up_cb(GObject *source_object,
                                           GAsyncResult *res,
                                           gpointer user_data)
@@ -570,4 +555,3 @@ static void handle_smartcard_msg(SpiceChannel *channel, SpiceMsgIn *in)
     }
 #endif
 }
-
diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index 11bf38c..239fe12 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -81,7 +81,7 @@ struct _SpiceUsbredirChannelPrivate {
 #endif
 };
 
-static void spice_usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
+static void channel_set_handlers(SpiceChannelClass *klass);
 static void spice_usbredir_channel_up(SpiceChannel *channel);
 static void spice_usbredir_channel_dispose(GObject *obj);
 static void spice_usbredir_channel_finalize(GObject *obj);
@@ -136,11 +136,11 @@ static void spice_usbredir_channel_class_init(SpiceUsbredirChannelClass *klass)
 
     gobject_class->dispose       = spice_usbredir_channel_dispose;
     gobject_class->finalize      = spice_usbredir_channel_finalize;
-    channel_class->handle_msg    = spice_usbredir_handle_msg;
     channel_class->channel_up    = spice_usbredir_channel_up;
     channel_class->channel_reset = spice_usbredir_channel_reset;
 
     g_type_class_add_private(klass, sizeof(SpiceUsbredirChannelPrivate));
+    channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
 #endif
 }
 
@@ -188,9 +188,14 @@ static void spice_usbredir_channel_finalize(GObject *obj)
         G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize(obj);
 }
 
-static const spice_msg_handler usbredir_handlers[] = {
-    [ SPICE_MSG_SPICEVMC_DATA ] = usbredir_handle_msg,
-};
+static void channel_set_handlers(SpiceChannelClass *klass)
+{
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_SPICEVMC_DATA ] = usbredir_handle_msg,
+    };
+
+    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
+}
 
 /* ------------------------------------------------------------------ */
 /* private api                                                        */
@@ -608,23 +613,6 @@ static void do_emit_main_context(GObject *object, int event, gpointer params)
 
 /* --------------------------------------------------------------------- */
 /* coroutine context                                                     */
-static void spice_usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *msg)
-{
-    int type = spice_msg_in_type(msg);
-    SpiceChannelClass *parent_class;
-
-    g_return_if_fail(type < SPICE_N_ELEMENTS(usbredir_handlers));
-
-    parent_class = SPICE_CHANNEL_CLASS(spice_usbredir_channel_parent_class);
-
-    if (usbredir_handlers[type] != NULL)
-        usbredir_handlers[type](c, msg);
-    else if (parent_class->handle_msg)
-        parent_class->handle_msg(c, msg);
-    else
-        g_return_if_reached();
-}
-
 static void spice_usbredir_channel_up(SpiceChannel *c)
 {
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index 0070d71..92c9315 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -167,15 +167,9 @@ typedef void (*handler_msg_in)(SpiceChannel *channel, SpiceMsgIn *msg, gpointer
 void spice_channel_recv_msg(SpiceChannel *channel, handler_msg_in handler, gpointer data);
 
 /* channel-base.c */
-/* coroutine context */
-void spice_channel_handle_set_ack(SpiceChannel *channel, SpiceMsgIn *in);
-void spice_channel_handle_ping(SpiceChannel *channel, SpiceMsgIn *in);
-void spice_channel_handle_notify(SpiceChannel *channel, SpiceMsgIn *in);
-void spice_channel_handle_disconnect(SpiceChannel *channel, SpiceMsgIn *in);
 void spice_channel_set_handlers(SpiceChannelClass *klass,
                                 const spice_msg_handler* handlers, const int n);
 void spice_channel_handle_wait_for_channels(SpiceChannel *channel, SpiceMsgIn *in);
-void spice_channel_handle_migrate(SpiceChannel *channel, SpiceMsgIn *in);
 
 gint spice_channel_get_channel_id(SpiceChannel *channel);
 gint spice_channel_get_channel_type(SpiceChannel *channel);
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 093b292..a0d9c15 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2763,24 +2763,18 @@ void spice_channel_swap(SpiceChannel *channel, SpiceChannel *swap, gboolean swap
 #endif
 }
 
-static const spice_msg_handler base_handlers[] = {
-    [ SPICE_MSG_SET_ACK ]                  = spice_channel_handle_set_ack,
-    [ SPICE_MSG_PING ]                     = spice_channel_handle_ping,
-    [ SPICE_MSG_NOTIFY ]                   = spice_channel_handle_notify,
-    [ SPICE_MSG_DISCONNECTING ]            = spice_channel_handle_disconnect,
-    [ SPICE_MSG_WAIT_FOR_CHANNELS ]        = spice_channel_handle_wait_for_channels,
-    [ SPICE_MSG_MIGRATE ]                  = spice_channel_handle_migrate,
-};
-
 /* coroutine context */
 static void spice_channel_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
 {
+    SpiceChannelClass *klass = SPICE_CHANNEL_GET_CLASS(channel);
     int type = spice_msg_in_type(msg);
+    spice_msg_handler handler;
 
-    g_return_if_fail(type < SPICE_N_ELEMENTS(base_handlers));
-    g_return_if_fail(base_handlers[type] != NULL);
+    g_return_if_fail(type < klass->handlers->len);
 
-    base_handlers[type](channel, msg);
+    handler = g_array_index(klass->handlers, spice_msg_handler, type);
+    g_return_if_fail(handler != NULL);
+    handler(channel, msg);
 }
 
 static void spice_channel_reset_capabilities(SpiceChannel *channel)
commit ed830150b05ec44af285c49b1a633922636a6e41
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Sep 12 14:01:00 2013 +0200

    channel: add spice_channel_set_handlers()
    
    This function will allow to set base handlers and specific channel
    handlers in a common way, instead of each channel having to override the
    base channel virtual handle_msg().

diff --git a/gtk/channel-base.c b/gtk/channel-base.c
index dff3024..76d681a 100644
--- a/gtk/channel-base.c
+++ b/gtk/channel-base.c
@@ -189,3 +189,42 @@ void spice_channel_handle_migrate(SpiceChannel *channel, SpiceMsgIn *in)
         spice_msg_out_send_internal(out);
     }
 }
+
+
+static void set_handlers(SpiceChannelClass *klass,
+                         const spice_msg_handler* handlers, const int n)
+{
+    int i;
+
+    g_array_set_size(klass->handlers, MAX(klass->handlers->len, n));
+    for (i = 0; i < n; i++) {
+        if (handlers[i])
+            g_array_index(klass->handlers, spice_msg_handler, i) = handlers[i];
+    }
+}
+
+static void spice_channel_add_base_handlers(SpiceChannelClass *klass)
+{
+    static const spice_msg_handler handlers[] = {
+        [ SPICE_MSG_SET_ACK ]                  = spice_channel_handle_set_ack,
+        [ SPICE_MSG_PING ]                     = spice_channel_handle_ping,
+        [ SPICE_MSG_NOTIFY ]                   = spice_channel_handle_notify,
+        [ SPICE_MSG_DISCONNECTING ]            = spice_channel_handle_disconnect,
+        [ SPICE_MSG_WAIT_FOR_CHANNELS ]        = spice_channel_handle_wait_for_channels,
+        [ SPICE_MSG_MIGRATE ]                  = spice_channel_handle_migrate,
+    };
+
+    set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
+}
+
+G_GNUC_INTERNAL
+void spice_channel_set_handlers(SpiceChannelClass *klass,
+                                const spice_msg_handler* handlers, const int n)
+{
+    /* FIXME: use class private (requires glib 2.24) */
+    g_return_if_fail(klass->handlers == NULL);
+    klass->handlers = g_array_sized_new(FALSE, TRUE, sizeof(spice_msg_handler), n);
+
+    spice_channel_add_base_handlers(klass);
+    set_handlers(klass, handlers, n);
+}
diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index be061c5..0070d71 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -172,6 +172,8 @@ void spice_channel_handle_set_ack(SpiceChannel *channel, SpiceMsgIn *in);
 void spice_channel_handle_ping(SpiceChannel *channel, SpiceMsgIn *in);
 void spice_channel_handle_notify(SpiceChannel *channel, SpiceMsgIn *in);
 void spice_channel_handle_disconnect(SpiceChannel *channel, SpiceMsgIn *in);
+void spice_channel_set_handlers(SpiceChannelClass *klass,
+                                const spice_msg_handler* handlers, const int n);
 void spice_channel_handle_wait_for_channels(SpiceChannel *channel, SpiceMsgIn *in);
 void spice_channel_handle_migrate(SpiceChannel *channel, SpiceMsgIn *in);
 
diff --git a/gtk/spice-channel.h b/gtk/spice-channel.h
index 0507b68..705dddf 100644
--- a/gtk/spice-channel.h
+++ b/gtk/spice-channel.h
@@ -94,11 +94,12 @@ struct _SpiceChannelClass
     /* virtual methods, coroutine context */
     void (*channel_send_migration_handshake)(SpiceChannel *channel);
 
+    GArray                      *handlers;
     /*
      * If adding fields to this struct, remove corresponding
      * amount of padding to avoid changing overall struct size
      */
-    gchar _spice_reserved[SPICE_RESERVED_PADDING - sizeof(void *)];
+    gchar _spice_reserved[SPICE_RESERVED_PADDING - 2 * sizeof(void *)];
 };
 
 GType spice_channel_get_type(void);
commit 5b6e3d1c16457c926322ce28d341af2e8d39efb5
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Aug 21 18:09:11 2013 +0200

    display: use bitfields for surface flags
    
    Checking by value make the flag fields useless. Unfortunately, when
    adding more flags, the server will have to ensure it can safely send it,
    by checking some of new client caps (for some features).

diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 75fa8c2..704d5a7 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -1741,7 +1741,8 @@ static void display_handle_surface_create(SpiceChannel *channel, SpiceMsgIn *in)
     surface->stride = create->width * 4;
     surface->size   = surface->height * surface->stride;
 
-    if (create->flags == SPICE_SURFACE_FLAGS_PRIMARY) {
+    if (create->flags & SPICE_SURFACE_FLAGS_PRIMARY) {
+        SPICE_DEBUG("primary flags: %d", create->flags);
         surface->primary = true;
         create_canvas(channel, surface);
         if (c->mark_false_event_id != 0) {
commit 838a3dfe2529b0bb296ead1cd047cfe1d43649c5
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Fri Sep 13 15:12:45 2013 +0200

    Update spice-common

diff --git a/spice-common b/spice-common
index fc27fb2..3363fe7 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit fc27fb20b8ecd3e07809aec884f99f2121712bc9
+Subproject commit 3363fe79f9d450774dd018a794c0b4381c1ef0a7


More information about the Spice-commits mailing list