[Spice-devel] [PATCH 14/14] Convert RedChannel heirarchy to GObject

Frediano Ziglio fziglio at redhat.com
Wed May 4 08:28:27 UTC 2016


A patch of this size cannot be accepted, no matter how good it is.
We need to find a way to split it.

Any proposal would be good.

Frediano

> 
> When using private structs with GObject, there's a maximum size of (I
> think) 64k, which was exceeded by the DisplayChannel object. To make
> this work, I had to make several of the arrays here dynamically
> allocated rather than statically allocated.
> ---
>  server/Makefile.am                      |   5 +
>  server/common-graphics-channel-client.c |   3 +-
>  server/common-graphics-channel-client.h |   3 +-
>  server/common-graphics-channel.c        | 181 ++++++++
>  server/common-graphics-channel.h        |  98 +++++
>  server/cursor-channel.c                 | 126 +++---
>  server/cursor-channel.h                 |  37 +-
>  server/dcc-send.c                       |  44 +-
>  server/dcc.c                            |  61 +--
>  server/dcc.h                            |   4 +-
>  server/display-channel-private.h        |  81 ++++
>  server/display-channel.c                | 726
>  ++++++++++++++++++++------------
>  server/display-channel.h                | 158 +++----
>  server/dummy-channel-client.c           |  17 +-
>  server/dummy-channel.c                  |  58 +++
>  server/dummy-channel.h                  |  61 +++
>  server/inputs-channel.c                 | 262 +++++++-----
>  server/inputs-channel.h                 |  30 ++
>  server/main-channel-client.c            |  47 +--
>  server/main-channel-client.h            |   4 +-
>  server/main-channel.c                   | 241 ++++++-----
>  server/main-channel.h                   |  44 +-
>  server/red-channel-client-private.h     |  19 +
>  server/red-channel-client.c             | 197 +++++----
>  server/red-channel-client.h             |   6 +-
>  server/red-channel.c                    | 684 ++++++++++++++++++++----------
>  server/red-channel.h                    | 190 ++++-----
>  server/red-parse-qxl.h                  |   2 +
>  server/red-qxl.c                        |  21 +-
>  server/red-replay-qxl.c                 |   2 +-
>  server/red-worker.c                     | 197 ++-------
>  server/red-worker.h                     |  64 ---
>  server/reds-private.h                   |   3 +-
>  server/reds.c                           |  67 +--
>  server/smartcard.c                      | 131 ++++--
>  server/sound.c                          |  43 +-
>  server/spicevmc.c                       | 339 ++++++++++-----
>  server/stream.c                         |  67 +--
>  server/stream.h                         |   3 -
>  39 files changed, 2753 insertions(+), 1573 deletions(-)
>  create mode 100644 server/common-graphics-channel.c
>  create mode 100644 server/common-graphics-channel.h
>  create mode 100644 server/display-channel-private.h
>  create mode 100644 server/dummy-channel.c
>  create mode 100644 server/dummy-channel.h
> 
> diff --git a/server/Makefile.am b/server/Makefile.am
> index b1de055..2679082 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -73,6 +73,8 @@ libserver_la_SOURCES =				\
>  	cache-item.h				\
>  	char-device.c				\
>  	char-device.h				\
> +	common-graphics-channel.c			\
> +	common-graphics-channel.h			\
>  	common-graphics-channel-client.c		\
>  	common-graphics-channel-client.h		\
>  	common-graphics-channel-client-private.h	\
> @@ -102,6 +104,8 @@ libserver_la_SOURCES =				\
>  	red-channel-client.c			\
>  	red-channel-client.h			\
>  	red-channel-client-private.h		\
> +	dummy-channel.c				\
> +	dummy-channel.h				\
>  	dummy-channel-client.c			\
>  	dummy-channel-client.h			\
>  	red-common.h				\
> @@ -124,6 +128,7 @@ libserver_la_SOURCES =				\
>  	red-worker.h				\
>  	display-channel.c			\
>  	display-channel.h			\
> +	display-channel-private.h		\
>  	cursor-channel-client.c		\
>  	cursor-channel-client.h		\
>  	cursor-channel.c			\
> diff --git a/server/common-graphics-channel-client.c
> b/server/common-graphics-channel-client.c
> index e83855b..6277d0b 100644
> --- a/server/common-graphics-channel-client.c
> +++ b/server/common-graphics-channel-client.c
> @@ -19,6 +19,7 @@
>  #endif
>  
>  #include "common-graphics-channel-client-private.h"
> +#include "common-graphics-channel.h"
>  #include "dcc.h"
>  #include "red-channel-client.h"
>  
> @@ -89,7 +90,7 @@ static void
> common_graphics_channel_client_constructed(GObject *object)
>                                               self->priv->is_low_bandwidth ?
>                                               WIDE_CLIENT_ACK_WINDOW :
>                                               NARROW_CLIENT_ACK_WINDOW);
>  
> -    channel->during_target_migrate = self->priv->migration_target;
> +    common_graphics_channel_set_during_target_migrate(channel,
> self->priv->migration_target);
>  }
>  
>  static void
>  common_graphics_channel_client_class_init(CommonGraphicsChannelClientClass
>  *klass)
> diff --git a/server/common-graphics-channel-client.h
> b/server/common-graphics-channel-client.h
> index dc1173a..7acb3a2 100644
> --- a/server/common-graphics-channel-client.h
> +++ b/server/common-graphics-channel-client.h
> @@ -21,6 +21,8 @@
>  #include "red-common.h"
>  #include "red-channel-client.h"
>  
> +#define COMMON_CLIENT_TIMEOUT (NSEC_PER_SEC * 30)
> +
>  G_BEGIN_DECLS
>  
>  #define TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT
>  common_graphics_channel_client_get_type()
> @@ -49,7 +51,6 @@ struct CommonGraphicsChannelClientClass
>  
>  GType common_graphics_channel_client_get_type(void) G_GNUC_CONST;
>  
> -typedef struct CommonGraphicsChannel CommonGraphicsChannel;
>  typedef struct RedClient RedClient;
>  typedef struct RedsStream RedsStream;
>  
> diff --git a/server/common-graphics-channel.c
> b/server/common-graphics-channel.c
> new file mode 100644
> index 0000000..fe211aa
> --- /dev/null
> +++ b/server/common-graphics-channel.c
> @@ -0,0 +1,181 @@
> +/* common-graphics-channel.c */
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <fcntl.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <netinet/tcp.h>
> +
> +#include "common-graphics-channel.h"
> +#include "common-graphics-channel-client.h"
> +
> +#define CHANNEL_RECEIVE_BUF_SIZE 1024
> +
> +G_DEFINE_ABSTRACT_TYPE(CommonGraphicsChannel, common_graphics_channel,
> RED_TYPE_CHANNEL)
> +
> +#define GRAPHICS_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o),
> TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannelPrivate))
> +
> +struct CommonGraphicsChannelPrivate
> +{
> +    QXLInstance *qxl;
> +    uint8_t recv_buf[CHANNEL_RECEIVE_BUF_SIZE];
> +    uint32_t id_alloc; // bitfield. TODO - use this instead of shift scheme.
> +    int during_target_migrate; /* TRUE when the client that is associated
> with the channel
> +                                  is during migration. Turned off when the
> vm is started.
> +                                  The flag is used to avoid sending messages
> that are artifacts
> +                                  of the transition from stopped vm to
> loaded vm (e.g., recreation
> +                                  of the primary surface) */
> +};
> +
> +enum {
> +    PROP0,
> +    PROP_QXL
> +};
> +
> +static void
> +common_graphics_channel_get_property(GObject *object,
> +                                   guint property_id,
> +                                   GValue *value,
> +                                   GParamSpec *pspec)
> +{
> +    CommonGraphicsChannel *self = COMMON_GRAPHICS_CHANNEL(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_QXL:
> +            g_value_set_pointer(value, self->priv->qxl);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void
> +common_graphics_channel_set_property(GObject *object,
> +                                   guint property_id,
> +                                   const GValue *value,
> +                                   GParamSpec *pspec)
> +{
> +    CommonGraphicsChannel *self = COMMON_GRAPHICS_CHANNEL(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_QXL:
> +            self->priv->qxl = g_value_get_pointer(value);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static int common_channel_config_socket(RedChannelClient *rcc)
> +{
> +    RedClient *client = red_channel_client_get_client(rcc);
> +    MainChannelClient *mcc = red_client_get_main(client);
> +    RedsStream *stream = red_channel_client_get_stream(rcc);
> +    CommonGraphicsChannelClient *ccc = COMMON_GRAPHICS_CHANNEL_CLIENT(rcc);
> +    int flags;
> +    int delay_val;
> +    gboolean low_bw;
> +
> +    if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
> +        spice_warning("accept failed, %s", strerror(errno));
> +        return FALSE;
> +    }
> +
> +    if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) {
> +        spice_warning("accept failed, %s", strerror(errno));
> +        return FALSE;
> +    }
> +
> +    // TODO - this should be dynamic, not one time at channel creation
> +    low_bw = main_channel_client_is_low_bandwidth(mcc);
> +    common_graphics_channel_client_set_low_bandwidth(ccc, low_bw);
> +    delay_val = low_bw ? 0 : 1;
> +    /* FIXME: Using Nagle's Algorithm can lead to apparent delays, depending
> +     * on the delayed ack timeout on the other side.
> +     * Instead of using Nagle's, we need to implement message buffering on
> +     * the application level.
> +     * see: http://www.stuartcheshire.org/papers/NagleDelayedAck/
> +     */
> +    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val,
> +                   sizeof(delay_val)) == -1) {
> +        if (errno != ENOTSUP) {
> +            spice_warning("setsockopt failed, %s", strerror(errno));
> +        }
> +    }
> +    return TRUE;
> +}
> +
> +static uint8_t *common_alloc_recv_buf(RedChannelClient *rcc, uint16_t type,
> uint32_t size)
> +{
> +    RedChannel *channel = red_channel_client_get_channel(rcc);
> +    CommonGraphicsChannel *common = COMMON_GRAPHICS_CHANNEL(channel);
> +
> +    /* SPICE_MSGC_MIGRATE_DATA is the only client message whose size is
> dynamic */
> +    if (type == SPICE_MSGC_MIGRATE_DATA) {
> +        return spice_malloc(size);
> +    }
> +
> +    if (size > CHANNEL_RECEIVE_BUF_SIZE) {
> +        spice_critical("unexpected message size %u (max is %d)", size,
> CHANNEL_RECEIVE_BUF_SIZE);
> +        return NULL;
> +    }
> +    return common->priv->recv_buf;
> +}
> +
> +static void common_release_recv_buf(RedChannelClient *rcc, uint16_t type,
> uint32_t size,
> +                                    uint8_t* msg)
> +{
> +    if (type == SPICE_MSGC_MIGRATE_DATA) {
> +        free(msg);
> +    }
> +}
> +
> +static void
> +common_graphics_channel_class_init(CommonGraphicsChannelClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
> +
> +    g_type_class_add_private(klass, sizeof(CommonGraphicsChannelPrivate));
> +
> +    object_class->get_property = common_graphics_channel_get_property;
> +    object_class->set_property = common_graphics_channel_set_property;
> +
> +    channel_class->config_socket = common_channel_config_socket;
> +    channel_class->alloc_recv_buf = common_alloc_recv_buf;
> +    channel_class->release_recv_buf = common_release_recv_buf;
> +
> +    g_object_class_install_property(object_class,
> +                                    PROP_QXL,
> +                                    g_param_spec_pointer("qxl",
> +                                                         "qxl",
> +                                                         "QXLInstance for
> this channel",
> +                                                         G_PARAM_READWRITE |
> +
> G_PARAM_CONSTRUCT_ONLY
> |
> +
> G_PARAM_STATIC_STRINGS));
> +}
> +
> +static void
> +common_graphics_channel_init(CommonGraphicsChannel *self)
> +{
> +    self->priv = GRAPHICS_CHANNEL_PRIVATE(self);
> +}
> +
> +void common_graphics_channel_set_during_target_migrate(CommonGraphicsChannel
> *self, gboolean value)
> +{
> +    self->priv->during_target_migrate = value;
> +}
> +
> +gboolean
> common_graphics_channel_get_during_target_migrate(CommonGraphicsChannel
> *self)
> +{
> +    return self->priv->during_target_migrate;
> +}
> +
> +QXLInstance* common_graphics_channel_get_qxl(CommonGraphicsChannel *self)
> +{
> +    return self->priv->qxl;
> +}
> diff --git a/server/common-graphics-channel.h
> b/server/common-graphics-channel.h
> new file mode 100644
> index 0000000..949470e
> --- /dev/null
> +++ b/server/common-graphics-channel.h
> @@ -0,0 +1,98 @@
> +/* common-graphics-channel.h */
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see
> <http://www.gnu.org/licenses/>.
> +   */
> +
> +#ifndef __COMMON_GRAPHICS_CHANNEL_H__
> +#define __COMMON_GRAPHICS_CHANNEL_H__
> +
> +#include <glib-object.h>
> +
> +#include "red-channel.h"
> +
> +G_BEGIN_DECLS
> +
> +#define TYPE_COMMON_GRAPHICS_CHANNEL common_graphics_channel_get_type()
> +
> +#define COMMON_GRAPHICS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannel))
> +#define COMMON_GRAPHICS_CHANNEL_CLASS(klass)
> (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COMMON_GRAPHICS_CHANNEL,
> CommonGraphicsChannelClass))
> +#define COMMON_IS_GRAPHICS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> TYPE_COMMON_GRAPHICS_CHANNEL))
> +#define COMMON_IS_GRAPHICS_CHANNEL_CLASS(klass)
> (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COMMON_GRAPHICS_CHANNEL))
> +#define COMMON_GRAPHICS_CHANNEL_GET_CLASS(obj)
> (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_COMMON_GRAPHICS_CHANNEL,
> CommonGraphicsChannelClass))
> +
> +typedef struct CommonGraphicsChannel CommonGraphicsChannel;
> +typedef struct CommonGraphicsChannelClass CommonGraphicsChannelClass;
> +typedef struct CommonGraphicsChannelPrivate CommonGraphicsChannelPrivate;
> +
> +struct CommonGraphicsChannel
> +{
> +    RedChannel parent;
> +
> +    CommonGraphicsChannelPrivate *priv;
> +};
> +
> +struct CommonGraphicsChannelClass
> +{
> +    RedChannelClass parent_class;
> +};
> +
> +GType common_graphics_channel_get_type(void) G_GNUC_CONST;
> +
> +void common_graphics_channel_set_during_target_migrate(CommonGraphicsChannel
> *self, gboolean value);
> +gboolean
> common_graphics_channel_get_during_target_migrate(CommonGraphicsChannel
> *self);
> +QXLInstance* common_graphics_channel_get_qxl(CommonGraphicsChannel *self);
> +
> +enum {
> +    RED_PIPE_ITEM_TYPE_VERB = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
> +    RED_PIPE_ITEM_TYPE_INVAL_ONE,
> +
> +    RED_PIPE_ITEM_TYPE_COMMON_LAST
> +};
> +
> +typedef struct RedVerbItem {
> +    RedPipeItem base;
> +    uint16_t verb;
> +} RedVerbItem;
> +
> +static inline void red_marshall_verb(RedChannelClient *rcc, RedVerbItem
> *item)
> +{
> +    red_channel_client_init_send_data(rcc, item->verb, NULL);
> +}
> +
> +static inline void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb)
> +{
> +    RedVerbItem *item = spice_new(RedVerbItem, 1);
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_VERB);
> +    item->verb = verb;
> +    red_channel_client_pipe_add(rcc, &item->base);
> +}
> +
> +static inline void red_pipe_add_verb_proxy(RedChannelClient *rcc, gpointer
> data)
> +{
> +    uint16_t verb = GPOINTER_TO_UINT(data);
> +    red_pipe_add_verb(rcc, verb);
> +}
> +
> +static inline void red_pipes_add_verb(RedChannel *channel, uint16_t verb)
> +{
> +    red_channel_apply_clients_data(channel, red_pipe_add_verb_proxy,
> GUINT_TO_POINTER(verb));
> +}
> +
> +
> +G_END_DECLS
> +
> +#endif /* __COMMON_GRAPHICS_CHANNEL_H__ */
> diff --git a/server/cursor-channel.c b/server/cursor-channel.c
> index 032a1e1..26c72d8 100644
> --- a/server/cursor-channel.c
> +++ b/server/cursor-channel.c
> @@ -24,6 +24,7 @@
>  #include "common-graphics-channel-client-private.h"
>  #include "cursor-channel.h"
>  #include "reds.h"
> +#include "red-qxl.h"
>  
>  enum {
>      RED_PIPE_ITEM_TYPE_CURSOR = RED_PIPE_ITEM_TYPE_COMMON_LAST,
> @@ -45,9 +46,12 @@ typedef struct RedCursorPipeItem {
>      int refs;
>  } RedCursorPipeItem;
>  
> -struct CursorChannel {
> -    CommonGraphicsChannel common; // Must be the first thing
> +G_DEFINE_TYPE(CursorChannel, cursor_channel, TYPE_COMMON_GRAPHICS_CHANNEL)
>  
> +#define CURSOR_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o),
> TYPE_CURSOR_CHANNEL, CursorChannelPrivate))
> +
> +struct CursorChannelPrivate
> +{
>      CursorItem *item;
>      int cursor_visible;
>      SpicePoint16 cursor_position;
> @@ -104,10 +108,10 @@ static void cursor_item_unref(CursorItem *item)
>  
>  static void cursor_set_item(CursorChannel *cursor, CursorItem *item)
>  {
> -    if (cursor->item)
> -        cursor_item_unref(cursor->item);
> +    if (cursor->priv->item)
> +        cursor_item_unref(cursor->priv->item);
>  
> -    cursor->item = item ? cursor_item_ref(item) : NULL;
> +    cursor->priv->item = item ? cursor_item_ref(item) : NULL;
>  }
>  
>  static RedPipeItem *new_cursor_pipe_item(RedChannelClient *rcc, void *data,
>  int num)
> @@ -237,12 +241,12 @@ static void red_marshall_cursor_init(RedChannelClient
> *rcc, SpiceMarshaller *bas
>      cursor_channel = (CursorChannel*)red_channel_client_get_channel(rcc);
>  
>      red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_INIT, NULL);
> -    msg.visible = cursor_channel->cursor_visible;
> -    msg.position = cursor_channel->cursor_position;
> -    msg.trail_length = cursor_channel->cursor_trail_length;
> -    msg.trail_frequency = cursor_channel->cursor_trail_frequency;
> +    msg.visible = cursor_channel->priv->cursor_visible;
> +    msg.position = cursor_channel->priv->cursor_position;
> +    msg.trail_length = cursor_channel->priv->cursor_trail_length;
> +    msg.trail_frequency = cursor_channel->priv->cursor_trail_frequency;
>  
> -    cursor_fill(ccc, &msg.cursor, cursor_channel->item, &info);
> +    cursor_fill(ccc, &msg.cursor, cursor_channel->priv->item, &info);
>      spice_marshall_msg_cursor_init(base_marshaller, &msg);
>      add_buf_from_info(base_marshaller, &info);
>  }
> @@ -251,8 +255,7 @@ static void cursor_marshall(RedChannelClient *rcc,
>                              SpiceMarshaller *m,
>                              RedCursorPipeItem *cursor_pipe_item)
>  {
> -    CursorChannel *cursor_channel =
> SPICE_CONTAINEROF(red_channel_client_get_channel(rcc),
> -                                                      CursorChannel,
> common.base);
> +    CursorChannel *cursor_channel =
> CURSOR_CHANNEL(red_channel_client_get_channel(rcc));
>      CursorChannelClient *ccc = CURSOR_CHANNEL_CLIENT(rcc);
>      CursorItem *item = cursor_pipe_item->cursor_item;
>      RedPipeItem *pipe_item = &cursor_pipe_item->base;
> @@ -277,7 +280,7 @@ static void cursor_marshall(RedChannelClient *rcc,
>  
>              red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_SET,
>              pipe_item);
>              cursor_set.position = cmd->u.set.position;
> -            cursor_set.visible = cursor_channel->cursor_visible;
> +            cursor_set.visible = cursor_channel->priv->cursor_visible;
>  
>              cursor_fill(ccc, &cursor_set.cursor, item, &info);
>              spice_marshall_msg_cursor_set(m, &cursor_set);
> @@ -378,27 +381,17 @@ static void
> cursor_channel_release_item(RedChannelClient *rcc, RedPipeItem *item
>      }
>  }
>  
> -CursorChannel* cursor_channel_new(RedWorker *worker)
> +CursorChannel* cursor_channel_new(SpiceServer *reds, QXLInstance *qxl,
> +                                  const SpiceCoreInterfaceInternal *core)
>  {
> -    CursorChannel *cursor_channel;
> -    CommonGraphicsChannel *channel = NULL;
> -    ChannelCbs cbs = {
> -        .on_disconnect =  cursor_channel_client_on_disconnect,
> -        .send_item = cursor_channel_send_item,
> -        .hold_item = cursor_channel_hold_pipe_item,
> -        .release_item = cursor_channel_release_item
> -    };
> -
>      spice_info("create cursor channel");
> -    channel = red_worker_new_channel(worker, sizeof(CursorChannel),
> "cursor_channel",
> -                                     SPICE_CHANNEL_CURSOR, 0,
> -                                     &cbs,
> red_channel_client_handle_message);
> -
> -    cursor_channel = (CursorChannel *)channel;
> -    cursor_channel->cursor_visible = TRUE;
> -    cursor_channel->mouse_mode = SPICE_MOUSE_MODE_SERVER;
> -
> -    return cursor_channel;
> +    return g_object_new(TYPE_CURSOR_CHANNEL,
> +                        "spice-server", reds,
> +                        "core-interface", core,
> +                        "channel-type", SPICE_CHANNEL_CURSOR,
> +                        "migration-flags", 0,
> +                        "qxl", qxl,
> +                        NULL);
>  }
>  
>  void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd
>  *cursor_cmd)
> @@ -409,35 +402,36 @@ void cursor_channel_process_cmd(CursorChannel *cursor,
> RedCursorCmd *cursor_cmd)
>      spice_return_if_fail(cursor);
>      spice_return_if_fail(cursor_cmd);
>  
> -    cursor_item = cursor_item_new(cursor->common.qxl, cursor_cmd);
> +    cursor_item =
> cursor_item_new(common_graphics_channel_get_qxl(COMMON_GRAPHICS_CHANNEL(cursor)),
> +                                  cursor_cmd);
>  
>      switch (cursor_cmd->type) {
>      case QXL_CURSOR_SET:
> -        cursor->cursor_visible = cursor_cmd->u.set.visible;
> +        cursor->priv->cursor_visible = cursor_cmd->u.set.visible;
>          cursor_set_item(cursor, cursor_item);
>          break;
>      case QXL_CURSOR_MOVE:
> -        cursor_show = !cursor->cursor_visible;
> -        cursor->cursor_visible = TRUE;
> -        cursor->cursor_position = cursor_cmd->u.position;
> +        cursor_show = !cursor->priv->cursor_visible;
> +        cursor->priv->cursor_visible = TRUE;
> +        cursor->priv->cursor_position = cursor_cmd->u.position;
>          break;
>      case QXL_CURSOR_HIDE:
> -        cursor->cursor_visible = FALSE;
> +        cursor->priv->cursor_visible = FALSE;
>          break;
>      case QXL_CURSOR_TRAIL:
> -        cursor->cursor_trail_length = cursor_cmd->u.trail.length;
> -        cursor->cursor_trail_frequency = cursor_cmd->u.trail.frequency;
> +        cursor->priv->cursor_trail_length = cursor_cmd->u.trail.length;
> +        cursor->priv->cursor_trail_frequency =
> cursor_cmd->u.trail.frequency;
>          break;
>      default:
>          spice_warning("invalid cursor command %u", cursor_cmd->type);
>          return;
>      }
>  
> -    if (red_channel_is_connected(&cursor->common.base) &&
> -        (cursor->mouse_mode == SPICE_MOUSE_MODE_SERVER
> +    if (red_channel_is_connected(RED_CHANNEL(cursor)) &&
> +        (cursor->priv->mouse_mode == SPICE_MOUSE_MODE_SERVER
>           || cursor_cmd->type != QXL_CURSOR_MOVE
>           || cursor_show)) {
> -        red_channel_pipes_new_add(&cursor->common.base,
> +        red_channel_pipes_new_add(RED_CHANNEL(cursor),
>                                    new_cursor_pipe_item, cursor_item);
>      }
>  
> @@ -446,34 +440,34 @@ void cursor_channel_process_cmd(CursorChannel *cursor,
> RedCursorCmd *cursor_cmd)
>  
>  void cursor_channel_reset(CursorChannel *cursor)
>  {
> -    RedChannel *channel = &cursor->common.base;
> +    RedChannel *channel = RED_CHANNEL(cursor);
>  
>      spice_return_if_fail(cursor);
>  
>      cursor_set_item(cursor, NULL);
> -    cursor->cursor_visible = TRUE;
> -    cursor->cursor_position.x = cursor->cursor_position.y = 0;
> -    cursor->cursor_trail_length = cursor->cursor_trail_frequency = 0;
> +    cursor->priv->cursor_visible = TRUE;
> +    cursor->priv->cursor_position.x = cursor->priv->cursor_position.y = 0;
> +    cursor->priv->cursor_trail_length = cursor->priv->cursor_trail_frequency
> = 0;
>  
>      if (red_channel_is_connected(channel)) {
>          red_channel_pipes_add_type(channel,
>          RED_PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
> -        if (!cursor->common.during_target_migrate) {
> +        if
> (!common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(cursor)))
> {
>              red_pipes_add_verb(channel, SPICE_MSG_CURSOR_RESET);
>          }
> -        if (!red_channel_wait_all_sent(&cursor->common.base,
> +        if (!red_channel_wait_all_sent(RED_CHANNEL(cursor),
>                                         COMMON_CLIENT_TIMEOUT)) {
> -            red_channel_apply_clients(channel,
> +            red_channel_apply_clients(RED_CHANNEL(cursor),
>                                        red_channel_client_disconnect_if_pending_send);
>          }
>      }
>  }
>  
> -void cursor_channel_init(CursorChannel *cursor, CursorChannelClient *client)
> +void cursor_channel_do_init(CursorChannel *cursor, CursorChannelClient
> *client)
>  {
>      spice_return_if_fail(cursor);
>  
> -    if (!red_channel_is_connected(&cursor->common.base)
> -        || COMMON_GRAPHICS_CHANNEL(cursor)->during_target_migrate) {
> +    if (!red_channel_is_connected(RED_CHANNEL(cursor))
> +        ||
> common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(cursor)))
> {
>          spice_debug("during_target_migrate: skip init");
>          return;
>      }
> @@ -489,5 +483,29 @@ void cursor_channel_set_mouse_mode(CursorChannel
> *cursor, uint32_t mode)
>  {
>      spice_return_if_fail(cursor);
>  
> -    cursor->mouse_mode = mode;
> +    cursor->priv->mouse_mode = mode;
> +}
> +
> +static void
> +cursor_channel_class_init(CursorChannelClass *klass)
> +{
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
> +
> +    g_type_class_add_private(klass, sizeof(CursorChannelPrivate));
> +
> +    channel_class->parser =
> spice_get_client_channel_parser(SPICE_CHANNEL_CURSOR, NULL);
> +    channel_class->handle_parsed = red_channel_client_handle_message;
> +
> +    channel_class->on_disconnect =  cursor_channel_client_on_disconnect;
> +    channel_class->send_item = cursor_channel_send_item;
> +    channel_class->hold_item = cursor_channel_hold_pipe_item;
> +    channel_class->release_item = cursor_channel_release_item;
> +}
> +
> +static void
> +cursor_channel_init(CursorChannel *self)
> +{
> +    self->priv = CURSOR_CHANNEL_PRIVATE(self);
> +    self->priv->cursor_visible = TRUE;
> +    self->priv->mouse_mode = SPICE_MOUSE_MODE_SERVER;
>  }
> diff --git a/server/cursor-channel.h b/server/cursor-channel.h
> index 2b09b21..81ad96f 100644
> --- a/server/cursor-channel.h
> +++ b/server/cursor-channel.h
> @@ -19,16 +19,47 @@
>  # define CURSOR_CHANNEL_H_
>  
>  #include "cursor-channel-client.h"
> -#include "red-worker.h"
> +#include "common-graphics-channel.h"
> +#include "red-parse-qxl.h"
> +
> +G_BEGIN_DECLS
> +
> +#define TYPE_CURSOR_CHANNEL cursor_channel_get_type()
> +
> +#define CURSOR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> TYPE_CURSOR_CHANNEL, CursorChannel))
> +#define CURSOR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
> TYPE_CURSOR_CHANNEL, CursorChannelClass))
> +#define IS_CURSOR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> TYPE_CURSOR_CHANNEL))
> +#define IS_CURSOR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
> TYPE_CURSOR_CHANNEL))
> +#define CURSOR_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
> TYPE_CURSOR_CHANNEL, CursorChannelClass))
>  
>  typedef struct CursorChannel CursorChannel;
> +typedef struct CursorChannelClass CursorChannelClass;
> +typedef struct CursorChannelPrivate CursorChannelPrivate;
> +
> +struct CursorChannel
> +{
> +    CommonGraphicsChannel parent;
> +
> +    CursorChannelPrivate *priv;
> +};
> +
> +struct CursorChannelClass
> +{
> +    CommonGraphicsChannelClass parent_class;
> +};
> +
> +GType cursor_channel_get_type(void) G_GNUC_CONST;
> +
>  typedef struct CursorItem CursorItem;
>  
> -CursorChannel*       cursor_channel_new         (RedWorker *worker);
> +CursorChannel*       cursor_channel_new         (SpiceServer *reds,
> QXLInstance *qxl,
> +                                                 const
> SpiceCoreInterfaceInternal *core);
>  void                 cursor_channel_disconnect  (CursorChannel
>  *cursor_channel);
>  void                 cursor_channel_reset       (CursorChannel *cursor);
> -void                 cursor_channel_init        (CursorChannel *cursor,
> CursorChannelClient* client);
> +void                 cursor_channel_do_init     (CursorChannel *cursor,
> CursorChannelClient* client);
>  void                 cursor_channel_process_cmd (CursorChannel *cursor,
>  RedCursorCmd *cursor_cmd);
>  void                 cursor_channel_set_mouse_mode(CursorChannel *cursor,
>  uint32_t mode);
>  
> +G_END_DECLS
> +
>  #endif /* CURSOR_CHANNEL_H_ */
> diff --git a/server/dcc-send.c b/server/dcc-send.c
> index 2525753..9b9af93 100644
> --- a/server/dcc-send.c
> +++ b/server/dcc-send.c
> @@ -21,6 +21,7 @@
>  
>  #include "dcc-private.h"
>  #include "display-channel.h"
> +#include "display-channel-private.h"
>  
>  #include "common/marshaller.h"
>  #include "common/generated_server_marshallers.h"
> @@ -94,9 +95,9 @@ static int is_surface_area_lossy(DisplayChannelClient *dcc,
> uint32_t surface_id,
>      QRegion lossy_region;
>      DisplayChannel *display = DCC_TO_DC(dcc);
>  
> -    spice_return_val_if_fail(validate_surface(display, surface_id), FALSE);
> +    spice_return_val_if_fail(display_channel_validate_surface(display,
> surface_id), FALSE);
>  
> -    surface = &display->surfaces[surface_id];
> +    surface = &display->priv->surfaces[surface_id];
>      surface_lossy_region =
>      &dcc->priv->surface_client_lossy_region[surface_id];
>  
>      if (!area) {
> @@ -197,8 +198,7 @@ static void
> red_display_add_image_to_pixmap_cache(RedChannelClient *rcc,
>                                                    int is_lossy)
>  {
>      DisplayChannel *display_channel =
> -        SPICE_CONTAINEROF(red_channel_client_get_channel(rcc),
> DisplayChannel,
> -                          common.base);
> +        DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
>      DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc);
>  
>      if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
> @@ -210,13 +210,13 @@ static void
> red_display_add_image_to_pixmap_cache(RedChannelClient *rcc,
>                  io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
>                  dcc->priv->send_data.pixmap_cache_items[dcc->priv->send_data.num_pixmap_cache_items++]
>                  =
>                      image->descriptor.id;
> -                stat_inc_counter(reds,
> display_channel->add_to_cache_counter, 1);
> +                stat_inc_counter(reds,
> display_channel->priv->add_to_cache_counter, 1);
>              }
>          }
>      }
>  
>      if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
> -        stat_inc_counter(reds, display_channel->non_cache_counter, 1);
> +        stat_inc_counter(reds, display_channel->priv->non_cache_counter, 1);
>      }
>  }
>  
> @@ -367,7 +367,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc,
> SpiceMarshaller *m,
>              dcc->priv->send_data.pixmap_cache_items[dcc->priv->send_data.num_pixmap_cache_items++]
>              =
>                                                                                 image.descriptor.id;
>              if (can_lossy || !lossy_cache_item) {
> -                if (!display->enable_jpeg || lossy_cache_item) {
> +                if (!display->priv->enable_jpeg || lossy_cache_item) {
>                      image.descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
>                  } else {
>                      // making sure, in multiple monitor scenario, that lossy
>                      items that
> @@ -379,7 +379,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc,
> SpiceMarshaller *m,
>                                       &bitmap_palette_out,
>                                       &lzplt_palette_out);
>                  spice_assert(bitmap_palette_out == NULL);
>                  spice_assert(lzplt_palette_out == NULL);
> -                stat_inc_counter(reds, display->cache_hits_counter, 1);
> +                stat_inc_counter(reds, display->priv->cache_hits_counter,
> 1);
>                  pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock);
>                  return FILL_BITS_TYPE_CACHE;
>              } else {
> @@ -396,13 +396,13 @@ static FillBitsType fill_bits(DisplayChannelClient
> *dcc, SpiceMarshaller *m,
>          RedSurface *surface;
>  
>          surface_id = simage->u.surface.surface_id;
> -        if (!validate_surface(display, surface_id)) {
> +        if (!display_channel_validate_surface(display, surface_id)) {
>              spice_warning("Invalid surface in SPICE_IMAGE_TYPE_SURFACE");
>              pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock);
>              return FILL_BITS_TYPE_SURFACE;
>          }
>  
> -        surface = &display->surfaces[surface_id];
> +        surface = &display->priv->surfaces[surface_id];
>          image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE;
>          image.descriptor.flags = 0;
>          image.descriptor.width = surface->context.width;
> @@ -1692,7 +1692,7 @@ static int red_marshall_stream_data(RedChannelClient
> *rcc,
>          height = stream->height;
>      }
>  
> -    StreamAgent *agent = &dcc->priv->stream_agents[get_stream_id(display,
> stream)];
> +    StreamAgent *agent =
> &dcc->priv->stream_agents[display_channel_get_stream_id(display, stream)];
>      uint64_t time_now = spice_get_monotonic_time_ns();
>      size_t outbuf_size;
>  
> @@ -1739,7 +1739,7 @@ static int red_marshall_stream_data(RedChannelClient
> *rcc,
>  
>          red_channel_client_init_send_data(rcc,
>          SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
>  
> -        stream_data.base.id = get_stream_id(display, stream);
> +        stream_data.base.id = display_channel_get_stream_id(display,
> stream);
>          stream_data.base.multi_media_time = frame_mm_time;
>          stream_data.data_size = n;
>  
> @@ -1749,7 +1749,7 @@ static int red_marshall_stream_data(RedChannelClient
> *rcc,
>  
>          red_channel_client_init_send_data(rcc,
>          SPICE_MSG_DISPLAY_STREAM_DATA_SIZED, NULL);
>  
> -        stream_data.base.id = get_stream_id(display, stream);
> +        stream_data.base.id = display_channel_get_stream_id(display,
> stream);
>          stream_data.base.multi_media_time = frame_mm_time;
>          stream_data.data_size = n;
>          stream_data.width = width;
> @@ -1822,8 +1822,7 @@ static void
> display_channel_marshall_migrate_data(RedChannelClient *rcc,
>      DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc);
>      SpiceMigrateDataDisplay display_data = {0,};
>  
> -    display_channel = SPICE_CONTAINEROF(red_channel_client_get_channel(rcc),
> -                                        DisplayChannel, common.base);
> +    display_channel = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
>  
>      red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, NULL);
>      spice_marshaller_add_uint32(base_marshaller,
>      SPICE_MIGRATE_DATA_DISPLAY_MAGIC);
> @@ -1853,7 +1852,7 @@ static void
> display_channel_marshall_migrate_data(RedChannelClient *rcc,
>      spice_marshaller_add(base_marshaller,
>                           (uint8_t *)&display_data, sizeof(display_data) -
>                           sizeof(uint32_t));
>      display_channel_marshall_migrate_data_surfaces(dcc, base_marshaller,
> -
> display_channel->enable_jpeg);
> +
> display_channel->priv->enable_jpeg);
>  }
>  
>  static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc,
> @@ -2132,8 +2131,7 @@ static void marshall_qxl_drawable(RedChannelClient
> *rcc,
>  
>      Drawable *item = dpi->drawable;
>      DisplayChannel *display =
> -        SPICE_CONTAINEROF(red_channel_client_get_channel(rcc),
> DisplayChannel,
> -                          common.base);
> +        DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
>  
>      spice_return_if_fail(display);
>      /* allow sized frames to be streamed, even if they where replaced by
>      another frame, since
> @@ -2141,7 +2139,7 @@ static void marshall_qxl_drawable(RedChannelClient
> *rcc,
>      if ((item->stream || item->sized_stream) &&
>      red_marshall_stream_data(rcc, m, item)) {
>          return;
>      }
> -    if (display->enable_jpeg)
> +    if (display->priv->enable_jpeg)
>          marshall_lossy_qxl_drawable(rcc, m, dpi);
>      else
>          marshall_lossless_qxl_drawable(rcc, m, dpi);
> @@ -2160,7 +2158,7 @@ static void marshall_stream_start(RedChannelClient
> *rcc,
>      SpiceClipRects clip_rects;
>  
>      stream_create.surface_id = 0;
> -    stream_create.id = get_stream_id(DCC_TO_DC(dcc), stream);
> +    stream_create.id = display_channel_get_stream_id(DCC_TO_DC(dcc),
> stream);
>      stream_create.flags = stream->top_down ? SPICE_STREAM_FLAGS_TOP_DOWN :
>      0;
>      stream_create.codec_type = SPICE_VIDEO_CODEC_TYPE_MJPEG;
>  
> @@ -2196,7 +2194,7 @@ static void marshall_stream_clip(RedChannelClient *rcc,
>      red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CLIP,
>      &item->base);
>      SpiceMsgDisplayStreamClip stream_clip;
>  
> -    stream_clip.id = get_stream_id(DCC_TO_DC(dcc), agent->stream);
> +    stream_clip.id = display_channel_get_stream_id(DCC_TO_DC(dcc),
> agent->stream);
>      stream_clip.clip.type = item->clip_type;
>      stream_clip.clip.rects = item->rects;
>  
> @@ -2210,7 +2208,7 @@ static void marshall_stream_end(RedChannelClient *rcc,
>      SpiceMsgDisplayStreamDestroy destroy;
>  
>      red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY,
>      NULL);
> -    destroy.id = get_stream_id(DCC_TO_DC(dcc), agent->stream);
> +    destroy.id = display_channel_get_stream_id(DCC_TO_DC(dcc),
> agent->stream);
>      stream_agent_stop(agent);
>      spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy);
>  }
> @@ -2319,7 +2317,7 @@ static void marshall_gl_scanout(RedChannelClient *rcc,
>  {
>      DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc);
>      DisplayChannel *display_channel = DCC_TO_DC(dcc);
> -    QXLInstance* qxl = display_channel->common.qxl;
> +    QXLInstance* qxl =
> common_graphics_channel_get_qxl(COMMON_GRAPHICS_CHANNEL(display_channel));
>  
>      SpiceMsgDisplayGlScanoutUnix *scanout = red_qxl_get_gl_scanout(qxl);
>      if (scanout != NULL) {
> diff --git a/server/dcc.c b/server/dcc.c
> index 6b5517e..d3c77f4 100644
> --- a/server/dcc.c
> +++ b/server/dcc.c
> @@ -22,6 +22,7 @@
>  #include "dcc-private.h"
>  #include "dcc.h"
>  #include "display-channel.h"
> +#include "display-channel-private.h"
>  #include "red-channel-client-private.h"
>  #include "spice-server-enums.h"
>  
> @@ -305,12 +306,12 @@ void dcc_create_surface(DisplayChannelClient *dcc, int
> surface_id)
>      flags = is_primary_surface(DCC_TO_DC(dcc), surface_id) ?
>      SPICE_SURFACE_FLAGS_PRIMARY : 0;
>  
>      /* don't send redundant create surface commands to client */
> -    if (!dcc || display->common.during_target_migrate ||
> +    if (!dcc ||
> common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(display))
> ||
>          dcc->priv->surface_client_created[surface_id]) {
>          return;
>      }
>      channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(dcc));
> -    surface = &display->surfaces[surface_id];
> +    surface = &display->priv->surfaces[surface_id];
>      create = red_surface_create_item_new(channel,
>                                           surface_id, surface->context.width,
>                                           surface->context.height,
> @@ -327,7 +328,7 @@ RedImageItem
> *dcc_add_surface_area_image(DisplayChannelClient *dcc,
>                                           int can_lossy)
>  {
>      DisplayChannel *display = DCC_TO_DC(dcc);
> -    RedSurface *surface = &display->surfaces[surface_id];
> +    RedSurface *surface = &display->priv->surfaces[surface_id];
>      SpiceCanvas *canvas = surface->context.canvas;
>      RedImageItem *item;
>      int stride;
> @@ -393,7 +394,7 @@ void dcc_push_surface_image(DisplayChannelClient *dcc,
> int surface_id)
>      }
>  
>      display = DCC_TO_DC(dcc);
> -    surface = &display->surfaces[surface_id];
> +    surface = &display->priv->surfaces[surface_id];
>      if (!surface->context.canvas) {
>          return;
>      }
> @@ -495,7 +496,7 @@ static void dcc_init_stream_agents(DisplayChannelClient
> *dcc)
>      dcc->priv->stream_agents = g_new0(StreamAgent, NUM_STREAMS);
>      for (i = 0; i < NUM_STREAMS; i++) {
>          StreamAgent *agent = &dcc->priv->stream_agents[i];
> -        agent->stream = &display->streams_buf[i];
> +        agent->stream = &display->priv->streams_buf[i];
>          region_init(&agent->vis_region);
>          region_init(&agent->clip);
>          red_pipe_item_init(&agent->create_item,
>          RED_PIPE_ITEM_TYPE_STREAM_CREATE);
> @@ -552,7 +553,7 @@ DisplayChannelClient *dcc_new(DisplayChannel *display,
>  
>  static void dcc_create_all_streams(DisplayChannelClient *dcc)
>  {
> -    Ring *ring = &DCC_TO_DC(dcc)->streams;
> +    Ring *ring = &DCC_TO_DC(dcc)->priv->streams;
>      RingItem *item = ring;
>  
>      while ((item = ring_next(ring, item))) {
> @@ -606,7 +607,7 @@ void dcc_start(DisplayChannelClient *dcc)
>          return;
>  
>      red_channel_client_ack_zero_messages_window(RED_CHANNEL_CLIENT(dcc));
> -    if (display->surfaces[0].context.canvas) {
> +    if (display->priv->surfaces[0].context.canvas) {
>          display_channel_current_flush(display, 0);
>          red_channel_client_pipe_add_type(rcc,
>          RED_PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE);
>          dcc_create_surface(dcc, 0);
> @@ -693,7 +694,7 @@ static RedMonitorsConfigItem
> *red_monitors_config_item_new(RedChannel* channel,
>  void dcc_push_monitors_config(DisplayChannelClient *dcc)
>  {
>      DisplayChannel *dc = DCC_TO_DC(dcc);
> -    MonitorsConfig *monitors_config = dc->monitors_config;
> +    MonitorsConfig *monitors_config = dc->priv->monitors_config;
>      RedMonitorsConfigItem *mci;
>      RedChannel *channel;
>  
> @@ -709,7 +710,7 @@ void dcc_push_monitors_config(DisplayChannelClient *dcc)
>  
>      channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(dcc));
>      mci = red_monitors_config_item_new(channel,
> -
> monitors_config_ref(dc->monitors_config));
> +
> monitors_config_ref(dc->priv->monitors_config));
>      red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &mci->pipe_item);
>      red_channel_client_push(RED_CHANNEL_CLIENT(dcc));
>  }
> @@ -777,7 +778,7 @@ void dcc_destroy_surface(DisplayChannelClient *dcc,
> uint32_t surface_id)
>      display = DCC_TO_DC(dcc);
>      channel = RED_CHANNEL(display);
>  
> -    if (COMMON_GRAPHICS_CHANNEL(display)->during_target_migrate ||
> +    if
> (common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(display))
> ||
>          !dcc->priv->surface_client_created[surface_id]) {
>          return;
>      }
> @@ -859,7 +860,7 @@ static int dcc_compress_image_glz(DisplayChannelClient
> *dcc,
>  {
>      DisplayChannel *display_channel = DCC_TO_DC(dcc);
>      stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &display_channel->zlib_glz_stat);
> +    stat_start_time_init(&start_time,
> &display_channel->priv->zlib_glz_stat);
>      spice_assert(bitmap_fmt_is_rgb(src->format));
>      GlzData *glz_data = &dcc->priv->glz_data;
>      ZlibData *zlib_data;
> @@ -890,12 +891,12 @@ static int dcc_compress_image_glz(DisplayChannelClient
> *dcc,
>                            glz_drawable_instance,
>                            &glz_drawable_instance->context);
>  
> -    stat_compress_add(&display_channel->glz_stat, start_time, src->stride *
> src->y, glz_size);
> +    stat_compress_add(&display_channel->priv->glz_stat, start_time,
> src->stride * src->y, glz_size);
>  
> -    if (!display_channel->enable_zlib_glz_wrap || (glz_size <
> MIN_GLZ_SIZE_FOR_ZLIB)) {
> +    if (!display_channel->priv->enable_zlib_glz_wrap || (glz_size <
> MIN_GLZ_SIZE_FOR_ZLIB)) {
>          goto glz;
>      }
> -    stat_start_time_init(&start_time, &display_channel->zlib_glz_stat);
> +    stat_start_time_init(&start_time,
> &display_channel->priv->zlib_glz_stat);
>      zlib_data = &dcc->priv->zlib_data;
>  
>      encoder_data_init(&zlib_data->data, dcc);
> @@ -922,7 +923,7 @@ static int dcc_compress_image_glz(DisplayChannelClient
> *dcc,
>      o_comp_data->comp_buf = zlib_data->data.bufs_head;
>      o_comp_data->comp_buf_size = zlib_size;
>  
> -    stat_compress_add(&display_channel->zlib_glz_stat, start_time, glz_size,
> zlib_size);
> +    stat_compress_add(&display_channel->priv->zlib_glz_stat, start_time,
> glz_size, zlib_size);
>      return TRUE;
>  glz:
>      dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
> @@ -944,7 +945,7 @@ static int dcc_compress_image_lz(DisplayChannelClient
> *dcc,
>      int size;            // size of the compressed data
>  
>      stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->lz_stat);
> +    stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->priv->lz_stat);
>  
>  #ifdef COMPRESS_DEBUG
>      spice_info("LZ LOCAL compress");
> @@ -995,7 +996,7 @@ static int dcc_compress_image_lz(DisplayChannelClient
> *dcc,
>          o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
>      }
>  
> -    stat_compress_add(&DCC_TO_DC(dcc)->lz_stat, start_time, src->stride *
> src->y,
> +    stat_compress_add(&DCC_TO_DC(dcc)->priv->lz_stat, start_time,
> src->stride * src->y,
>                        o_comp_data->comp_buf_size);
>      return TRUE;
>  }
> @@ -1016,7 +1017,7 @@ static int dcc_compress_image_jpeg(DisplayChannelClient
> *dcc, SpiceImage *dest,
>      int stride;
>      uint8_t *lz_out_start_byte;
>      stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->jpeg_alpha_stat);
> +    stat_start_time_init(&start_time,
> &DCC_TO_DC(dcc)->priv->jpeg_alpha_stat);
>  
>  #ifdef COMPRESS_DEBUG
>      spice_info("JPEG compress");
> @@ -1080,7 +1081,7 @@ static int dcc_compress_image_jpeg(DisplayChannelClient
> *dcc, SpiceImage *dest,
>          o_comp_data->comp_buf_size = jpeg_size;
>          o_comp_data->is_lossy = TRUE;
>  
> -        stat_compress_add(&DCC_TO_DC(dcc)->jpeg_stat, start_time,
> src->stride * src->y,
> +        stat_compress_add(&DCC_TO_DC(dcc)->priv->jpeg_stat, start_time,
> src->stride * src->y,
>                            o_comp_data->comp_buf_size);
>          return TRUE;
>      }
> @@ -1122,7 +1123,7 @@ static int dcc_compress_image_jpeg(DisplayChannelClient
> *dcc, SpiceImage *dest,
>      o_comp_data->comp_buf = jpeg_data->data.bufs_head;
>      o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size;
>      o_comp_data->is_lossy = TRUE;
> -    stat_compress_add(&DCC_TO_DC(dcc)->jpeg_alpha_stat, start_time,
> src->stride * src->y,
> +    stat_compress_add(&DCC_TO_DC(dcc)->priv->jpeg_alpha_stat, start_time,
> src->stride * src->y,
>                        o_comp_data->comp_buf_size);
>      return TRUE;
>  }
> @@ -1135,7 +1136,7 @@ static int dcc_compress_image_lz4(DisplayChannelClient
> *dcc, SpiceImage *dest,
>      Lz4EncoderContext *lz4 = dcc->priv->lz4;
>      int lz4_size = 0;
>      stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->lz4_stat);
> +    stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->priv->lz4_stat);
>  
>  #ifdef COMPRESS_DEBUG
>      spice_info("LZ4 compress");
> @@ -1172,7 +1173,7 @@ static int dcc_compress_image_lz4(DisplayChannelClient
> *dcc, SpiceImage *dest,
>      o_comp_data->comp_buf = lz4_data->data.bufs_head;
>      o_comp_data->comp_buf_size = lz4_size;
>  
> -    stat_compress_add(&DCC_TO_DC(dcc)->lz4_stat, start_time, src->stride *
> src->y,
> +    stat_compress_add(&DCC_TO_DC(dcc)->priv->lz4_stat, start_time,
> src->stride * src->y,
>                        o_comp_data->comp_buf_size);
>      return TRUE;
>  }
> @@ -1186,7 +1187,7 @@ static int dcc_compress_image_quic(DisplayChannelClient
> *dcc, SpiceImage *dest,
>      volatile QuicImageType type;
>      int size, stride;
>      stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->quic_stat);
> +    stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->priv->quic_stat);
>  
>  #ifdef COMPRESS_DEBUG
>      spice_info("QUIC compress");
> @@ -1246,7 +1247,7 @@ static int dcc_compress_image_quic(DisplayChannelClient
> *dcc, SpiceImage *dest,
>      o_comp_data->comp_buf = quic_data->data.bufs_head;
>      o_comp_data->comp_buf_size = size << 2;
>  
> -    stat_compress_add(&DCC_TO_DC(dcc)->quic_stat, start_time, src->stride *
> src->y,
> +    stat_compress_add(&DCC_TO_DC(dcc)->priv->quic_stat, start_time,
> src->stride * src->y,
>                        o_comp_data->comp_buf_size);
>      return TRUE;
>  }
> @@ -1345,14 +1346,14 @@ int dcc_compress_image(DisplayChannelClient *dcc,
>      stat_start_time_t start_time;
>      int success = FALSE;
>  
> -    stat_start_time_init(&start_time, &display_channel->off_stat);
> +    stat_start_time_init(&start_time, &display_channel->priv->off_stat);
>  
>      image_compression = get_compression_for_bitmap(src,
>      dcc->priv->image_compression, drawable);
>      switch (image_compression) {
>      case SPICE_IMAGE_COMPRESSION_OFF:
>          break;
>      case SPICE_IMAGE_COMPRESSION_QUIC:
> -        if (can_lossy && display_channel->enable_jpeg &&
> +        if (can_lossy && display_channel->priv->enable_jpeg &&
>              (src->format != SPICE_BITMAP_FMT_RGBA ||
>              !bitmap_has_extra_stride(src))) {
>              success = dcc_compress_image_jpeg(dcc, dest, src, o_comp_data);
>              break;
> @@ -1392,7 +1393,7 @@ lz_compress:
>  
>      if (!success) {
>          uint64_t image_size = src->stride * src->y;
> -        stat_compress_add(&display_channel->off_stat, start_time,
> image_size, image_size);
> +        stat_compress_add(&display_channel->priv->off_stat, start_time,
> image_size, image_size);
>      }
>  
>      return success;
> @@ -1739,15 +1740,15 @@ int dcc_handle_migrate_data(DisplayChannelClient
> *dcc, uint32_t size, void *mess
>      if (migrate_data->low_bandwidth_setting) {
>          red_channel_client_ack_set_client_window(RED_CHANNEL_CLIENT(dcc),
>          WIDE_CLIENT_ACK_WINDOW);
>          if (dcc->priv->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) {
> -            display->enable_jpeg = TRUE;
> +            display->priv->enable_jpeg = TRUE;
>          }
>          if (dcc->priv->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) {
> -            display->enable_zlib_glz_wrap = TRUE;
> +            display->priv->enable_zlib_glz_wrap = TRUE;
>          }
>      }
>  
>      surfaces = (uint8_t *)message + migrate_data->surfaces_at_client_ptr;
> -    surfaces_restored = display->enable_jpeg ?
> +    surfaces_restored = display->priv->enable_jpeg ?
>          restore_surfaces_lossy(dcc, (MigrateDisplaySurfacesAtClientLossy
>          *)surfaces) :
>          restore_surfaces_lossless(dcc,
>          (MigrateDisplaySurfacesAtClientLossless*)surfaces);
>  
> diff --git a/server/dcc.h b/server/dcc.h
> index 9567afd..ab51047 100644
> --- a/server/dcc.h
> +++ b/server/dcc.h
> @@ -21,7 +21,7 @@
>  #include <glib-object.h>
>  #include "image-cache.h"
>  #include "pixmap-cache.h"
> -#include "red-worker.h"
> +#include "common-graphics-channel-client.h"
>  #include "display-limits.h"
>  
>  G_BEGIN_DECLS
> @@ -69,7 +69,6 @@ GType display_channel_client_get_type(void) G_GNUC_CONST;
>  
>  #define MAX_PIPE_SIZE 50
>  
> -/* FIXME: remove */
>  typedef struct DisplayChannel DisplayChannel;
>  typedef struct Stream Stream;
>  typedef struct StreamAgent StreamAgent;
> @@ -87,7 +86,6 @@ typedef struct FreeList {
>      WaitForChannels wait;
>  } FreeList;
>  
> -#define DCC_TO_WORKER(dcc)
> ((RedWorker*)((CommonGraphicsChannel*)(red_channel_client_get_channel((RedChannelClient*)dcc)))->worker)
>  #define DCC_TO_DC(dcc)
>  ((DisplayChannel*)red_channel_client_get_channel((RedChannelClient*)dcc))
>  
>  typedef struct RedSurfaceCreateItem {
> diff --git a/server/display-channel-private.h
> b/server/display-channel-private.h
> new file mode 100644
> index 0000000..039a93d
> --- /dev/null
> +++ b/server/display-channel-private.h
> @@ -0,0 +1,81 @@
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see
> <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef DISPLAY_CHANNEL_PRIVATE_H_
> +#define DISPLAY_CHANNEL_PRIVATE_H_
> +
> +struct DisplayChannelPrivate
> +{
> +    DisplayChannel *pub;
> +
> +    uint32_t bits_unique;
> +
> +    MonitorsConfig *monitors_config;
> +
> +    uint32_t renderer;
> +    int enable_jpeg;
> +    int enable_zlib_glz_wrap;
> +
> +    Ring current_list; // of TreeItem
> +    uint32_t current_size;
> +
> +    uint32_t drawable_count;
> +    _Drawable *drawables;
> +    _Drawable *free_drawables;
> +
> +    int stream_video;
> +    uint32_t stream_count;
> +    Stream *streams_buf;
> +    Stream *free_streams;
> +    Ring streams;
> +    ItemTrace *items_trace;
> +    uint32_t next_item_trace;
> +    uint64_t streams_size_total;
> +
> +    RedSurface *surfaces;
> +    uint32_t n_surfaces;
> +    SpiceImageSurfaces image_surfaces;
> +
> +    ImageCache *image_cache;
> +    RedCompressBuf *free_compress_bufs;
> +
> +    int gl_draw_async_count;
> +
> +/* TODO: some day unify this, make it more runtime.. */
> +    stat_info_t add_stat;
> +    stat_info_t exclude_stat;
> +    stat_info_t __exclude_stat;
> +#ifdef RED_WORKER_STAT
> +    uint32_t add_count;
> +    uint32_t add_with_shadow_count;
> +#endif
> +#ifdef RED_STATISTICS
> +    uint64_t *cache_hits_counter;
> +    uint64_t *add_to_cache_counter;
> +    uint64_t *non_cache_counter;
> +#endif
> +    stat_info_t off_stat;
> +    stat_info_t lz_stat;
> +    stat_info_t glz_stat;
> +    stat_info_t quic_stat;
> +    stat_info_t jpeg_stat;
> +    stat_info_t zlib_glz_stat;
> +    stat_info_t jpeg_alpha_stat;
> +    stat_info_t lz4_stat;
> +};
> +
> +#endif /* DISPLAY_CHANNEL_PRIVATE_H_ */
> diff --git a/server/display-channel.c b/server/display-channel.c
> index 352058a..91b4255 100644
> --- a/server/display-channel.c
> +++ b/server/display-channel.c
> @@ -19,6 +19,138 @@
>  #endif
>  
>  #include "display-channel.h"
> +#include "display-channel-private.h"
> +
> +G_DEFINE_TYPE(DisplayChannel, display_channel, TYPE_COMMON_GRAPHICS_CHANNEL)
> +
> +#define DISPLAY_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o),
> TYPE_DISPLAY_CHANNEL, DisplayChannelPrivate))
> +
> +enum {
> +    PROP0,
> +    PROP_N_SURFACES
> +};
> +
> +static void
> +display_channel_get_property(GObject *object,
> +                             guint property_id,
> +                             GValue *value,
> +                             GParamSpec *pspec)
> +{
> +    DisplayChannel *self = DISPLAY_CHANNEL(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_N_SURFACES:
> +            g_value_set_uint(value, self->priv->n_surfaces);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void
> +display_channel_set_property(GObject *object,
> +                             guint property_id,
> +                             const GValue *value,
> +                             GParamSpec *pspec)
> +{
> +    DisplayChannel *self = DISPLAY_CHANNEL(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_N_SURFACES:
> +            self->priv->n_surfaces = g_value_get_uint(value);
> +            if (self->priv->surfaces == NULL)
> +                self->priv->surfaces = g_new0(RedSurface,
> self->priv->n_surfaces);
> +            else
> +                self->priv->surfaces = g_renew(RedSurface,
> self->priv->surfaces, self->priv->n_surfaces);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void
> +display_channel_finalize(GObject *object)
> +{
> +    DisplayChannel *self = DISPLAY_CHANNEL(object);
> +
> +    G_OBJECT_CLASS(display_channel_parent_class)->finalize(object);
> +
> +    g_free(self->priv->drawables);
> +    g_free(self->priv->surfaces);
> +    g_free(self->priv->streams_buf);
> +    g_free(self->priv->items_trace);
> +    g_free(self->priv->image_cache);
> +}
> +
> +static void
> +display_channel_constructed(GObject *object)
> +{
> +    DisplayChannel *self = DISPLAY_CHANNEL(object);
> +
> +    G_OBJECT_CLASS(display_channel_parent_class)->constructed(object);
> +
> +    self->priv->renderer = RED_RENDERER_INVALID;
> +
> +    stat_init(&self->priv->add_stat, "add", CLOCK_THREAD_CPUTIME_ID);
> +    stat_init(&self->priv->exclude_stat, "exclude",
> CLOCK_THREAD_CPUTIME_ID);
> +    stat_init(&self->priv->__exclude_stat, "__exclude",
> CLOCK_THREAD_CPUTIME_ID);
> +#ifdef RED_STATISTICS
> +    QXLInstance *qxl = common_graphics_channel_get_qxl(&self->parent);
> +    RedsState *reds = red_qxl_get_server(qxl->st);
> +    RedChannel *channel = RED_CHANNEL(self);
> +    self->priv->cache_hits_counter =
> +        stat_add_counter(reds, red_channel_get_stat_node(channel),
> +                         "cache_hits", TRUE);
> +    self->priv->add_to_cache_counter =
> +        stat_add_counter(reds, red_channel_get_stat_node(channel),
> +                         "add_to_cache", TRUE);
> +    self->priv->non_cache_counter =
> +        stat_add_counter(reds, red_channel_get_stat_node(channel),
> +                         "non_cache", TRUE);
> +#endif
> +    image_cache_init(self->priv->image_cache);
> +    self->priv->stream_video = SPICE_STREAM_VIDEO_OFF;
> +    display_channel_init_streams(self);
> +}
> +
> +static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces,
> uint32_t surface_id)
> +{
> +    DisplayChannelPrivate *p = SPICE_CONTAINEROF(surfaces,
> DisplayChannelPrivate, image_surfaces);
> +    DisplayChannel *display = p->pub;
> +
> +    spice_return_val_if_fail(display_channel_validate_surface(display,
> surface_id), NULL);
> +
> +    return p->surfaces[surface_id].context.canvas;
> +}
> +
> +static void drawables_init(DisplayChannel *display);
> +static void
> +display_channel_init(DisplayChannel *self)
> +{
> +    static SpiceImageSurfacesOps image_surfaces_ops = {
> +        image_surfaces_get,
> +    };
> +
> +    self->priv = DISPLAY_CHANNEL_PRIVATE(self);
> +    self->priv->pub = self;
> +
> +    stat_compress_init(&self->priv->lz_stat, "lz", CLOCK_THREAD_CPUTIME_ID);
> +    stat_compress_init(&self->priv->glz_stat, "glz",
> CLOCK_THREAD_CPUTIME_ID);
> +    stat_compress_init(&self->priv->quic_stat, "quic",
> CLOCK_THREAD_CPUTIME_ID);
> +    stat_compress_init(&self->priv->jpeg_stat, "jpeg",
> CLOCK_THREAD_CPUTIME_ID);
> +    stat_compress_init(&self->priv->zlib_glz_stat, "zlib",
> CLOCK_THREAD_CPUTIME_ID);
> +    stat_compress_init(&self->priv->jpeg_alpha_stat, "jpeg_alpha",
> CLOCK_THREAD_CPUTIME_ID);
> +    stat_compress_init(&self->priv->lz4_stat, "lz4",
> CLOCK_THREAD_CPUTIME_ID);
> +
> +    ring_init(&self->priv->current_list);
> +    drawables_init(self);
> +    self->priv->image_surfaces.ops = &image_surfaces_ops;
> +    self->priv->streams_buf = g_new0(Stream, NUM_STREAMS);
> +    self->priv->items_trace = g_new0(ItemTrace, NUM_TRACE_ITEMS);
> +    self->priv->image_cache = g_new0(ImageCache, 1);
> +}
>  
>  static void drawable_draw(DisplayChannel *display, Drawable *drawable);
>  
> @@ -26,7 +158,7 @@ uint32_t display_channel_generate_uid(DisplayChannel
> *display)
>  {
>      spice_return_val_if_fail(display != NULL, 0);
>  
> -    return ++display->bits_unique;
> +    return ++display->priv->bits_unique;
>  }
>  
>  #define stat_start(stat, var)                                        \
> @@ -36,107 +168,111 @@ void
> display_channel_compress_stats_reset(DisplayChannel *display)
>  {
>      spice_return_if_fail(display);
>  
> -    stat_reset(&display->off_stat);
> -    stat_reset(&display->quic_stat);
> -    stat_reset(&display->lz_stat);
> -    stat_reset(&display->glz_stat);
> -    stat_reset(&display->jpeg_stat);
> -    stat_reset(&display->zlib_glz_stat);
> -    stat_reset(&display->jpeg_alpha_stat);
> -    stat_reset(&display->lz4_stat);
> +    stat_reset(&display->priv->off_stat);
> +    stat_reset(&display->priv->quic_stat);
> +    stat_reset(&display->priv->lz_stat);
> +    stat_reset(&display->priv->glz_stat);
> +    stat_reset(&display->priv->jpeg_stat);
> +    stat_reset(&display->priv->zlib_glz_stat);
> +    stat_reset(&display->priv->jpeg_alpha_stat);
> +    stat_reset(&display->priv->lz4_stat);
>  }
>  
> -void display_channel_compress_stats_print(const DisplayChannel
> *display_channel)
> +void display_channel_compress_stats_print(DisplayChannel *display_channel)
>  {
>      spice_return_if_fail(display_channel);
>  
>  #ifdef COMPRESS_STAT
> +    DisplayChannelPrivate *priv = display_channel->priv;
>      uint64_t glz_enc_size;
> +    uint32_t id;
> +
> +    glz_enc_size = priv->enable_zlib_glz_wrap ?
> +                       priv->zlib_glz_stat.comp_size :
> +                       priv->glz_stat.comp_size;
>  
> -    glz_enc_size = display_channel->enable_zlib_glz_wrap ?
> -                       display_channel->zlib_glz_stat.comp_size :
> -                       display_channel->glz_stat.comp_size;
> +    g_object_get(display_channel, "id", &id, NULL);
>  
> -    spice_info("==> Compression stats for display %u",
> display_channel->common.base.id);
> +    spice_info("==> Compression stats for display %u", id);
>      spice_info("Method   \t  count
>      \torig_size(MB)\tenc_size(MB)\tenc_time(s)");
>      spice_info("OFF     \t%8d\t%13.2f\t%12.2f\t%12.2f",
> -               display_channel->off_stat.count,
> -               stat_byte_to_mega(display_channel->off_stat.orig_size),
> -               stat_byte_to_mega(display_channel->off_stat.comp_size),
> -               stat_cpu_time_to_sec(display_channel->off_stat.total)
> +               priv->off_stat.count,
> +               stat_byte_to_mega(priv->off_stat.orig_size),
> +               stat_byte_to_mega(priv->off_stat.comp_size),
> +               stat_cpu_time_to_sec(priv->off_stat.total)
>                 );
>      spice_info("QUIC     \t%8d\t%13.2f\t%12.2f\t%12.2f",
> -               display_channel->quic_stat.count,
> -               stat_byte_to_mega(display_channel->quic_stat.orig_size),
> -               stat_byte_to_mega(display_channel->quic_stat.comp_size),
> -               stat_cpu_time_to_sec(display_channel->quic_stat.total)
> +               priv->quic_stat.count,
> +               stat_byte_to_mega(priv->quic_stat.orig_size),
> +               stat_byte_to_mega(priv->quic_stat.comp_size),
> +               stat_cpu_time_to_sec(priv->quic_stat.total)
>                 );
>      spice_info("GLZ      \t%8d\t%13.2f\t%12.2f\t%12.2f",
> -               display_channel->glz_stat.count,
> -               stat_byte_to_mega(display_channel->glz_stat.orig_size),
> -               stat_byte_to_mega(display_channel->glz_stat.comp_size),
> -               stat_cpu_time_to_sec(display_channel->glz_stat.total)
> +               priv->glz_stat.count,
> +               stat_byte_to_mega(priv->glz_stat.orig_size),
> +               stat_byte_to_mega(priv->glz_stat.comp_size),
> +               stat_cpu_time_to_sec(priv->glz_stat.total)
>                 );
>      spice_info("ZLIB GLZ \t%8d\t%13.2f\t%12.2f\t%12.2f",
> -               display_channel->zlib_glz_stat.count,
> -               stat_byte_to_mega(display_channel->zlib_glz_stat.orig_size),
> -               stat_byte_to_mega(display_channel->zlib_glz_stat.comp_size),
> -               stat_cpu_time_to_sec(display_channel->zlib_glz_stat.total)
> +               priv->zlib_glz_stat.count,
> +               stat_byte_to_mega(priv->zlib_glz_stat.orig_size),
> +               stat_byte_to_mega(priv->zlib_glz_stat.comp_size),
> +               stat_cpu_time_to_sec(priv->zlib_glz_stat.total)
>                 );
>      spice_info("LZ       \t%8d\t%13.2f\t%12.2f\t%12.2f",
> -               display_channel->lz_stat.count,
> -               stat_byte_to_mega(display_channel->lz_stat.orig_size),
> -               stat_byte_to_mega(display_channel->lz_stat.comp_size),
> -               stat_cpu_time_to_sec(display_channel->lz_stat.total)
> +               priv->lz_stat.count,
> +               stat_byte_to_mega(priv->lz_stat.orig_size),
> +               stat_byte_to_mega(priv->lz_stat.comp_size),
> +               stat_cpu_time_to_sec(priv->lz_stat.total)
>                 );
>      spice_info("JPEG     \t%8d\t%13.2f\t%12.2f\t%12.2f",
> -               display_channel->jpeg_stat.count,
> -               stat_byte_to_mega(display_channel->jpeg_stat.orig_size),
> -               stat_byte_to_mega(display_channel->jpeg_stat.comp_size),
> -               stat_cpu_time_to_sec(display_channel->jpeg_stat.total)
> +               priv->jpeg_stat.count,
> +               stat_byte_to_mega(priv->jpeg_stat.orig_size),
> +               stat_byte_to_mega(priv->jpeg_stat.comp_size),
> +               stat_cpu_time_to_sec(priv->jpeg_stat.total)
>                 );
>      spice_info("JPEG-RGBA\t%8d\t%13.2f\t%12.2f\t%12.2f",
> -               display_channel->jpeg_alpha_stat.count,
> -
> stat_byte_to_mega(display_channel->jpeg_alpha_stat.orig_size),
> -
> stat_byte_to_mega(display_channel->jpeg_alpha_stat.comp_size),
> -               stat_cpu_time_to_sec(display_channel->jpeg_alpha_stat.total)
> +               priv->jpeg_alpha_stat.count,
> +               stat_byte_to_mega(priv->jpeg_alpha_stat.orig_size),
> +               stat_byte_to_mega(priv->jpeg_alpha_stat.comp_size),
> +               stat_cpu_time_to_sec(priv->jpeg_alpha_stat.total)
>                 );
>      spice_info("LZ4      \t%8d\t%13.2f\t%12.2f\t%12.2f",
> -               display_channel->lz4_stat.count,
> -               stat_byte_to_mega(display_channel->lz4_stat.orig_size),
> -               stat_byte_to_mega(display_channel->lz4_stat.comp_size),
> -               stat_cpu_time_to_sec(display_channel->lz4_stat.total)
> +               priv->lz4_stat.count,
> +               stat_byte_to_mega(priv->lz4_stat.orig_size),
> +               stat_byte_to_mega(priv->lz4_stat.comp_size),
> +               stat_cpu_time_to_sec(priv->lz4_stat.total)
>                 );
>      spice_info("-------------------------------------------------------------------");
>      spice_info("Total    \t%8d\t%13.2f\t%12.2f\t%12.2f",
> -               display_channel->lz_stat.count +
> display_channel->glz_stat.count +
> -
> display_channel->off_stat.count
> +
> -
> display_channel->quic_stat.count
> +
> -
> display_channel->jpeg_stat.count
> +
> -
> display_channel->lz4_stat.count
> +
> -
> display_channel->jpeg_alpha_stat.count,
> -               stat_byte_to_mega(display_channel->lz_stat.orig_size +
> -                                 display_channel->glz_stat.orig_size +
> -                                 display_channel->off_stat.orig_size +
> -                                 display_channel->quic_stat.orig_size +
> -                                 display_channel->jpeg_stat.orig_size +
> -                                 display_channel->lz4_stat.orig_size +
> -
> display_channel->jpeg_alpha_stat.orig_size),
> -               stat_byte_to_mega(display_channel->lz_stat.comp_size +
> +               display_channel->priv->lz_stat.count +
> display_channel->priv->glz_stat.count +
> +                                                priv->off_stat.count +
> +                                                priv->quic_stat.count +
> +                                                priv->jpeg_stat.count +
> +                                                priv->lz4_stat.count +
> +                                                priv->jpeg_alpha_stat.count,
> +               stat_byte_to_mega(priv->lz_stat.orig_size +
> +                                 priv->glz_stat.orig_size +
> +                                 priv->off_stat.orig_size +
> +                                 priv->quic_stat.orig_size +
> +                                 priv->jpeg_stat.orig_size +
> +                                 priv->lz4_stat.orig_size +
> +                                 priv->jpeg_alpha_stat.orig_size),
> +               stat_byte_to_mega(priv->lz_stat.comp_size +
>                                   glz_enc_size +
> -                                 display_channel->off_stat.comp_size +
> -                                 display_channel->quic_stat.comp_size +
> -                                 display_channel->jpeg_stat.comp_size +
> -                                 display_channel->lz4_stat.comp_size +
> -
> display_channel->jpeg_alpha_stat.comp_size),
> -               stat_cpu_time_to_sec(display_channel->lz_stat.total +
> -                                    display_channel->glz_stat.total +
> -                                    display_channel->zlib_glz_stat.total +
> -                                    display_channel->off_stat.total +
> -                                    display_channel->quic_stat.total +
> -                                    display_channel->jpeg_stat.total +
> -                                    display_channel->lz4_stat.total +
> -                                    display_channel->jpeg_alpha_stat.total)
> +                                 priv->off_stat.comp_size +
> +                                 priv->quic_stat.comp_size +
> +                                 priv->jpeg_stat.comp_size +
> +                                 priv->lz4_stat.comp_size +
> +                                 priv->jpeg_alpha_stat.comp_size),
> +               stat_cpu_time_to_sec(priv->lz_stat.total +
> +                                    priv->glz_stat.total +
> +                                    priv->zlib_glz_stat.total +
> +                                    priv->off_stat.total +
> +                                    priv->quic_stat.total +
> +                                    priv->jpeg_stat.total +
> +                                    priv->lz4_stat.total +
> +                                    priv->jpeg_alpha_stat.total)
>                 );
>  #endif
>  }
> @@ -189,7 +325,7 @@ MonitorsConfig* monitors_config_new(QXLHead *heads,
> ssize_t nheads, ssize_t max)
>  int display_channel_get_streams_timeout(DisplayChannel *display)
>  {
>      int timeout = INT_MAX;
> -    Ring *ring = &display->streams;
> +    Ring *ring = &display->priv->streams;
>      RingItem *item = ring;
>  
>      red_time_t now = spice_get_monotonic_time_ns();
> @@ -227,12 +363,18 @@ void display_channel_set_stream_video(DisplayChannel
> *display, int stream_video)
>          return;
>      }
>  
> -    display->stream_video = stream_video;
> +    display->priv->stream_video = stream_video;
> +}
> +
> +int display_channel_get_stream_video(DisplayChannel *display)
> +{
> +    return display->priv->stream_video;
>  }
>  
> +
>  static void stop_streams(DisplayChannel *display)
>  {
> -    Ring *ring = &display->streams;
> +    Ring *ring = &display->priv->streams;
>      RingItem *item = ring_get_head(ring);
>  
>      while (item) {
> @@ -245,14 +387,14 @@ static void stop_streams(DisplayChannel *display)
>          }
>      }
>  
> -    display->next_item_trace = 0;
> -    memset(display->items_trace, 0, sizeof(display->items_trace));
> +    display->priv->next_item_trace = 0;
> +    memset(display->priv->items_trace, 0, NUM_TRACE_ITEMS *
> sizeof(*display->priv->items_trace));
>  }
>  
>  void display_channel_surface_unref(DisplayChannel *display, uint32_t
>  surface_id)
>  {
> -    RedSurface *surface = &display->surfaces[surface_id];
> -    QXLInstance *qxl = display->common.qxl;
> +    RedSurface *surface = &display->priv->surfaces[surface_id];
> +    QXLInstance *qxl = common_graphics_channel_get_qxl(&display->parent);
>      DisplayChannelClient *dcc;
>      GList *link, *next;
>  
> @@ -283,6 +425,13 @@ void display_channel_surface_unref(DisplayChannel
> *display, uint32_t surface_id)
>      spice_warn_if_fail(ring_is_empty(&surface->depend_on_me));
>  }
>  
> +/* TODO: perhaps rename to "ready" or "realized" ? */
> +bool display_channel_surface_has_canvas(DisplayChannel *display,
> +                                        uint32_t surface_id)
> +{
> +    return display->priv->surfaces[surface_id].context.canvas != NULL;
> +}
> +
>  static void streams_update_visible_region(DisplayChannel *display, Drawable
>  *drawable)
>  {
>      Ring *ring;
> @@ -298,7 +447,7 @@ static void streams_update_visible_region(DisplayChannel
> *display, Drawable *dra
>          return;
>      }
>  
> -    ring = &display->streams;
> +    ring = &display->priv->streams;
>      item = ring_get_head(ring);
>  
>      while (item) {
> @@ -312,7 +461,7 @@ static void streams_update_visible_region(DisplayChannel
> *display, Drawable *dra
>          }
>  
>          FOREACH_DCC(display, link, next, dcc) {
> -            agent = dcc_get_stream_agent(dcc, get_stream_id(display,
> stream));
> +            agent = dcc_get_stream_agent(dcc,
> display_channel_get_stream_id(display, stream));
>  
>              if (region_intersects(&agent->vis_region,
>              &drawable->tree_item.base.rgn)) {
>                  region_exclude(&agent->vis_region,
>                  &drawable->tree_item.base.rgn);
> @@ -350,7 +499,7 @@ static void pipes_add_drawable_after(DisplayChannel
> *display,
>          pipes_add_drawable(display, drawable);
>          return;
>      }
> -    if (num_other_linked != display->common.base.clients_num) {
> +    if (num_other_linked != red_channel_get_n_clients(RED_CHANNEL(display)))
> {
>          GList *link, *next;
>          spice_debug("TODO: not O(n^2)");
>          FOREACH_DCC(display, link, next, dcc) {
> @@ -374,11 +523,11 @@ static void current_add_drawable(DisplayChannel
> *display,
>      RedSurface *surface;
>      uint32_t surface_id = drawable->surface_id;
>  
> -    surface = &display->surfaces[surface_id];
> +    surface = &display->priv->surfaces[surface_id];
>      ring_add_after(&drawable->tree_item.base.siblings_link, pos);
> -    ring_add(&display->current_list, &drawable->list_link);
> +    ring_add(&display->priv->current_list, &drawable->list_link);
>      ring_add(&surface->current_list, &drawable->surface_list_link);
> -    display->current_size++;
> +    display->priv->current_size++;
>      drawable->refs++;
>  }
>  
> @@ -391,7 +540,7 @@ static void current_remove_drawable(DisplayChannel
> *display, Drawable *item)
>      ring_remove(&item->list_link);
>      ring_remove(&item->surface_list_link);
>      drawable_unref(item);
> -    display->current_size--;
> +    display->priv->current_size--;
>  }
>  
>  static void drawable_remove_from_pipes(Drawable *drawable)
> @@ -448,7 +597,7 @@ static void current_remove(DisplayChannel *display,
> TreeItem *item)
>  
>  static void current_remove_all(DisplayChannel *display, int surface_id)
>  {
> -    Ring *ring = &display->surfaces[surface_id].current;
> +    Ring *ring = &display->priv->surfaces[surface_id].current;
>      RingItem *ring_item;
>  
>      while ((ring_item = ring_get_head(ring))) {
> @@ -506,7 +655,7 @@ static int current_add_equal(DisplayChannel *display,
> DrawItem *item, TreeItem *
>  
>              /* sending the drawable to clients that already received
>               * (or will receive) other_drawable */
> -            link = RED_CHANNEL(display)->clients;
> +            link = red_channel_get_clients(RED_CHANNEL(display));
>              dpi_ring_item = ring_get_head(&other_drawable->pipes);
>              /* dpi contains a sublist of dcc's, ordered the same */
>              while (link) {
> @@ -555,7 +704,7 @@ static void __exclude_region(DisplayChannel *display,
> Ring *ring, TreeItem *item
>                               Ring **top_ring, Drawable *frame_candidate)
>  {
>      QRegion and_rgn;
> -    stat_start(&display->__exclude_stat, start_time);
> +    stat_start(&display->priv->__exclude_stat, start_time);
>  
>      region_clone(&and_rgn, rgn);
>      region_and(&and_rgn, &item->rgn);
> @@ -617,14 +766,14 @@ static void __exclude_region(DisplayChannel *display,
> Ring *ring, TreeItem *item
>          }
>      }
>      region_destroy(&and_rgn);
> -    stat_add(&display->__exclude_stat, start_time);
> +    stat_add(&display->priv->__exclude_stat, start_time);
>  }
>  
>  static void exclude_region(DisplayChannel *display, Ring *ring, RingItem
>  *ring_item,
>                             QRegion *rgn, TreeItem **last, Drawable
>                             *frame_candidate)
>  {
>      Ring *top_ring;
> -    stat_start(&display->exclude_stat, start_time);
> +    stat_start(&display->priv->exclude_stat, start_time);
>  
>      if (!ring_item) {
>          return;
> @@ -659,7 +808,7 @@ static void exclude_region(DisplayChannel *display, Ring
> *ring, RingItem *ring_i
>              }
>  
>              if (region_is_empty(rgn)) {
> -                stat_add(&display->exclude_stat, start_time);
> +                stat_add(&display->priv->exclude_stat, start_time);
>                  return;
>              }
>          }
> @@ -667,7 +816,7 @@ static void exclude_region(DisplayChannel *display, Ring
> *ring, RingItem *ring_i
>          while ((last && *last == (TreeItem *)ring_item) ||
>                 !(ring_item = ring_next(ring, ring_item))) {
>              if (ring == top_ring) {
> -                stat_add(&display->exclude_stat, start_time);
> +                stat_add(&display->priv->exclude_stat, start_time);
>                  return;
>              }
>              ring_item = &container->base.siblings_link;
> @@ -679,9 +828,9 @@ static void exclude_region(DisplayChannel *display, Ring
> *ring, RingItem *ring_i
>  
>  static int current_add_with_shadow(DisplayChannel *display, Ring *ring,
>  Drawable *item)
>  {
> -    stat_start(&display->add_stat, start_time);
> +    stat_start(&display->priv->add_stat, start_time);
>  #ifdef RED_WORKER_STAT
> -    ++display->add_with_shadow_count;
> +    ++display->priv->add_with_shadow_count;
>  #endif
>  
>      RedDrawable *red_drawable = item->red_drawable;
> @@ -692,7 +841,7 @@ static int current_add_with_shadow(DisplayChannel
> *display, Ring *ring, Drawable
>  
>      Shadow *shadow = shadow_new(&item->tree_item, &delta);
>      if (!shadow) {
> -        stat_add(&display->add_stat, start_time);
> +        stat_add(&display->priv->add_stat, start_time);
>          return FALSE;
>      }
>      // item and his shadow must initially be placed in the same container.
> @@ -716,7 +865,7 @@ static int current_add_with_shadow(DisplayChannel
> *display, Ring *ring, Drawable
>              stream_detach_behind(display, &item->tree_item.base.rgn, item);
>          }
>      }
> -    stat_add(&display->add_stat, start_time);
> +    stat_add(&display->priv->add_stat, start_time);
>      return TRUE;
>  }
>  
> @@ -726,7 +875,7 @@ static int current_add(DisplayChannel *display, Ring
> *ring, Drawable *drawable)
>      RingItem *now;
>      QRegion exclude_rgn;
>      RingItem *exclude_base = NULL;
> -    stat_start(&display->add_stat, start_time);
> +    stat_start(&display->priv->add_stat, start_time);
>  
>      spice_assert(!region_is_empty(&item->base.rgn));
>      region_init(&exclude_rgn);
> @@ -748,7 +897,7 @@ static int current_add(DisplayChannel *display, Ring
> *ring, Drawable *drawable)
>              if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) &&
>                                                     !(test_res &
>                                                     REGION_TEST_LEFT_EXCLUSIVE)
>                                                     &&
>                                                     current_add_equal(display,
>                                                     item, sibling)) {
> -                stat_add(&display->add_stat, start_time);
> +                stat_add(&display->priv->add_stat, start_time);
>                  return FALSE;
>              }
>  
> @@ -834,7 +983,7 @@ static int current_add(DisplayChannel *display, Ring
> *ring, Drawable *drawable)
>          }
>      }
>      region_destroy(&exclude_rgn);
> -    stat_add(&display->add_stat, start_time);
> +    stat_add(&display->priv->add_stat, start_time);
>      return TRUE;
>  }
>  
> @@ -843,7 +992,7 @@ static bool drawable_can_stream(DisplayChannel *display,
> Drawable *drawable)
>      RedDrawable *red_drawable = drawable->red_drawable;
>      SpiceImage *image;
>  
> -    if (display->stream_video == SPICE_STREAM_VIDEO_OFF) {
> +    if (display->priv->stream_video == SPICE_STREAM_VIDEO_OFF) {
>          return FALSE;
>      }
>  
> @@ -863,7 +1012,7 @@ static bool drawable_can_stream(DisplayChannel *display,
> Drawable *drawable)
>          return FALSE;
>      }
>  
> -    if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) {
> +    if (display->priv->stream_video == SPICE_STREAM_VIDEO_FILTER) {
>          SpiceRect* rect;
>          int size;
>  
> @@ -880,26 +1029,26 @@ static bool drawable_can_stream(DisplayChannel
> *display, Drawable *drawable)
>  void display_channel_print_stats(DisplayChannel *display)
>  {
>  #ifdef RED_WORKER_STAT
> -    stat_time_t total = display->add_stat.total;
> +    stat_time_t total = display->priv->add_stat.total;
>      spice_info("add with shadow count %u",
> -               display->add_with_shadow_count);
> -    display->add_with_shadow_count = 0;
> +               display->priv->add_with_shadow_count);
> +    display->priv->add_with_shadow_count = 0;
>      spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f",
> -               display->add_stat.count,
> +               display->priv->add_stat.count,
>                 stat_cpu_time_to_sec(total),
> -               display->exclude_stat.count,
> -               stat_cpu_time_to_sec(display->exclude_stat.total),
> -               display->__exclude_stat.count,
> -               stat_cpu_time_to_sec(display->__exclude_stat.total));
> +               display->priv->exclude_stat.count,
> +               stat_cpu_time_to_sec(display->priv->exclude_stat.total),
> +               display->priv->__exclude_stat.count,
> +               stat_cpu_time_to_sec(display->priv->__exclude_stat.total));
>      spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%",
> -               (double)(total - display->exclude_stat.total) / total * 100,
> -               (double)(display->exclude_stat.total) / total * 100,
> -               (double)(display->exclude_stat.total -
> -                        display->__exclude_stat.total) /
> display->exclude_stat.total * 100,
> -               (double)(display->__exclude_stat.total) /
> display->exclude_stat.total * 100);
> -    stat_reset(&display->add_stat);
> -    stat_reset(&display->exclude_stat);
> -    stat_reset(&display->__exclude_stat);
> +               (double)(total - display->priv->exclude_stat.total) / total *
> 100,
> +               (double)(display->priv->exclude_stat.total) / total * 100,
> +               (double)(display->priv->exclude_stat.total -
> +                        display->priv->__exclude_stat.total) /
> display->priv->exclude_stat.total * 100,
> +               (double)(display->priv->__exclude_stat.total) /
> display->priv->exclude_stat.total * 100);
> +    stat_reset(&display->priv->add_stat);
> +    stat_reset(&display->priv->exclude_stat);
> +    stat_reset(&display->priv->__exclude_stat);
>  #endif
>  }
>  
> @@ -914,7 +1063,7 @@ static void drawable_ref_surface_deps(DisplayChannel
> *display, Drawable *drawabl
>          if (surface_id == -1) {
>              continue;
>          }
> -        surface = &display->surfaces[surface_id];
> +        surface = &display->priv->surfaces[surface_id];
>          surface->refs++;
>      }
>  }
> @@ -923,7 +1072,7 @@ static void surface_read_bits(DisplayChannel *display,
> int surface_id,
>                                const SpiceRect *area, uint8_t *dest, int
>                                dest_stride)
>  {
>      SpiceCanvas *canvas;
> -    RedSurface *surface = &display->surfaces[surface_id];
> +    RedSurface *surface = &display->priv->surfaces[surface_id];
>  
>      canvas = surface->context.canvas;
>      canvas->ops->read_bits(canvas, dest, dest_stride, area);
> @@ -941,7 +1090,7 @@ static void handle_self_bitmap(DisplayChannel *display,
> Drawable *drawable)
>      int bpp;
>      int all_set;
>  
> -    surface = &display->surfaces[drawable->surface_id];
> +    surface = &display->priv->surfaces[drawable->surface_id];
>  
>      bpp = SPICE_SURFACE_FMT_DEPTH(surface->context.format) / 8;
>      width = red_drawable->self_bitmap_area.right -
>      red_drawable->self_bitmap_area.left;
> @@ -993,7 +1142,7 @@ static void
> surface_add_reverse_dependency(DisplayChannel *display, int surface_
>          return;
>      }
>  
> -    surface = &display->surfaces[surface_id];
> +    surface = &display->priv->surfaces[surface_id];
>  
>      depend_item->drawable = drawable;
>      ring_add(&surface->depend_on_me, &depend_item->ring_item);
> @@ -1027,7 +1176,7 @@ static void draw_depend_on_me(DisplayChannel *display,
> uint32_t surface_id)
>      RedSurface *surface;
>      RingItem *ring_item;
>  
> -    surface = &display->surfaces[surface_id];
> +    surface = &display->priv->surfaces[surface_id];
>  
>      while ((ring_item = ring_get_tail(&surface->depend_on_me))) {
>          Drawable *drawable;
> @@ -1045,10 +1194,10 @@ static int validate_drawable_bbox(DisplayChannel
> *display, RedDrawable *drawable
>          /* surface_id must be validated before calling into
>           * validate_drawable_bbox
>           */
> -        if (!validate_surface(display, drawable->surface_id)) {
> +        if (!display_channel_validate_surface(display,
> drawable->surface_id)) {
>              return FALSE;
>          }
> -        context = &display->surfaces[surface_id].context;
> +        context = &display->priv->surfaces[surface_id].context;
>  
>          if (drawable->bbox.top < 0)
>                  return FALSE;
> @@ -1088,7 +1237,7 @@ static Drawable
> *display_channel_get_drawable(DisplayChannel *display, uint8_t e
>      }
>      for (x = 0; x < 3; ++x) {
>          if (red_drawable->surface_deps[x] != -1
> -            && !validate_surface(display, red_drawable->surface_deps[x])) {
> +            && !display_channel_validate_surface(display,
> red_drawable->surface_deps[x])) {
>              return NULL;
>          }
>      }
> @@ -1102,7 +1251,7 @@ static Drawable
> *display_channel_get_drawable(DisplayChannel *display, uint8_t e
>      drawable->red_drawable = red_drawable_ref(red_drawable);
>  
>      drawable->surface_id = red_drawable->surface_id;
> -    display->surfaces[drawable->surface_id].refs++;
> +    display->priv->surfaces[drawable->surface_id].refs++;
>  
>      memcpy(drawable->surface_deps, red_drawable->surface_deps,
>      sizeof(drawable->surface_deps));
>      /*
> @@ -1152,7 +1301,7 @@ static void display_channel_add_drawable(DisplayChannel
> *display, Drawable *draw
>          return;
>      }
>  
> -    Ring *ring = &display->surfaces[surface_id].current;
> +    Ring *ring = &display->priv->surfaces[surface_id].current;
>      int add_to_pipe;
>      if (has_shadow(red_drawable)) {
>          add_to_pipe = current_add_with_shadow(display, ring, drawable);
> @@ -1165,7 +1314,7 @@ static void display_channel_add_drawable(DisplayChannel
> *display, Drawable *draw
>          pipes_add_drawable(display, drawable);
>  
>  #ifdef RED_WORKER_STAT
> -    if ((++display->add_count % 100) == 0)
> +    if ((++display->priv->add_count % 100) == 0)
>          display_channel_print_stats(display);
>  #endif
>  }
> @@ -1189,18 +1338,18 @@ void display_channel_process_draw(DisplayChannel
> *display, RedDrawable *red_draw
>  int display_channel_wait_for_migrate_data(DisplayChannel *display)
>  {
>      uint64_t end_time = spice_get_monotonic_time_ns() +
>      DISPLAY_CLIENT_MIGRATE_DATA_TIMEOUT;
> -    RedChannel *channel = &display->common.base;
>      RedChannelClient *rcc;
>      int ret = FALSE;
> +    GList *clients = red_channel_get_clients(RED_CHANNEL(display));;
>  
> -    if (!red_channel_is_waiting_for_migrate_data(&display->common.base)) {
> +    if (!red_channel_is_waiting_for_migrate_data(RED_CHANNEL(display))) {
>          return FALSE;
>      }
>  
>      spice_debug(NULL);
> -    spice_warn_if_fail(g_list_length(channel->clients) == 1);
> +    spice_warn_if_fail(g_list_length(clients) == 1);
>  
> -    rcc = channel->clients->data;
> +    rcc = clients->data;
>  
>      g_object_ref(rcc);
>      for (;;) {
> @@ -1228,8 +1377,8 @@ void display_channel_flush_all_surfaces(DisplayChannel
> *display)
>  {
>      int x;
>  
> -    for (x = 0; x < NUM_SURFACES; ++x) {
> -        if (display->surfaces[x].context.canvas) {
> +    for (x = 0; x < display->priv->n_surfaces; ++x) {
> +        if (display->priv->surfaces[x].context.canvas) {
>              display_channel_current_flush(display, x);
>          }
>      }
> @@ -1261,7 +1410,7 @@ void display_channel_free_glz_drawables(DisplayChannel
> *display)
>  
>  static bool free_one_drawable(DisplayChannel *display, int force_glz_free)
>  {
> -    RingItem *ring_item = ring_get_tail(&display->current_list);
> +    RingItem *ring_item = ring_get_tail(&display->priv->current_list);
>      Drawable *drawable;
>      Container *container;
>  
> @@ -1287,7 +1436,7 @@ static bool free_one_drawable(DisplayChannel *display,
> int force_glz_free)
>  
>  void display_channel_current_flush(DisplayChannel *display, int surface_id)
>  {
> -    while (!ring_is_empty(&display->surfaces[surface_id].current_list)) {
> +    while
> (!ring_is_empty(&display->priv->surfaces[surface_id].current_list)) {
>          free_one_drawable(display, FALSE);
>      }
>      current_remove_all(display, surface_id);
> @@ -1299,7 +1448,7 @@ void display_channel_free_some(DisplayChannel *display)
>      DisplayChannelClient *dcc;
>      GList *link, *next;
>  
> -    spice_debug("#draw=%d, #glz_draw=%d", display->drawable_count,
> +    spice_debug("#draw=%d, #glz_draw=%d", display->priv->drawable_count,
>                  display->glz_drawable_count);
>      FOREACH_DCC(display, link, next, dcc) {
>          GlzSharedDictionary *glz_dict = dcc_get_glz_dictionary(dcc);
> @@ -1312,7 +1461,7 @@ void display_channel_free_some(DisplayChannel *display)
>          }
>      }
>  
> -    while (!ring_is_empty(&display->current_list) && n++ <
> RED_RELEASE_BUNCH_SIZE) {
> +    while (!ring_is_empty(&display->priv->current_list) && n++ <
> RED_RELEASE_BUNCH_SIZE) {
>          free_one_drawable(display, TRUE);
>      }
>  
> @@ -1329,29 +1478,30 @@ static Drawable* drawable_try_new(DisplayChannel
> *display)
>  {
>      Drawable *drawable;
>  
> -    if (!display->free_drawables)
> +    if (!display->priv->free_drawables)
>          return NULL;
>  
> -    drawable = &display->free_drawables->u.drawable;
> -    display->free_drawables = display->free_drawables->u.next;
> -    display->drawable_count++;
> +    drawable = &display->priv->free_drawables->u.drawable;
> +    display->priv->free_drawables = display->priv->free_drawables->u.next;
> +    display->priv->drawable_count++;
>  
>      return drawable;
>  }
>  
>  static void drawable_free(DisplayChannel *display, Drawable *drawable)
>  {
> -    ((_Drawable *)drawable)->u.next = display->free_drawables;
> -    display->free_drawables = (_Drawable *)drawable;
> +    ((_Drawable *)drawable)->u.next = display->priv->free_drawables;
> +    display->priv->free_drawables = (_Drawable *)drawable;
>  }
>  
>  static void drawables_init(DisplayChannel *display)
>  {
>      int i;
>  
> -    display->free_drawables = NULL;
> +    display->priv->drawables = g_new0(_Drawable, NUM_DRAWABLES);
> +    display->priv->free_drawables = NULL;
>      for (i = 0; i < NUM_DRAWABLES; i++) {
> -        drawable_free(display, &display->drawables[i].u.drawable);
> +        drawable_free(display, &display->priv->drawables[i].u.drawable);
>      }
>  }
>  
> @@ -1449,11 +1599,12 @@ void drawable_unref(Drawable *drawable)
>          SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->drawable =
>          NULL;
>          ring_remove(item);
>      }
> +
>      if (drawable->red_drawable) {
>          red_drawable_unref(drawable->red_drawable);
>      }
>      drawable_free(display, drawable);
> -    display->drawable_count--;
> +    display->priv->drawable_count--;
>  }
>  
>  static void drawable_deps_draw(DisplayChannel *display, Drawable *drawable)
> @@ -1478,11 +1629,11 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>  
>      drawable_deps_draw(display, drawable);
>  
> -    surface = &display->surfaces[drawable->surface_id];
> +    surface = &display->priv->surfaces[drawable->surface_id];
>      canvas = surface->context.canvas;
>      spice_return_if_fail(canvas);
>  
> -    image_cache_aging(&display->image_cache);
> +    image_cache_aging(display->priv->image_cache);
>  
>      region_add(&surface->draw_dirty_region, &drawable->red_drawable->bbox);
>  
> @@ -1490,8 +1641,8 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_FILL: {
>          SpiceFill fill = drawable->red_drawable->u.fill;
>          SpiceImage img1, img2;
> -        image_cache_localize_brush(&display->image_cache, &fill.brush,
> &img1);
> -        image_cache_localize_mask(&display->image_cache, &fill.mask, &img2);
> +        image_cache_localize_brush(display->priv->image_cache, &fill.brush,
> &img1);
> +        image_cache_localize_mask(display->priv->image_cache, &fill.mask,
> &img2);
>          canvas->ops->draw_fill(canvas, &drawable->red_drawable->bbox,
>                                 &clip, &fill);
>          break;
> @@ -1499,17 +1650,17 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_OPAQUE: {
>          SpiceOpaque opaque = drawable->red_drawable->u.opaque;
>          SpiceImage img1, img2, img3;
> -        image_cache_localize_brush(&display->image_cache, &opaque.brush,
> &img1);
> -        image_cache_localize(&display->image_cache, &opaque.src_bitmap,
> &img2, drawable);
> -        image_cache_localize_mask(&display->image_cache, &opaque.mask,
> &img3);
> +        image_cache_localize_brush(display->priv->image_cache,
> &opaque.brush, &img1);
> +        image_cache_localize(display->priv->image_cache, &opaque.src_bitmap,
> &img2, drawable);
> +        image_cache_localize_mask(display->priv->image_cache, &opaque.mask,
> &img3);
>          canvas->ops->draw_opaque(canvas, &drawable->red_drawable->bbox,
>          &clip, &opaque);
>          break;
>      }
>      case QXL_DRAW_COPY: {
>          SpiceCopy copy = drawable->red_drawable->u.copy;
>          SpiceImage img1, img2;
> -        image_cache_localize(&display->image_cache, &copy.src_bitmap, &img1,
> drawable);
> -        image_cache_localize_mask(&display->image_cache, &copy.mask, &img2);
> +        image_cache_localize(display->priv->image_cache, &copy.src_bitmap,
> &img1, drawable);
> +        image_cache_localize_mask(display->priv->image_cache, &copy.mask,
> &img2);
>          canvas->ops->draw_copy(canvas, &drawable->red_drawable->bbox,
>                                 &clip, &copy);
>          break;
> @@ -1517,7 +1668,7 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_TRANSPARENT: {
>          SpiceTransparent transparent =
>          drawable->red_drawable->u.transparent;
>          SpiceImage img1;
> -        image_cache_localize(&display->image_cache, &transparent.src_bitmap,
> &img1, drawable);
> +        image_cache_localize(display->priv->image_cache,
> &transparent.src_bitmap, &img1, drawable);
>          canvas->ops->draw_transparent(canvas,
>                                        &drawable->red_drawable->bbox, &clip,
>                                        &transparent);
>          break;
> @@ -1525,7 +1676,7 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_ALPHA_BLEND: {
>          SpiceAlphaBlend alpha_blend = drawable->red_drawable->u.alpha_blend;
>          SpiceImage img1;
> -        image_cache_localize(&display->image_cache, &alpha_blend.src_bitmap,
> &img1, drawable);
> +        image_cache_localize(display->priv->image_cache,
> &alpha_blend.src_bitmap, &img1, drawable);
>          canvas->ops->draw_alpha_blend(canvas,
>                                        &drawable->red_drawable->bbox, &clip,
>                                        &alpha_blend);
>          break;
> @@ -1538,8 +1689,8 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_BLEND: {
>          SpiceBlend blend = drawable->red_drawable->u.blend;
>          SpiceImage img1, img2;
> -        image_cache_localize(&display->image_cache, &blend.src_bitmap,
> &img1, drawable);
> -        image_cache_localize_mask(&display->image_cache, &blend.mask,
> &img2);
> +        image_cache_localize(display->priv->image_cache, &blend.src_bitmap,
> &img1, drawable);
> +        image_cache_localize_mask(display->priv->image_cache, &blend.mask,
> &img2);
>          canvas->ops->draw_blend(canvas, &drawable->red_drawable->bbox,
>                                  &clip, &blend);
>          break;
> @@ -1547,7 +1698,7 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_BLACKNESS: {
>          SpiceBlackness blackness = drawable->red_drawable->u.blackness;
>          SpiceImage img1;
> -        image_cache_localize_mask(&display->image_cache, &blackness.mask,
> &img1);
> +        image_cache_localize_mask(display->priv->image_cache,
> &blackness.mask, &img1);
>          canvas->ops->draw_blackness(canvas,
>                                      &drawable->red_drawable->bbox, &clip,
>                                      &blackness);
>          break;
> @@ -1555,7 +1706,7 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_WHITENESS: {
>          SpiceWhiteness whiteness = drawable->red_drawable->u.whiteness;
>          SpiceImage img1;
> -        image_cache_localize_mask(&display->image_cache, &whiteness.mask,
> &img1);
> +        image_cache_localize_mask(display->priv->image_cache,
> &whiteness.mask, &img1);
>          canvas->ops->draw_whiteness(canvas,
>                                      &drawable->red_drawable->bbox, &clip,
>                                      &whiteness);
>          break;
> @@ -1563,7 +1714,7 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_INVERS: {
>          SpiceInvers invers = drawable->red_drawable->u.invers;
>          SpiceImage img1;
> -        image_cache_localize_mask(&display->image_cache, &invers.mask,
> &img1);
> +        image_cache_localize_mask(display->priv->image_cache, &invers.mask,
> &img1);
>          canvas->ops->draw_invers(canvas,
>                                   &drawable->red_drawable->bbox, &clip,
>                                   &invers);
>          break;
> @@ -1571,9 +1722,9 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_ROP3: {
>          SpiceRop3 rop3 = drawable->red_drawable->u.rop3;
>          SpiceImage img1, img2, img3;
> -        image_cache_localize_brush(&display->image_cache, &rop3.brush,
> &img1);
> -        image_cache_localize(&display->image_cache, &rop3.src_bitmap, &img2,
> drawable);
> -        image_cache_localize_mask(&display->image_cache, &rop3.mask, &img3);
> +        image_cache_localize_brush(display->priv->image_cache, &rop3.brush,
> &img1);
> +        image_cache_localize(display->priv->image_cache, &rop3.src_bitmap,
> &img2, drawable);
> +        image_cache_localize_mask(display->priv->image_cache, &rop3.mask,
> &img3);
>          canvas->ops->draw_rop3(canvas, &drawable->red_drawable->bbox,
>                                 &clip, &rop3);
>          break;
> @@ -1581,9 +1732,9 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_COMPOSITE: {
>          SpiceComposite composite = drawable->red_drawable->u.composite;
>          SpiceImage src, mask;
> -        image_cache_localize(&display->image_cache, &composite.src_bitmap,
> &src, drawable);
> +        image_cache_localize(display->priv->image_cache,
> &composite.src_bitmap, &src, drawable);
>          if (composite.mask_bitmap)
> -            image_cache_localize(&display->image_cache,
> &composite.mask_bitmap, &mask, drawable);
> +            image_cache_localize(display->priv->image_cache,
> &composite.mask_bitmap, &mask, drawable);
>          canvas->ops->draw_composite(canvas, &drawable->red_drawable->bbox,
>                                      &clip, &composite);
>          break;
> @@ -1591,7 +1742,7 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_STROKE: {
>          SpiceStroke stroke = drawable->red_drawable->u.stroke;
>          SpiceImage img1;
> -        image_cache_localize_brush(&display->image_cache, &stroke.brush,
> &img1);
> +        image_cache_localize_brush(display->priv->image_cache,
> &stroke.brush, &img1);
>          canvas->ops->draw_stroke(canvas,
>                                   &drawable->red_drawable->bbox, &clip,
>                                   &stroke);
>          break;
> @@ -1599,8 +1750,8 @@ static void drawable_draw(DisplayChannel *display,
> Drawable *drawable)
>      case QXL_DRAW_TEXT: {
>          SpiceText text = drawable->red_drawable->u.text;
>          SpiceImage img1, img2;
> -        image_cache_localize_brush(&display->image_cache, &text.fore_brush,
> &img1);
> -        image_cache_localize_brush(&display->image_cache, &text.back_brush,
> &img2);
> +        image_cache_localize_brush(display->priv->image_cache,
> &text.fore_brush, &img1);
> +        image_cache_localize_brush(display->priv->image_cache,
> &text.back_brush, &img2);
>          canvas->ops->draw_text(canvas, &drawable->red_drawable->bbox,
>                                 &clip, &text);
>          break;
> @@ -1692,11 +1843,11 @@ void display_channel_draw_until(DisplayChannel
> *display, const SpiceRect *area,
>      spice_return_if_fail(last);
>      spice_return_if_fail(ring_item_is_linked(&last->list_link));
>  
> -    surface = &display->surfaces[surface_id];
> +    surface = &display->priv->surfaces[surface_id];
>  
>      if (surface_id != last->surface_id) {
>          // find the nearest older drawable from the appropriate surface
> -        ring = &display->current_list;
> +        ring = &display->priv->current_list;
>          ring_item = &last->list_link;
>          while ((ring_item = ring_next(ring, ring_item))) {
>              now = SPICE_CONTAINEROF(ring_item, Drawable, list_link);
> @@ -1732,12 +1883,12 @@ void display_channel_draw(DisplayChannel *display,
> const SpiceRect *area, int su
>      spice_debug("surface %d: area ==>", surface_id);
>      rect_debug(area);
>  
> -    spice_return_if_fail(surface_id >= 0 && surface_id < NUM_SURFACES);
> +    spice_return_if_fail(surface_id >= 0 && surface_id <
> display->priv->n_surfaces);
>      spice_return_if_fail(area);
>      spice_return_if_fail(area->left >= 0 && area->top >= 0 &&
>                           area->left < area->right && area->top <
>                           area->bottom);
>  
> -    surface = &display->surfaces[surface_id];
> +    surface = &display->priv->surfaces[surface_id];
>  
>      last = current_find_intersects_rect(&surface->current_list, NULL, area);
>      if (last)
> @@ -1769,12 +1920,12 @@ void display_channel_update(DisplayChannel *display,
>      SpiceRect rect;
>      RedSurface *surface;
>  
> -    spice_return_if_fail(validate_surface(display, surface_id));
> +    spice_return_if_fail(display_channel_validate_surface(display,
> surface_id));
>  
>      red_get_rect_ptr(&rect, area);
>      display_channel_draw(display, &rect, surface_id);
>  
> -    surface = &display->surfaces[surface_id];
> +    surface = &display->priv->surfaces[surface_id];
>      if (*qxl_dirty_rects == NULL) {
>          *num_dirty_rects =
>          pixman_region32_n_rects(&surface->draw_dirty_region);
>          *qxl_dirty_rects = spice_new0(QXLRect, *num_dirty_rects);
> @@ -1812,9 +1963,9 @@ void display_channel_destroy_surface(DisplayChannel
> *display, uint32_t surface_i
>  
>  void display_channel_destroy_surface_wait(DisplayChannel *display, uint32_t
>  surface_id)
>  {
> -    if (!validate_surface(display, surface_id))
> +    if (!display_channel_validate_surface(display, surface_id))
>          return;
> -    if (!display->surfaces[surface_id].context.canvas)
> +    if (!display->priv->surfaces[surface_id].context.canvas)
>          return;
>  
>      draw_depend_on_me(display, surface_id);
> @@ -1833,16 +1984,16 @@ void display_channel_destroy_surfaces(DisplayChannel
> *display)
>  
>      spice_debug(NULL);
>      //to handle better
> -    for (i = 0; i < NUM_SURFACES; ++i) {
> -        if (display->surfaces[i].context.canvas) {
> +    for (i = 0; i < display->priv->n_surfaces; ++i) {
> +        if (display->priv->surfaces[i].context.canvas) {
>              display_channel_destroy_surface_wait(display, i);
> -            if (display->surfaces[i].context.canvas) {
> +            if (display->priv->surfaces[i].context.canvas) {
>                  display_channel_surface_unref(display, i);
>              }
> -            spice_assert(!display->surfaces[i].context.canvas);
> +            spice_assert(!display->priv->surfaces[i].context.canvas);
>          }
>      }
> -    spice_warn_if_fail(ring_is_empty(&display->streams));
> +    spice_warn_if_fail(ring_is_empty(&display->priv->streams));
>  
>      if (red_channel_is_connected(RED_CHANNEL(display))) {
>          red_channel_pipes_add_type(RED_CHANNEL(display),
>          RED_PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE);
> @@ -1873,8 +2024,8 @@ create_canvas_for_surface(DisplayChannel *display,
> RedSurface *surface, uint32_t
>      case RED_RENDERER_SW:
>          canvas = canvas_create_for_data(surface->context.width,
>          surface->context.height, surface->context.format,
>                                          surface->context.line_0,
>                                          surface->context.stride,
> -                                        &display->image_cache.base,
> -                                        &display->image_surfaces, NULL,
> NULL, NULL);
> +                                        &display->priv->image_cache->base,
> +                                        &display->priv->image_surfaces,
> NULL, NULL, NULL);
>          surface->context.top_down = TRUE;
>          surface->context.canvas_draws_on_surface = TRUE;
>          return canvas;
> @@ -1889,7 +2040,7 @@ void display_channel_create_surface(DisplayChannel
> *display, uint32_t surface_id
>                                      uint32_t height, int32_t stride,
>                                      uint32_t format,
>                                      void *line_0, int data_is_valid, int
>                                      send_client)
>  {
> -    RedSurface *surface = &display->surfaces[surface_id];
> +    RedSurface *surface = &display->priv->surfaces[surface_id];
>  
>      spice_warn_if_fail(!surface->context.canvas);
>  
> @@ -1914,21 +2065,21 @@ void display_channel_create_surface(DisplayChannel
> *display, uint32_t surface_id
>      region_init(&surface->draw_dirty_region);
>      surface->refs = 1;
>  
> -    if (display->renderer == RED_RENDERER_INVALID) {
> +    if (display->priv->renderer == RED_RENDERER_INVALID) {
>          int i;
> -        QXLInstance *qxl = display->common.qxl;
> +        QXLInstance *qxl =
> common_graphics_channel_get_qxl(&display->parent);
>          RedsState *reds = red_qxl_get_server(qxl->st);
>          GArray *renderers = reds_get_renderers(reds);
>          for (i = 0; i < renderers->len; i++) {
>              uint32_t renderer = g_array_index(renderers, uint32_t, i);
>              surface->context.canvas = create_canvas_for_surface(display,
>              surface, renderer);
>              if (surface->context.canvas) {
> -                display->renderer = renderer;
> +                display->priv->renderer = renderer;
>                  break;
>              }
>          }
>      } else {
> -        surface->context.canvas = create_canvas_for_surface(display,
> surface, display->renderer);
> +        surface->context.canvas = create_canvas_for_surface(display,
> surface, display->priv->renderer);
>      }
>  
>      spice_return_if_fail(surface->context.canvas);
> @@ -1952,7 +2103,7 @@ static void on_disconnect(RedChannelClient *rcc)
>  
>      // this was the last channel client
>      spice_debug("#draw=%d, #glz_draw=%d",
> -                display->drawable_count,
> +                display->priv->drawable_count,
>                  display->glz_drawable_count);
>  }
>  
> @@ -2007,72 +2158,27 @@ static int handle_migrate_data(RedChannelClient *rcc,
> uint32_t size, void *messa
>      return dcc_handle_migrate_data(DISPLAY_CHANNEL_CLIENT(rcc), size,
>      message);
>  }
>  
> -static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces,
> uint32_t surface_id)
> -{
> -    DisplayChannel *display = SPICE_CONTAINEROF(surfaces, DisplayChannel,
> image_surfaces);
> -
> -    spice_return_val_if_fail(validate_surface(display, surface_id), NULL);
> -
> -    return display->surfaces[surface_id].context.canvas;
> -}
> -
> -DisplayChannel* display_channel_new(SpiceServer *reds, RedWorker *worker,
> +DisplayChannel* display_channel_new(SpiceServer *reds,
> +                                    QXLInstance *qxl,
> +                                    const SpiceCoreInterfaceInternal* core,
>                                      int migrate, int stream_video,
>                                      uint32_t n_surfaces)
>  {
>      DisplayChannel *display;
> -    ChannelCbs cbs = {
> -        .on_disconnect = on_disconnect,
> -        .send_item = send_item,
> -        .hold_item = hold_item,
> -        .release_item = release_item,
> -        .handle_migrate_flush_mark = handle_migrate_flush_mark,
> -        .handle_migrate_data = handle_migrate_data,
> -        .handle_migrate_data_get_serial = handle_migrate_data_get_serial
> -    };
> -    static SpiceImageSurfacesOps image_surfaces_ops = {
> -        image_surfaces_get,
> -    };
>  
> +    /* FIXME: migrate is not used...? */
>      spice_info("create display channel");
> -    display = (DisplayChannel *)red_worker_new_channel(
> -        worker, sizeof(*display), "display_channel",
> -        SPICE_CHANNEL_DISPLAY,
> -        SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER,
> -        &cbs, dcc_handle_message);
> -    spice_return_val_if_fail(display, NULL);
> -
> -    clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID;
> -    stat_init(&display->add_stat, "add", stat_clock);
> -    stat_init(&display->exclude_stat, "exclude", stat_clock);
> -    stat_init(&display->__exclude_stat, "__exclude", stat_clock);
> -#ifdef RED_STATISTICS
> -    RedChannel *channel = RED_CHANNEL(display);
> -    display->cache_hits_counter = stat_add_counter(reds, channel->stat,
> -                                                   "cache_hits", TRUE);
> -    display->add_to_cache_counter = stat_add_counter(reds, channel->stat,
> -                                                     "add_to_cache", TRUE);
> -    display->non_cache_counter = stat_add_counter(reds, channel->stat,
> -                                                  "non_cache", TRUE);
> -#endif
> -    stat_compress_init(&display->lz_stat, "lz", stat_clock);
> -    stat_compress_init(&display->glz_stat, "glz", stat_clock);
> -    stat_compress_init(&display->quic_stat, "quic", stat_clock);
> -    stat_compress_init(&display->jpeg_stat, "jpeg", stat_clock);
> -    stat_compress_init(&display->zlib_glz_stat, "zlib", stat_clock);
> -    stat_compress_init(&display->jpeg_alpha_stat, "jpeg_alpha", stat_clock);
> -    stat_compress_init(&display->lz4_stat, "lz4", stat_clock);
> -
> -    display->n_surfaces = n_surfaces;
> -    display->renderer = RED_RENDERER_INVALID;
> -
> -    ring_init(&display->current_list);
> -    display->image_surfaces.ops = &image_surfaces_ops;
> -    drawables_init(display);
> -    image_cache_init(&display->image_cache);
> -    display->stream_video = stream_video;
> -    display_channel_init_streams(display);
> -
> +    display = g_object_new(TYPE_DISPLAY_CHANNEL,
> +                           "spice-server", reds,
> +                           "core-interface", core,
> +                           "channel-type", SPICE_CHANNEL_DISPLAY,
> +                           "migration-flags", (SPICE_MIGRATE_NEED_FLUSH |
> SPICE_MIGRATE_NEED_DATA_TRANSFER),
> +                           "qxl", qxl,
> +                           "n-surfaces", n_surfaces,
> +                           NULL);
> +    if (display) {
> +        display_channel_set_stream_video(display, stream_video);
> +    }
>      return display;
>  }
>  
> @@ -2084,11 +2190,11 @@ void
> display_channel_process_surface_cmd(DisplayChannel *display, RedSurfaceCmd
>      uint8_t *data;
>  
>      surface_id = surface->surface_id;
> -    if SPICE_UNLIKELY(surface_id >= display->n_surfaces) {
> +    if SPICE_UNLIKELY(surface_id >= display->priv->n_surfaces) {
>          return;
>      }
>  
> -    red_surface = &display->surfaces[surface_id];
> +    red_surface = &display->priv->surfaces[surface_id];
>  
>      switch (surface->type) {
>      case QXL_SURFACE_CMD_CREATE: {
> @@ -2129,18 +2235,18 @@ void
> display_channel_update_compression(DisplayChannel *display, DisplayChannelC
>  {
>      gboolean is_low_bw =
>      common_graphics_channel_client_is_low_bandwidth(COMMON_GRAPHICS_CHANNEL_CLIENT(dcc));
>      if (dcc_get_jpeg_state(dcc) == SPICE_WAN_COMPRESSION_AUTO) {
> -        display->enable_jpeg = is_low_bw;
> +        display->priv->enable_jpeg = is_low_bw;
>      } else {
> -        display->enable_jpeg = (dcc_get_jpeg_state(dcc) ==
> SPICE_WAN_COMPRESSION_ALWAYS);
> +        display->priv->enable_jpeg = (dcc_get_jpeg_state(dcc) ==
> SPICE_WAN_COMPRESSION_ALWAYS);
>      }
>  
>      if (dcc_get_zlib_glz_state(dcc) == SPICE_WAN_COMPRESSION_AUTO) {
> -        display->enable_zlib_glz_wrap = is_low_bw;
> +        display->priv->enable_zlib_glz_wrap = is_low_bw;
>      } else {
> -        display->enable_zlib_glz_wrap = (dcc_get_zlib_glz_state(dcc) ==
> SPICE_WAN_COMPRESSION_ALWAYS);
> +        display->priv->enable_zlib_glz_wrap = (dcc_get_zlib_glz_state(dcc)
> == SPICE_WAN_COMPRESSION_ALWAYS);
>      }
> -    spice_info("jpeg %s", display->enable_jpeg ? "enabled" : "disabled");
> -    spice_info("zlib-over-glz %s", display->enable_zlib_glz_wrap ? "enabled"
> : "disabled");
> +    spice_info("jpeg %s", display->priv->enable_jpeg ? "enabled" :
> "disabled");
> +    spice_info("zlib-over-glz %s", display->priv->enable_zlib_glz_wrap ?
> "enabled" : "disabled");
>  }
>  
>  void display_channel_gl_scanout(DisplayChannel *display)
> @@ -2150,9 +2256,9 @@ void display_channel_gl_scanout(DisplayChannel
> *display)
>  
>  static void set_gl_draw_async_count(DisplayChannel *display, int num)
>  {
> -    QXLInstance *qxl = display->common.qxl;
> +    QXLInstance *qxl = common_graphics_channel_get_qxl(&display->parent);
>  
> -    display->gl_draw_async_count = num;
> +    display->priv->gl_draw_async_count = num;
>  
>      if (num == 0) {
>          red_qxl_gl_draw_async_complete(qxl);
> @@ -2163,7 +2269,7 @@ void display_channel_gl_draw(DisplayChannel *display,
> SpiceMsgDisplayGlDraw *dra
>  {
>      int num;
>  
> -    spice_return_if_fail(display->gl_draw_async_count == 0);
> +    spice_return_if_fail(display->priv->gl_draw_async_count == 0);
>  
>      num = red_channel_pipes_new_add_push(RED_CHANNEL(display),
>      dcc_gl_draw_item_new, draw);
>      set_gl_draw_async_count(display, num);
> @@ -2171,5 +2277,93 @@ void display_channel_gl_draw(DisplayChannel *display,
> SpiceMsgDisplayGlDraw *dra
>  
>  void display_channel_gl_draw_done(DisplayChannel *display)
>  {
> -    set_gl_draw_async_count(display, display->gl_draw_async_count - 1);
> +    set_gl_draw_async_count(display, display->priv->gl_draw_async_count -
> 1);
> +}
> +
> +int display_channel_get_stream_id(DisplayChannel *display, Stream *stream)
> +{
> +    return (int)(stream - display->priv->streams_buf);
> +}
> +
> +gboolean display_channel_validate_surface(DisplayChannel *display, uint32_t
> surface_id)
> +{
> +    if SPICE_UNLIKELY(surface_id >= display->priv->n_surfaces) {
> +        spice_warning("invalid surface_id %u", surface_id);
> +        return 0;
> +    }
> +    if (!display->priv->surfaces[surface_id].context.canvas) {
> +        spice_warning("canvas address is %p for %d (and is NULL)\n",
> +                   &(display->priv->surfaces[surface_id].context.canvas),
> surface_id);
> +        spice_warning("failed on %d", surface_id);
> +        return 0;
> +    }
> +    return 1;
> +}
> +
> +void display_channel_update_monitors_config(DisplayChannel *display,
> +                                            QXLMonitorsConfig *config,
> +                                            uint16_t count, uint16_t
> max_allowed)
> +{
> +    if (display->priv->monitors_config)
> +        monitors_config_unref(display->priv->monitors_config);
> +
> +    display->priv->monitors_config =
> +        monitors_config_new(config->heads, count, max_allowed);
> +
> +}
> +
> +void display_channel_set_monitors_config_to_primary(DisplayChannel *display)
> +{
> +    DrawContext *context = &display->priv->surfaces[0].context;
> +    QXLHead head = { 0, };
> +
> +    spice_return_if_fail(display->priv->surfaces[0].context.canvas);
> +
> +    if (display->priv->monitors_config)
> +        monitors_config_unref(display->priv->monitors_config);
> +
> +    head.width = context->width;
> +    head.height = context->height;
> +    display->priv->monitors_config = monitors_config_new(&head, 1, 1);
> +}
> +
> +void display_channel_reset_image_cache(DisplayChannel *self)
> +{
> +    image_cache_reset(self->priv->image_cache);
> +}
> +
> +static void
> +display_channel_class_init(DisplayChannelClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
> +
> +    g_type_class_add_private(klass, sizeof(DisplayChannelPrivate));
> +
> +    object_class->get_property = display_channel_get_property;
> +    object_class->set_property = display_channel_set_property;
> +    object_class->constructed = display_channel_constructed;
> +    object_class->finalize = display_channel_finalize;
> +
> +    channel_class->parser =
> spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL);
> +    channel_class->handle_parsed = dcc_handle_message;
> +
> +    channel_class->on_disconnect = on_disconnect;
> +    channel_class->send_item = send_item;
> +    channel_class->hold_item = hold_item;
> +    channel_class->release_item = release_item;
> +    channel_class->handle_migrate_flush_mark = handle_migrate_flush_mark;
> +    channel_class->handle_migrate_data = handle_migrate_data;
> +    channel_class->handle_migrate_data_get_serial =
> handle_migrate_data_get_serial;
> +
> +    g_object_class_install_property(object_class,
> +                                    PROP_N_SURFACES,
> +                                    g_param_spec_uint("n-surfaces",
> +                                                      "number of surfaces",
> +                                                      "Number of surfaces
> for this channel",
> +                                                      0, G_MAXUINT,
> +                                                      0,
> +                                                      G_PARAM_CONSTRUCT_ONLY
> |
> +                                                      G_PARAM_READWRITE |
> +
> G_PARAM_STATIC_STRINGS));
>  }
> diff --git a/server/display-channel.h b/server/display-channel.h
> index cb0a1e3..597d615 100644
> --- a/server/display-channel.h
> +++ b/server/display-channel.h
> @@ -20,31 +20,63 @@
>  
>  #include <setjmp.h>
>  
> -#include "common/rect.h"
> -#include "reds-stream.h"
>  #include "cache-item.h"
> -#include "pixmap-cache.h"
>  #include "sw-canvas.h"
> -#include "stat.h"
> -#include "reds.h"
> -#include "memslot.h"
> -#include "red-parse-qxl.h"
> -#include "red-record-qxl.h"
> +#include "common-graphics-channel.h"
> +#include "common/rect.h"
> +#include "dcc-encoders.h"
> +#include "dcc.h"
>  #include "demarshallers.h"
> -#include "red-channel.h"
> -#include "red-qxl.h"
>  #include "dispatcher.h"
>  #include "main-channel.h"
> -#include "migration-protocol.h"
>  #include "main-dispatcher.h"
> +#include "memslot.h"
> +#include "migration-protocol.h"
> +#include "mjpeg-encoder.h"
> +#include "pixmap-cache.h"
> +#include "red-channel.h"
> +#include "red-qxl.h"
> +#include "red-parse-qxl.h"
> +#include "red-record-qxl.h"
> +#include "reds-stream.h"
> +#include "reds.h"
>  #include "spice-bitmap-utils.h"
> -#include "image-cache.h"
> -#include "utils.h"
> -#include "tree.h"
> +#include "stat.h"
>  #include "stream.h"
> -#include "dcc.h"
> -#include "dcc-encoders.h"
> +#include "tree.h"
> +#include "utils.h"
> +
> +G_BEGIN_DECLS
>  
> +#define TYPE_DISPLAY_CHANNEL display_channel_get_type()
> +
> +#define DISPLAY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> TYPE_DISPLAY_CHANNEL, DisplayChannel))
> +#define DISPLAY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
> TYPE_DISPLAY_CHANNEL, DisplayChannelClass))
> +#define IS_DISPLAY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> TYPE_DISPLAY_CHANNEL))
> +#define IS_DISPLAY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
> TYPE_DISPLAY_CHANNEL))
> +#define DISPLAY_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
> TYPE_DISPLAY_CHANNEL, DisplayChannelClass))
> +
> +typedef struct DisplayChannel DisplayChannel;
> +typedef struct DisplayChannelClass DisplayChannelClass;
> +typedef struct DisplayChannelPrivate DisplayChannelPrivate;
> +
> +struct DisplayChannel
> +{
> +    CommonGraphicsChannel parent;
> +
> +    uint32_t glz_drawable_count;
> +
> +    DisplayChannelPrivate *priv;
> +};
> +
> +struct DisplayChannelClass
> +{
> +    CommonGraphicsChannelClass parent_class;
> +};
> +
> +GType display_channel_get_type(void) G_GNUC_CONST;
> +
> +G_END_DECLS
>  typedef struct DependItem {
>      Drawable *drawable;
>      RingItem ring_item;
> @@ -164,68 +196,8 @@ struct _Drawable {
>      } u;
>  };
>  
> -struct DisplayChannel {
> -    CommonGraphicsChannel common; // Must be the first thing
> -    uint32_t bits_unique;
> -
> -    MonitorsConfig *monitors_config;
> -
> -    uint32_t renderer;
> -    int enable_jpeg;
> -    int enable_zlib_glz_wrap;
> -
> -    Ring current_list; // of TreeItem
> -    uint32_t current_size;
> -
> -    uint32_t drawable_count;
> -    _Drawable drawables[NUM_DRAWABLES];
> -    _Drawable *free_drawables;
> -
> -    uint32_t glz_drawable_count;
> -
> -    int stream_video;
> -    uint32_t stream_count;
> -    Stream streams_buf[NUM_STREAMS];
> -    Stream *free_streams;
> -    Ring streams;
> -    ItemTrace items_trace[NUM_TRACE_ITEMS];
> -    uint32_t next_item_trace;
> -    uint64_t streams_size_total;
> -
> -    RedSurface surfaces[NUM_SURFACES];
> -    uint32_t n_surfaces;
> -    SpiceImageSurfaces image_surfaces;
> -
> -    ImageCache image_cache;
> -    RedCompressBuf *free_compress_bufs;
> -
> -    int gl_draw_async_count;
> -
> -/* TODO: some day unify this, make it more runtime.. */
> -    stat_info_t add_stat;
> -    stat_info_t exclude_stat;
> -    stat_info_t __exclude_stat;
> -#ifdef RED_WORKER_STAT
> -    uint32_t add_count;
> -    uint32_t add_with_shadow_count;
> -#endif
> -#ifdef RED_STATISTICS
> -    uint64_t *cache_hits_counter;
> -    uint64_t *add_to_cache_counter;
> -    uint64_t *non_cache_counter;
> -#endif
> -    stat_info_t off_stat;
> -    stat_info_t lz_stat;
> -    stat_info_t glz_stat;
> -    stat_info_t quic_stat;
> -    stat_info_t jpeg_stat;
> -    stat_info_t zlib_glz_stat;
> -    stat_info_t jpeg_alpha_stat;
> -    stat_info_t lz4_stat;
> -};
> -
>  #define FOREACH_DCC(channel, _link, _next, _data)                   \
> -    for (_link = (channel ? RED_CHANNEL(channel)->clients : NULL), \
> +    for (_link = (channel ? red_channel_get_clients(RED_CHANNEL(channel)) :
> NULL), \
>           _next = (_link ? _link->next : NULL), \
>           _data = (_link ? _link->data : NULL); \
>           _link; \
> @@ -233,10 +205,7 @@ struct DisplayChannel {
>           _next = (_link ? _link->next : NULL), \
>           _data = (_link ? _link->data : NULL))
>  
> -static inline int get_stream_id(DisplayChannel *display, Stream *stream)
> -{
> -    return (int)(stream - display->streams_buf);
> -}
> +int display_channel_get_stream_id(DisplayChannel *display, Stream *stream);
>  
>  typedef struct RedSurfaceDestroyItem {
>      SpiceMsgSurfaceDestroy surface_destroy;
> @@ -251,7 +220,8 @@ typedef struct RedUpgradeItem {
>  
>  
>  DisplayChannel*            display_channel_new
>  (SpiceServer *reds,
> -
> RedWorker
> *worker,
> +
> QXLInstance
> *qxl,
> +                                                                      const
> SpiceCoreInterfaceInternal* core,
>                                                                        int
>                                                                        migrate,
>                                                                        int
>                                                                        stream_video,
>                                                                        uint32_t
>                                                                        n_surfaces);
> @@ -275,13 +245,16 @@ void                       display_channel_update
> (DisplayCha
>  void                       display_channel_free_some
>  (DisplayChannel *display);
>  void                       display_channel_set_stream_video
>  (DisplayChannel *display,
>                                                                        int
>                                                                        stream_video);
> +int                        display_channel_get_stream_video
> (DisplayChannel *display);
>  int                        display_channel_get_streams_timeout
>  (DisplayChannel *display);
> -void                       display_channel_compress_stats_print      (const
> DisplayChannel *display);
> +void                       display_channel_compress_stats_print
> (DisplayChannel *display);
>  void                       display_channel_compress_stats_reset
>  (DisplayChannel *display);
>  Drawable *                 display_channel_drawable_try_new
>  (DisplayChannel *display,
>                                                                        int
>                                                                        process_commands_generation);
>  void                       display_channel_surface_unref
>  (DisplayChannel *display,
>                                                                        uint32_t
>                                                                        surface_id);
> +bool                       display_channel_surface_has_canvas
> (DisplayChannel *display,
> +
> uint32_t
> surface_id);
>  void                       display_channel_current_flush
>  (DisplayChannel *display,
>                                                                        int
>                                                                        surface_id);
>  int                        display_channel_wait_for_migrate_data
>  (DisplayChannel *display);
> @@ -306,21 +279,12 @@ void                       display_channel_gl_scanout
> (DisplayCha
>  void                       display_channel_gl_draw
>  (DisplayChannel *display,
>                                                                        SpiceMsgDisplayGlDraw
>                                                                        *draw);
>  void                       display_channel_gl_draw_done
>  (DisplayChannel *display);
> +void display_channel_update_monitors_config(DisplayChannel *display,
> QXLMonitorsConfig *config,
> +                                            uint16_t count, uint16_t
> max_allowed);
> +void display_channel_set_monitors_config_to_primary(DisplayChannel
> *display);
>  
> -static inline int validate_surface(DisplayChannel *display, uint32_t
> surface_id)
> -{
> -    if SPICE_UNLIKELY(surface_id >= display->n_surfaces) {
> -        spice_warning("invalid surface_id %u", surface_id);
> -        return 0;
> -    }
> -    if (!display->surfaces[surface_id].context.canvas) {
> -        spice_warning("canvas address is %p for %d (and is NULL)\n",
> -                   &(display->surfaces[surface_id].context.canvas),
> surface_id);
> -        spice_warning("failed on %d", surface_id);
> -        return 0;
> -    }
> -    return 1;
> -}
> +gboolean display_channel_validate_surface(DisplayChannel *display, uint32_t
> surface_id);
> +void display_channel_reset_image_cache(DisplayChannel *self);
>  
>  static inline int is_equal_path(SpicePath *path1, SpicePath *path2)
>  {
> diff --git a/server/dummy-channel-client.c b/server/dummy-channel-client.c
> index a0354fd..6f0b868 100644
> --- a/server/dummy-channel-client.c
> +++ b/server/dummy-channel-client.c
> @@ -36,9 +36,11 @@ struct DummyChannelClientPrivate
>  
>  static int dummy_channel_client_pre_create_validate(RedChannel *channel,
>  RedClient  *client)
>  {
> -    if (red_client_get_channel(client, channel->type, channel->id)) {
> +    uint32_t type, id;
> +    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
> +    if (red_client_get_channel(client, type, id)) {
>          spice_printerr("Error client %p: duplicate channel type %d id %d",
> -                       client, channel->type, channel->id);
> +                       client, type, id);
>          return FALSE;
>      }
>      return TRUE;
> @@ -53,6 +55,9 @@ static gboolean
> dummy_channel_client_initable_init(GInitable *initable,
>      RedChannelClient *rcc = RED_CHANNEL_CLIENT(self);
>      RedClient *client = red_channel_client_get_client(rcc);
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> +    uint32_t type, id;
> +
> +    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
>      pthread_mutex_lock(&client->lock);
>      if (!dummy_channel_client_pre_create_validate(channel,
>                                                    client)) {
> @@ -60,7 +65,7 @@ static gboolean
> dummy_channel_client_initable_init(GInitable *initable,
>                      SPICE_SERVER_ERROR,
>                      SPICE_SERVER_ERROR_FAILED,
>                      "Client %p: duplicate channel type %d id %d",
> -                    client, channel->type, channel->id);
> +                    client, type, id);
>          goto cleanup;
>      }
>  
> @@ -94,10 +99,12 @@ void dummy_channel_client_disconnect(RedChannelClient
> *rcc)
>      DummyChannelClient *self = DUMMY_CHANNEL_CLIENT(rcc);
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>      GList *link;
> +    uint32_t type, id;
>  
> -    if (channel && (link = g_list_find(channel->clients, rcc))) {
> +    if (channel && (link = g_list_find(red_channel_get_clients(channel),
> rcc))) {
> +        g_object_get(channel, "channel-type", &type, "id", &id, NULL);
>          spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel,
> -                       channel->type, channel->id);
> +                       type, id);
>          red_channel_remove_client(channel, link->data);
>      }
>      self->priv->connected = FALSE;
> diff --git a/server/dummy-channel.c b/server/dummy-channel.c
> new file mode 100644
> index 0000000..6ec7842
> --- /dev/null
> +++ b/server/dummy-channel.c
> @@ -0,0 +1,58 @@
> +/* dummy-channel.c */
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include "dummy-channel.h"
> +
> +G_DEFINE_TYPE(DummyChannel, dummy_channel, RED_TYPE_CHANNEL)
> +
> +#define DUMMY_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o),
> TYPE_DUMMY_CHANNEL, DummyChannelPrivate))
> +
> +struct DummyChannelPrivate
> +{
> +    gpointer padding;
> +};
> +
> +static void
> +dummy_channel_class_init(DummyChannelClass *klass)
> +{
> +    g_type_class_add_private(klass, sizeof(DummyChannelPrivate));
> +}
> +
> +static void
> +dummy_channel_init(DummyChannel *self)
> +{
> +    self->priv = DUMMY_CHANNEL_PRIVATE(self);
> +}
> +
> +// TODO: red_worker can use this one
> +static void dummy_watch_update_mask(SpiceWatch *watch, int event_mask)
> +{
> +}
> +
> +static SpiceWatch *dummy_watch_add(int fd, int event_mask, SpiceWatchFunc
> func, void *opaque)
> +{
> +    return NULL; // apparently allowed?
> +}
> +
> +static void dummy_watch_remove(SpiceWatch *watch)
> +{
> +}
> +
> +// TODO: actually, since I also use channel_client_dummym, no need for core.
> Can be NULL
> +static const SpiceCoreInterface dummy_core = {
> +    .watch_update_mask = dummy_watch_update_mask,
> +    .watch_add = dummy_watch_add,
> +    .watch_remove = dummy_watch_remove,
> +};
> +
> +RedChannel *dummy_channel_new(RedsState *reds, uint32_t type, uint32_t id)
> +{
> +    return g_object_new(TYPE_DUMMY_CHANNEL,
> +                        "spice-server", reds,
> +                        "core-interface", &dummy_core,
> +                        "channel-type", type,
> +                        "id", id,
> +                        NULL);
> +}
> diff --git a/server/dummy-channel.h b/server/dummy-channel.h
> new file mode 100644
> index 0000000..dd2f005
> --- /dev/null
> +++ b/server/dummy-channel.h
> @@ -0,0 +1,61 @@
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see
> <http://www.gnu.org/licenses/>.
> +   */
> +
> +#ifndef __DUMMY_CHANNEL_H__
> +#define __DUMMY_CHANNEL_H__
> +
> +#include <glib-object.h>
> +
> +#include "red-channel.h"
> +
> +G_BEGIN_DECLS
> +
> +// TODO: tmp, for channels that don't use RedChannel yet (e.g., snd
> channel), but
> +// do use the client callbacks. So the channel clients are not connected
> (the channel doesn't
> +// have list of them, but they do have a link to the channel, and the client
> has a list of them)
> +
> +#define TYPE_DUMMY_CHANNEL dummy_channel_get_type()
> +
> +#define DUMMY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> TYPE_DUMMY_CHANNEL, DummyChannel))
> +#define DUMMY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
> TYPE_DUMMY_CHANNEL, DummyChannelClass))
> +#define _IS_DUMMY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> TYPE_DUMMY_CHANNEL))
> +#define _IS_DUMMY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
> TYPE_DUMMY_CHANNEL))
> +#define DUMMY_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
> TYPE_DUMMY_CHANNEL, DummyChannelClass))
> +
> +typedef struct DummyChannel DummyChannel;
> +typedef struct DummyChannelClass DummyChannelClass;
> +typedef struct DummyChannelPrivate DummyChannelPrivate;
> +
> +struct DummyChannel
> +{
> +    RedChannel parent;
> +
> +    DummyChannelPrivate *priv;
> +};
> +
> +struct DummyChannelClass
> +{
> +    RedChannelClass parent_class;
> +};
> +
> +GType dummy_channel_get_type(void) G_GNUC_CONST;
> +
> +RedChannel *dummy_channel_new(RedsState *reds, uint32_t type, uint32_t id);
> +
> +G_END_DECLS
> +
> +#endif /* __DUMMY_CHANNEL_H__ */
> diff --git a/server/inputs-channel.c b/server/inputs-channel.c
> index c0fdc98..32e2cfe 100644
> --- a/server/inputs-channel.c
> +++ b/server/inputs-channel.c
> @@ -57,6 +57,83 @@
>  #define RECEIVE_BUF_SIZE \
>      (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) *
>      SPICE_AGENT_MAX_DATA_SIZE)
>  
> +G_DEFINE_TYPE(InputsChannel, inputs_channel, RED_TYPE_CHANNEL)
> +
> +#define CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o),
> TYPE_INPUTS_CHANNEL, InputsChannelPrivate))
> +
> +struct InputsChannelPrivate
> +{
> +    uint8_t recv_buf[RECEIVE_BUF_SIZE];
> +    VDAgentMouseState mouse_state;
> +    int src_during_migrate;
> +
> +    SpiceKbdInstance *keyboard;
> +    SpiceMouseInstance *mouse;
> +    SpiceTabletInstance *tablet;
> +};
> +
> +
> +static void
> +inputs_channel_get_property(GObject *object,
> +                            guint property_id,
> +                            GValue *value,
> +                            GParamSpec *pspec)
> +{
> +    switch (property_id)
> +    {
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void
> +inputs_channel_set_property(GObject *object,
> +                            guint property_id,
> +                            const GValue *value,
> +                            GParamSpec *pspec)
> +{
> +    switch (property_id)
> +    {
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void inputs_connect(RedChannel *channel, RedClient *client,
> +                           RedsStream *stream, int migration,
> +                           int num_common_caps, uint32_t *common_caps,
> +                           int num_caps, uint32_t *caps);
> +static void inputs_migrate(RedChannelClient *rcc);
> +static SpiceTimer *key_modifiers_timer;
> +static void key_modifiers_sender(void *opaque);
> +
> +static void
> +inputs_channel_constructed(GObject *object)
> +{
> +    ClientCbs client_cbs = { NULL, };
> +    InputsChannel *self = INPUTS_CHANNEL(object);
> +    RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
> +
> +    G_OBJECT_CLASS(inputs_channel_parent_class)->constructed(object);
> +
> +    client_cbs.connect = inputs_connect;
> +    client_cbs.migrate = inputs_migrate;
> +    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL);
> +
> +    red_channel_set_cap(RED_CHANNEL(self), SPICE_INPUTS_CAP_KEY_SCANCODE);
> +    reds_register_channel(reds, RED_CHANNEL(self));
> +
> +    if (!(key_modifiers_timer = reds_core_timer_add(reds,
> key_modifiers_sender, self))) {
> +        spice_error("key modifiers timer create failed");
> +    }
> +}
> +
> +static void
> +inputs_channel_init(InputsChannel *self)
> +{
> +    self->priv = CHANNEL_PRIVATE(self);
> +}
> +
>  struct SpiceKbdState {
>      bool push_ext;
>  
> @@ -101,17 +178,6 @@ RedsState*
> spice_tablet_state_get_server(SpiceTabletState *st)
>      return st->reds;
>  }
>  
> -struct InputsChannel {
> -    RedChannel base;
> -    uint8_t recv_buf[RECEIVE_BUF_SIZE];
> -    VDAgentMouseState mouse_state;
> -    int src_during_migrate;
> -
> -    SpiceKbdInstance *keyboard;
> -    SpiceMouseInstance *mouse;
> -    SpiceTabletInstance *tablet;
> -};
> -
>  typedef struct RedInputsPipeItem {
>      RedPipeItem base;
>  } RedInputsPipeItem;
> @@ -126,8 +192,6 @@ typedef struct RedInputsInitPipeItem {
>      uint8_t modifiers;
>  } RedInputsInitPipeItem;
>  
> -static SpiceTimer *key_modifiers_timer;
> -
>  
>  #define KEY_MODIFIERS_TTL (MSEC_PER_SEC * 2)
>  
> @@ -139,26 +203,26 @@ void
> inputs_channel_set_tablet_logical_size(InputsChannel *inputs, int x_res, in
>  {
>      SpiceTabletInterface *sif;
>  
> -    sif = SPICE_CONTAINEROF(inputs->tablet->base.sif, SpiceTabletInterface,
> base);
> -    sif->set_logical_size(inputs->tablet, x_res, y_res);
> +    sif = SPICE_CONTAINEROF(inputs->priv->tablet->base.sif,
> SpiceTabletInterface, base);
> +    sif->set_logical_size(inputs->priv->tablet, x_res, y_res);
>  }
>  
>  VDAgentMouseState *inputs_channel_get_mouse_state(InputsChannel *inputs)
>  {
> -    return &inputs->mouse_state;
> +    return &inputs->priv->mouse_state;
>  }
>  
>  static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
>                                                   uint16_t type,
>                                                   uint32_t size)
>  {
> -    InputsChannel *inputs_channel =
> (InputsChannel*)red_channel_client_get_channel(rcc);
> +    InputsChannel *inputs =
> (InputsChannel*)red_channel_client_get_channel(rcc);
>  
>      if (size > RECEIVE_BUF_SIZE) {
>          spice_printerr("error: too large incoming message");
>          return NULL;
>      }
> -    return inputs_channel->recv_buf;
> +    return inputs->priv->recv_buf;
>  }
>  
>  static void inputs_channel_release_msg_rcv_buf(RedChannelClient *rcc,
> @@ -276,10 +340,10 @@ static void inputs_channel_send_item(RedChannelClient
> *rcc, RedPipeItem *base)
>  static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t
>  size, uint16_t type,
>                                          void *message)
>  {
> -    InputsChannel *inputs_channel = (InputsChannel
> *)red_channel_client_get_channel(rcc);
> +    InputsChannel *inputs =
> INPUTS_CHANNEL(red_channel_client_get_channel(rcc));
>      InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc);
>      uint32_t i;
> -    RedsState *reds = red_channel_get_server(&inputs_channel->base);
> +    RedsState *reds = red_channel_get_server(RED_CHANNEL(inputs));
>  
>      switch (type) {
>      case SPICE_MSGC_INPUTS_KEY_DOWN: {
> @@ -297,19 +361,19 @@ static int
> inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui
>              if (code == 0) {
>                  break;
>              }
> -            kbd_push_scan(inputs_channel_get_keyboard(inputs_channel),
> code);
> +            kbd_push_scan(inputs_channel_get_keyboard(inputs), code);
>          }
>          break;
>      }
>      case SPICE_MSGC_INPUTS_KEY_SCANCODE: {
>          uint8_t *code = message;
>          for (i = 0; i < size; i++) {
> -            kbd_push_scan(inputs_channel_get_keyboard(inputs_channel),
> code[i]);
> +            kbd_push_scan(inputs_channel_get_keyboard(inputs), code[i]);
>          }
>          break;
>      }
>      case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
> -        SpiceMouseInstance *mouse =
> inputs_channel_get_mouse(inputs_channel);
> +        SpiceMouseInstance *mouse = inputs_channel_get_mouse(inputs);
>          SpiceMsgcMouseMotion *mouse_motion = message;
>  
>          inputs_channel_client_on_mouse_motion(icc);
> @@ -324,7 +388,7 @@ static int inputs_channel_handle_parsed(RedChannelClient
> *rcc, uint32_t size, ui
>      }
>      case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
>          SpiceMsgcMousePosition *pos = message;
> -        SpiceTabletInstance *tablet =
> inputs_channel_get_tablet(inputs_channel);
> +        SpiceTabletInstance *tablet = inputs_channel_get_tablet(inputs);
>  
>          inputs_channel_client_on_mouse_motion(icc);
>          if (reds_get_mouse_mode(reds) != SPICE_MOUSE_MODE_CLIENT) {
> @@ -337,7 +401,7 @@ static int inputs_channel_handle_parsed(RedChannelClient
> *rcc, uint32_t size, ui
>              sif->position(tablet, pos->x, pos->y,
>              RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state));
>              break;
>          }
> -        VDAgentMouseState *mouse_state = &inputs_channel->mouse_state;
> +        VDAgentMouseState *mouse_state = &inputs->priv->mouse_state;
>          mouse_state->x = pos->x;
>          mouse_state->y = pos->y;
>          mouse_state->buttons =
>          RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state);
> @@ -355,20 +419,20 @@ static int
> inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui
>          }
>          if (reds_get_mouse_mode(reds) == SPICE_MOUSE_MODE_CLIENT) {
>              if (reds_config_get_agent_mouse(reds) && reds_has_vdagent(reds))
>              {
> -                inputs_channel->mouse_state.buttons =
> +                inputs->priv->mouse_state.buttons =
>                      RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state)
>                      |
>                      (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) |
>                      (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0);
> -                reds_handle_agent_mouse_event(reds,
> &inputs_channel->mouse_state);
> -            } else if (inputs_channel_get_tablet(inputs_channel)) {
> +                reds_handle_agent_mouse_event(reds,
> &inputs->priv->mouse_state);
> +            } else if (inputs_channel_get_tablet(inputs)) {
>                  SpiceTabletInterface *sif;
> -                sif =
> SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif,
> SpiceTabletInterface, base);
> -                sif->wheel(inputs_channel_get_tablet(inputs_channel), dz,
> RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
> +                sif =
> SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs)->base.sif,
> SpiceTabletInterface, base);
> +                sif->wheel(inputs_channel_get_tablet(inputs), dz,
> RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
>              }
> -        } else if (inputs_channel_get_mouse(inputs_channel)) {
> +        } else if (inputs_channel_get_mouse(inputs)) {
>              SpiceMouseInterface *sif;
> -            sif =
> SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif,
> SpiceMouseInterface, base);
> -            sif->motion(inputs_channel_get_mouse(inputs_channel), 0, 0, dz,
> +            sif =
> SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs)->base.sif,
> SpiceMouseInterface, base);
> +            sif->motion(inputs_channel_get_mouse(inputs), 0, 0, dz,
>                          RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
>          }
>          break;
> @@ -377,18 +441,18 @@ static int
> inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui
>          SpiceMsgcMouseRelease *mouse_release = message;
>          if (reds_get_mouse_mode(reds) == SPICE_MOUSE_MODE_CLIENT) {
>              if (reds_config_get_agent_mouse(reds) && reds_has_vdagent(reds))
>              {
> -                inputs_channel->mouse_state.buttons =
> +                inputs->priv->mouse_state.buttons =
>                      RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state);
> -                reds_handle_agent_mouse_event(reds,
> &inputs_channel->mouse_state);
> -            } else if (inputs_channel_get_tablet(inputs_channel)) {
> +                reds_handle_agent_mouse_event(reds,
> &inputs->priv->mouse_state);
> +            } else if (inputs_channel_get_tablet(inputs)) {
>                  SpiceTabletInterface *sif;
> -                sif =
> SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif,
> SpiceTabletInterface, base);
> -                sif->buttons(inputs_channel_get_tablet(inputs_channel),
> RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
> +                sif =
> SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs)->base.sif,
> SpiceTabletInterface, base);
> +                sif->buttons(inputs_channel_get_tablet(inputs),
> RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
>              }
> -        } else if (inputs_channel_get_mouse(inputs_channel)) {
> +        } else if (inputs_channel_get_mouse(inputs)) {
>              SpiceMouseInterface *sif;
> -            sif =
> SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif,
> SpiceMouseInterface, base);
> -            sif->buttons(inputs_channel_get_mouse(inputs_channel),
> +            sif =
> SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs)->base.sif,
> SpiceMouseInterface, base);
> +            sif->buttons(inputs_channel_get_mouse(inputs),
>                           RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
>          }
>          break;
> @@ -396,7 +460,7 @@ static int inputs_channel_handle_parsed(RedChannelClient
> *rcc, uint32_t size, ui
>      case SPICE_MSGC_INPUTS_KEY_MODIFIERS: {
>          SpiceMsgcKeyModifiers *modifiers = message;
>          uint8_t leds;
> -        SpiceKbdInstance *keyboard =
> inputs_channel_get_keyboard(inputs_channel);
> +        SpiceKbdInstance *keyboard = inputs_channel_get_keyboard(inputs);
>  
>          if (!keyboard) {
>              break;
> @@ -520,17 +584,17 @@ static void inputs_connect(RedChannel *channel,
> RedClient *client,
>  static void inputs_migrate(RedChannelClient *rcc)
>  {
>      InputsChannel *inputs =
>      (InputsChannel*)red_channel_client_get_channel(rcc);
> -    inputs->src_during_migrate = TRUE;
> +    inputs->priv->src_during_migrate = TRUE;
>      red_channel_client_default_migrate(rcc);
>  }
>  
>  static void inputs_channel_push_keyboard_modifiers(InputsChannel *inputs,
>  uint8_t modifiers)
>  {
> -    if (!inputs || !red_channel_is_connected(&inputs->base) ||
> -        inputs->src_during_migrate) {
> +    if (!inputs || !red_channel_is_connected(RED_CHANNEL(inputs)) ||
> +        inputs->priv->src_during_migrate) {
>          return;
>      }
> -    red_channel_pipes_new_add_push(&inputs->base,
> +    red_channel_pipes_new_add_push(RED_CHANNEL(inputs),
>          red_inputs_key_modifiers_item_new, (void*)&modifiers);
>  }
>  
> @@ -574,117 +638,113 @@ static int
> inputs_channel_handle_migrate_data(RedChannelClient *rcc,
>      return TRUE;
>  }
>  
> -InputsChannel* inputs_channel_new(RedsState *reds)
> +static void
> +inputs_channel_class_init(InputsChannelClass *klass)
>  {
> -    ChannelCbs channel_cbs = { NULL, };
> -    ClientCbs client_cbs = { NULL, };
> -    InputsChannel *inputs;
> -
> -    channel_cbs.config_socket = inputs_channel_config_socket;
> -    channel_cbs.on_disconnect = inputs_channel_on_disconnect;
> -    channel_cbs.send_item = inputs_channel_send_item;
> -    channel_cbs.hold_item = inputs_channel_hold_pipe_item;
> -    channel_cbs.release_item = inputs_channel_release_pipe_item;
> -    channel_cbs.alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf;
> -    channel_cbs.release_recv_buf = inputs_channel_release_msg_rcv_buf;
> -    channel_cbs.handle_migrate_data = inputs_channel_handle_migrate_data;
> -    channel_cbs.handle_migrate_flush_mark =
> inputs_channel_handle_migrate_flush_mark;
> -
> -    inputs = (InputsChannel *)red_channel_create_parser(
> -                                    sizeof(InputsChannel),
> -                                    reds,
> -                                    reds_get_core_interface(reds),
> -                                    SPICE_CHANNEL_INPUTS, 0,
> -                                    FALSE, /* handle_acks */
> -
> spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS,
> NULL),
> -                                    inputs_channel_handle_parsed,
> -                                    &channel_cbs,
> -                                    SPICE_MIGRATE_NEED_FLUSH |
> SPICE_MIGRATE_NEED_DATA_TRANSFER);
> -
> -    if (!inputs) {
> -        spice_error("failed to allocate Inputs Channel");
> -    }
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
>  
> -    client_cbs.connect = inputs_connect;
> -    client_cbs.migrate = inputs_migrate;
> -    red_channel_register_client_cbs(&inputs->base, &client_cbs, NULL);
> +    g_type_class_add_private(klass, sizeof(InputsChannelPrivate));
>  
> -    red_channel_set_cap(&inputs->base, SPICE_INPUTS_CAP_KEY_SCANCODE);
> -    reds_register_channel(reds, &inputs->base);
> +    object_class->get_property = inputs_channel_get_property;
> +    object_class->set_property = inputs_channel_set_property;
> +    object_class->constructed = inputs_channel_constructed;
> +
> +    channel_class->parser =
> spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL);
> +    channel_class->handle_parsed = inputs_channel_handle_parsed;
> +
> +    /* channel callbacks */
> +    channel_class->config_socket = inputs_channel_config_socket;
> +    channel_class->on_disconnect = inputs_channel_on_disconnect;
> +    channel_class->send_item = inputs_channel_send_item;
> +    channel_class->hold_item = inputs_channel_hold_pipe_item;
> +    channel_class->release_item = inputs_channel_release_pipe_item;
> +    channel_class->alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf;
> +    channel_class->release_recv_buf = inputs_channel_release_msg_rcv_buf;
> +    channel_class->handle_migrate_data = inputs_channel_handle_migrate_data;
> +    channel_class->handle_migrate_flush_mark =
> inputs_channel_handle_migrate_flush_mark;
> +}
> +
> +InputsChannel* inputs_channel_new(RedsState *reds)
> +{
> +    return  g_object_new(TYPE_INPUTS_CHANNEL,
> +                         "spice-server", reds,
> +                         "core-interface", reds_get_core_interface(reds),
> +                         "channel-type", (int)SPICE_CHANNEL_INPUTS,
> +                         "id", 0,
> +                         "handle-acks", FALSE,
> +                         "migration-flags", (guint)(SPICE_MIGRATE_NEED_FLUSH
> | SPICE_MIGRATE_NEED_DATA_TRANSFER),
> +                         NULL);
>  
> -    if (!(key_modifiers_timer = reds_core_timer_add(reds,
> key_modifiers_sender, inputs))) {
> -        spice_error("key modifiers timer create failed");
> -    }
> -    return inputs;
>  }
>  
>  SpiceKbdInstance* inputs_channel_get_keyboard(InputsChannel *inputs)
>  {
> -    return inputs->keyboard;
> +    return inputs->priv->keyboard;
>  }
>  
>  int inputs_channel_set_keyboard(InputsChannel *inputs, SpiceKbdInstance
>  *keyboard)
>  {
> -    if (inputs->keyboard) {
> +    if (inputs->priv->keyboard) {
>          spice_printerr("already have keyboard");
>          return -1;
>      }
> -    inputs->keyboard = keyboard;
> -    inputs->keyboard->st =
> spice_kbd_state_new(red_channel_get_server(&inputs->base));
> +    inputs->priv->keyboard = keyboard;
> +    inputs->priv->keyboard->st =
> spice_kbd_state_new(red_channel_get_server(RED_CHANNEL(inputs)));
>      return 0;
>  }
>  
>  SpiceMouseInstance* inputs_channel_get_mouse(InputsChannel *inputs)
>  {
> -    return inputs->mouse;
> +    return inputs->priv->mouse;
>  }
>  
>  int inputs_channel_set_mouse(InputsChannel *inputs, SpiceMouseInstance
>  *mouse)
>  {
> -    if (inputs->mouse) {
> +    if (inputs->priv->mouse) {
>          spice_printerr("already have mouse");
>          return -1;
>      }
> -    inputs->mouse = mouse;
> -    inputs->mouse->st = spice_mouse_state_new();
> +    inputs->priv->mouse = mouse;
> +    inputs->priv->mouse->st = spice_mouse_state_new();
>      return 0;
>  }
>  
>  SpiceTabletInstance* inputs_channel_get_tablet(InputsChannel *inputs)
>  {
> -    return inputs->tablet;
> +    return inputs->priv->tablet;
>  }
>  
>  int inputs_channel_set_tablet(InputsChannel *inputs, SpiceTabletInstance
>  *tablet, RedsState *reds)
>  {
> -    if (inputs->tablet) {
> +    if (inputs->priv->tablet) {
>          spice_printerr("already have tablet");
>          return -1;
>      }
> -    inputs->tablet = tablet;
> -    inputs->tablet->st = spice_tablet_state_new();
> -    inputs->tablet->st->reds = reds;
> +    inputs->priv->tablet = tablet;
> +    inputs->priv->tablet->st = spice_tablet_state_new();
> +    inputs->priv->tablet->st->reds = reds;
>      return 0;
>  }
>  
>  int inputs_channel_has_tablet(InputsChannel *inputs)
>  {
> -    return inputs != NULL && inputs->tablet != NULL;
> +    return inputs != NULL && inputs->priv->tablet != NULL;
>  }
>  
>  void inputs_channel_detach_tablet(InputsChannel *inputs, SpiceTabletInstance
>  *tablet)
>  {
>      spice_printerr("");
> -    inputs->tablet = NULL;
> +    inputs->priv->tablet = NULL;
>  }
>  
>  gboolean inputs_channel_is_src_during_migrate(InputsChannel *inputs)
>  {
> -    return inputs->src_during_migrate;
> +    return inputs->priv->src_during_migrate;
>  }
>  
>  void inputs_channel_set_src_during_migrate(InputsChannel *inputs,
>                                             gboolean value)
>  {
> -    inputs->src_during_migrate = value;
> +    inputs->priv->src_during_migrate = value;
>  }
> diff --git a/server/inputs-channel.h b/server/inputs-channel.h
> index 5317578..1dbb3a2 100644
> --- a/server/inputs-channel.h
> +++ b/server/inputs-channel.h
> @@ -21,14 +21,42 @@
>  // Inputs channel, dealing with keyboard, mouse, tablet.
>  // This include should only be used by reds.c and inputs-channel.c
>  
> +#include <glib-object.h>
>  #include <stdint.h>
>  #include <spice/vd_agent.h>
>  
>  #include "red-channel.h"
>  
> +G_BEGIN_DECLS
> +
> +#define TYPE_INPUTS_CHANNEL inputs_channel_get_type()
> +
> +#define INPUTS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> TYPE_INPUTS_CHANNEL, InputsChannel))
> +#define INPUTS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
> TYPE_INPUTS_CHANNEL, InputsChannelClass))
> +#define INPUTS_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> TYPE_INPUTS_CHANNEL))
> +#define INPUTS_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
> TYPE_INPUTS_CHANNEL))
> +#define INPUTS_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
> TYPE_INPUTS_CHANNEL, InputsChannelClass))
> +
>  typedef struct InputsChannel InputsChannel;
> +typedef struct InputsChannelClass InputsChannelClass;
> +typedef struct InputsChannelPrivate InputsChannelPrivate;
> +
> +struct InputsChannel
> +{
> +    RedChannel parent;
> +
> +    InputsChannelPrivate *priv;
> +};
> +
> +struct InputsChannelClass
> +{
> +    RedChannelClass parent_class;
> +};
> +
> +GType inputs_channel_get_type(void) G_GNUC_CONST;
>  
>  InputsChannel* inputs_channel_new(RedsState *reds);
> +
>  VDAgentMouseState *inputs_channel_get_mouse_state(InputsChannel *inputs);
>  void inputs_channel_on_keyboard_leds_change(InputsChannel *inputs, uint8_t
>  leds);
>  void inputs_channel_set_tablet_logical_size(InputsChannel *inputs, int
>  x_res, int y_res);
> @@ -53,4 +81,6 @@ enum {
>      RED_PIPE_ITEM_MIGRATE_DATA,
>  };
>  
> +G_END_DECLS
> +
>  #endif
> diff --git a/server/main-channel-client.c b/server/main-channel-client.c
> index 82cb5c3..f18be1c 100644
> --- a/server/main-channel-client.c
> +++ b/server/main-channel-client.c
> @@ -101,16 +101,6 @@ static void main_channel_client_set_property(GObject
> *object,
>      }
>  }
>  
> -static void main_channel_client_dispose(GObject *object)
> -{
> -    G_OBJECT_CLASS(main_channel_client_parent_class)->dispose(object);
> -}
> -
> -static void main_channel_client_finalize(GObject *object)
> -{
> -    G_OBJECT_CLASS(main_channel_client_parent_class)->finalize(object);
> -}
> -
>  static void ping_timer_cb(void *opaque);
>  static void main_channel_client_constructed(GObject *object)
>  {
> @@ -135,8 +125,6 @@ static void
> main_channel_client_class_init(MainChannelClientClass *klass)
>  
>      object_class->get_property = main_channel_client_get_property;
>      object_class->set_property = main_channel_client_set_property;
> -    object_class->dispose = main_channel_client_dispose;
> -    object_class->finalize = main_channel_client_finalize;
>      object_class->constructed = main_channel_client_constructed;
>  
>      g_object_class_install_property(object_class,
> @@ -341,15 +329,11 @@ void
> main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
>      spice_printerr("client %p connected: %d seamless %d",
>                     client, success, seamless);
>      if (mcc->priv->mig_wait_connect) {
> -        MainChannel *main_channel = SPICE_CONTAINEROF(channel, MainChannel,
> base);
> +        MainChannel *main_channel = MAIN_CHANNEL(channel);
>  
>          mcc->priv->mig_wait_connect = FALSE;
>          mcc->priv->mig_connect_ok = success;
> -        spice_assert(main_channel->num_clients_mig_wait);
> -        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
> -        if (!--main_channel->num_clients_mig_wait) {
> -            reds_on_main_migrate_connected(channel->reds, seamless &&
> success);
> -        }
> +        main_channel_on_migrate_connected(main_channel, seamless &&
> success);
>      } else {
>          if (success) {
>              spice_printerr("client %p MIGRATE_CANCEL", client);
> @@ -362,7 +346,7 @@ void
> main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
>                                                          uint32_t
>                                                          src_version)
>  {
>      RedChannel *channel =
>      red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc));
> -    if (reds_on_migrate_dst_set_seamless(channel->reds, mcc, src_version)) {
> +    if (reds_on_migrate_dst_set_seamless(red_channel_get_server(channel),
> mcc, src_version)) {
>          mcc->priv->seamless_mig_dst = TRUE;
>          red_channel_client_pipe_add_empty_msg(RED_CHANNEL_CLIENT(mcc),
>                                               SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
> @@ -429,7 +413,10 @@ void main_channel_client_handle_pong(MainChannelClient
> *mcc, SpiceMsgPing *ping,
>          red_channel_client_handle_message(rcc, size, SPICE_MSGC_PONG, ping);
>      }
>  #ifdef RED_STATISTICS
> -    stat_update_value(red_channel_client_get_channel(rcc)->reds, roundtrip);
> +    {
> +        RedChannel *channel = red_channel_client_get_channel(rcc);
> +        stat_update_value(red_channel_get_server(channel), roundtrip);
> +    }
>  #endif
>  }
>  
> @@ -470,7 +457,7 @@ void
> main_channel_client_migrate_dst_complete(MainChannelClient *mcc)
>      RedChannel *channel =
>      red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc));
>      if (mcc->priv->mig_wait_prev_complete) {
>          if (mcc->priv->mig_wait_prev_try_seamless) {
> -            spice_assert(channel->clients_num == 1);
> +            spice_assert(red_channel_get_n_clients(channel) == 1);
>              red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(mcc),
>                                               RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
>          } else {
> @@ -528,9 +515,11 @@ static void do_ping_client(MainChannelClient *mcc,
>          if (has_interval && interval > 0) {
>              mcc->priv->ping_interval = interval * MSEC_PER_SEC;
>          }
> -        reds_core_timer_start(channel->reds, mcc->priv->ping_timer,
> mcc->priv->ping_interval);
> +        reds_core_timer_start(red_channel_get_server(channel),
> +                              mcc->priv->ping_timer,
> mcc->priv->ping_interval);
>      } else if (!strcmp(opt, "off")) {
> -        reds_core_timer_cancel(channel->reds, mcc->priv->ping_timer);
> +        reds_core_timer_cancel(red_channel_get_server(channel),
> +                               mcc->priv->ping_timer);
>      } else {
>          return;
>      }
> @@ -543,11 +532,13 @@ static void ping_timer_cb(void *opaque)
>  
>      if (!red_channel_client_is_connected(RED_CHANNEL_CLIENT(mcc))) {
>          spice_printerr("not connected to peer, ping off");
> -        reds_core_timer_cancel(channel->reds, mcc->priv->ping_timer);
> +        reds_core_timer_cancel(red_channel_get_server(channel),
> +                               mcc->priv->ping_timer);
>          return;
>      }
>      do_ping_client(mcc, NULL, 0, 0);
> -    reds_core_timer_start(channel->reds, mcc->priv->ping_timer,
> mcc->priv->ping_interval);
> +    reds_core_timer_start(red_channel_get_server(channel),
> +                          mcc->priv->ping_timer, mcc->priv->ping_interval);
>  }
>  #endif /* RED_STATISTICS */
>  
> @@ -611,15 +602,14 @@ uint64_t
> main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
>  void main_channel_client_migrate(RedChannelClient *rcc)
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> -    reds_on_main_channel_migrate(channel->reds, MAIN_CHANNEL_CLIENT(rcc));
> +    reds_on_main_channel_migrate(red_channel_get_server(channel),
> +                                 MAIN_CHANNEL_CLIENT(rcc));
>      red_channel_client_default_migrate(rcc);
>  }
>  
>  gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc)
>  {
>      RedChannelClient *rcc = RED_CHANNEL_CLIENT(mcc);
> -    MainChannel* main_channel =
> SPICE_CONTAINEROF(red_channel_client_get_channel(rcc),
> -                                                  MainChannel, base);
>      if (red_channel_client_test_remote_cap(rcc,
>                                             SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE))
>                                             {
>          RedClient *client = red_channel_client_get_client(rcc);
> @@ -633,7 +623,6 @@ gboolean
> main_channel_client_connect_semi_seamless(MainChannelClient *mcc)
>              mcc->priv->mig_wait_connect = TRUE;
>          }
>          mcc->priv->mig_connect_ok = FALSE;
> -        main_channel->num_clients_mig_wait++;
>          return TRUE;
>      }
>      return FALSE;
> diff --git a/server/main-channel-client.h b/server/main-channel-client.h
> index 5d284ad..50713ea 100644
> --- a/server/main-channel-client.h
> +++ b/server/main-channel-client.h
> @@ -23,8 +23,6 @@
>  
>  G_BEGIN_DECLS
>  
> -typedef struct MainChannel MainChannel;
> -
>  #define TYPE_MAIN_CHANNEL_CLIENT main_channel_client_get_type()
>  
>  #define MAIN_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
>  TYPE_MAIN_CHANNEL_CLIENT, MainChannelClient))
> @@ -49,6 +47,8 @@ struct MainChannelClientClass
>      RedChannelClientClass parent_class;
>  };
>  
> +typedef struct MainChannel MainChannel;
> +
>  GType main_channel_client_get_type(void) G_GNUC_CONST;
>  
>  MainChannelClient *main_channel_client_create(MainChannel *main_chan,
>  RedClient *client,
> diff --git a/server/main-channel.c b/server/main-channel.c
> index e3d6c57..554c20d 100644
> --- a/server/main-channel.c
> +++ b/server/main-channel.c
> @@ -50,15 +50,51 @@
>  #include "utils.h"
>  
>  #define ZERO_BUF_SIZE 4096
> +// approximate max receive message size for main channel
> +#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \
> +    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) *
> SPICE_AGENT_MAX_DATA_SIZE)
>  
>  static const uint8_t zero_page[ZERO_BUF_SIZE] = {0};
>  
> +
> +G_DEFINE_TYPE(MainChannel, main_channel, RED_TYPE_CHANNEL)
> +
> +#define MAIN_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o),
> TYPE_MAIN_CHANNEL, MainChannelPrivate))
> +
> +struct MainChannelPrivate
> +{
> +    uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE];
> +    RedsMigSpice mig_target; // TODO: add refs and release (afrer all
> clients completed migration in one way or the other?)
> +    int num_clients_mig_wait;
> +};
> +
> +static void
> +main_channel_constructed(GObject *object)
> +{
> +    MainChannel *self = MAIN_CHANNEL(object);
> +    ClientCbs client_cbs = { NULL, };
> +
> +    G_OBJECT_CLASS(main_channel_parent_class)->constructed(object);
> +
> +    red_channel_set_cap(RED_CHANNEL(self),
> SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> +    red_channel_set_cap(RED_CHANNEL(self), SPICE_MAIN_CAP_SEAMLESS_MIGRATE);
> +
> +    client_cbs.migrate = main_channel_client_migrate;
> +    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL);
> +}
> +
> +static void
> +main_channel_init(MainChannel *self)
> +{
> +    self->priv = MAIN_CHANNEL_PRIVATE(self);
> +}
> +
>  static void main_channel_release_pipe_item(RedChannelClient *rcc,
>                                             RedPipeItem *base, int
>                                             item_pushed);
>  
>  int main_channel_is_connected(MainChannel *main_chan)
>  {
> -    return red_channel_is_connected(&main_chan->base);
> +    return red_channel_is_connected(RED_CHANNEL(main_chan));
>  }
>  
>  /*
> @@ -78,7 +114,7 @@ RedClient *main_channel_get_client_by_link_id(MainChannel
> *main_chan, uint32_t c
>      MainChannelClient *mcc;
>      RedChannelClient *rcc;
>  
> -    for (link = main_chan->base.clients; link != NULL; link = link->next) {
> +    for (link = red_channel_get_clients(RED_CHANNEL(main_chan)); link !=
> NULL; link = link->next) {
>          rcc = link->data;
>          mcc = MAIN_CHANNEL_CLIENT(rcc);
>          if (main_channel_client_get_connection_id(mcc) == connection_id) {
> @@ -134,7 +170,7 @@ static void
> main_channel_marshall_channels(RedChannelClient *rcc,
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>  
>      red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST,
>      item);
> -    channels_info = reds_msg_channels_new(channel->reds);
> +    channels_info = reds_msg_channels_new(red_channel_get_server(channel));
>      spice_marshall_msg_main_channels_list(m, channels_info);
>      free(channels_info);
>  }
> @@ -167,7 +203,7 @@ void main_channel_push_mouse_mode(MainChannel *main_chan,
> int current_mode,
>          .is_client_mouse_allowed=is_client_mouse_allowed,
>      };
>  
> -    red_channel_pipes_new_add_push(&main_chan->base,
> +    red_channel_pipes_new_add_push(RED_CHANNEL(main_chan),
>          main_mouse_mode_item_new, &info);
>  }
>  
> @@ -188,10 +224,10 @@ static void
> main_channel_marshall_mouse_mode(RedChannelClient *rcc,
>  
>  void main_channel_push_agent_connected(MainChannel *main_chan)
>  {
> -    if (red_channel_test_remote_cap(&main_chan->base,
> SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) {
> -        red_channel_pipes_add_type(&main_chan->base,
> RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS);
> +    if (red_channel_test_remote_cap(RED_CHANNEL(main_chan),
> SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) {
> +        red_channel_pipes_add_type(RED_CHANNEL(main_chan),
> RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS);
>      } else {
> -        red_channel_pipes_add_empty_msg(&main_chan->base,
> SPICE_MSG_MAIN_AGENT_CONNECTED);
> +        red_channel_pipes_add_empty_msg(RED_CHANNEL(main_chan),
> SPICE_MSG_MAIN_AGENT_CONNECTED);
>      }
>  }
>  
> @@ -208,7 +244,7 @@ static void
> main_channel_marshall_agent_connected(SpiceMarshaller *m,
>  
>  void main_channel_push_agent_disconnected(MainChannel *main_chan)
>  {
> -    red_channel_pipes_add_type(&main_chan->base,
> RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED);
> +    red_channel_pipes_add_type(RED_CHANNEL(main_chan),
> RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED);
>  }
>  
>  static void main_channel_marshall_agent_disconnected(RedChannelClient *rcc,
> @@ -242,7 +278,7 @@ static void
> main_channel_marshall_agent_data(RedChannelClient *rcc,
>  
>  static void main_channel_push_migrate_data_item(MainChannel *main_chan)
>  {
> -    red_channel_pipes_add_type(&main_chan->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA);
> +    red_channel_pipes_add_type(RED_CHANNEL(main_chan),
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA);
>  }
>  
>  static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
> @@ -251,7 +287,7 @@ static void
> main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>      red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
> -    reds_marshall_migrate_data(channel->reds, m); // TODO: from reds split.
> ugly separation.
> +    reds_marshall_migrate_data(red_channel_get_server(channel), m); // TODO:
> from reds split. ugly separation.
>  }
>  
>  static int main_channel_handle_migrate_data(RedChannelClient *rcc,
> @@ -262,7 +298,7 @@ static int
> main_channel_handle_migrate_data(RedChannelClient *rcc,
>      SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
>  
>      /* not supported with multi-clients */
> -    spice_assert(channel->clients_num == 1);
> +    spice_assert(red_channel_get_n_clients(channel) == 1);
>  
>      if (size < sizeof(SpiceMigrateDataHeader) +
>      sizeof(SpiceMigrateDataMain)) {
>          spice_printerr("bad message size %u", size);
> @@ -274,7 +310,9 @@ static int
> main_channel_handle_migrate_data(RedChannelClient *rcc,
>          spice_error("bad header");
>          return FALSE;
>      }
> -    return reds_handle_migrate_data(channel->reds, mcc,
> (SpiceMigrateDataMain *)(header + 1), size);
> +    return reds_handle_migrate_data(red_channel_get_server(channel), mcc,
> +                                    (SpiceMigrateDataMain *)(header + 1),
> +                                    size);
>  }
>  
>  static void main_channel_marshall_init(RedChannelClient *rcc,
> @@ -292,7 +330,7 @@ static void main_channel_marshall_init(RedChannelClient
> *rcc,
>      if (item->is_client_mouse_allowed) {
>          init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT;
>      }
> -    init.agent_connected = reds_has_vdagent(channel->reds);
> +    init.agent_connected =
> reds_has_vdagent(red_channel_get_server(channel));
>      init.agent_tokens = REDS_AGENT_WINDOW_SIZE;
>      init.multi_media_time = item->multi_media_time;
>      init.ram_hint = item->ram_hint;
> @@ -317,7 +355,7 @@ static void main_channel_marshall_notify(RedChannelClient
> *rcc,
>  static void main_channel_fill_migrate_dst_info(MainChannel *main_channel,
>                                                 SpiceMigrationDstInfo
>                                                 *dst_info)
>  {
> -    RedsMigSpice *mig_dst = &main_channel->mig_target;
> +    RedsMigSpice *mig_dst = &main_channel->priv->mig_target;
>      dst_info->port = mig_dst->port;
>      dst_info->sport = mig_dst->sport;
>      dst_info->host_size = strlen(mig_dst->host) + 1;
> @@ -336,11 +374,9 @@ static void
> main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelCl
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>      SpiceMsgMainMigrationBegin migrate;
> -    MainChannel *main_ch;
>  
>      red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN,
>      item);
> -    main_ch = SPICE_CONTAINEROF(channel, MainChannel, base);
> -    main_channel_fill_migrate_dst_info(main_ch, &migrate.dst_info);
> +    main_channel_fill_migrate_dst_info(MAIN_CHANNEL(channel),
> &migrate.dst_info);
>      spice_marshall_msg_main_migrate_begin(m, &migrate);
>  }
>  
> @@ -350,11 +386,9 @@ static void
> main_channel_marshall_migrate_begin_seamless(SpiceMarshaller *m,
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>      SpiceMsgMainMigrateBeginSeamless migrate_seamless;
> -    MainChannel *main_ch;
>  
>      red_channel_client_init_send_data(rcc,
>      SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS, item);
> -    main_ch = SPICE_CONTAINEROF(channel, MainChannel, base);
> -    main_channel_fill_migrate_dst_info(main_ch, &migrate_seamless.dst_info);
> +    main_channel_fill_migrate_dst_info(MAIN_CHANNEL(channel),
> &migrate_seamless.dst_info);
>      migrate_seamless.src_mig_version = SPICE_MIGRATION_PROTOCOL_VERSION;
>      spice_marshall_msg_main_migrate_begin_seamless(m, &migrate_seamless);
>  }
> @@ -365,29 +399,29 @@ void main_channel_push_multi_media_time(MainChannel
> *main_chan, int time)
>          .time = time,
>      };
>  
> -    red_channel_pipes_new_add_push(&main_chan->base,
> +    red_channel_pipes_new_add_push(RED_CHANNEL(main_chan),
>          main_multi_media_time_item_new, &info);
>  }
>  
>  static void main_channel_fill_mig_target(MainChannel *main_channel,
>  RedsMigSpice *mig_target)
>  {
>      spice_assert(mig_target);
> -    free(main_channel->mig_target.host);
> -    main_channel->mig_target.host = spice_strdup(mig_target->host);
> -    free(main_channel->mig_target.cert_subject);
> +    free(main_channel->priv->mig_target.host);
> +    main_channel->priv->mig_target.host = spice_strdup(mig_target->host);
> +    free(main_channel->priv->mig_target.cert_subject);
>      if (mig_target->cert_subject) {
> -        main_channel->mig_target.cert_subject =
> spice_strdup(mig_target->cert_subject);
> +        main_channel->priv->mig_target.cert_subject =
> spice_strdup(mig_target->cert_subject);
>      } else {
> -        main_channel->mig_target.cert_subject = NULL;
> +        main_channel->priv->mig_target.cert_subject = NULL;
>      }
> -    main_channel->mig_target.port = mig_target->port;
> -    main_channel->mig_target.sport = mig_target->sport;
> +    main_channel->priv->mig_target.port = mig_target->port;
> +    main_channel->priv->mig_target.sport = mig_target->sport;
>  }
>  
>  void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice
>  *mig_target)
>  {
>      main_channel_fill_mig_target(main_chan, mig_target);
> -    red_channel_pipes_add_type(&main_chan->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
> +    red_channel_pipes_add_type(RED_CHANNEL(main_chan),
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
>  }
>  
>  static void main_channel_marshall_migrate_switch(SpiceMarshaller *m,
>  RedChannelClient *rcc,
> @@ -395,18 +429,18 @@ static void
> main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelC
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>      SpiceMsgMainMigrationSwitchHost migrate;
> -    MainChannel *main_ch;
> +    MainChannel *main_chan;
>  
>      spice_printerr("");
>      red_channel_client_init_send_data(rcc,
>      SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST, item);
> -    main_ch = SPICE_CONTAINEROF(channel, MainChannel, base);
> -    migrate.port = main_ch->mig_target.port;
> -    migrate.sport = main_ch->mig_target.sport;
> -    migrate.host_size = strlen(main_ch->mig_target.host) + 1;
> -    migrate.host_data = (uint8_t *)main_ch->mig_target.host;
> -    if (main_ch->mig_target.cert_subject) {
> -        migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject)
> + 1;
> -        migrate.cert_subject_data = (uint8_t
> *)main_ch->mig_target.cert_subject;
> +    main_chan = MAIN_CHANNEL(channel);
> +    migrate.port = main_chan->priv->mig_target.port;
> +    migrate.sport = main_chan->priv->mig_target.sport;
> +    migrate.host_size = strlen(main_chan->priv->mig_target.host) + 1;
> +    migrate.host_data = (uint8_t *)main_chan->priv->mig_target.host;
> +    if (main_chan->priv->mig_target.cert_subject) {
> +        migrate.cert_subject_size =
> strlen(main_chan->priv->mig_target.cert_subject) + 1;
> +        migrate.cert_subject_data = (uint8_t
> *)main_chan->priv->mig_target.cert_subject;
>      } else {
>          migrate.cert_subject_size = 0;
>          migrate.cert_subject_data = NULL;
> @@ -536,8 +570,9 @@ static int main_channel_handle_parsed(RedChannelClient
> *rcc, uint32_t size, uint
>                                        void *message)
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> -    MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base);
> +    MainChannel *main_chan = MAIN_CHANNEL(channel);
>      MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
> +    RedsState *reds = red_channel_get_server(channel);
>  
>      switch (type) {
>      case SPICE_MSGC_MAIN_AGENT_START: {
> @@ -548,18 +583,18 @@ static int main_channel_handle_parsed(RedChannelClient
> *rcc, uint32_t size, uint
>              return FALSE;
>          }
>          tokens = (SpiceMsgcMainAgentStart *)message;
> -        reds_on_main_agent_start(channel->reds, mcc, tokens->num_tokens);
> +        reds_on_main_agent_start(reds, mcc, tokens->num_tokens);
>          break;
>      }
>      case SPICE_MSGC_MAIN_AGENT_DATA: {
> -        reds_on_main_agent_data(channel->reds, mcc, message, size);
> +        reds_on_main_agent_data(reds, mcc, message, size);
>          break;
>      }
>      case SPICE_MSGC_MAIN_AGENT_TOKEN: {
>          SpiceMsgcMainAgentTokens *tokens;
>  
>          tokens = (SpiceMsgcMainAgentTokens *)message;
> -        reds_on_main_agent_tokens(channel->reds, mcc, tokens->num_tokens);
> +        reds_on_main_agent_tokens(reds, mcc, tokens->num_tokens);
>          break;
>      }
>      case SPICE_MSGC_MAIN_ATTACH_CHANNELS:
> @@ -583,7 +618,7 @@ static int main_channel_handle_parsed(RedChannelClient
> *rcc, uint32_t size, uint
>              ((SpiceMsgcMainMigrateDstDoSeamless *)message)->src_version);
>          break;
>      case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
> -        reds_on_main_mouse_mode_request(channel->reds, message, size);
> +        reds_on_main_mouse_mode_request(reds, message, size);
>          break;
>      case SPICE_MSGC_PONG: {
>          main_channel_client_handle_pong(mcc, (SpiceMsgPing *)message, size);
> @@ -605,13 +640,13 @@ static uint8_t
> *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
>                                                 uint32_t size)
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> -    MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base);
> +    MainChannel *main_chan = MAIN_CHANNEL(channel);
>      MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
>  
>      if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
> -        return reds_get_agent_data_buffer(channel->reds, mcc, size);
> +        return reds_get_agent_data_buffer(red_channel_get_server(channel),
> mcc, size);
>      } else {
> -        return main_chan->recv_buf;
> +        return main_chan->priv->recv_buf;
>      }
>  }
>  
> @@ -622,7 +657,7 @@ static void
> main_channel_release_msg_rcv_buf(RedChannelClient *rcc,
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>      if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
> -        reds_release_agent_data_buffer(channel->reds, msg);
> +        reds_release_agent_data_buffer(red_channel_get_server(channel),
> msg);
>      }
>  }
>  
> @@ -639,8 +674,7 @@ static int
> main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>      spice_debug(NULL);
> -    main_channel_push_migrate_data_item(SPICE_CONTAINEROF(channel,
> -                                        MainChannel, base));
> +    main_channel_push_migrate_data_item(MAIN_CHANNEL(channel));
>      return TRUE;
>  }
>  
> @@ -665,12 +699,12 @@ MainChannelClient *main_channel_link(MainChannel
> *channel, RedClient *client,
>  
>  int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa,
>  socklen_t *salen)
>  {
> -    return main_chan ?
> getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
> +    return main_chan ?
> getsockname(red_channel_get_first_socket(RED_CHANNEL(main_chan)), sa, salen)
> : -1;
>  }
>  
>  int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa,
>  socklen_t *salen)
>  {
> -    return main_chan ?
> getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
> +    return main_chan ?
> getpeername(red_channel_get_first_socket(RED_CHANNEL(main_chan)), sa, salen)
> : -1;
>  }
>  
>  // TODO: ? shouldn't it disonnect all clients? or shutdown all
>  main_channels?
> @@ -678,79 +712,57 @@ void main_channel_close(MainChannel *main_chan)
>  {
>      int socketfd;
>  
> -    if (main_chan && (socketfd =
> red_channel_get_first_socket(&main_chan->base)) != -1) {
> +    if (main_chan && (socketfd =
> red_channel_get_first_socket(RED_CHANNEL(main_chan))) != -1) {
>          close(socketfd);
>      }
>  }
>  
>  MainChannel* main_channel_new(RedsState *reds)
>  {
> -    RedChannel *channel;
> -    ChannelCbs channel_cbs = { NULL, };
> -    ClientCbs client_cbs = {NULL, };
> -
> -    channel_cbs.config_socket = main_channel_config_socket;
> -    channel_cbs.on_disconnect = main_channel_client_on_disconnect;
> -    channel_cbs.send_item = main_channel_send_item;
> -    channel_cbs.hold_item = main_channel_hold_pipe_item;
> -    channel_cbs.release_item = main_channel_release_pipe_item;
> -    channel_cbs.alloc_recv_buf = main_channel_alloc_msg_rcv_buf;
> -    channel_cbs.release_recv_buf = main_channel_release_msg_rcv_buf;
> -    channel_cbs.handle_migrate_flush_mark =
> main_channel_handle_migrate_flush_mark;
> -    channel_cbs.handle_migrate_data = main_channel_handle_migrate_data;
> -
>      // TODO: set the migration flag of the channel
> -    channel = red_channel_create_parser(sizeof(MainChannel), reds,
> -                                        reds_get_core_interface(reds),
> -                                        SPICE_CHANNEL_MAIN, 0,
> -                                        FALSE, /* handle_acks */
> -
> spice_get_client_channel_parser(SPICE_CHANNEL_MAIN,
> NULL),
> -                                        main_channel_handle_parsed,
> -                                        &channel_cbs,
> -                                        SPICE_MIGRATE_NEED_FLUSH |
> SPICE_MIGRATE_NEED_DATA_TRANSFER);
> -    spice_assert(channel);
> -    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> -    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE);
> -
> -    client_cbs.migrate = main_channel_client_migrate;
> -    red_channel_register_client_cbs(channel, &client_cbs, NULL);
> -
> -    return (MainChannel *)channel;
> +    return g_object_new(TYPE_MAIN_CHANNEL,
> +                        "spice-server", reds,
> +                        "core-interface", reds_get_core_interface(reds),
> +                        "channel-type", (gint)SPICE_CHANNEL_MAIN,
> +                        "id", 0,
> +                        "handle-acks", FALSE, /* handle_acks */
> +                        "migration-flags", (SPICE_MIGRATE_NEED_FLUSH |
> SPICE_MIGRATE_NEED_DATA_TRANSFER),
> +                        NULL);
>  }
>  
>  static int main_channel_connect_semi_seamless(MainChannel *main_channel)
>  {
>      GList *link;
>  
> -    for (link = main_channel->base.clients; link != NULL; link = link->next)
> {
> +    for (link = red_channel_get_clients(RED_CHANNEL(main_channel)); link !=
> NULL; link = link->next) {
>          RedChannelClient *rcc = link->data;
>          MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
>          if (main_channel_client_connect_semi_seamless(mcc))
> -            main_channel->num_clients_mig_wait++;
> +            main_channel->priv->num_clients_mig_wait++;
>      }
> -    return main_channel->num_clients_mig_wait;
> +    return main_channel->priv->num_clients_mig_wait;
>  }
>  
>  static int main_channel_connect_seamless(MainChannel *main_channel)
>  {
>      GList *link;
>  
> -    spice_assert(g_list_length(main_channel->base.clients) == 1);
> +    spice_assert(red_channel_get_n_clients(RED_CHANNEL(main_channel)) == 1);
>  
> -    for (link = main_channel->base.clients; link != NULL; link = link->next)
> {
> +    for (link = red_channel_get_clients(RED_CHANNEL(main_channel)); link !=
> NULL; link = link->next) {
>          RedChannelClient *rcc = link->data;
>          MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
>          main_channel_client_connect_seamless(mcc);
> -        main_channel->num_clients_mig_wait++;
> +        main_channel->priv->num_clients_mig_wait++;
>      }
> -    return main_channel->num_clients_mig_wait;
> +    return main_channel->priv->num_clients_mig_wait;
>  }
>  
>  int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice
>  *mig_target,
>                                   int try_seamless)
>  {
>      main_channel_fill_mig_target(main_channel, mig_target);
> -    main_channel->num_clients_mig_wait = 0;
> +    main_channel->priv->num_clients_mig_wait = 0;
>  
>      if (!main_channel_is_connected(main_channel)) {
>          return 0;
> @@ -760,8 +772,10 @@ int main_channel_migrate_connect(MainChannel
> *main_channel, RedsMigSpice *mig_ta
>          return main_channel_connect_semi_seamless(main_channel);
>      } else {
>          RedChannelClient *rcc;
> +        GList *clients = red_channel_get_clients(RED_CHANNEL(main_channel));
>  
> -        rcc = main_channel->base.clients->data;
> +        /* just test the first one */
> +        rcc = clients->data;
>  
>          if (!red_channel_client_test_remote_cap(rcc,
>                                                  SPICE_MAIN_CAP_SEAMLESS_MIGRATE))
>                                                  {
> @@ -777,27 +791,28 @@ void main_channel_migrate_cancel_wait(MainChannel
> *main_chan)
>  {
>      GList *link;
>  
> -    for (link = main_chan->base.clients; link != NULL; link = link->next) {
> +    for (link = red_channel_get_clients(RED_CHANNEL(main_chan)); link !=
> NULL; link = link->next) {
>          RedChannelClient *rcc = link->data;
>          MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
>          main_channel_client_migrate_cancel_wait(mcc);
>      }
> -    main_chan->num_clients_mig_wait = 0;
> +    main_chan->priv->num_clients_mig_wait = 0;
>  }
>  
>  int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
>  {
>      GList *link;
> +    GList *clients = red_channel_get_clients(RED_CHANNEL(main_chan));
>      int semi_seamless_count = 0;
>  
>      spice_printerr("");
>  
> -    if (!main_chan->base.clients) {
> +    if (!clients) {
>          spice_printerr("no peer connected");
>          return 0;
>      }
>  
> -    for (link = main_chan->base.clients; link != NULL; link = link->next) {
> +    for (link = clients; link != NULL; link = link->next) {
>          RedChannelClient *rcc = link->data;
>          MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
>          if (main_channel_client_migrate_src_complete(mcc, success))
> @@ -805,3 +820,39 @@ int main_channel_migrate_src_complete(MainChannel
> *main_chan, int success)
>     }
>     return semi_seamless_count;
>  }
> +
> +void main_channel_on_migrate_connected(MainChannel *main_channel, gboolean
> seamless)
> +{
> +        g_return_if_fail(main_channel->priv->num_clients_mig_wait);
> +        g_warn_if_fail(!seamless || main_channel->priv->num_clients_mig_wait
> == 1);
> +        if (!--main_channel->priv->num_clients_mig_wait) {
> +
> reds_on_main_migrate_connected(red_channel_get_server(RED_CHANNEL(main_channel)),
> +                                           seamless);
> +        }
> +}
> +
> +static void
> +main_channel_class_init(MainChannelClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
> +
> +    g_type_class_add_private(klass, sizeof(MainChannelPrivate));
> +
> +    object_class->constructed = main_channel_constructed;
> +
> +    channel_class->parser =
> spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL);
> +    channel_class->handle_parsed = main_channel_handle_parsed;
> +
> +    /* channel callbacks */
> +    channel_class->config_socket = main_channel_config_socket;
> +    channel_class->on_disconnect = main_channel_client_on_disconnect;
> +    channel_class->send_item = main_channel_send_item;
> +    channel_class->hold_item = main_channel_hold_pipe_item;
> +    channel_class->release_item = main_channel_release_pipe_item;
> +    channel_class->alloc_recv_buf = main_channel_alloc_msg_rcv_buf;
> +    channel_class->release_recv_buf = main_channel_release_msg_rcv_buf;
> +    channel_class->handle_migrate_flush_mark =
> main_channel_handle_migrate_flush_mark;
> +    channel_class->handle_migrate_data = main_channel_handle_migrate_data;
> +}
> +
> diff --git a/server/main-channel.h b/server/main-channel.h
> index 43a2679..ce67241 100644
> --- a/server/main-channel.h
> +++ b/server/main-channel.h
> @@ -18,21 +18,47 @@
>  #ifndef __MAIN_CHANNEL_H__
>  #define __MAIN_CHANNEL_H__
>  
> +#include <glib-object.h>
>  #include <stdint.h>
>  #include <spice/vd_agent.h>
> +
>  #include "common/marshaller.h"
>  #include "red-channel.h"
>  #include "main-channel-client.h"
>  
> +G_BEGIN_DECLS
> +
> +#define TYPE_MAIN_CHANNEL main_channel_get_type()
> +
> +#define MAIN_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> TYPE_MAIN_CHANNEL, MainChannel))
> +#define MAIN_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
> TYPE_MAIN_CHANNEL, MainChannelClass))
> +#define IS_MAIN_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> TYPE_MAIN_CHANNEL))
> +#define IS_MAIN_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
> TYPE_MAIN_CHANNEL))
> +#define MAIN_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
> TYPE_MAIN_CHANNEL, MainChannelClass))
> +
> +typedef struct MainChannel MainChannel;
> +typedef struct MainChannelClass MainChannelClass;
> +typedef struct MainChannelPrivate MainChannelPrivate;
> +
> +struct MainChannel
> +{
> +    RedChannel parent;
> +
> +    MainChannelPrivate *priv;
> +};
> +
> +struct MainChannelClass
> +{
> +    RedChannelClass parent_class;
> +};
> +
> +GType main_channel_get_type(void) G_GNUC_CONST;
> +
>  // TODO: Defines used to calculate receive buffer size, and also by reds.c
>  // other options: is to make a reds_main_consts.h, to duplicate defines.
>  #define REDS_AGENT_WINDOW_SIZE 10
>  #define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
>  
> -// approximate max receive message size for main channel
> -#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \
> -    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) *
> SPICE_AGENT_MAX_DATA_SIZE)
> -
>  struct RedsMigSpice {
>      char *host;
>      char *cert_subject;
> @@ -41,13 +67,6 @@ struct RedsMigSpice {
>  };
>  typedef struct RedsMigSpice RedsMigSpice;
>  
> -typedef struct MainChannel {
> -    RedChannel base;
> -    uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE];
> -    RedsMigSpice mig_target; // TODO: add refs and release (afrer all
> clients completed migration in one way or the other?)
> -    int num_clients_mig_wait;
> -} MainChannel;
> -
>  
>  MainChannel *main_channel_new(RedsState *reds);
>  RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan,
>  uint32_t link_id);
> @@ -77,5 +96,8 @@ int main_channel_migrate_connect(MainChannel *main_channel,
> RedsMigSpice *mig_ta
>  void main_channel_migrate_cancel_wait(MainChannel *main_chan);
>  /* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was
>  sent*/
>  int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
> +void main_channel_on_migrate_connected(MainChannel *main_channel, gboolean
> seamless);
> +
> +G_END_DECLS
>  
>  #endif
> diff --git a/server/red-channel-client-private.h
> b/server/red-channel-client-private.h
> index c7009fe..7fe54d0 100644
> --- a/server/red-channel-client-private.h
> +++ b/server/red-channel-client-private.h
> @@ -21,6 +21,25 @@
>  #include "red-channel-client.h"
>  #include "red-channel.h"
>  
> +typedef struct RedChannelClientLatencyMonitor {
> +    int state;
> +    uint64_t last_pong_time;
> +    SpiceTimer *timer;
> +    uint32_t id;
> +    int tcp_nodelay;
> +    int warmup_was_sent;
> +
> +    int64_t roundtrip;
> +} RedChannelClientLatencyMonitor;
> +
> +typedef struct RedChannelClientConnectivityMonitor {
> +    int state;
> +    uint32_t out_bytes;
> +    uint32_t in_bytes;
> +    uint32_t timeout;
> +    SpiceTimer *timer;
> +} RedChannelClientConnectivityMonitor;
> +
>  struct RedChannelClientPrivate {
>      RedChannel *channel;
>      RedClient  *client;
> diff --git a/server/red-channel-client.c b/server/red-channel-client.c
> index 1c0339b..5e937b6 100644
> --- a/server/red-channel-client.c
> +++ b/server/red-channel-client.c
> @@ -158,6 +158,8 @@ static const SpiceDataHeaderOpaque mini_header_wrapper =
> {NULL, sizeof(SpiceMini
>  
>  static void red_channel_client_start_ping_timer(RedChannelClient *rcc,
>  uint32_t timeout)
>  {
> +    SpiceCoreInterfaceInternal *core;
> +
>      if (!rcc->priv->latency_monitor.timer) {
>          return;
>      }
> @@ -165,11 +167,15 @@ static void
> red_channel_client_start_ping_timer(RedChannelClient *rcc, uint32_t
>          return;
>      }
>      rcc->priv->latency_monitor.state = PING_STATE_TIMER;
> -    rcc->priv->channel->core->timer_start(rcc->priv->latency_monitor.timer,
> timeout);
> +
> +    core = red_channel_get_core_interface(rcc->priv->channel);
> +    core->timer_start(rcc->priv->latency_monitor.timer, timeout);
>  }
>  
>  static void red_channel_client_cancel_ping_timer(RedChannelClient *rcc)
>  {
> +    SpiceCoreInterfaceInternal *core;
> +
>      if (!rcc->priv->latency_monitor.timer) {
>          return;
>      }
> @@ -177,7 +183,8 @@ static void
> red_channel_client_cancel_ping_timer(RedChannelClient *rcc)
>          return;
>      }
>  
> -
> rcc->priv->channel->core->timer_cancel(rcc->priv->latency_monitor.timer);
> +    core = red_channel_get_core_interface(rcc->priv->channel);
> +    core->timer_cancel(rcc->priv->latency_monitor.timer);
>      rcc->priv->latency_monitor.state = PING_STATE_NONE;
>  }
>  
> @@ -252,10 +259,10 @@ red_channel_client_set_property(GObject *object,
>              break;
>          case PROP_CHANNEL:
>              if (self->priv->channel)
> -                red_channel_unref(self->priv->channel);
> +                g_object_unref(self->priv->channel);
>              self->priv->channel = g_value_get_pointer(value);
>              if (self->priv->channel)
> -                red_channel_ref(self->priv->channel);
> +                g_object_ref(self->priv->channel);
>              break;
>          case PROP_CLIENT:
>              self->priv->client = g_value_get_pointer(value);
> @@ -312,7 +319,7 @@ red_channel_client_finalize(GObject *object)
>  
>      red_channel_client_destroy_remote_caps(self);
>      if (self->priv->channel) {
> -        red_channel_unref(self->priv->channel);
> +        g_object_unref(self->priv->channel);
>      }
>  
>      G_OBJECT_CLASS(red_channel_client_parent_class)->finalize(object);
> @@ -438,7 +445,6 @@ void red_channel_client_on_output(void *opaque, int n)
>      if (rcc->priv->connectivity_monitor.timer) {
>          rcc->priv->connectivity_monitor.out_bytes += n;
>      }
> -    stat_inc_counter(reds, rcc->priv->channel->out_bytes_counter, n);
>  }
>  
>  void red_channel_client_on_input(void *opaque, int n)
> @@ -468,12 +474,14 @@ void red_channel_client_prepare_out_msg(void *opaque,
> struct iovec *vec,
>  
>  void red_channel_client_on_out_block(void *opaque)
>  {
> +    SpiceCoreInterfaceInternal *core;
>      RedChannelClient *rcc = (RedChannelClient *)opaque;
>  
>      rcc->priv->send_data.blocked = TRUE;
> -    rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch,
> -                                                SPICE_WATCH_EVENT_READ |
> -                                                SPICE_WATCH_EVENT_WRITE);
> +    core = red_channel_get_core_interface(rcc->priv->channel);
> +    core->watch_update_mask(rcc->priv->stream->watch,
> +                            SPICE_WATCH_EVENT_READ |
> +                            SPICE_WATCH_EVENT_WRITE);
>  }
>  
>  static inline int
>  red_channel_client_urgent_marshaller_is_active(RedChannelClient *rcc)
> @@ -533,9 +541,9 @@ static void
> red_channel_client_send_migrate(RedChannelClient *rcc)
>      SpiceMsgMigrate migrate;
>  
>      red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE, NULL);
> -    migrate.flags = rcc->priv->channel->migration_flags;
> +    g_object_get(rcc->priv->channel, "migration-flags", &migrate.flags,
> NULL);
>      spice_marshall_msg_migrate(rcc->priv->send_data.marshaller, &migrate);
> -    if (rcc->priv->channel->migration_flags & SPICE_MIGRATE_NEED_FLUSH) {
> +    if (migrate.flags & SPICE_MIGRATE_NEED_FLUSH) {
>          rcc->priv->wait_migrate_flush_mark = TRUE;
>      }
>  
> @@ -607,7 +615,7 @@ static void red_channel_client_send_item(RedChannelClient
> *rcc, RedPipeItem *ite
>              red_channel_client_send_ping(rcc);
>              break;
>          default:
> -            rcc->priv->channel->channel_cbs.send_item(rcc, item);
> +            red_channel_send_item(rcc->priv->channel, rcc, item);
>              return;
>      }
>      free(item);
> @@ -625,7 +633,7 @@ static void
> red_channel_client_release_item(RedChannelClient *rcc,
>              free(item);
>              break;
>          default:
> -            rcc->priv->channel->channel_cbs.release_item(rcc, item,
> item_pushed);
> +            red_channel_release_item(rcc->priv->channel, rcc, item,
> item_pushed);
>      }
>  }
>  
> @@ -670,9 +678,10 @@ void red_channel_client_on_out_msg_done(void *opaque)
>  
>      red_channel_client_release_sent_item(rcc);
>      if (rcc->priv->send_data.blocked) {
> +        SpiceCoreInterfaceInternal *core =
> red_channel_get_core_interface(rcc->priv->channel);
>          rcc->priv->send_data.blocked = FALSE;
> -
> rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch,
> -                                                    SPICE_WATCH_EVENT_READ);
> +        core->watch_update_mask(rcc->priv->stream->watch,
> +                                SPICE_WATCH_EVENT_READ);
>      }
>  
>      if (red_channel_client_urgent_marshaller_is_active(rcc)) {
> @@ -750,8 +759,13 @@ static void red_channel_client_ping_timer(void *opaque)
>  
>  static inline int red_channel_client_waiting_for_ack(RedChannelClient *rcc)
>  {
> -    return (rcc->priv->channel->handle_acks &&
> -            (rcc->priv->ack_data.messages_window >
> rcc->priv->ack_data.client_window * 2));
> +    gboolean handle_acks;
> +    g_object_get(rcc->priv->channel,
> +                 "handle-acks", &handle_acks,
> +                 NULL);
> +
> +    return (handle_acks && (rcc->priv->ack_data.messages_window >
> +                            rcc->priv->ack_data.client_window * 2));
>  }
>  
>  /*
> @@ -792,6 +806,7 @@ static void red_channel_client_connectivity_timer(void
> *opaque)
>      }
>  
>      if (is_alive) {
> +        SpiceCoreInterfaceInternal *core =
> red_channel_get_core_interface(rcc->priv->channel);
>          monitor->in_bytes = 0;
>          monitor->out_bytes = 0;
>          if (rcc->priv->send_data.blocked ||
>          red_channel_client_waiting_for_ack(rcc)) {
> @@ -802,18 +817,24 @@ static void red_channel_client_connectivity_timer(void
> *opaque)
>          } else {
>               monitor->state = CONNECTIVITY_STATE_CONNECTED;
>          }
> -
> rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer,
> +        core->timer_start(rcc->priv->connectivity_monitor.timer,
>                                                rcc->priv->connectivity_monitor.timeout);
>      } else {
> +        uint32_t type, id;
> +        g_object_get(rcc->priv->channel,
> +                     "channel-type", &type,
> +                     "id", &id,
> +                     NULL);
>          monitor->state = CONNECTIVITY_STATE_DISCONNECTED;
>          spice_warning("rcc %p on channel %d:%d has been unresponsive for
>          more than %u ms, disconnecting",
> -                      rcc, rcc->priv->channel->type, rcc->priv->channel->id,
> monitor->timeout);
> +                      rcc, type, id, monitor->timeout);
>          red_channel_client_disconnect(rcc);
>      }
>  }
>  
>  void red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc,
>  uint32_t timeout_ms)
>  {
> +    SpiceCoreInterfaceInternal *core =
> red_channel_get_core_interface(rcc->priv->channel);
>      if (!red_channel_client_is_connected(rcc)) {
>          return;
>      }
> @@ -826,8 +847,8 @@ void
> red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc, uin
>       * on this channel.
>       */
>      if (rcc->priv->latency_monitor.timer == NULL) {
> -        rcc->priv->latency_monitor.timer =
> rcc->priv->channel->core->timer_add(
> -            rcc->priv->channel->core, red_channel_client_ping_timer, rcc);
> +        rcc->priv->latency_monitor.timer = core->timer_add(
> +            core, red_channel_client_ping_timer, rcc);
>          if (!red_client_during_migrate_at_target(rcc->priv->client)) {
>              red_channel_client_start_ping_timer(rcc,
>              PING_TEST_IDLE_NET_TIMEOUT_MS);
>          }
> @@ -835,12 +856,12 @@ void
> red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc, uin
>      }
>      if (rcc->priv->connectivity_monitor.timer == NULL) {
>          rcc->priv->connectivity_monitor.state =
>          CONNECTIVITY_STATE_CONNECTED;
> -        rcc->priv->connectivity_monitor.timer =
> rcc->priv->channel->core->timer_add(
> -            rcc->priv->channel->core, red_channel_client_connectivity_timer,
> rcc);
> +        rcc->priv->connectivity_monitor.timer = core->timer_add(
> +            core, red_channel_client_connectivity_timer, rcc);
>          rcc->priv->connectivity_monitor.timeout = timeout_ms;
>          if (!red_client_during_migrate_at_target(rcc->priv->client)) {
> -
> rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer,
> -
> rcc->priv->connectivity_monitor.timeout);
> +            core->timer_start(rcc->priv->connectivity_monitor.timer,
> +                              rcc->priv->connectivity_monitor.timeout);
>          }
>      }
>  }
> @@ -861,9 +882,11 @@ static void red_channel_client_event(int fd, int event,
> void *data)
>  
>  static int red_channel_client_pre_create_validate(RedChannel *channel,
>  RedClient  *client)
>  {
> -    if (red_client_get_channel(client, channel->type, channel->id)) {
> +    uint32_t type, id;
> +    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
> +    if (red_client_get_channel(client, type, id)) {
>          spice_printerr("Error client %p: duplicate channel type %d id %d",
> -                       client, channel->type, channel->id);
> +                       client, type, id);
>          return FALSE;
>      }
>      return TRUE;
> @@ -874,24 +897,28 @@ static gboolean
> red_channel_client_initable_init(GInitable *initable,
>                                                   GError **error)
>  {
>      GError *local_error = NULL;
> +    SpiceCoreInterfaceInternal *core;
>      RedChannelClient *self = RED_CHANNEL_CLIENT(initable);
>      pthread_mutex_lock(&self->priv->client->lock);
>      if (!red_channel_client_pre_create_validate(self->priv->channel,
>      self->priv->client)) {
> +        uint32_t id, type;
> +        g_object_get(self->priv->channel,
> +                     "channel-type", &type,
> +                     "id", &id,
> +                     NULL);
>          g_set_error(&local_error,
>                      SPICE_SERVER_ERROR,
>                      SPICE_SERVER_ERROR_FAILED,
>                      "Client %p: duplicate channel type %d id %d",
> -                    self->priv->client, self->priv->channel->type,
> -                    self->priv->channel->id);
> +                    self->priv->client, type, id);
>          goto cleanup;
>      }
>  
> +    core = red_channel_get_core_interface(self->priv->channel);
>      if (self->priv->monitor_latency
>          && reds_stream_get_family(self->priv->stream) != AF_UNIX) {
>          self->priv->latency_monitor.timer =
> -            self->priv->channel->core->timer_add(self->priv->channel->core,
> -
> red_channel_client_ping_timer,
> -                                                 self);
> +            core->timer_add(core, red_channel_client_ping_timer, self);
>  
>          if (!self->priv->client->during_target_migrate) {
>              red_channel_client_start_ping_timer(self,
> @@ -901,27 +928,26 @@ static gboolean
> red_channel_client_initable_init(GInitable *initable,
>      }
>  
>      self->incoming.opaque = self;
> -    self->incoming.cb = &self->priv->channel->incoming_cb;
> +    self->incoming.cb =
> red_channel_get_incoming_handler(self->priv->channel);
>      self->incoming.header.data = self->incoming.header_buf;
>      self->incoming.serial = 1;
>  
>      self->outgoing.opaque = self;
> -    self->outgoing.cb = &self->priv->channel->outgoing_cb;
> +    self->outgoing.cb =
> red_channel_get_outgoing_handler(self->priv->channel);
>      self->outgoing.pos = 0;
>      self->outgoing.size = 0;
>  
>      if (self->priv->stream)
>          self->priv->stream->watch =
> -            self->priv->channel->core->watch_add(self->priv->channel->core,
> -                                                 self->priv->stream->socket,
> -                                                 SPICE_WATCH_EVENT_READ,
> -                                                 red_channel_client_event,
> -                                                 self);
> -    self->priv->id = self->priv->channel->clients_num;
> +            core->watch_add(core, self->priv->stream->socket,
> +                            SPICE_WATCH_EVENT_READ,
> +                            red_channel_client_event,
> +                            self);
> +    self->priv->id = red_channel_get_n_clients(self->priv->channel);
>      red_channel_add_client(self->priv->channel, self);
>      red_client_add_channel(self->priv->client, self);
>  
> -    if (!self->priv->channel->channel_cbs.config_socket(self)) {
> +    if (!red_channel_config_socket(self->priv->channel, self)) {
>          g_set_error_literal(&local_error,
>                              SPICE_SERVER_ERROR,
>                              SPICE_SERVER_ERROR_FAILED,
> @@ -981,8 +1007,9 @@ void
> red_channel_client_seamless_migration_done(RedChannelClient *rcc)
>              red_channel_client_start_ping_timer(rcc,
>              PING_TEST_IDLE_NET_TIMEOUT_MS);
>          }
>          if (rcc->priv->connectivity_monitor.timer) {
> -
> rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer,
> -
> rcc->priv->connectivity_monitor.timeout);
> +            SpiceCoreInterfaceInternal *core =
> red_channel_get_core_interface(rcc->priv->channel);
> +            core->timer_start(rcc->priv->connectivity_monitor.timer,
> +                              rcc->priv->connectivity_monitor.timeout);
>          }
>      }
>  }
> @@ -1001,13 +1028,14 @@ int
> red_channel_client_is_waiting_for_migrate_data(RedChannelClient *rcc)
>  
>  void red_channel_client_default_migrate(RedChannelClient *rcc)
>  {
> +    SpiceCoreInterfaceInternal *core =
> red_channel_get_core_interface(rcc->priv->channel);
>      if (rcc->priv->latency_monitor.timer) {
>          red_channel_client_cancel_ping_timer(rcc);
> -
> rcc->priv->channel->core->timer_remove(rcc->priv->latency_monitor.timer);
> +        core->timer_remove(rcc->priv->latency_monitor.timer);
>          rcc->priv->latency_monitor.timer = NULL;
>      }
>      if (rcc->priv->connectivity_monitor.timer) {
> -
> rcc->priv->channel->core->timer_remove(rcc->priv->connectivity_monitor.timer);
> +        core->timer_remove(rcc->priv->connectivity_monitor.timer);
>          rcc->priv->connectivity_monitor.timer = NULL;
>      }
>      red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_MIGRATE);
> @@ -1024,7 +1052,8 @@ void red_channel_client_destroy(RedChannelClient *rcc)
>  void red_channel_client_shutdown(RedChannelClient *rcc)
>  {
>      if (rcc->priv->stream && !rcc->priv->stream->shutdown) {
> -        rcc->priv->channel->core->watch_remove(rcc->priv->stream->watch);
> +        SpiceCoreInterfaceInternal *core =
> red_channel_get_core_interface(rcc->priv->channel);
> +        core->watch_remove(rcc->priv->stream->watch);
>          rcc->priv->stream->watch = NULL;
>          shutdown(rcc->priv->stream->socket, SHUT_RDWR);
>          rcc->priv->stream->shutdown = TRUE;
> @@ -1259,8 +1288,10 @@ void red_channel_client_push(RedChannelClient *rcc)
>      }
>      if (red_channel_client_no_item_being_sent(rcc) &&
>      ring_is_empty(&rcc->priv->pipe)
>          && rcc->priv->stream->watch) {
> -
> rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch,
> -                                              SPICE_WATCH_EVENT_READ);
> +        SpiceCoreInterfaceInternal *core;
> +        core = red_channel_get_core_interface(rcc->priv->channel);
> +        core->watch_update_mask(rcc->priv->stream->watch,
> +                                SPICE_WATCH_EVENT_READ);
>      }
>      rcc->priv->during_send = FALSE;
>      g_object_unref(rcc);
> @@ -1331,11 +1362,12 @@ static void
> red_channel_client_handle_pong(RedChannelClient *rcc, SpiceMsgPing *
>      red_channel_client_start_ping_timer(rcc, PING_TEST_TIMEOUT_MS);
>  }
>  
> -static void red_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
> +static void red_channel_client_handle_migrate_flush_mark(RedChannelClient
> *rcc)
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> -    if (channel->channel_cbs.handle_migrate_flush_mark) {
> -        channel->channel_cbs.handle_migrate_flush_mark(rcc);
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel);
> +    if (klass->handle_migrate_flush_mark) {
> +        klass->handle_migrate_flush_mark(rcc);
>      }
>  }
>  
> @@ -1346,23 +1378,30 @@ static void
> red_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
>  //  3) source migrates to target
>  //  4) target sends data to all
>  // So need to make all the handlers work with per channel/client data (what
>  data exactly?)
> -static void red_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t
> size, void *message)
> +static void red_channel_client_handle_migrate_data(RedChannelClient *rcc,
> +                                                   uint32_t size,
> +                                                   void
> +                                                   *message)
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel);
> +    uint32_t type, id;
> +
> +    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
>      spice_debug("channel type %d id %d rcc %p size %u",
> -                channel->type, channel->id, rcc, size);
> -    if (!channel->channel_cbs.handle_migrate_data) {
> +                type, id, rcc, size);
> +    if (!klass->handle_migrate_data) {
>          return;
>      }
>      if (!red_channel_client_is_waiting_for_migrate_data(rcc)) {
>          spice_channel_client_error(rcc, "unexpected");
>          return;
>      }
> -    if (channel->channel_cbs.handle_migrate_data_get_serial) {
> +    if (klass->handle_migrate_data_get_serial) {
>          red_channel_client_set_message_serial(rcc,
> -            channel->channel_cbs.handle_migrate_data_get_serial(rcc, size,
> message));
> +            klass->handle_migrate_data_get_serial(rcc, size, message));
>      }
> -    if (!channel->channel_cbs.handle_migrate_data(rcc, size, message)) {
> +    if (!klass->handle_migrate_data(rcc, size, message)) {
>          spice_channel_client_error(rcc, "handle_migrate_data failed");
>          return;
>      }
> @@ -1394,11 +1433,11 @@ int
> red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size,
>              spice_error("unexpected flush mark");
>              return FALSE;
>          }
> -        red_channel_handle_migrate_flush_mark(rcc);
> +        red_channel_client_handle_migrate_flush_mark(rcc);
>          rcc->priv->wait_migrate_flush_mark = FALSE;
>          break;
>      case SPICE_MSGC_MIGRATE_DATA:
> -        red_channel_handle_migrate_data(rcc, size, message);
> +        red_channel_client_handle_migrate_data(rcc, size, message);
>          break;
>      case SPICE_MSGC_PONG:
>          red_channel_client_handle_pong(rcc, message);
> @@ -1417,7 +1456,7 @@ void red_channel_client_init_send_data(RedChannelClient
> *rcc, uint16_t msg_type,
>      rcc->priv->send_data.header.set_msg_type(&rcc->priv->send_data.header,
>      msg_type);
>      rcc->priv->send_data.item = item;
>      if (item) {
> -        rcc->priv->channel->channel_cbs.hold_item(rcc, item);
> +        red_channel_hold_item(rcc->priv->channel, rcc, item);
>      }
>  }
>  
> @@ -1477,9 +1516,10 @@ static inline gboolean
> client_pipe_add(RedChannelClient *rcc, RedPipeItem *item,
>          return FALSE;
>      }
>      if (ring_is_empty(&rcc->priv->pipe) && rcc->priv->stream->watch) {
> -
> rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch,
> -                                         SPICE_WATCH_EVENT_READ |
> -                                         SPICE_WATCH_EVENT_WRITE);
> +        SpiceCoreInterfaceInternal *core;
> +        core = red_channel_get_core_interface(rcc->priv->channel);
> +        core->watch_update_mask(rcc->priv->stream->watch,
> +                                SPICE_WATCH_EVENT_READ |
> SPICE_WATCH_EVENT_WRITE);
>      }
>      rcc->priv->pipe_size++;
>      ring_add(pos, &item->link);
> @@ -1558,7 +1598,7 @@ uint32_t
> red_channel_client_get_pipe_size(RedChannelClient *rcc)
>  static gboolean red_channel_client_default_is_connected(RedChannelClient
>  *rcc)
>  {
>      return rcc->priv->channel
> -        && (g_list_find(rcc->priv->channel->clients, rcc) != NULL);
> +        && (g_list_find(red_channel_get_clients(rcc->priv->channel), rcc) !=
> NULL);
>  }
>  
>  gboolean red_channel_client_is_connected(RedChannelClient *rcc)
> @@ -1611,27 +1651,30 @@ void red_channel_client_push_set_ack(RedChannelClient
> *rcc)
>  static void red_channel_client_default_disconnect(RedChannelClient *rcc)
>  {
>      RedChannel *channel = rcc->priv->channel;
> +    SpiceCoreInterfaceInternal *core =
> red_channel_get_core_interface(channel);
> +    uint32_t type, id;
>  
>      if (!red_channel_client_is_connected(rcc)) {
>          return;
>      }
> +    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
>      spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel,
> -                   channel->type, channel->id);
> +                   type, id);
>      red_channel_client_pipe_clear(rcc);
>      if (rcc->priv->stream->watch) {
> -        channel->core->watch_remove(rcc->priv->stream->watch);
> +        core->watch_remove(rcc->priv->stream->watch);
>          rcc->priv->stream->watch = NULL;
>      }
>      if (rcc->priv->latency_monitor.timer) {
> -        channel->core->timer_remove(rcc->priv->latency_monitor.timer);
> +        core->timer_remove(rcc->priv->latency_monitor.timer);
>          rcc->priv->latency_monitor.timer = NULL;
>      }
>      if (rcc->priv->connectivity_monitor.timer) {
> -        channel->core->timer_remove(rcc->priv->connectivity_monitor.timer);
> +        core->timer_remove(rcc->priv->connectivity_monitor.timer);
>          rcc->priv->connectivity_monitor.timer = NULL;
>      }
>      red_channel_remove_client(channel, rcc);
> -    channel->channel_cbs.on_disconnect(rcc);
> +    red_channel_on_disconnect(channel, rcc);
>  }
>  
>  void red_channel_client_disconnect(RedChannelClient *rcc)
> @@ -1688,7 +1731,7 @@ int
> red_channel_client_wait_pipe_item_sent(RedChannelClient *rcc,
>          end_time = UINT64_MAX;
>      }
>  
> -    rcc->priv->channel->channel_cbs.hold_item(rcc, item);
> +    red_channel_hold_item(rcc->priv->channel, rcc, item);
>  
>      if (red_channel_client_is_blocked(rcc)) {
>          red_channel_client_receive(rcc);
> @@ -1771,13 +1814,19 @@ void
> red_channel_client_pipe_remove_and_release(RedChannelClient *rcc,
>  gboolean red_channel_client_set_migration_seamless(RedChannelClient *rcc)
>  {
>      gboolean ret = FALSE;
> -
> -    if (rcc->priv->channel->migration_flags &
> SPICE_MIGRATE_NEED_DATA_TRANSFER) {
> +    uint32_t type, id, flags;
> +
> +    g_object_get(rcc->priv->channel,
> +                 "channel-type", &type,
> +                 "id", &id,
> +                 "migration-flags", &flags,
> +                 NULL);
> +    if (flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) {
>          rcc->priv->wait_migrate_data = TRUE;
>          ret = TRUE;
>      }
> -    spice_debug("channel type %d id %d rcc %p wait data %d",
> rcc->priv->channel->type, rcc->priv->channel->id, rcc,
> -        rcc->priv->wait_migrate_data);
> +    spice_debug("channel type %d id %d rcc %p wait data %d", type, id, rcc,
> +                rcc->priv->wait_migrate_data);
>  
>      return ret;
>  }
> diff --git a/server/red-channel-client.h b/server/red-channel-client.h
> index d8cc317..6649485 100644
> --- a/server/red-channel-client.h
> +++ b/server/red-channel-client.h
> @@ -46,10 +46,10 @@ G_BEGIN_DECLS
>  #define RED_IS_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
>  RED_TYPE_CHANNEL_CLIENT))
>  #define RED_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
>  RED_TYPE_CHANNEL_CLIENT, RedChannelClientClass))
>  
> -typedef struct RedChannel RedChannel;
>  typedef struct RedClient RedClient;
>  typedef struct IncomingHandler IncomingHandler;
>  
> +typedef struct RedChannel RedChannel;
>  typedef struct RedChannelClient RedChannelClient;
>  typedef struct RedChannelClientClass RedChannelClientClass;
>  typedef struct RedChannelClientPrivate RedChannelClientPrivate;
> @@ -161,8 +161,10 @@ GType red_channel_client_get_type(void) G_GNUC_CONST;
>  #define spice_channel_client_error(rcc, format, ...)
>  \
>      do {
>      \
>          RedChannel *_ch = red_channel_client_get_channel(rcc);
>          \
> +        uint32_t _type, _id;
> \
> +        g_object_get(_ch, "channel-type", &_type, "id", &_id, NULL);
> \
>          spice_warning("rcc %p type %u id %u: " format, rcc,
>          \
> -                    _ch->type, _ch->id, ## __VA_ARGS__);
> \
> +                    type, id, ## __VA_ARGS__);
> \
>          red_channel_client_shutdown(rcc);
>          \
>      } while (0)
>  
> diff --git a/server/red-channel.c b/server/red-channel.c
> index c714d90..df865f0 100644
> --- a/server/red-channel.c
> +++ b/server/red-channel.c
> @@ -65,9 +65,135 @@
>   * from the channel's thread.
>  */
>  
> -void red_channel_receive(RedChannel *channel)
> +G_DEFINE_ABSTRACT_TYPE(RedChannel, red_channel, G_TYPE_OBJECT)
> +
> +#define CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o),
> RED_TYPE_CHANNEL, RedChannelPrivate))
> +
> +struct RedChannelPrivate
> +{
> +    uint32_t type;
> +    uint32_t id;
> +
> +    SpiceCoreInterfaceInternal *core;
> +    gboolean handle_acks;
> +
> +    // RedChannel will hold only connected channel clients (logic - when
> pushing pipe item to all channel clients, there
> +    // is no need to go over disconnect clients)
> +    // . While client will hold the channel clients till it is destroyed
> +    // and then it will destroy them as well.
> +    // However RCC still holds a reference to the Channel.
> +    // Maybe replace these logic with ref count?
> +    // TODO: rename to 'connected_clients'?
> +    GList *clients;
> +
> +    RedChannelCapabilities local_caps;
> +    uint32_t migration_flags;
> +
> +    void *data;
> +
> +    OutgoingHandlerInterface outgoing_cb;
> +    IncomingHandlerInterface incoming_cb;
> +
> +    ClientCbs client_cbs;
> +    // TODO: when different channel_clients are in different threads from
> Channel -> need to protect!
> +    pthread_t thread_id;
> +    RedsState *reds;
> +#ifdef RED_STATISTICS
> +    StatNodeRef stat;
> +    uint64_t *out_bytes_counter;
> +#endif
> +};
> +
> +enum {
> +    PROP0,
> +    PROP_SPICE_SERVER,
> +    PROP_CORE_INTERFACE,
> +    PROP_TYPE,
> +    PROP_ID,
> +    PROP_HANDLE_ACKS,
> +    PROP_MIGRATION_FLAGS
> +};
> +
> +static void
> +red_channel_get_property(GObject *object,
> +                         guint property_id,
> +                         GValue *value,
> +                         GParamSpec *pspec)
>  {
> -    g_list_foreach(channel->clients, (GFunc)red_channel_client_receive,
> NULL);
> +    RedChannel *self = RED_CHANNEL(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_SPICE_SERVER:
> +            g_value_set_pointer(value, self->priv->reds);
> +            break;
> +        case PROP_CORE_INTERFACE:
> +            g_value_set_pointer(value, self->priv->core);
> +            break;
> +        case PROP_TYPE:
> +            g_value_set_int(value, self->priv->type);
> +            break;
> +        case PROP_ID:
> +            g_value_set_uint(value, self->priv->id);
> +            break;
> +        case PROP_HANDLE_ACKS:
> +            g_value_set_boolean(value, self->priv->handle_acks);
> +            break;
> +        case PROP_MIGRATION_FLAGS:
> +            g_value_set_uint(value, self->priv->migration_flags);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void
> +red_channel_set_property(GObject *object,
> +                         guint property_id,
> +                         const GValue *value,
> +                         GParamSpec *pspec)
> +{
> +    RedChannel *self = RED_CHANNEL(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_SPICE_SERVER:
> +            self->priv->reds = g_value_get_pointer(value);
> +            break;
> +        case PROP_CORE_INTERFACE:
> +            self->priv->core = g_value_get_pointer(value);
> +            break;
> +        case PROP_TYPE:
> +            self->priv->type = g_value_get_int(value);
> +            break;
> +        case PROP_ID:
> +            self->priv->id = g_value_get_uint(value);
> +            break;
> +        case PROP_HANDLE_ACKS:
> +            self->priv->handle_acks = g_value_get_boolean(value);
> +            break;
> +        case PROP_MIGRATION_FLAGS:
> +            self->priv->migration_flags = g_value_get_uint(value);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void
> +red_channel_finalize(GObject *object)
> +{
> +    RedChannel *self = RED_CHANNEL(object);
> +
> +    if (self->priv->local_caps.num_common_caps) {
> +        free(self->priv->local_caps.common_caps);
> +    }
> +
> +    if (self->priv->local_caps.num_caps) {
> +        free(self->priv->local_caps.caps);
> +    }
> +
> +    G_OBJECT_CLASS(red_channel_parent_class)->finalize(object);
>  }
>  
>  static void red_channel_client_default_peer_on_error(RedChannelClient *rcc)
> @@ -75,17 +201,170 @@ static void
> red_channel_client_default_peer_on_error(RedChannelClient *rcc)
>      red_channel_client_disconnect(rcc);
>  }
>  
> +static void red_channel_on_output(void *opaque, int n)
> +{
> +    RedChannelClient *rcc = opaque;
> +    RedChannel *self = red_channel_client_get_channel(rcc);;
> +
> +    red_channel_client_on_output(opaque, n);
> +
> +    stat_inc_counter(self->priv->reds, self->priv->out_bytes_counter, n);
> +}
> +
> +static void
> +red_channel_constructed(GObject *object)
> +{
> +    RedChannel *self = RED_CHANNEL(object);
> +    spice_debug("%p: channel type %d id %d thread_id 0x%lx", self,
> +                self->priv->type, self->priv->id, self->priv->thread_id);
> +
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    self->priv->incoming_cb.alloc_msg_buf =
> +        (alloc_msg_recv_buf_proc)klass->alloc_recv_buf;
> +    self->priv->incoming_cb.release_msg_buf =
> +        (release_msg_recv_buf_proc)klass->release_recv_buf;
> +    self->priv->incoming_cb.handle_message =
> (handle_message_proc)klass->handle_message;
> +    self->priv->incoming_cb.handle_parsed =
> (handle_parsed_proc)klass->handle_parsed;
> +    self->priv->incoming_cb.parser = klass->parser;
> +
> +    G_OBJECT_CLASS(red_channel_parent_class)->constructed(object);
> +}
> +
> +static void red_channel_client_default_connect(RedChannel *channel,
> RedClient *client,
> +                                               RedsStream *stream,
> +                                               int migration,
> +                                               int num_common_caps, uint32_t
> *common_caps,
> +                                               int num_caps, uint32_t *caps)
> +{
> +    spice_error("not implemented");
> +}
> +
> +static void red_channel_client_default_disconnect(RedChannelClient *base)
> +{
> +    red_channel_client_disconnect(base);
> +}
> +
> +static void
> +red_channel_class_init(RedChannelClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +
> +    g_type_class_add_private(klass, sizeof (RedChannelPrivate));
> +
> +    object_class->get_property = red_channel_get_property;
> +    object_class->set_property = red_channel_set_property;
> +    object_class->finalize = red_channel_finalize;
> +    object_class->constructed = red_channel_constructed;
> +
> +    g_object_class_install_property(object_class,
> +                                    PROP_SPICE_SERVER,
> +                                    g_param_spec_pointer("spice-server",
> +                                                         "spice-server",
> +                                                         "The spice server
> associated with this channel",
> +                                                         G_PARAM_READWRITE |
> +
> G_PARAM_CONSTRUCT_ONLY
> |
> +
> G_PARAM_STATIC_STRINGS));
> +
> +    g_object_class_install_property(object_class,
> +                                    PROP_CORE_INTERFACE,
> +                                    g_param_spec_pointer("core-interface",
> +                                                         "core-interface",
> +                                                         "The
> SpiceCoreInterface server associated with this channel",
> +                                                         G_PARAM_READWRITE |
> +
> G_PARAM_CONSTRUCT_ONLY
> |
> +
> G_PARAM_STATIC_STRINGS));
> +
> +    /* FIXME: generate enums for this in spice-common? */
> +    g_object_class_install_property(object_class,
> +                                    PROP_TYPE,
> +                                    g_param_spec_int("channel-type",
> +                                                     "channel type",
> +                                                     "Type of this channel",
> +                                                     0,
> +                                                     SPICE_END_CHANNEL,
> +                                                     0,
> +                                                     G_PARAM_READWRITE |
> +                                                     G_PARAM_CONSTRUCT_ONLY
> |
> +
> G_PARAM_STATIC_STRINGS));
> +
> +    g_object_class_install_property(object_class,
> +                                    PROP_ID,
> +                                    g_param_spec_uint("id",
> +                                                      "id",
> +                                                      "ID of this channel",
> +                                                      0,
> +                                                      G_MAXUINT,
> +                                                      0,
> +                                                      G_PARAM_READWRITE |
> +                                                      G_PARAM_CONSTRUCT_ONLY
> |
> +
> G_PARAM_STATIC_STRINGS));
> +
> +    g_object_class_install_property(object_class,
> +                                    PROP_HANDLE_ACKS,
> +                                    g_param_spec_boolean("handle-acks",
> +                                                         "Handle ACKs",
> +                                                         "Whether this
> channel handles ACKs",
> +                                                         FALSE,
> +                                                         G_PARAM_READWRITE |
> +
> G_PARAM_CONSTRUCT_ONLY
> |
> +
> G_PARAM_STATIC_STRINGS));
> +
> +    g_object_class_install_property(object_class,
> +                                    PROP_MIGRATION_FLAGS,
> +                                    g_param_spec_uint("migration-flags",
> +                                                      "migration flags",
> +                                                      "Migration flags for
> this channel",
> +                                                      0,
> +                                                      G_MAXUINT,
> +                                                      0,
> +                                                      G_PARAM_READWRITE |
> +                                                      G_PARAM_CONSTRUCT_ONLY
> |
> +
> G_PARAM_STATIC_STRINGS));
> +}
> +
> +static void
> +red_channel_init(RedChannel *self)
> +{
> +    self->priv = CHANNEL_PRIVATE(self);
> +
> +    red_channel_set_common_cap(self, SPICE_COMMON_CAP_MINI_HEADER);
> +    self->priv->thread_id = pthread_self();
> +    self->priv->out_bytes_counter = 0;
> +
> +    // TODO: send incoming_cb as parameters instead of duplicating?
> +    self->priv->incoming_cb.on_error =
> +        (on_incoming_error_proc)red_channel_client_default_peer_on_error;
> +    self->priv->incoming_cb.on_input = red_channel_client_on_input;
> +    self->priv->outgoing_cb.get_msg_size =
> red_channel_client_get_out_msg_size;
> +    self->priv->outgoing_cb.prepare = red_channel_client_prepare_out_msg;
> +    self->priv->outgoing_cb.on_block = red_channel_client_on_out_block;
> +    self->priv->outgoing_cb.on_error =
> +        (on_outgoing_error_proc)red_channel_client_default_peer_on_error;
> +    self->priv->outgoing_cb.on_msg_done =
> red_channel_client_on_out_msg_done;
> +    self->priv->outgoing_cb.on_output = red_channel_on_output;
> +
> +    self->priv->client_cbs.connect = red_channel_client_default_connect;
> +    self->priv->client_cbs.disconnect =
> red_channel_client_default_disconnect;
> +    self->priv->client_cbs.migrate = red_channel_client_default_migrate;
> +}
> +
> +
> +void red_channel_receive(RedChannel *channel)
> +{
> +    g_list_foreach(channel->priv->clients,
> (GFunc)red_channel_client_receive, NULL);
> +}
> +
>  void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc)
>  {
>      spice_assert(rcc);
> -    channel->clients = g_list_append(channel->clients, rcc);
> +    channel->priv->clients = g_list_append(channel->priv->clients, rcc);
>  }
>  
>  int red_channel_test_remote_common_cap(RedChannel *channel, uint32_t cap)
>  {
>      GList *link;
>  
> -    for (link = channel->clients; link != NULL; link = link->next) {
> +    for (link = channel->priv->clients; link != NULL; link = link->next) {
>          RedChannelClient *rcc = link->data;
>  
>          if (!red_channel_client_test_remote_common_cap(rcc, cap)) {
> @@ -99,7 +378,7 @@ int red_channel_test_remote_cap(RedChannel *channel,
> uint32_t cap)
>  {
>      GList *link;
>  
> -    for (link = channel->clients; link != NULL; link = link->next) {
> +    for (link = channel->priv->clients; link != NULL; link = link->next) {
>          RedChannelClient *rcc = link->data;
>  
>          if (!red_channel_client_test_remote_cap(rcc, cap)) {
> @@ -136,7 +415,7 @@ gboolean
> red_client_seamless_migration_done_for_channel(RedClient *client,
>  int red_channel_is_waiting_for_migrate_data(RedChannel *channel)
>  {
>      RedChannelClient *rcc;
> -    guint n_clients = g_list_length(channel->clients);
> +    guint n_clients = g_list_length(channel->priv->clients);
>  
>      if (!red_channel_is_connected(channel)) {
>          return FALSE;
> @@ -146,190 +425,42 @@ int red_channel_is_waiting_for_migrate_data(RedChannel
> *channel)
>          return FALSE;
>      }
>      spice_assert(n_clients == 1);
> -    rcc = channel->clients->data;
> +    rcc = channel->priv->clients->data;
>      return red_channel_client_is_waiting_for_migrate_data(rcc);
>  }
>  
> -static void red_channel_client_default_connect(RedChannel *channel,
> RedClient *client,
> -                                               RedsStream *stream,
> -                                               int migration,
> -                                               int num_common_caps, uint32_t
> *common_caps,
> -                                               int num_caps, uint32_t *caps)
> -{
> -    spice_error("not implemented");
> -}
> -
> -static void red_channel_client_default_disconnect(RedChannelClient *base)
> -{
> -    red_channel_client_disconnect(base);
> -}
> -
> -RedChannel *red_channel_create(int size,
> -                               RedsState *reds,
> -                               const SpiceCoreInterfaceInternal *core,
> -                               uint32_t type, uint32_t id,
> -                               int handle_acks,
> -                               channel_handle_message_proc handle_message,
> -                               const ChannelCbs *channel_cbs,
> -                               uint32_t migration_flags)
> -{
> -    RedChannel *channel;
> -    ClientCbs client_cbs = { NULL, };
> -
> -    spice_assert(size >= sizeof(*channel));
> -    spice_assert(channel_cbs->config_socket && channel_cbs->on_disconnect &&
> handle_message &&
> -           channel_cbs->alloc_recv_buf && channel_cbs->release_item);
> -    spice_assert(channel_cbs->handle_migrate_data ||
> -                 !(migration_flags & SPICE_MIGRATE_NEED_DATA_TRANSFER));
> -    channel = spice_malloc0(size);
> -    channel->type = type;
> -    channel->id = id;
> -    channel->refs = 1;
> -    channel->handle_acks = handle_acks;
> -    channel->migration_flags = migration_flags;
> -    memcpy(&channel->channel_cbs, channel_cbs, sizeof(ChannelCbs));
> -
> -    channel->reds = reds;
> -    channel->core = core;
> -
> -    // TODO: send incoming_cb as parameters instead of duplicating?
> -    channel->incoming_cb.alloc_msg_buf =
> (alloc_msg_recv_buf_proc)channel_cbs->alloc_recv_buf;
> -    channel->incoming_cb.release_msg_buf =
> (release_msg_recv_buf_proc)channel_cbs->release_recv_buf;
> -    channel->incoming_cb.handle_message =
> (handle_message_proc)handle_message;
> -    channel->incoming_cb.on_error =
> -        (on_incoming_error_proc)red_channel_client_default_peer_on_error;
> -    channel->incoming_cb.on_input = red_channel_client_on_input;
> -    channel->outgoing_cb.get_msg_size = red_channel_client_get_out_msg_size;
> -    channel->outgoing_cb.prepare = red_channel_client_prepare_out_msg;
> -    channel->outgoing_cb.on_block = red_channel_client_on_out_block;
> -    channel->outgoing_cb.on_error =
> -        (on_outgoing_error_proc)red_channel_client_default_peer_on_error;
> -    channel->outgoing_cb.on_msg_done = red_channel_client_on_out_msg_done;
> -    channel->outgoing_cb.on_output = red_channel_client_on_output;
> -
> -    client_cbs.connect = red_channel_client_default_connect;
> -    client_cbs.disconnect = red_channel_client_default_disconnect;
> -    client_cbs.migrate = red_channel_client_default_migrate;
> -
> -    red_channel_register_client_cbs(channel, &client_cbs, NULL);
> -    red_channel_set_common_cap(channel, SPICE_COMMON_CAP_MINI_HEADER);
> -
> -    channel->thread_id = pthread_self();
> -
> -    channel->out_bytes_counter = 0;
> -
> -    spice_debug("channel type %d id %d thread_id 0x%lx",
> -                channel->type, channel->id, channel->thread_id);
> -    return channel;
> -}
> -
> -// TODO: red_worker can use this one
> -static void dummy_watch_update_mask(SpiceWatch *watch, int event_mask)
> -{
> -}
> -
> -static SpiceWatch *dummy_watch_add(const SpiceCoreInterfaceInternal *iface,
> -                                   int fd, int event_mask, SpiceWatchFunc
> func, void *opaque)
> -{
> -    return NULL; // apparently allowed?
> -}
> -
> -static void dummy_watch_remove(SpiceWatch *watch)
> -{
> -}
> -
> -// TODO: actually, since I also use channel_client_dummy, no need for core.
> Can be NULL
> -static const SpiceCoreInterfaceInternal dummy_core = {
> -    .watch_update_mask = dummy_watch_update_mask,
> -    .watch_add = dummy_watch_add,
> -    .watch_remove = dummy_watch_remove,
> -};
> -
> -RedChannel *red_channel_create_dummy(int size, RedsState *reds, uint32_t
> type, uint32_t id)
> -{
> -    RedChannel *channel;
> -    ClientCbs client_cbs = { NULL, };
> -
> -    spice_assert(size >= sizeof(*channel));
> -    channel = spice_malloc0(size);
> -    channel->type = type;
> -    channel->id = id;
> -    channel->refs = 1;
> -    channel->reds = reds;
> -    channel->core = &dummy_core;
> -    client_cbs.connect = red_channel_client_default_connect;
> -    client_cbs.disconnect = red_channel_client_default_disconnect;
> -    client_cbs.migrate = red_channel_client_default_migrate;
> -
> -    red_channel_register_client_cbs(channel, &client_cbs, NULL);
> -    red_channel_set_common_cap(channel, SPICE_COMMON_CAP_MINI_HEADER);
> -
> -    channel->thread_id = pthread_self();
> -    spice_debug("channel type %d id %d thread_id 0x%lx",
> -                channel->type, channel->id, channel->thread_id);
> -
> -    channel->out_bytes_counter = 0;
> -
> -    return channel;
> -}
> -
> -static int do_nothing_handle_message(RedChannelClient *rcc,
> -                                     uint16_t type,
> -                                     uint32_t size,
> -                                     uint8_t *msg)
> -{
> -    return TRUE;
> -}
> -
> -RedChannel *red_channel_create_parser(int size,
> -                                      RedsState *reds,
> -                                      const SpiceCoreInterfaceInternal
> *core,
> -                                      uint32_t type, uint32_t id,
> -                                      int handle_acks,
> -                                      spice_parse_channel_func_t parser,
> -                                      channel_handle_parsed_proc
> handle_parsed,
> -                                      const ChannelCbs *channel_cbs,
> -                                      uint32_t migration_flags)
> +void red_channel_set_stat_node(RedChannel *channel, StatNodeRef stat)
>  {
> -    RedChannel *channel = red_channel_create(size, reds, core, type, id,
> -                                             handle_acks,
> -                                             do_nothing_handle_message,
> -                                             channel_cbs,
> -                                             migration_flags);
> -
> -    if (channel == NULL) {
> -        return NULL;
> -    }
> -    channel->incoming_cb.handle_parsed = (handle_parsed_proc)handle_parsed;
> -    channel->incoming_cb.parser = parser;
> +    spice_return_if_fail(channel != NULL);
> +    spice_return_if_fail(channel->priv->stat == 0);
>  
> -    return channel;
> +#ifdef RED_STATISTICS
> +    channel->priv->stat = stat;
> +    channel->priv->out_bytes_counter = stat_add_counter(channel->priv->reds,
> stat, "out_bytes", TRUE);
> +#endif
>  }
>  
> -void red_channel_set_stat_node(RedChannel *channel, StatNodeRef stat)
> +StatNodeRef red_channel_get_stat_node(RedChannel *channel)
>  {
> -    spice_return_if_fail(channel != NULL);
> -    spice_return_if_fail(channel->stat == 0);
> -
>  #ifdef RED_STATISTICS
> -    channel->stat = stat;
> -    channel->out_bytes_counter = stat_add_counter(channel->reds, stat,
> "out_bytes", TRUE);
> +    return channel->priv->stat;
>  #endif
> +    return 0;
>  }
>  
>  void red_channel_register_client_cbs(RedChannel *channel, const ClientCbs
>  *client_cbs, gpointer cbs_data)
>  {
> -    spice_assert(client_cbs->connect || channel->type ==
> SPICE_CHANNEL_MAIN);
> -    channel->client_cbs.connect = client_cbs->connect;
> +    spice_assert(client_cbs->connect || channel->priv->type ==
> SPICE_CHANNEL_MAIN);
> +    channel->priv->client_cbs.connect = client_cbs->connect;
>  
>      if (client_cbs->disconnect) {
> -        channel->client_cbs.disconnect = client_cbs->disconnect;
> +        channel->priv->client_cbs.disconnect = client_cbs->disconnect;
>      }
>  
>      if (client_cbs->migrate) {
> -        channel->client_cbs.migrate = client_cbs->migrate;
> +        channel->priv->client_cbs.migrate = client_cbs->migrate;
>      }
> -    channel->data = cbs_data;
> +    channel->priv->data = cbs_data;
>  }
>  
>  int test_capability(const uint32_t *caps, int num_caps, uint32_t cap)
> @@ -356,32 +487,12 @@ static void add_capability(uint32_t **caps, int
> *num_caps, uint32_t cap)
>  
>  void red_channel_set_common_cap(RedChannel *channel, uint32_t cap)
>  {
> -    add_capability(&channel->local_caps.common_caps,
> &channel->local_caps.num_common_caps, cap);
> +    add_capability(&channel->priv->local_caps.common_caps,
> &channel->priv->local_caps.num_common_caps, cap);
>  }
>  
>  void red_channel_set_cap(RedChannel *channel, uint32_t cap)
>  {
> -    add_capability(&channel->local_caps.caps, &channel->local_caps.num_caps,
> cap);
> -}
> -
> -void red_channel_ref(RedChannel *channel)
> -{
> -    channel->refs++;
> -}
> -
> -void red_channel_unref(RedChannel *channel)
> -{
> -    if (--channel->refs == 0) {
> -        if (channel->local_caps.num_common_caps) {
> -            free(channel->local_caps.common_caps);
> -        }
> -
> -        if (channel->local_caps.num_caps) {
> -            free(channel->local_caps.caps);
> -        }
> -
> -        free(channel);
> -    }
> +    add_capability(&channel->priv->local_caps.caps,
> &channel->priv->local_caps.num_caps, cap);
>  }
>  
>  void red_channel_destroy(RedChannel *channel)
> @@ -390,13 +501,13 @@ void red_channel_destroy(RedChannel *channel)
>          return;
>      }
>  
> -    g_list_foreach(channel->clients, (GFunc)red_channel_client_destroy,
> NULL);
> -    red_channel_unref(channel);
> +    g_list_foreach(channel->priv->clients,
> (GFunc)red_channel_client_destroy, NULL);
> +    g_object_unref(channel);
>  }
>  
>  void red_channel_send(RedChannel *channel)
>  {
> -    g_list_foreach(channel->clients, (GFunc)red_channel_client_send, NULL);
> +    g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_send,
> NULL);
>  }
>  
>  void red_channel_push(RedChannel *channel)
> @@ -405,14 +516,14 @@ void red_channel_push(RedChannel *channel)
>          return;
>      }
>  
> -    g_list_foreach(channel->clients, (GFunc)red_channel_client_push, NULL);
> +    g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_push,
> NULL);
>  }
>  
>  // TODO: this function doesn't make sense because the window should be
>  client (WAN/LAN)
>  // specific
>  void red_channel_init_outgoing_messages_window(RedChannel *channel)
>  {
> -    g_list_foreach(channel->clients,
> (GFunc)red_channel_client_init_outgoing_messages_window, NULL);
> +    g_list_foreach(channel->priv->clients,
> (GFunc)red_channel_client_init_outgoing_messages_window, NULL);
>  }
>  
>  static void red_channel_client_pipe_add_type_proxy(gpointer data, gpointer
>  user_data)
> @@ -423,7 +534,7 @@ static void
> red_channel_client_pipe_add_type_proxy(gpointer data, gpointer user_
>  
>  void red_channel_pipes_add_type(RedChannel *channel, int pipe_item_type)
>  {
> -    g_list_foreach(channel->clients, red_channel_client_pipe_add_type_proxy,
> +    g_list_foreach(channel->priv->clients,
> red_channel_client_pipe_add_type_proxy,
>                     GINT_TO_POINTER(pipe_item_type));
>  }
>  
> @@ -435,12 +546,12 @@ static void
> red_channel_client_pipe_add_empty_msg_proxy(gpointer data, gpointer
>  
>  void red_channel_pipes_add_empty_msg(RedChannel *channel, int msg_type)
>  {
> -    g_list_foreach(channel->clients,
> red_channel_client_pipe_add_empty_msg_proxy, GINT_TO_POINTER(msg_type));
> +    g_list_foreach(channel->priv->clients,
> red_channel_client_pipe_add_empty_msg_proxy, GINT_TO_POINTER(msg_type));
>  }
>  
>  int red_channel_is_connected(RedChannel *channel)
>  {
> -    return channel && channel->clients;
> +    return channel && channel->priv->clients;
>  }
>  
>  void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc)
> @@ -448,19 +559,19 @@ void red_channel_remove_client(RedChannel *channel,
> RedChannelClient *rcc)
>      GList *link;
>      g_return_if_fail(channel == red_channel_client_get_channel(rcc));
>  
> -    if (!pthread_equal(pthread_self(), channel->thread_id)) {
> +    if (!pthread_equal(pthread_self(), channel->priv->thread_id)) {
>          spice_warning("channel type %d id %d - "
>                        "channel->thread_id (0x%lx) != pthread_self (0x%lx)."
>                        "If one of the threads is != io-thread && !=
>                        vcpu-thread, "
>                        "this might be a BUG",
> -                      channel->type, channel->id,
> -                      channel->thread_id, pthread_self());
> +                      channel->priv->type, channel->priv->id,
> +                      channel->priv->thread_id, pthread_self());
>      }
>      spice_return_if_fail(channel);
> -    link = g_list_find(channel->clients, rcc);
> +    link = g_list_find(channel->priv->clients, rcc);
>      spice_return_if_fail(link != NULL);
>  
> -    channel->clients = g_list_remove_link(channel->clients, link);
> +    channel->priv->clients = g_list_remove_link(channel->priv->clients,
> link);
>      // TODO: should we set rcc->channel to NULL???
>  }
>  
> @@ -474,17 +585,35 @@ void red_client_remove_channel(RedChannelClient *rcc)
>  
>  void red_channel_disconnect(RedChannel *channel)
>  {
> -    g_list_foreach(channel->clients, (GFunc)red_channel_client_disconnect,
> NULL);
> +    g_list_foreach(channel->priv->clients,
> (GFunc)red_channel_client_disconnect, NULL);
> +}
> +
> +void red_channel_connect(RedChannel *channel, RedClient *client,
> +                         RedsStream *stream, int migration, int
> num_common_caps,
> +                         uint32_t *common_caps, int num_caps, uint32_t
> *caps)
> +{
> +    channel->priv->client_cbs.connect(channel, client, stream, migration,
> +                                      num_common_caps, common_caps,
> num_caps,
> +                                      caps);
>  }
>  
>  void red_channel_apply_clients(RedChannel *channel, channel_client_callback
>  cb)
>  {
> -    g_list_foreach(channel->clients, (GFunc)cb, NULL);
> +    g_list_foreach(channel->priv->clients, (GFunc)cb, NULL);
>  }
>  
>  void red_channel_apply_clients_data(RedChannel *channel,
>  channel_client_callback_data cb, void *data)
>  {
> -    g_list_foreach(channel->clients, (GFunc)cb, data);
> +    g_list_foreach(channel->priv->clients, (GFunc)cb, data);
> +}
> +
> +GList *red_channel_get_clients(RedChannel *channel)
> +{
> +    return channel->priv->clients;
> +}
> +guint red_channel_get_n_clients(RedChannel *channel)
> +{
> +    return g_list_length(channel->priv->clients);
>  }
>  
>  int red_channel_all_blocked(RedChannel *channel)
> @@ -492,10 +621,10 @@ int red_channel_all_blocked(RedChannel *channel)
>      GList *link;
>      RedChannelClient *rcc;
>  
> -    if (!channel || !channel->clients) {
> +    if (!channel || !channel->priv->clients) {
>          return FALSE;
>      }
> -    for (link = channel->clients; link != NULL; link = link->next) {
> +    for (link = channel->priv->clients; link != NULL; link = link->next) {
>          rcc = link->data;
>          if (!red_channel_client_is_blocked(rcc)) {
>              return FALSE;
> @@ -509,7 +638,7 @@ int red_channel_any_blocked(RedChannel *channel)
>      GList *link;
>      RedChannelClient *rcc;
>  
> -    for (link = channel->clients; link != NULL; link = link->next) {
> +    for (link = channel->priv->clients; link != NULL; link = link->next) {
>          rcc = link->data;
>          if (red_channel_client_is_blocked(rcc)) {
>              return TRUE;
> @@ -523,10 +652,10 @@ int red_channel_get_first_socket(RedChannel *channel)
>      RedChannelClient *rcc;
>      RedsStream *stream;
>  
> -    if (!channel || !channel->clients) {
> +    if (!channel || !channel->priv->clients) {
>          return -1;
>      }
> -    rcc = channel->clients->data;
> +    rcc = channel->priv->clients->data;
>      stream = red_channel_client_get_stream(rcc);
>  
>      return stream->socket;
> @@ -537,7 +666,7 @@ int red_channel_no_item_being_sent(RedChannel *channel)
>      GList *link;
>      RedChannelClient *rcc;
>  
> -    for (link = channel->clients; link != NULL; link = link->next) {
> +    for (link = channel->priv->clients; link != NULL; link = link->next) {
>          rcc = link->data;
>          if (!red_channel_client_no_item_being_sent(rcc)) {
>              return FALSE;
> @@ -617,7 +746,7 @@ void red_client_migrate(RedClient *client)
>          rcc = link->data;
>          channel = red_channel_client_get_channel(rcc);
>          if (red_channel_client_is_connected(rcc)) {
> -            channel->client_cbs.migrate(rcc);
> +            channel->priv->client_cbs.migrate(rcc);
>          }
>          link = next;
>      }
> @@ -650,7 +779,7 @@ void red_client_destroy(RedClient *client)
>          // to wait for disconnection)
>          // TODO: should we go back to async. For this we need to use
>          // ref count for channel clients.
> -        channel->client_cbs.disconnect(rcc);
> +        channel->priv->client_cbs.disconnect(rcc);
>          spice_assert(red_channel_client_pipe_is_empty(rcc));
>          spice_assert(red_channel_client_no_item_being_sent(rcc));
>          red_channel_client_destroy(rcc);
> @@ -670,7 +799,7 @@ RedChannelClient *red_client_get_channel(RedClient
> *client, int type, int id)
>          RedChannel *channel;
>          rcc = link->data;
>          channel = red_channel_client_get_channel(rcc);
> -        if (channel->type == type && channel->id == id) {
> +        if (channel->priv->type == type && channel->priv->id == id) {
>              ret = rcc;
>              break;
>          }
> @@ -763,7 +892,7 @@ static int red_channel_pipes_create_batch(RedChannel
> *channel,
>      spice_assert(creator != NULL);
>      spice_assert(pipe_add != NULL);
>  
> -    link = channel->clients;
> +    link = channel->priv->clients;
>      while (link != NULL) {
>          next = link->next;
>          rcc = link->data;
> @@ -806,7 +935,7 @@ uint32_t red_channel_max_pipe_size(RedChannel *channel)
>      RedChannelClient *rcc;
>      uint32_t pipe_size = 0;
>  
> -    for (link = channel->clients; link != NULL; link = link->next) {
> +    for (link = channel->priv->clients; link != NULL; link = link->next) {
>          uint32_t new_size;
>          rcc = link->data;
>          new_size = red_channel_client_get_pipe_size(rcc);
> @@ -821,7 +950,7 @@ uint32_t red_channel_min_pipe_size(RedChannel *channel)
>      RedChannelClient *rcc;
>      uint32_t pipe_size = ~0;
>  
> -    for (link = channel->clients; link != NULL; link = link->next) {
> +    for (link = channel->priv->clients; link != NULL; link = link->next) {
>          uint32_t new_size;
>          rcc = link->data;
>          new_size = red_channel_client_get_pipe_size(rcc);
> @@ -836,7 +965,7 @@ uint32_t red_channel_sum_pipes_size(RedChannel *channel)
>      RedChannelClient *rcc;
>      uint32_t sum = 0;
>  
> -    for (link = channel->clients; link != NULL; link = link->next) {
> +    for (link = channel->priv->clients; link != NULL; link = link->next) {
>          rcc = link->data;
>          sum += red_channel_client_get_pipe_size(rcc);
>      }
> @@ -879,5 +1008,116 @@ int red_channel_wait_all_sent(RedChannel *channel,
>  
>  RedsState* red_channel_get_server(RedChannel *channel)
>  {
> -    return channel->reds;
> +    return channel->priv->reds;
> +}
> +
> +SpiceCoreInterfaceInternal* red_channel_get_core_interface(RedChannel
> *channel)
> +{
> +    return channel->priv->core;
> +}
> +
> +int red_channel_config_socket(RedChannel *self, RedChannelClient *rcc)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_val_if_fail(klass->config_socket, FALSE);
> +
> +    return klass->config_socket(rcc);
> +}
> +
> +void red_channel_on_disconnect(RedChannel *self, RedChannelClient *rcc)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_if_fail(klass->on_disconnect);
> +
> +    klass->on_disconnect(rcc);
> +}
> +
> +void red_channel_send_item(RedChannel *self, RedChannelClient *rcc,
> RedPipeItem *item)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_if_fail(klass->send_item);
> +
> +    klass->send_item(rcc, item);
> +}
> +
> +void red_channel_hold_item(RedChannel *self, RedChannelClient *rcc,
> RedPipeItem *item)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_if_fail(klass->hold_item);
> +
> +    klass->hold_item(rcc, item);
> +}
> +
> +void red_channel_release_item(RedChannel *self, RedChannelClient *rcc,
> +                              RedPipeItem *item, int item_pushed)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_if_fail(klass->release_item);
> +
> +    klass->release_item(rcc, item, item_pushed);
> +}
> +
> +uint8_t* red_channel_alloc_recv_buf(RedChannel *self, RedChannelClient *rcc,
> +                                    uint16_t type, uint32_t size)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_val_if_fail(klass->alloc_recv_buf, NULL);
> +
> +    return klass->alloc_recv_buf(rcc, type, size);
> +}
> +
> +void red_channel_release_recv_buf(RedChannel *self, RedChannelClient *rcc,
> +                                  uint16_t type, uint32_t size, uint8_t
> *msg)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_if_fail(klass->release_recv_buf);
> +
> +    klass->release_recv_buf(rcc, type, size, msg);
> +}
> +
> +int red_channel_handle_migrate_flush_mark(RedChannel *self, RedChannelClient
> *rcc)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_val_if_fail(klass->handle_migrate_flush_mark, FALSE);
> +
> +    return klass->handle_migrate_flush_mark(rcc);
> +}
> +
> +int red_channel_handle_migrate_data(RedChannel *self, RedChannelClient *rcc,
> +                                    uint32_t size, void *message)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_val_if_fail(klass->handle_migrate_data, FALSE);
> +
> +    return klass->handle_migrate_data(rcc, size, message);
> +}
> +
> +uint64_t red_channel_handle_migrate_data_get_serial(RedChannel *self,
> +                                                    RedChannelClient *rcc,
> +                                                    uint32_t size, void
> *message)
> +{
> +    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self);
> +    g_return_val_if_fail(klass->handle_migrate_data_get_serial, 0);
> +
> +    return klass->handle_migrate_data_get_serial(rcc, size, message);
> +}
> +
> +IncomingHandlerInterface* red_channel_get_incoming_handler(RedChannel *self)
> +{
> +    return &self->priv->incoming_cb;
> +}
> +
> +OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel *self)
> +{
> +    return &self->priv->outgoing_cb;
> +}
> +
> +void red_channel_reset_thread_id(RedChannel *self)
> +{
> +    self->priv->thread_id = pthread_self();
> +}
> +
> +RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self)
> +{
> +    return &self->priv->local_caps;
>  }
> diff --git a/server/red-channel.h b/server/red-channel.h
> index 5c1d555..1b8abce 100644
> --- a/server/red-channel.h
> +++ b/server/red-channel.h
> @@ -36,9 +36,10 @@
>  #include "red-pipe-item.h"
>  #include "red-channel-client.h"
>  
> -/* Red Channel interface */
> +#include <glib-object.h>
> +
> +G_BEGIN_DECLS
>  
> -typedef struct RedChannel RedChannel;
>  typedef struct RedClient RedClient;
>  
>  typedef uint8_t *(*channel_alloc_msg_recv_buf_proc)(RedChannelClient
>  *channel,
> @@ -71,13 +72,53 @@ typedef void (*channel_client_connect_proc)(RedChannel
> *channel, RedClient *clie
>  typedef void (*channel_client_disconnect_proc)(RedChannelClient *base);
>  typedef void (*channel_client_migrate_proc)(RedChannelClient *base);
>  
> -// TODO: add ASSERTS for thread_id  in client and channel calls
> -//
> +
>  /*
> - * callbacks that are triggered from channel client stream events.
> - * They are called from the thread that listen to the stream events.
> + * callbacks that are triggered from client events.
> + * They should be called from the thread that handles the RedClient
>   */
>  typedef struct {
> +    channel_client_connect_proc connect;
> +    channel_client_disconnect_proc disconnect;
> +    channel_client_migrate_proc migrate;
> +} ClientCbs;
> +
> +int test_capability(const uint32_t *caps, int num_caps, uint32_t cap);
> +
> +#define RED_TYPE_CHANNEL red_channel_get_type()
> +
> +#define RED_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> RED_TYPE_CHANNEL, RedChannel))
> +#define RED_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
> RED_TYPE_CHANNEL, RedChannelClass))
> +#define RED_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> RED_TYPE_CHANNEL))
> +#define RED_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
> RED_TYPE_CHANNEL))
> +#define RED_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
> RED_TYPE_CHANNEL, RedChannelClass))
> +
> +typedef struct RedChannel RedChannel;
> +typedef struct RedChannelClass RedChannelClass;
> +typedef struct RedChannelPrivate RedChannelPrivate;
> +
> +struct RedChannel
> +{
> +    GObject parent;
> +
> +    RedChannelPrivate *priv;
> +};
> +
> +struct RedChannelClass
> +{
> +    GObjectClass parent_class;
> +
> +    /* subclasses must implement either handle_message(), or both parser()
> and
> +     * handle_parsed() */
> +    channel_handle_message_proc handle_message;
> +    spice_parse_channel_func_t parser;
> +    channel_handle_parsed_proc handle_parsed;
> +
> +    // TODO: add ASSERTS for thread_id  in client and channel calls
> +    /*
> +     * callbacks that are triggered from channel client stream events.
> +     * They are called from the thread that listen to the stream events.
> +     */
>      channel_configure_socket_proc config_socket;
>      channel_disconnect_proc on_disconnect;
>      channel_send_pipe_item_proc send_item;
> @@ -88,18 +129,9 @@ typedef struct {
>      channel_handle_migrate_flush_mark_proc handle_migrate_flush_mark;
>      channel_handle_migrate_data_proc handle_migrate_data;
>      channel_handle_migrate_data_get_serial_proc
>      handle_migrate_data_get_serial;
> -} ChannelCbs;
> -
> +};
>  
> -/*
> - * callbacks that are triggered from client events.
> - * They should be called from the thread that handles the RedClient
> - */
> -typedef struct {
> -    channel_client_connect_proc connect;
> -    channel_client_disconnect_proc disconnect;
> -    channel_client_migrate_proc migrate;
> -} ClientCbs;
> +/* Red Channel interface */
>  
>  typedef struct RedChannelCapabilities {
>      int num_common_caps;
> @@ -108,94 +140,20 @@ typedef struct RedChannelCapabilities {
>      uint32_t *caps;
>  } RedChannelCapabilities;
>  
> -int test_capability(const uint32_t *caps, int num_caps, uint32_t cap);
> -
> -typedef struct RedChannelClientLatencyMonitor {
> -    int state;
> -    uint64_t last_pong_time;
> -    SpiceTimer *timer;
> -    uint32_t id;
> -    int tcp_nodelay;
> -    int warmup_was_sent;
> -
> -    int64_t roundtrip;
> -} RedChannelClientLatencyMonitor;
> -
> -typedef struct RedChannelClientConnectivityMonitor {
> -    int state;
> -    uint32_t out_bytes;
> -    uint32_t in_bytes;
> -    uint32_t timeout;
> -    SpiceTimer *timer;
> -} RedChannelClientConnectivityMonitor;
> -
> -struct RedChannel {
> -    uint32_t type;
> -    uint32_t id;
> -
> -    uint32_t refs;
> -
> -    RingItem link; // channels link for reds
> -
> -    const SpiceCoreInterfaceInternal *core;
> -    int handle_acks;
> -
> -    // RedChannel will hold only connected channel clients (logic - when
> pushing pipe item to all channel clients, there
> -    // is no need to go over disconnect clients)
> -    // . While client will hold the channel clients till it is destroyed
> -    // and then it will destroy them as well.
> -    // However RCC still holds a reference to the Channel.
> -    // Maybe replace these logic with ref count?
> -    // TODO: rename to 'connected_clients'?
> -    GList *clients;
> -    uint32_t clients_num;
> -
> -    OutgoingHandlerInterface outgoing_cb;
> -    IncomingHandlerInterface incoming_cb;
> -
> -    ChannelCbs channel_cbs;
> -    ClientCbs client_cbs;
> -
> -    RedChannelCapabilities local_caps;
> -    uint32_t migration_flags;
> -
> -    void *data;
> -
> -    // TODO: when different channel_clients are in different threads from
> Channel -> need to protect!
> -    pthread_t thread_id;
> -    RedsState *reds;
> -#ifdef RED_STATISTICS
> -    StatNodeRef stat;
> -    uint64_t *out_bytes_counter;
> -#endif
> -};
> -
> -#define RED_CHANNEL(Channel) ((RedChannel *)(Channel))
> -
> -/* if one of the callbacks should cause disconnect, use red_channel_shutdown
> and don't
> - * explicitly destroy the channel */
> -RedChannel *red_channel_create(int size,
> -                               RedsState *reds,
> -                               const SpiceCoreInterfaceInternal *core,
> -                               uint32_t type, uint32_t id,
> -                               int handle_acks,
> -                               channel_handle_message_proc handle_message,
> -                               const ChannelCbs *channel_cbs,
> -                               uint32_t migration_flags);
> +GType red_channel_get_type(void) G_GNUC_CONST;
>  
>  /* alternative constructor, meant for marshaller based (inputs,main)
>  channels,
>   * will become default eventually */
> +/*
>  RedChannel *red_channel_create_parser(int size,
>                                        RedsState *reds,
>                                        const SpiceCoreInterfaceInternal
>                                        *core,
>                                        uint32_t type, uint32_t id,
> -                                      int handle_acks,
> +                                      gboolean handle_acks,
>                                        spice_parse_channel_func_t parser,
>                                        channel_handle_parsed_proc
>                                        handle_parsed,
> -                                      const ChannelCbs *channel_cbs,
>                                        uint32_t migration_flags);
> -void red_channel_ref(RedChannel *channel);
> -void red_channel_unref(RedChannel *channel);
> +                                      */
>  void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc);
>  void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc);
>  
> @@ -206,11 +164,6 @@ void red_channel_register_client_cbs(RedChannel
> *channel, const ClientCbs *clien
>  void red_channel_set_common_cap(RedChannel *channel, uint32_t cap);
>  void red_channel_set_cap(RedChannel *channel, uint32_t cap);
>  
> -// TODO: tmp, for channels that don't use RedChannel yet (e.g., snd
> channel), but
> -// do use the client callbacks. So the channel clients are not connected
> (the channel doesn't
> -// have list of them, but they do have a link to the channel, and the client
> has a list of them)
> -RedChannel *red_channel_create_dummy(int size, RedsState *reds, uint32_t
> type, uint32_t id);
> -
>  int red_channel_is_connected(RedChannel *channel);
>  
>  /* seamless migration is supported for only one client. This routine
> @@ -274,6 +227,9 @@ void red_channel_receive(RedChannel *channel);
>  void red_channel_send(RedChannel *channel);
>  // For red_worker
>  void red_channel_disconnect(RedChannel *channel);
> +void red_channel_connect(RedChannel *channel, RedClient *client,
> +                         RedsStream *stream, int migration, int
> num_common_caps,
> +                         uint32_t *common_caps, int num_caps, uint32_t
> *caps);
>  
>  /* return the sum of all the rcc pipe size */
>  uint32_t red_channel_max_pipe_size(RedChannel *channel);
> @@ -286,8 +242,38 @@ uint32_t red_channel_sum_pipes_size(RedChannel
> *channel);
>  typedef void (*channel_client_callback)(RedChannelClient *rcc);
>  typedef void (*channel_client_callback_data)(RedChannelClient *rcc, void
>  *data);
>  void red_channel_apply_clients(RedChannel *channel, channel_client_callback
>  v);
> -void red_channel_apply_clients_data(RedChannel *channel,
> channel_client_callback_data v, void * data);
> +void red_channel_apply_clients_data(RedChannel *channel,
> channel_client_callback_data v, void *data);
> +GList *red_channel_get_clients(RedChannel *channel);
> +guint red_channel_get_n_clients(RedChannel *channel);
>  struct RedsState* red_channel_get_server(RedChannel *channel);
> +SpiceCoreInterfaceInternal* red_channel_get_core_interface(RedChannel
> *channel);
> +
> +/* channel callback function */
> +int red_channel_config_socket(RedChannel *self, RedChannelClient *rcc);
> +void red_channel_on_disconnect(RedChannel *self, RedChannelClient *rcc);
> +void red_channel_send_item(RedChannel *self, RedChannelClient *rcc,
> RedPipeItem *item);
> +void red_channel_hold_item(RedChannel *self, RedChannelClient *rcc,
> RedPipeItem *item);
> +void red_channel_release_item(RedChannel *channel, RedChannelClient *rcc,
> +                              RedPipeItem *item, int item_pushed);
> +uint8_t* red_channel_alloc_recv_buf(RedChannel *self, RedChannelClient *rcc,
> +                                    uint16_t type, uint32_t size);
> +void red_channel_release_recv_buf(RedChannel *self, RedChannelClient *rcc,
> +                                  uint16_t type, uint32_t size, uint8_t
> *msg);
> +int red_channel_handle_migrate_flush_mark(RedChannel *self, RedChannelClient
> *rcc);
> +int red_channel_handle_migrate_data(RedChannel *self, RedChannelClient *rcc,
> +                                    uint32_t size, void *message);
> +uint64_t red_channel_handle_migrate_data_get_serial(RedChannel *self,
> +                                                    RedChannelClient *rcc,
> +                                                    uint32_t size, void
> *message);
> +void red_channel_reset_thread_id(RedChannel *self);
> +StatNodeRef red_channel_get_stat_node(RedChannel *channel);
> +
> +/* FIXME: do these even need to be in RedChannel? It's really only used in
> + * RedChannelClient. Needs refactoring */
> +IncomingHandlerInterface* red_channel_get_incoming_handler(RedChannel
> *self);
> +OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel
> *self);
> +
> +RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel
> *self);
>  
>  struct RedClient {
>      RedsState *reds;
> @@ -362,4 +348,6 @@ int red_channel_wait_all_sent(RedChannel *channel,
>  
>  #define CHANNEL_BLOCKED_SLEEP_DURATION 10000 //micro
>  
> +G_END_DECLS
> +
>  #endif
> diff --git a/server/red-parse-qxl.h b/server/red-parse-qxl.h
> index 9c30572..5da6916 100644
> --- a/server/red-parse-qxl.h
> +++ b/server/red-parse-qxl.h
> @@ -64,6 +64,8 @@ static inline RedDrawable *red_drawable_ref(RedDrawable
> *drawable)
>      return drawable;
>  }
>  
> +void red_drawable_unref(RedDrawable *red_drawable);
> +
>  typedef struct RedUpdateCmd {
>      QXLReleaseInfoExt release_info_ext;
>      SpiceRect area;
> diff --git a/server/red-qxl.c b/server/red-qxl.c
> index ef39f0e..d255eb6 100644
> --- a/server/red-qxl.c
> +++ b/server/red-qxl.c
> @@ -81,7 +81,7 @@ static void red_qxl_set_display_peer(RedChannel *channel,
> RedClient *client,
>      Dispatcher *dispatcher;
>  
>      spice_debug("%s", "");
> -    dispatcher = (Dispatcher *)channel->data;
> +    dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel),
> "dispatcher");
>      payload.client = client;
>      payload.stream = stream;
>      payload.migration = migration;
> @@ -108,7 +108,7 @@ static void
> red_qxl_disconnect_display_peer(RedChannelClient *rcc)
>          return;
>      }
>  
> -    dispatcher = (Dispatcher *)channel->data;
> +    dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel),
> "dispatcher");
>  
>      spice_printerr("");
>      payload.rcc = rcc;
> @@ -125,11 +125,14 @@ static void red_qxl_display_migrate(RedChannelClient
> *rcc)
>      RedWorkerMessageDisplayMigrate payload;
>      Dispatcher *dispatcher;
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> +    uint32_t type, id;
> +
>      if (!channel) {
>          return;
>      }
> -    dispatcher = (Dispatcher *)channel->data;
> -    spice_printerr("channel type %u id %u", channel->type, channel->id);
> +    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
> +    dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel),
> "dispatcher");
> +    spice_printerr("channel type %u id %u", type, id);
>      payload.rcc = rcc;
>      dispatcher_send_message(dispatcher,
>                              RED_WORKER_MESSAGE_DISPLAY_MIGRATE,
> @@ -142,7 +145,7 @@ static void red_qxl_set_cursor_peer(RedChannel *channel,
> RedClient *client, Reds
>                                      uint32_t *caps)
>  {
>      RedWorkerMessageCursorConnect payload = {0,};
> -    Dispatcher *dispatcher = (Dispatcher *)channel->data;
> +    Dispatcher *dispatcher = (Dispatcher
> *)g_object_get_data(G_OBJECT(channel), "dispatcher");
>      spice_printerr("");
>      payload.client = client;
>      payload.stream = stream;
> @@ -170,7 +173,7 @@ static void
> red_qxl_disconnect_cursor_peer(RedChannelClient *rcc)
>          return;
>      }
>  
> -    dispatcher = (Dispatcher *)channel->data;
> +    dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel),
> "dispatcher");
>      spice_printerr("");
>      payload.rcc = rcc;
>  
> @@ -184,12 +187,14 @@ static void red_qxl_cursor_migrate(RedChannelClient
> *rcc)
>      RedWorkerMessageCursorMigrate payload;
>      Dispatcher *dispatcher;
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> +    uint32_t type, id;
>  
>      if (!channel) {
>          return;
>      }
> -    dispatcher = (Dispatcher *)channel->data;
> -    spice_printerr("channel type %u id %u", channel->type, channel->id);
> +    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
> +    dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel),
> "dispatcher");
> +    spice_printerr("channel type %u id %u", type, id);
>      payload.rcc = rcc;
>      dispatcher_send_message(dispatcher,
>                              RED_WORKER_MESSAGE_CURSOR_MIGRATE,
> diff --git a/server/red-replay-qxl.c b/server/red-replay-qxl.c
> index 281bc7a..a64d438 100644
> --- a/server/red-replay-qxl.c
> +++ b/server/red-replay-qxl.c
> @@ -24,7 +24,7 @@
>  #include <zlib.h>
>  #include <pthread.h>
>  #include "reds.h"
> -#include "red-worker.h"
> +#include "red-qxl.h"
>  #include "red-common.h"
>  #include "memslot.h"
>  #include "red-parse-qxl.h"
> diff --git a/server/red-worker.c b/server/red-worker.c
> index 5be29aa..0d462b5 100644
> --- a/server/red-worker.c
> +++ b/server/red-worker.c
> @@ -23,16 +23,12 @@
>  
>  #include <stdio.h>
>  #include <stdarg.h>
> -#include <fcntl.h>
> -#include <sys/socket.h>
> -#include <netinet/in.h>
>  #include <stdlib.h>
>  #include <errno.h>
>  #include <string.h>
>  #include <unistd.h>
>  #include <poll.h>
>  #include <pthread.h>
> -#include <netinet/tcp.h>
>  #include <openssl/ssl.h>
>  #include <inttypes.h>
>  #include <glib.h>
> @@ -97,33 +93,8 @@ static RedsState* red_worker_get_server(RedWorker
> *worker);
>  
>  static int display_is_connected(RedWorker *worker)
>  {
> -    return (worker->display_channel && red_channel_is_connected(
> -        &worker->display_channel->common.base));
> -}
> -
> -static uint8_t *common_alloc_recv_buf(RedChannelClient *rcc, uint16_t type,
> uint32_t size)
> -{
> -    RedChannel *channel = red_channel_client_get_channel(rcc);
> -    CommonGraphicsChannel *common = SPICE_CONTAINEROF(channel,
> CommonGraphicsChannel, base);
> -
> -    /* SPICE_MSGC_MIGRATE_DATA is the only client message whose size is
> dynamic */
> -    if (type == SPICE_MSGC_MIGRATE_DATA) {
> -        return spice_malloc(size);
> -    }
> -
> -    if (size > CHANNEL_RECEIVE_BUF_SIZE) {
> -        spice_critical("unexpected message size %u (max is %d)", size,
> CHANNEL_RECEIVE_BUF_SIZE);
> -        return NULL;
> -    }
> -    return common->recv_buf;
> -}
> -
> -static void common_release_recv_buf(RedChannelClient *rcc, uint16_t type,
> uint32_t size,
> -                                    uint8_t* msg)
> -{
> -    if (type == SPICE_MSGC_MIGRATE_DATA) {
> -        free(msg);
> -    }
> +    return worker->display_channel &&
> +        red_channel_is_connected(RED_CHANNEL(worker->display_channel));
>  }
>  
>  void red_drawable_unref(RedDrawable *red_drawable)
> @@ -244,7 +215,7 @@ static int red_process_display(RedWorker *worker, int
> *ring_is_empty)
>                                     &update, ext_cmd.cmd.data)) {
>                  break;
>              }
> -            if (!validate_surface(worker->display_channel,
> update.surface_id)) {
> +            if (!display_channel_validate_surface(worker->display_channel,
> update.surface_id)) {
>                  spice_warning("Invalid surface in QXL_CMD_UPDATE");
>              } else {
>                  display_channel_draw(worker->display_channel, &update.area,
>                  update.surface_id);
> @@ -285,7 +256,7 @@ static int red_process_display(RedWorker *worker, int
> *ring_is_empty)
>              spice_error("bad command type");
>          }
>          n++;
> -        if (red_channel_all_blocked(&worker->display_channel->common.base)
> +        if (red_channel_all_blocked(RED_CHANNEL(worker->display_channel))
>              || spice_get_monotonic_time_ns() - start > NSEC_PER_SEC / 100) {
>              worker->event_timeout = 0;
>              return n;
> @@ -398,80 +369,6 @@ static void flush_all_qxl_commands(RedWorker *worker)
>      flush_cursor_commands(worker);
>  }
>  
> -static int common_channel_config_socket(RedChannelClient *rcc)
> -{
> -    RedClient *client = red_channel_client_get_client(rcc);
> -    MainChannelClient *mcc = red_client_get_main(client);
> -    RedsStream *stream = red_channel_client_get_stream(rcc);
> -    CommonGraphicsChannelClient *ccc = COMMON_GRAPHICS_CHANNEL_CLIENT(rcc);
> -    int flags;
> -    int delay_val;
> -    gboolean low_bw;
> -
> -    if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
> -        spice_warning("accept failed, %s", strerror(errno));
> -        return FALSE;
> -    }
> -
> -    if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) {
> -        spice_warning("accept failed, %s", strerror(errno));
> -        return FALSE;
> -    }
> -
> -    // TODO - this should be dynamic, not one time at channel creation
> -    low_bw = main_channel_client_is_low_bandwidth(mcc);
> -    common_graphics_channel_client_set_low_bandwidth(ccc, low_bw);
> -    delay_val = low_bw ? 0 : 1;
> -    /* FIXME: Using Nagle's Algorithm can lead to apparent delays, depending
> -     * on the delayed ack timeout on the other side.
> -     * Instead of using Nagle's, we need to implement message buffering on
> -     * the application level.
> -     * see: http://www.stuartcheshire.org/papers/NagleDelayedAck/
> -     */
> -    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val,
> -                   sizeof(delay_val)) == -1) {
> -        if (errno != ENOTSUP) {
> -            spice_warning("setsockopt failed, %s", strerror(errno));
> -        }
> -    }
> -    return TRUE;
> -}
> -
> -CommonGraphicsChannel *red_worker_new_channel(RedWorker *worker, int size,
> -                                              const char *name,
> -                                              uint32_t channel_type, int
> migration_flags,
> -                                              ChannelCbs *channel_cbs,
> -                                              channel_handle_parsed_proc
> handle_parsed)
> -{
> -    RedChannel *channel = NULL;
> -    CommonGraphicsChannel *common;
> -
> -    spice_return_val_if_fail(worker, NULL);
> -    spice_return_val_if_fail(channel_cbs, NULL);
> -    spice_return_val_if_fail(!channel_cbs->config_socket, NULL);
> -    spice_return_val_if_fail(!channel_cbs->alloc_recv_buf, NULL);
> -    spice_return_val_if_fail(!channel_cbs->release_recv_buf, NULL);
> -
> -    channel_cbs->config_socket = common_channel_config_socket;
> -    channel_cbs->alloc_recv_buf = common_alloc_recv_buf;
> -    channel_cbs->release_recv_buf = common_release_recv_buf;
> -
> -    channel = red_channel_create_parser(size, red_worker_get_server(worker),
> -                                        &worker->core, channel_type,
> -                                        worker->qxl->id, TRUE /* handle_acks
> */,
> -
> spice_get_client_channel_parser(channel_type,
> NULL),
> -                                        handle_parsed,
> -                                        channel_cbs,
> -                                        migration_flags);
> -    spice_return_val_if_fail(channel, NULL);
> -    red_channel_set_stat_node(channel,
> stat_add_node(red_worker_get_server(worker),
> -                                                     worker->stat, name,
> TRUE));
> -
> -    common = (CommonGraphicsChannel *)channel;
> -    common->qxl = worker->qxl;
> -    return common;
> -}
> -
>  static void guest_set_client_capabilities(RedWorker *worker)
>  {
>      int i;
> @@ -503,7 +400,7 @@ static void guest_set_client_capabilities(RedWorker
> *worker)
>          return;
>      }
>      if ((worker->display_channel == NULL) ||
> -        (RED_CHANNEL(worker->display_channel)->clients_num == 0)) {
> +        (red_channel_get_n_clients(RED_CHANNEL(worker->display_channel)) ==
> 0)) {
>          red_qxl_set_client_capabilities(worker->qxl, FALSE, caps);
>      } else {
>          // Take least common denominator
> @@ -542,7 +439,7 @@ static void cursor_connect(RedWorker *worker, RedClient
> *client, RedsStream *str
>      red_channel_client_ack_zero_messages_window(rcc);
>      red_channel_client_push_set_ack(rcc);
>  
> -    cursor_channel_init(channel, ccc);
> +    cursor_channel_do_init(channel, ccc);
>  }
>  
>  static void handle_dev_update_async(void *opaque, void *payload)
> @@ -608,18 +505,6 @@ static void handle_dev_destroy_surfaces(void *opaque,
> void *payload)
>      cursor_channel_reset(worker->cursor_channel);
>  }
>  
> -static void display_update_monitors_config(DisplayChannel *display,
> -                                           QXLMonitorsConfig *config,
> -                                           uint16_t count, uint16_t
> max_allowed)
> -{
> -
> -    if (display->monitors_config)
> -        monitors_config_unref(display->monitors_config);
> -
> -    display->monitors_config =
> -        monitors_config_new(config->heads, count, max_allowed);
> -}
> -
>  static void red_worker_push_monitors_config(RedWorker *worker)
>  {
>      DisplayChannelClient *dcc;
> @@ -630,21 +515,6 @@ static void red_worker_push_monitors_config(RedWorker
> *worker)
>      }
>  }
>  
> -static void set_monitors_config_to_primary(DisplayChannel *display)
> -{
> -    DrawContext *context = &display->surfaces[0].context;
> -    QXLHead head = { 0, };
> -
> -    spice_return_if_fail(display->surfaces[0].context.canvas);
> -
> -    if (display->monitors_config)
> -        monitors_config_unref(display->monitors_config);
> -
> -    head.width = context->width;
> -    head.height = context->height;
> -    display->monitors_config = monitors_config_new(&head, 1, 1);
> -}
> -
>  static void dev_create_primary_surface(RedWorker *worker, uint32_t
>  surface_id,
>                                         QXLDevSurfaceCreate surface)
>  {
> @@ -675,20 +545,22 @@ static void dev_create_primary_surface(RedWorker
> *worker, uint32_t surface_id,
>  
>      display_channel_create_surface(display, 0, surface.width,
>      surface.height, surface.stride, surface.format,
>                                     line_0, surface.flags &
>                                     QXL_SURF_FLAG_KEEP_DATA, TRUE);
> -    set_monitors_config_to_primary(display);
> +    display_channel_set_monitors_config_to_primary(display);
>  
> -    if (display_is_connected(worker) &&
> !worker->display_channel->common.during_target_migrate) {
> +    if (display_is_connected(worker) &&
> +
> !common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(worker->display_channel)))
> +    {
>          /* guest created primary, so it will (hopefully) send a
>          monitors_config
>           * now, don't send our own temporary one */
>          if (!worker->driver_cap_monitors_config) {
>              red_worker_push_monitors_config(worker);
>          }
> -        red_pipes_add_verb(&worker->display_channel->common.base,
> +        red_pipes_add_verb(RED_CHANNEL(worker->display_channel),
>                             SPICE_MSG_DISPLAY_MARK);
> -        red_channel_push(&worker->display_channel->common.base);
> +        red_channel_push(RED_CHANNEL(worker->display_channel));
>      }
>  
> -    cursor_channel_init(worker->cursor_channel, NULL);
> +    cursor_channel_do_init(worker->cursor_channel, NULL);
>  }
>  
>  static void handle_dev_create_primary_surface(void *opaque, void *payload)
> @@ -703,12 +575,12 @@ static void destroy_primary_surface(RedWorker *worker,
> uint32_t surface_id)
>  {
>      DisplayChannel *display = worker->display_channel;
>  
> -    if (!validate_surface(display, surface_id))
> +    if (!display_channel_validate_surface(display, surface_id))
>          return;
>      spice_warn_if_fail(surface_id == 0);
>  
>      spice_debug(NULL);
> -    if (!display->surfaces[surface_id].context.canvas) {
> +    if (!display_channel_surface_has_canvas(display, surface_id)) {
>          spice_warning("double destroy of primary surface");
>          return;
>      }
> @@ -717,8 +589,10 @@ static void destroy_primary_surface(RedWorker *worker,
> uint32_t surface_id)
>      display_channel_destroy_surface_wait(display, 0);
>      display_channel_surface_unref(display, 0);
>  
> +    /* FIXME: accessing private date only for warning purposes...
>      spice_warn_if_fail(ring_is_empty(&display->streams));
> -    spice_warn_if_fail(!display->surfaces[surface_id].context.canvas);
> +    */
> +    spice_warn_if_fail(!display_channel_surface_has_canvas(display,
> surface_id));
>  
>      cursor_channel_reset(worker->cursor_channel);
>  }
> @@ -784,10 +658,10 @@ static void handle_dev_start(void *opaque, void
> *payload)
>  
>      spice_assert(!worker->running);
>      if (worker->cursor_channel) {
> -
> COMMON_GRAPHICS_CHANNEL(worker->cursor_channel)->during_target_migrate
> = FALSE;
> +
> common_graphics_channel_set_during_target_migrate(COMMON_GRAPHICS_CHANNEL(worker->cursor_channel),
> FALSE);
>      }
>      if (worker->display_channel) {
> -        worker->display_channel->common.during_target_migrate = FALSE;
> +
> common_graphics_channel_set_during_target_migrate(COMMON_GRAPHICS_CHANNEL(worker->display_channel),
> FALSE);
>          display_channel_wait_for_migrate_data(worker->display_channel);
>      }
>      worker->running = TRUE;
> @@ -807,16 +681,18 @@ static void handle_dev_oom(void *opaque, void *payload)
>      RedWorker *worker = opaque;
>      DisplayChannel *display = worker->display_channel;
>  
> -    RedChannel *display_red_channel = &display->common.base;
> +    RedChannel *display_red_channel = RED_CHANNEL(display);
>      int ring_is_empty;
>  
>      spice_return_if_fail(worker->running);
>      // streams? but without streams also leak
> +#if FIXME
>      spice_debug("OOM1 #draw=%u, #glz_draw=%u current %u pipes %u",
>                  display->drawable_count,
>                  display->glz_drawable_count,
>                  display->current_size,
>                  red_channel_sum_pipes_size(display_red_channel));
> +#endif
>      while (red_process_display(worker, &ring_is_empty)) {
>          red_channel_push(display_red_channel);
>      }
> @@ -824,12 +700,14 @@ static void handle_dev_oom(void *opaque, void *payload)
>          display_channel_free_some(worker->display_channel);
>          red_qxl_flush_resources(worker->qxl);
>      }
> +#if FIXME
>      spice_debug("OOM2 #draw=%u, #glz_draw=%u current %u pipes %u",
>                  display->drawable_count,
>                  display->glz_drawable_count,
>                  display->current_size,
>                  red_channel_sum_pipes_size(display_red_channel));
>      red_qxl_clear_pending(worker->qxl->st, RED_DISPATCHER_PENDING_OOM);
> +#endif
>  }
>  
>  static void handle_dev_reset_cursor(void *opaque, void *payload)
> @@ -842,9 +720,7 @@ static void handle_dev_reset_cursor(void *opaque, void
> *payload)
>  static void handle_dev_reset_image_cache(void *opaque, void *payload)
>  {
>      RedWorker *worker = opaque;
> -    DisplayChannel *display = worker->display_channel;
> -
> -    image_cache_reset(&display->image_cache);
> +    display_channel_reset_image_cache(worker->display_channel);
>  }
>  
>  static void handle_dev_destroy_surface_wait_async(void *opaque, void
>  *payload)
> @@ -964,7 +840,7 @@ static void handle_dev_monitors_config_async(void
> *opaque, void *payload)
>          /* TODO: raise guest bug (requires added QXL interface) */
>          return;
>      }
> -    display_update_monitors_config(worker->display_channel,
> dev_monitors_config,
> +    display_channel_update_monitors_config(worker->display_channel,
> dev_monitors_config,
>                                     MIN(count, msg->max_monitors),
>                                     MIN(max_allowed, msg->max_monitors));
>      red_worker_push_monitors_config(worker);
> @@ -1509,17 +1385,28 @@ RedWorker* red_worker_new(QXLInstance *qxl,
>  
>      worker->event_timeout = INF_EVENT_WAIT;
>  
> -    worker->cursor_channel = cursor_channel_new(worker);
> +    worker->cursor_channel = cursor_channel_new(reds, worker->qxl,
> &worker->core);
> +    red_channel_set_stat_node(RED_CHANNEL(worker->cursor_channel),
> +                              stat_add_node(red_worker_get_server(worker),
> +                                            worker->stat,
> +                                            "cursor_channel", TRUE));
>      channel = RED_CHANNEL(worker->cursor_channel);
>      red_channel_register_client_cbs(channel, client_cursor_cbs, dispatcher);
> +    g_object_set_data(G_OBJECT(channel), "dispatcher", dispatcher);
>      reds_register_channel(reds, channel);
>  
>      // TODO: handle seemless migration. Temp, setting migrate to FALSE
> -    worker->display_channel = display_channel_new(reds, worker, FALSE,
> reds_get_streaming_video(reds),
> +    worker->display_channel = display_channel_new(reds, worker->qxl,
> &worker->core,
> +                                                  FALSE,
> reds_get_streaming_video(reds),
>                                                    init_info.n_surfaces);
> +    red_channel_set_stat_node(RED_CHANNEL(worker->display_channel),
> +                              stat_add_node(red_worker_get_server(worker),
> +                                            worker->stat,
> +                                            "display_channel", TRUE));
>  
>      channel = RED_CHANNEL(worker->display_channel);
>      red_channel_register_client_cbs(channel, client_display_cbs,
>      dispatcher);
> +    g_object_set_data(G_OBJECT(channel), "dispatcher", dispatcher);
>      red_channel_set_cap(channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG);
>      red_channel_set_cap(channel, SPICE_DISPLAY_CAP_PREF_COMPRESSION);
>      red_channel_set_cap(channel, SPICE_DISPLAY_CAP_STREAM_REPORT);
> @@ -1536,8 +1423,8 @@ SPICE_GNUC_NORETURN static void *red_worker_main(void
> *arg)
>      spice_assert(MAX_PIPE_SIZE > WIDE_CLIENT_ACK_WINDOW &&
>             MAX_PIPE_SIZE > NARROW_CLIENT_ACK_WINDOW); //ensure wakeup by ack
>             message
>  
> -    RED_CHANNEL(worker->cursor_channel)->thread_id = pthread_self();
> -    RED_CHANNEL(worker->display_channel)->thread_id = pthread_self();
> +    red_channel_reset_thread_id(RED_CHANNEL(worker->cursor_channel));
> +    red_channel_reset_thread_id(RED_CHANNEL(worker->display_channel));
>  
>      GMainLoop *loop = g_main_loop_new(worker->core.main_context, FALSE);
>      g_main_loop_run(loop);
> diff --git a/server/red-worker.h b/server/red-worker.h
> index 501128b..ebc5a97 100644
> --- a/server/red-worker.h
> +++ b/server/red-worker.h
> @@ -25,73 +25,9 @@
>  
>  typedef struct RedWorker RedWorker;
>  
> -#define COMMON_CLIENT_TIMEOUT (NSEC_PER_SEC * 30)
> -
> -#define CHANNEL_RECEIVE_BUF_SIZE 1024
> -typedef struct CommonGraphicsChannel {
> -    RedChannel base; // Must be the first thing
> -
> -    QXLInstance *qxl;
> -    uint8_t recv_buf[CHANNEL_RECEIVE_BUF_SIZE];
> -    uint32_t id_alloc; // bitfield. TODO - use this instead of shift scheme.
> -    int during_target_migrate; /* TRUE when the client that is associated
> with the channel
> -                                  is during migration. Turned off when the
> vm is started.
> -                                  The flag is used to avoid sending messages
> that are artifacts
> -                                  of the transition from stopped vm to
> loaded vm (e.g., recreation
> -                                  of the primary surface) */
> -} CommonGraphicsChannel;
> -
> -#define COMMON_GRAPHICS_CHANNEL(Channel) ((CommonGraphicsChannel*)(Channel))
> -
> -enum {
> -    RED_PIPE_ITEM_TYPE_VERB = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
> -    RED_PIPE_ITEM_TYPE_INVAL_ONE,
> -
> -    RED_PIPE_ITEM_TYPE_COMMON_LAST
> -};
> -
> -typedef struct RedVerbItem {
> -    RedPipeItem base;
> -    uint16_t verb;
> -} RedVerbItem;
> -
> -static inline void red_marshall_verb(RedChannelClient *rcc, RedVerbItem
> *item)
> -{
> -    red_channel_client_init_send_data(rcc, item->verb, NULL);
> -}
> -
> -static inline void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb)
> -{
> -    RedVerbItem *item = spice_new(RedVerbItem, 1);
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_VERB);
> -    item->verb = verb;
> -    red_channel_client_pipe_add(rcc, &item->base);
> -}
> -
> -static inline void red_pipe_add_verb_proxy(RedChannelClient *rcc, gpointer
> data)
> -{
> -    uint16_t verb = GPOINTER_TO_UINT(data);
> -    red_pipe_add_verb(rcc, verb);
> -}
> -
> -
> -static inline void red_pipes_add_verb(RedChannel *channel, uint16_t verb)
> -{
> -    red_channel_apply_clients_data(channel, red_pipe_add_verb_proxy,
> GUINT_TO_POINTER(verb));
> -}
> -
>  RedWorker* red_worker_new(QXLInstance *qxl,
>                            const ClientCbs *client_cursor_cbs,
>                            const ClientCbs *client_display_cbs);
>  bool       red_worker_run(RedWorker *worker);
>  
> -void red_drawable_unref(RedDrawable *red_drawable);
> -
> -CommonGraphicsChannel *red_worker_new_channel(RedWorker *worker, int size,
> -                                              const char *name,
> -                                              uint32_t channel_type, int
> migration_flags,
> -                                              ChannelCbs *channel_cbs,
> -                                              channel_handle_parsed_proc
> handle_parsed);
> -
>  #endif
> diff --git a/server/reds-private.h b/server/reds-private.h
> index 74a251b..4b86d2d 100644
> --- a/server/reds-private.h
> +++ b/server/reds-private.h
> @@ -120,8 +120,7 @@ struct RedsState {
>      Ring mig_target_clients;
>      int num_mig_target_clients;
>  
> -    int num_of_channels;
> -    Ring channels;
> +    GList *channels;
>      int mouse_mode;
>      int is_client_mouse_allowed;
>      int dispatcher_allows_client_mouse;
> diff --git a/server/reds.c b/server/reds.c
> index 9f5551e..27fc5da 100644
> --- a/server/reds.c
> +++ b/server/reds.c
> @@ -463,15 +463,13 @@ void stat_update_value(RedsState *reds, uint32_t value)
>  void reds_register_channel(RedsState *reds, RedChannel *channel)
>  {
>      spice_assert(reds);
> -    ring_add(&reds->channels, &channel->link);
> -    reds->num_of_channels++;
> +    reds->channels = g_list_append(reds->channels, channel);
>  }
>  
>  void reds_unregister_channel(RedsState *reds, RedChannel *channel)
>  {
> -    if (ring_item_is_linked(&channel->link)) {
> -        ring_remove(&channel->link);
> -        reds->num_of_channels--;
> +    if (g_list_find(reds->channels, channel)) {
> +        reds->channels = g_list_remove(reds->channels, channel);
>      } else {
>          spice_warning("not found");
>      }
> @@ -479,11 +477,13 @@ void reds_unregister_channel(RedsState *reds,
> RedChannel *channel)
>  
>  static RedChannel *reds_find_channel(RedsState *reds, uint32_t type,
>  uint32_t id)
>  {
> -    RingItem *now;
> +    GList *l;
>  
> -    RING_FOREACH(now, &reds->channels) {
> -        RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link);
> -        if (channel->type == type && channel->id == id) {
> +    for (l = reds->channels; l != NULL; l = l->next) {
> +        RedChannel *channel = l->data;
> +        uint32_t this_type, this_id;
> +        g_object_get(channel, "channel-type", &this_type, "id", &this_id,
> NULL);
> +        if (this_type == type && this_id == id) {
>              return channel;
>          }
>      }
> @@ -552,7 +552,7 @@ static void reds_reset_vdp(RedsState *reds)
>       *  In addition, there used to be a misshandling of AGENT_TOKENS message
>       in spice-gtk: it
>       *  overrides the amount of tokens, instead of adding the given amount.
>       */
> -    if (red_channel_test_remote_cap(&reds->main_channel->base,
> +    if (red_channel_test_remote_cap(RED_CHANNEL(reds->main_channel),
>                                      SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS))
>                                      {
>          dev->priv->agent_attached = FALSE;
>      } else {
> @@ -743,7 +743,7 @@ static void reds_agent_remove(RedsState *reds)
>      reds->vdagent = NULL;
>      reds_update_mouse_mode(reds);
>      if (reds_main_channel_connected(reds) &&
> -        !red_channel_is_waiting_for_migrate_data(&reds->main_channel->base))
> {
> +
> !red_channel_is_waiting_for_migrate_data(RED_CHANNEL(reds->main_channel)))
> {
>          main_channel_push_agent_disconnected(reds->main_channel);
>      }
>  }
> @@ -995,7 +995,9 @@ SPICE_GNUC_VISIBLE int
> spice_server_get_num_clients(SpiceServer *reds)
>  
>  static int channel_supports_multiple_clients(RedChannel *channel)
>  {
> -    switch (channel->type) {
> +    uint32_t type;
> +    g_object_get(channel, "channel-type", &type, NULL);
> +    switch (type) {
>      case SPICE_CHANNEL_MAIN:
>      case SPICE_CHANNEL_DISPLAY:
>      case SPICE_CHANNEL_CURSOR:
> @@ -1007,23 +1009,25 @@ static int
> channel_supports_multiple_clients(RedChannel *channel)
>  
>  static void reds_fill_channels(RedsState *reds, SpiceMsgChannels
>  *channels_info)
>  {
> -    RingItem *now;
> +    GList *l;
>      int used_channels = 0;
>  
> -    RING_FOREACH(now, &reds->channels) {
> -        RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link);
> +    for (l = reds->channels; l != NULL; l = l->next) {
> +        uint32_t type, id;
> +        RedChannel *channel = l->data;
>          if (reds->num_clients > 1 &&
>              !channel_supports_multiple_clients(channel)) {
>              continue;
>          }
> -        channels_info->channels[used_channels].type = channel->type;
> -        channels_info->channels[used_channels].id = channel->id;
> +        g_object_get(channel, "channel-type", &type, "id", &id, NULL);
> +        channels_info->channels[used_channels].type = type;
> +        channels_info->channels[used_channels].id = id;
>          used_channels++;
>      }
>  
>      channels_info->num_of_channels = used_channels;
> -    if (used_channels != reds->num_of_channels) {
> -        spice_warning("sent %d out of %d", used_channels,
> reds->num_of_channels);
> +    if (used_channels != g_list_length(reds->channels)) {
> +        spice_warning("sent %d out of %d", used_channels,
> g_list_length(reds->channels));
>      }
>  }
>  
> @@ -1034,7 +1038,7 @@ SpiceMsgChannels *reds_msg_channels_new(RedsState
> *reds)
>      spice_assert(reds != NULL);
>  
>      channels_info = (SpiceMsgChannels
>      *)spice_malloc(sizeof(SpiceMsgChannels)
> -                            + reds->num_of_channels *
> sizeof(SpiceChannelId));
> +                            + g_list_length(reds->channels) *
> sizeof(SpiceChannelId));
>  
>      reds_fill_channels(reds, channels_info);
>  
> @@ -1546,12 +1550,12 @@ static int reds_send_link_ack(RedsState *reds,
> RedLinkInfo *link)
>              return FALSE;
>          }
>          spice_assert(reds->main_channel);
> -        channel = &reds->main_channel->base;
> +        channel = RED_CHANNEL(reds->main_channel);
>      }
>  
>      reds_channel_init_auth_caps(link, channel); /* make sure common caps are
>      set */
>  
> -    channel_caps = &channel->local_caps;
> +    channel_caps = red_channel_get_local_capabilities(channel);
>      ack.num_common_caps = GUINT32_TO_LE(channel_caps->num_common_caps);
>      ack.num_channel_caps = GUINT32_TO_LE(channel_caps->num_caps);
>      hdr_size += channel_caps->num_common_caps * sizeof(uint32_t);
> @@ -1888,13 +1892,13 @@ static void reds_channel_do_link(RedChannel *channel,
> RedClient *client,
>      spice_assert(stream);
>  
>      caps = (uint32_t *)((uint8_t *)link_msg + link_msg->caps_offset);
> -    channel->client_cbs.connect(channel, client, stream,
> -                                red_client_during_migrate_at_target(client),
> -                                link_msg->num_common_caps,
> -                                link_msg->num_common_caps ? caps : NULL,
> -                                link_msg->num_channel_caps,
> -                                link_msg->num_channel_caps ?
> -                                caps + link_msg->num_common_caps : NULL);
> +    red_channel_connect(channel, client, stream,
> +                        red_client_during_migrate_at_target(client),
> +                        link_msg->num_common_caps,
> +                        link_msg->num_common_caps ? caps : NULL,
> +                        link_msg->num_channel_caps,
> +                        link_msg->num_channel_caps ?
> +                        caps + link_msg->num_common_caps : NULL);
>  }
>  
>  /*
> @@ -3123,7 +3127,7 @@ static RedCharDevice *attach_to_red_agent(RedsState
> *reds, SpiceCharDeviceInstan
>      dev->priv->plug_generation++;
>  
>      if (dev->priv->mig_data ||
> -        red_channel_is_waiting_for_migrate_data(&reds->main_channel->base))
> {
> +
> red_channel_is_waiting_for_migrate_data(RED_CHANNEL(reds->main_channel)))
> {
>          /* Migration in progress (code is running on the destination host):
>           * 1.  Add the client to spice char device, if it was not already
>           added.
>           * 2.a If this (qemu-kvm state load side of migration) happens first
> @@ -3442,7 +3446,6 @@ static int do_spice_init(RedsState *reds,
> SpiceCoreInterface *core_interface)
>      ring_init(&reds->clients);
>      reds->num_clients = 0;
>      reds->main_dispatcher = main_dispatcher_new(reds, reds->core);
> -    ring_init(&reds->channels);
>      ring_init(&reds->mig_target_clients);
>      reds->char_devices = NULL;
>      reds->mig_wait_disconnect_clients = NULL;
> @@ -3986,7 +3989,7 @@ SPICE_GNUC_VISIBLE int
> spice_server_migrate_connect(SpiceServer *reds, const cha
>       * be valid (see reds_reset_vdp for more details).
>       */
>      try_seamless = reds->seamless_migration_enabled &&
> -                   red_channel_test_remote_cap(&reds->main_channel->base,
> +
> red_channel_test_remote_cap(RED_CHANNEL(reds->main_channel),
>                     SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS);
>      /* main channel will take care of clients that are still during
>      migration (at target)*/
>      if (main_channel_migrate_connect(reds->main_channel,
>      reds->config->mig_spice,
> diff --git a/server/smartcard.c b/server/smartcard.c
> index ec42960..c681d39 100644
> --- a/server/smartcard.c
> +++ b/server/smartcard.c
> @@ -49,6 +49,61 @@
>  // Maximal length of APDU
>  #define APDUBufSize 270
>  
> +#define RED_TYPE_SMARTCARD_CHANNEL red_smartcard_channel_get_type()
> +
> +#define RED_SMARTCARD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannel))
> +#define RED_SMARTCARD_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
> RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelClass))
> +#define RED_IS_SMARTCARD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> RED_TYPE_SMARTCARD_CHANNEL))
> +#define RED_IS_SMARTCARD_CHANNEL_CLASS(klass)
> (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_SMARTCARD_CHANNEL))
> +#define RED_SMARTCARD_CHANNEL_GET_CLASS(obj)
> (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_SMARTCARD_CHANNEL,
> RedSmartcardChannelClass))
> +
> +typedef struct RedSmartcardChannel RedSmartcardChannel;
> +typedef struct RedSmartcardChannelClass RedSmartcardChannelClass;
> +typedef struct RedSmartcardChannelPrivate RedSmartcardChannelPrivate;
> +
> +struct RedSmartcardChannel
> +{
> +    RedChannel parent;
> +
> +    RedSmartcardChannelPrivate *priv;
> +};
> +
> +struct RedSmartcardChannelClass
> +{
> +    RedChannelClass parent_class;
> +};
> +
> +GType red_smartcard_channel_get_type(void) G_GNUC_CONST;
> +
> +G_DEFINE_TYPE(RedSmartcardChannel, red_smartcard_channel, RED_TYPE_CHANNEL)
> +
> +#define SMARTCARD_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o),
> RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelPrivate))
> +
> +struct RedSmartcardChannelPrivate
> +{
> +    gpointer padding;
> +};
> +
> +static void
> +red_smartcard_channel_init(RedSmartcardChannel *self)
> +{
> +    self->priv = SMARTCARD_CHANNEL_PRIVATE(self);
> +}
> +
> +RedSmartcardChannel *
> +red_smartcard_channel_new(RedsState *reds)
> +{
> +    return g_object_new(RED_TYPE_SMARTCARD_CHANNEL,
> +                        "spice-server", reds,
> +                        "core-interface", reds_get_core_interface(reds),
> +                        "channel-type", SPICE_CHANNEL_SMARTCARD,
> +                        "id", 0,
> +                        "handle-acks", FALSE /* handle_acks */,
> +                        "migration-flags", (SPICE_MIGRATE_NEED_FLUSH |
> SPICE_MIGRATE_NEED_DATA_TRANSFER),
> +                        NULL);
> +}
> +
> +
>  G_DEFINE_TYPE(RedCharDeviceSmartcard, red_char_device_smartcard,
>  RED_TYPE_CHAR_DEVICE)
>  
>  #define RED_CHAR_DEVICE_SMARTCARD_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE
>  ((o), RED_TYPE_CHAR_DEVICE_SMARTCARD, RedCharDeviceSmartcardPrivate))
> @@ -74,10 +129,6 @@ typedef struct RedMsgItem {
>  static RedMsgItem *smartcard_get_vsc_msg_item(RedChannelClient *rcc,
>  VSCMsgHeader *vheader);
>  static void smartcard_channel_client_pipe_add_push(RedChannelClient *rcc,
>  RedPipeItem *item);
>  
> -typedef struct SmartCardChannel {
> -    RedChannel base;
> -} SmartCardChannel;
> -
>  static struct Readers {
>      uint32_t num;
>      SpiceCharDeviceInstance* sin[SMARTCARD_MAX_READERS];
> @@ -531,43 +582,53 @@ static void smartcard_connect_client(RedChannel
> *channel, RedClient *client,
>      }
>  }
>  
> -SmartCardChannel *g_smartcard_channel;
> -
> -static void smartcard_init(RedsState *reds)
> +static void
> +red_smartcard_channel_constructed(GObject *object)
>  {
> -    ChannelCbs channel_cbs = { NULL, };
> +    RedSmartcardChannel *self = RED_SMARTCARD_CHANNEL(object);
> +    RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
>      ClientCbs client_cbs = { NULL, };
> -    uint32_t migration_flags = SPICE_MIGRATE_NEED_FLUSH |
> SPICE_MIGRATE_NEED_DATA_TRANSFER;
> -
> -    spice_assert(!g_smartcard_channel);
>  
> -    channel_cbs.config_socket = smartcard_channel_client_config_socket;
> -    channel_cbs.on_disconnect = smartcard_channel_client_on_disconnect;
> -    channel_cbs.send_item = smartcard_channel_send_item;
> -    channel_cbs.hold_item = smartcard_channel_hold_pipe_item;
> -    channel_cbs.release_item = smartcard_channel_release_pipe_item;
> -    channel_cbs.alloc_recv_buf = smartcard_channel_client_alloc_msg_rcv_buf;
> -    channel_cbs.release_recv_buf =
> smartcard_channel_client_release_msg_rcv_buf;
> -    channel_cbs.handle_migrate_flush_mark =
> smartcard_channel_client_handle_migrate_flush_mark;
> -    channel_cbs.handle_migrate_data =
> smartcard_channel_client_handle_migrate_data;
> -
> -    g_smartcard_channel =
> (SmartCardChannel*)red_channel_create(sizeof(SmartCardChannel),
> -                                             reds,
> -                                             reds_get_core_interface(reds),
> -                                             SPICE_CHANNEL_SMARTCARD, 0,
> -                                             FALSE /* handle_acks */,
> -
> smartcard_channel_client_handle_message,
> -                                             &channel_cbs,
> -                                             migration_flags);
> -
> -    if (!g_smartcard_channel) {
> -        spice_error("failed to allocate Smartcard Channel");
> -    }
> +    G_OBJECT_CLASS(red_smartcard_channel_parent_class)->constructed(object);
>  
>      client_cbs.connect = smartcard_connect_client;
> -    red_channel_register_client_cbs(&g_smartcard_channel->base, &client_cbs,
> NULL);
> +    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL);
> +
> +    reds_register_channel(reds, RED_CHANNEL(self));
> +}
> +
> +static void
> +red_smartcard_channel_class_init(RedSmartcardChannelClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
> +
> +    g_type_class_add_private(klass, sizeof(RedSmartcardChannelPrivate));
> +
> +    object_class->constructed = red_smartcard_channel_constructed;
> +
> +    channel_class->handle_message = smartcard_channel_client_handle_message,
> +
> +    channel_class->config_socket = smartcard_channel_client_config_socket;
> +    channel_class->on_disconnect = smartcard_channel_client_on_disconnect;
> +    channel_class->send_item = smartcard_channel_send_item;
> +    channel_class->hold_item = smartcard_channel_hold_pipe_item;
> +    channel_class->release_item = smartcard_channel_release_pipe_item;
> +    channel_class->alloc_recv_buf =
> smartcard_channel_client_alloc_msg_rcv_buf;
> +    channel_class->release_recv_buf =
> smartcard_channel_client_release_msg_rcv_buf;
> +    channel_class->handle_migrate_flush_mark =
> smartcard_channel_client_handle_migrate_flush_mark;
> +    channel_class->handle_migrate_data =
> smartcard_channel_client_handle_migrate_data;
> +
> +}
> +
> +/* FIXME: remove global */
> +RedSmartcardChannel *g_smartcard_channel;
> +
> +static void smartcard_init(RedsState *reds)
> +{
> +    spice_assert(!g_smartcard_channel);
>  
> -    reds_register_channel(reds, &g_smartcard_channel->base);
> +    g_smartcard_channel = red_smartcard_channel_new(reds);
>  }
>  
>  
> diff --git a/server/sound.c b/server/sound.c
> index e87428f..6982be3 100644
> --- a/server/sound.c
> +++ b/server/sound.c
> @@ -31,6 +31,7 @@
>  
>  #include "spice.h"
>  #include "red-common.h"
> +#include "dummy-channel.h"
>  #include "dummy-channel-client.h"
>  #include "main-channel.h"
>  #include "main-channel-client.h"
> @@ -218,6 +219,7 @@ static void snd_disconnect_channel(SndChannel *channel)
>      SndWorker *worker;
>      RedsState *reds;
>      RedChannel *red_channel;
> +    uint32_t type;
>  
>      if (!channel || !channel->stream) {
>          spice_debug("not connected");
> @@ -225,8 +227,9 @@ static void snd_disconnect_channel(SndChannel *channel)
>      }
>      red_channel = red_channel_client_get_channel(channel->channel_client);
>      reds = snd_channel_get_server(channel);
> +    g_object_get(red_channel, "channel-type", &type, NULL);
>      spice_debug("SndChannel=%p rcc=%p type=%d",
> -                 channel, channel->channel_client, red_channel->type);
> +                 channel, channel->channel_client, type);
>      worker = channel->worker;
>      channel->cleanup(channel);
>      red_channel_client_disconnect(worker->connection->channel_client);
> @@ -999,12 +1002,14 @@ static void
> snd_disconnect_channel_client(RedChannelClient *rcc)
>  {
>      SndWorker *worker;
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> +    uint32_t type;
>  
>      spice_assert(channel);
> -    spice_assert(channel->data);
> -    worker = (SndWorker *)channel->data;
> +    worker = (SndWorker *)g_object_get_data(G_OBJECT(channel),
> "sound-worker");
> +    spice_assert(worker);
> +    g_object_get(channel, "channel-type", &type, NULL);
>  
> -    spice_debug("channel-type=%d", channel->type);
> +    spice_debug("channel-type=%d", type);
>      if (worker->connection) {
>          spice_assert(worker->connection->channel_client == rcc);
>          snd_disconnect_channel(worker->connection);
> @@ -1059,6 +1064,7 @@ SPICE_GNUC_VISIBLE void
> spice_server_playback_start(SpicePlaybackInstance *sin)
>      sin->st->worker.active = 1;
>      if (!channel)
>          return;
> +
>      spice_assert(!playback_channel->base.active);
>      reds_disable_mm_time(snd_channel_get_server(channel));
>      playback_channel->base.active = TRUE;
> @@ -1078,6 +1084,7 @@ SPICE_GNUC_VISIBLE void
> spice_server_playback_stop(SpicePlaybackInstance *sin)
>      sin->st->worker.active = 0;
>      if (!channel)
>          return;
> +
>      spice_assert(playback_channel->base.active);
>      reds_enable_mm_time(snd_channel_get_server(channel));
>      playback_channel->base.active = FALSE;
> @@ -1146,7 +1153,9 @@ void snd_set_playback_latency(RedClient *client,
> uint32_t latency)
>      SndWorker *now = workers;
>  
>      for (; now; now = now->next) {
> -        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK &&
> now->connection &&
> +        uint32_t type;
> +        g_object_get(now->base_channel, "channel-type", &type, NULL);
> +        if (type == SPICE_CHANNEL_PLAYBACK && now->connection &&
>              red_channel_client_get_client(now->connection->channel_client)
>              == client) {
>  
>              if
>              (red_channel_client_test_remote_cap(now->connection->channel_client,
> @@ -1214,7 +1223,7 @@ static void snd_set_playback_peer(RedChannel *channel,
> RedClient *client, RedsSt
>                                    int migration, int num_common_caps,
>                                    uint32_t *common_caps,
>                                    int num_caps, uint32_t *caps)
>  {
> -    SndWorker *worker = channel->data;
> +    SndWorker *worker = g_object_get_data(G_OBJECT(channel),
> "sound-worker");
>      PlaybackChannel *playback_channel;
>      SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState,
>      worker);
>  
> @@ -1243,7 +1252,7 @@ static void snd_set_playback_peer(RedChannel *channel,
> RedClient *client, RedsSt
>                                            SPICE_PLAYBACK_CAP_CELT_0_5_1);
>      int client_can_opus =
>      red_channel_client_test_remote_cap(playback_channel->base.channel_client,
>                                            SPICE_PLAYBACK_CAP_OPUS);
> -    int playback_compression =
> reds_config_get_playback_compression(channel->reds);
> +    int playback_compression =
> reds_config_get_playback_compression(red_channel_get_server(channel));
>      int desired_mode = snd_desired_audio_mode(playback_compression,
>      st->frequency,
>                                                client_can_celt,
>                                                client_can_opus);
>      playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
> @@ -1272,8 +1281,8 @@ static void
> snd_record_migrate_channel_client(RedChannelClient *rcc)
>  
>      spice_debug(NULL);
>      spice_assert(channel);
> -    spice_assert(channel->data);
> -    worker = (SndWorker *)channel->data;
> +    worker = (SndWorker *)g_object_get_data(G_OBJECT(channel),
> "sound-worker");
> +    spice_assert(worker);
>  
>      if (worker->connection) {
>          spice_assert(worker->connection->channel_client == rcc);
> @@ -1463,7 +1472,7 @@ static void snd_set_record_peer(RedChannel *channel,
> RedClient *client, RedsStre
>                                  int migration, int num_common_caps, uint32_t
>                                  *common_caps,
>                                  int num_caps, uint32_t *caps)
>  {
> -    SndWorker *worker = channel->data;
> +    SndWorker *worker = g_object_get_data(G_OBJECT(channel),
> "sound-worker");
>      RecordChannel *record_channel;
>      SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState,
>      worker);
>  
> @@ -1501,8 +1510,8 @@ static void
> snd_playback_migrate_channel_client(RedChannelClient *rcc)
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>  
>      spice_assert(channel);
> -    spice_assert(channel->data);
> -    worker = (SndWorker *)channel->data;
> +    worker = (SndWorker *)g_object_get_data(G_OBJECT(channel),
> "sound-worker");
> +    spice_assert(worker);
>      spice_debug(NULL);
>  
>      if (worker->connection) {
> @@ -1543,8 +1552,9 @@ void snd_attach_playback(RedsState *reds,
> SpicePlaybackInstance *sin)
>      sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the
>      legacy rate */
>  
>      // TODO: Make RedChannel base of worker? instead of assigning it to
>      channel->data
> -    channel = red_channel_create_dummy(sizeof(RedChannel), reds,
> SPICE_CHANNEL_PLAYBACK, 0);
> +    channel = dummy_channel_new(reds, SPICE_CHANNEL_PLAYBACK, 0);
>  
> +    g_object_set_data(G_OBJECT(channel), "sound-worker", playback_worker);
>      client_cbs.connect = snd_set_playback_peer;
>      client_cbs.disconnect = snd_disconnect_channel_client;
>      client_cbs.migrate = snd_playback_migrate_channel_client;
> @@ -1572,8 +1582,9 @@ void snd_attach_record(RedsState *reds,
> SpiceRecordInstance *sin)
>      sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the
>      legacy rate */
>  
>      // TODO: Make RedChannel base of worker? instead of assigning it to
>      channel->data
> -    channel = red_channel_create_dummy(sizeof(RedChannel), reds,
> SPICE_CHANNEL_RECORD, 0);
> +    channel = dummy_channel_new(reds, SPICE_CHANNEL_RECORD, 0);
>  
> +    g_object_set_data(G_OBJECT(channel), "sound-worker", record_worker);
>      client_cbs.connect = snd_set_record_peer;
>      client_cbs.disconnect = snd_disconnect_channel_client;
>      client_cbs.migrate = snd_record_migrate_channel_client;
> @@ -1629,7 +1640,9 @@ void snd_set_playback_compression(int on)
>      SndWorker *now = workers;
>  
>      for (; now; now = now->next) {
> -        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK &&
> now->connection) {
> +        uint32_t type;
> +        g_object_get(now->base_channel, "channel-type", &type, NULL);
> +        if (type == SPICE_CHANNEL_PLAYBACK && now->connection) {
>              PlaybackChannel* playback = (PlaybackChannel*)now->connection;
>              SpicePlaybackState *st = SPICE_CONTAINEROF(now,
>              SpicePlaybackState, worker);
>              int client_can_celt =
>              red_channel_client_test_remote_cap(playback->base.channel_client,
> diff --git a/server/spicevmc.c b/server/spicevmc.c
> index 0b37b4a..6441542 100644
> --- a/server/spicevmc.c
> +++ b/server/spicevmc.c
> @@ -52,16 +52,6 @@ typedef struct RedVmcPipeItem {
>      uint32_t buf_used;
>  } RedVmcPipeItem;
>  
> -typedef struct SpiceVmcState {
> -    RedChannel channel; /* Must be the first item */
> -    RedChannelClient *rcc;
> -    RedCharDevice *chardev;
> -    SpiceCharDeviceInstance *chardev_sin;
> -    RedVmcPipeItem *pipe_item;
> -    RedCharDeviceWriteBuffer *recv_from_client_buf;
> -    uint8_t port_opened;
> -} SpiceVmcState;
> -
>  #define RED_TYPE_CHAR_DEVICE_SPICEVMC red_char_device_spicevmc_get_type()
>  
>  #define RED_CHAR_DEVICE_SPICEVMC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
>  RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmc))
> @@ -89,6 +79,133 @@ static RedCharDevice
> *red_char_device_spicevmc_new(SpiceCharDeviceInstance *sin,
>  
>  G_DEFINE_TYPE(RedCharDeviceSpiceVmc, red_char_device_spicevmc,
>  RED_TYPE_CHAR_DEVICE)
>  
> +#define RED_CHAR_DEVICE_SPICEVMC_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE
> ((o), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmcPrivate))
> +
> +#define SPICE_TYPE_VMC_STATE spice_vmc_state_get_type()
> +
> +#define SPICE_VMC_STATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> SPICE_TYPE_VMC_STATE, SpiceVmcState))
> +#define SPICE_VMC_STATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
> SPICE_TYPE_VMC_STATE, SpiceVmcStateClass))
> +#define SPICE_IS_VMC_STATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
> SPICE_TYPE_VMC_STATE))
> +#define SPICE_IS_VMC_STATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
> SPICE_TYPE_VMC_STATE))
> +#define SPICE_VMC_STATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
> SPICE_TYPE_VMC_STATE, SpiceVmcStateClass))
> +
> +typedef struct SpiceVmcState SpiceVmcState;
> +typedef struct SpiceVmcStateClass SpiceVmcStateClass;
> +typedef struct SpiceVmcStatePrivate SpiceVmcStatePrivate;
> +
> +struct SpiceVmcState
> +{
> +    RedChannel parent;
> +
> +    SpiceVmcStatePrivate *priv;
> +};
> +
> +struct SpiceVmcStateClass
> +{
> +    RedChannelClass parent_class;
> +};
> +
> +GType spice_vmc_state_get_type(void) G_GNUC_CONST;
> +
> +G_DEFINE_TYPE(SpiceVmcState, spice_vmc_state, RED_TYPE_CHANNEL)
> +
> +#define VMC_STATE_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o),
> SPICE_TYPE_VMC_STATE, SpiceVmcStatePrivate))
> +
> +struct SpiceVmcStatePrivate
> +{
> +    RedChannelClient *rcc;
> +    RedCharDevice *chardev;
> +    SpiceCharDeviceInstance *chardev_sin;
> +    RedVmcPipeItem *pipe_item;
> +    RedCharDeviceWriteBuffer *recv_from_client_buf;
> +    uint8_t port_opened;
> +};
> +
> +enum {
> +    PROP0,
> +    PROP_DEVICE_INSTANCE
> +};
> +
> +static void
> +spice_vmc_state_get_property(GObject *object,
> +                             guint property_id,
> +                             GValue *value,
> +                             GParamSpec *pspec)
> +{
> +    SpiceVmcState *self = SPICE_VMC_STATE(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_DEVICE_INSTANCE:
> +            g_value_set_pointer(value, self->priv->chardev_sin);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void
> +spice_vmc_state_set_property(GObject *object,
> +                             guint property_id,
> +                             const GValue *value,
> +                             GParamSpec *pspec)
> +{
> +    SpiceVmcState *self = SPICE_VMC_STATE(object);
> +
> +    switch (property_id)
> +    {
> +        case PROP_DEVICE_INSTANCE:
> +            self->priv->chardev_sin = g_value_get_pointer(value);
> +            break;
> +        default:
> +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +    }
> +}
> +
> +static void spicevmc_connect(RedChannel *channel, RedClient *client,
> +                             RedsStream *stream, int migration, int
> num_common_caps,
> +                             uint32_t *common_caps, int num_caps, uint32_t
> *caps);
> +
> +static void
> +spice_vmc_state_constructed(GObject *object)
> +{
> +    SpiceVmcState *self = SPICE_VMC_STATE(object);
> +    ClientCbs client_cbs = { NULL, };
> +    RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
> +
> +    G_OBJECT_CLASS(spice_vmc_state_parent_class)->constructed(object);
> +
> +    client_cbs.connect = spicevmc_connect;
> +    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL);
> +
> +    red_channel_init_outgoing_messages_window(RED_CHANNEL(self));
> +
> +    self->priv->chardev =
> red_char_device_spicevmc_new(self->priv->chardev_sin,
> +                                                          reds, self);
> +
> +    reds_register_channel(reds, RED_CHANNEL(self));
> +}
> +
> +static void
> +spice_vmc_state_init(SpiceVmcState *self)
> +{
> +    self->priv = VMC_STATE_PRIVATE(self);
> +}
> +
> +SpiceVmcState *spice_vmc_state_new(RedsState *reds, uint8_t channel_type,
> SpiceCharDeviceInstance *sin)
> +{
> +    static uint8_t id[256] = { 0, };
> +    return g_object_new(SPICE_TYPE_VMC_STATE,
> +                        "spice-server", reds,
> +                        "core-interface", reds_get_core_interface(reds),
> +                        "channel-type", channel_type,
> +                        "id", id[channel_type]++,
> +                        "handle-acks", FALSE,
> +                        "migration-flags", (SPICE_MIGRATE_NEED_FLUSH |
> SPICE_MIGRATE_NEED_DATA_TRANSFER),
> +                        "device-instance", sin,
> +                        NULL);
> +}
> +
>  typedef struct RedPortInitPipeItem {
>      RedPipeItem base;
>      char* name;
> @@ -117,17 +234,17 @@ static RedPipeItem
> *spicevmc_chardev_read_msg_from_dev(SpiceCharDeviceInstance *
>  
>      sif = spice_char_device_get_interface(sin);
>  
> -    if (!state->rcc) {
> +    if (!state->priv->rcc) {
>          return NULL;
>      }
>  
> -    if (!state->pipe_item) {
> +    if (!state->priv->pipe_item) {
>          msg_item = spice_new0(RedVmcPipeItem, 1);
> -        red_pipe_item_init(&msg_item->base,
> RED_PIPE_ITEM_TYPE_SPICEVMC_DATA);
> +        red_pipe_item_init_full(&msg_item->base,
> RED_PIPE_ITEM_TYPE_SPICEVMC_DATA, free);
>      } else {
> -        spice_assert(state->pipe_item->buf_used == 0);
> -        msg_item = state->pipe_item;
> -        state->pipe_item = NULL;
> +        spice_assert(state->priv->pipe_item->buf_used == 0);
> +        msg_item = state->priv->pipe_item;
> +        state->priv->pipe_item = NULL;
>      }
>  
>      n = sif->read(sin, msg_item->buf,
> @@ -137,7 +254,7 @@ static RedPipeItem
> *spicevmc_chardev_read_msg_from_dev(SpiceCharDeviceInstance *
>          msg_item->buf_used = n;
>          return (RedPipeItem *)msg_item;
>      } else {
> -        state->pipe_item = msg_item;
> +        state->priv->pipe_item = msg_item;
>          return NULL;
>      }
>  }
> @@ -149,21 +266,21 @@ static void
> spicevmc_chardev_send_msg_to_client(RedPipeItem *msg,
>      SpiceVmcState *state = opaque;
>      RedVmcPipeItem *vmc_msg = (RedVmcPipeItem *)msg;
>  
> -    spice_assert(red_channel_client_get_client(state->rcc) == client);
> +    spice_assert(red_channel_client_get_client(state->priv->rcc) == client);
>      red_pipe_item_ref(vmc_msg);
> -    red_channel_client_pipe_add_push(state->rcc, (RedPipeItem *)vmc_msg);
> +    red_channel_client_pipe_add_push(state->priv->rcc, (RedPipeItem
> *)vmc_msg);
>  }
>  
>  static void spicevmc_port_send_init(RedChannelClient *rcc)
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> -    SpiceVmcState *state = SPICE_CONTAINEROF(channel, SpiceVmcState,
> channel);
> -    SpiceCharDeviceInstance *sin = state->chardev_sin;
> +    SpiceVmcState *state = SPICE_VMC_STATE(channel);
> +    SpiceCharDeviceInstance *sin = state->priv->chardev_sin;
>      RedPortInitPipeItem *item = spice_malloc(sizeof(RedPortInitPipeItem));
>  
>      red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_PORT_INIT);
>      item->name = strdup(sin->portname);
> -    item->opened = state->port_opened;
> +    item->opened = state->priv->port_opened;
>      red_channel_client_pipe_add_push(rcc, &item->base);
>  }
>  
> @@ -188,10 +305,10 @@ static void spicevmc_char_dev_remove_client(RedClient
> *client, void *opaque)
>      SpiceVmcState *state = opaque;
>  
>      spice_printerr("vmc state %p, client %p", state, client);
> -    spice_assert(state->rcc &&
> -                 red_channel_client_get_client(state->rcc) == client);
> +    spice_assert(state->priv->rcc &&
> +                 red_channel_client_get_client(state->priv->rcc) == client);
>  
> -    red_channel_client_shutdown(state->rcc);
> +    red_channel_client_shutdown(state->priv->rcc);
>  }
>  
>  static int spicevmc_red_channel_client_config_socket(RedChannelClient *rcc)
> @@ -199,8 +316,10 @@ static int
> spicevmc_red_channel_client_config_socket(RedChannelClient *rcc)
>      int delay_val = 1;
>      RedsStream *stream = red_channel_client_get_stream(rcc);
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> +    uint32_t type;
>  
> -    if (channel->type == SPICE_CHANNEL_USBREDIR) {
> +    g_object_get(channel, "channel-type", &type, NULL);
> +    if (type == SPICE_CHANNEL_USBREDIR) {
>          if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY,
>                  &delay_val, sizeof(delay_val)) != 0) {
>              if (errno != ENOTSUP && errno != ENOPROTOOPT) {
> @@ -224,19 +343,19 @@ static void
> spicevmc_red_channel_client_on_disconnect(RedChannelClient *rcc)
>          return;
>      }
>  
> -    state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel);
> +    state = SPICE_VMC_STATE(channel);
>  
> -    if (state->recv_from_client_buf) { /* partial message which wasn't
> pushed to device */
> -        red_char_device_write_buffer_release(state->chardev,
> state->recv_from_client_buf);
> -        state->recv_from_client_buf = NULL;
> +    if (state->priv->recv_from_client_buf) { /* partial message which wasn't
> pushed to device */
> +        red_char_device_write_buffer_release(state->priv->chardev,
> state->priv->recv_from_client_buf);
> +        state->priv->recv_from_client_buf = NULL;
>      }
>  
> -    if (state->chardev) {
> -        if (red_char_device_client_exists(state->chardev, client)) {
> -            red_char_device_client_remove(state->chardev, client);
> +    if (state->priv->chardev) {
> +        if (red_char_device_client_exists(state->priv->chardev, client)) {
> +            red_char_device_client_remove(state->priv->chardev, client);
>          } else {
>              spice_printerr("client %p have already been removed from char
>              dev %p",
> -                           client, state->chardev);
> +                           client, state->priv->chardev);
>          }
>      }
>  
> @@ -245,17 +364,17 @@ static void
> spicevmc_red_channel_client_on_disconnect(RedChannelClient *rcc)
>      if (!red_channel_client_is_destroying(rcc))
>          red_channel_client_destroy(rcc);
>  
> -    state->rcc = NULL;
> -    sif = spice_char_device_get_interface(state->chardev_sin);
> +    state->priv->rcc = NULL;
> +    sif = spice_char_device_get_interface(state->priv->chardev_sin);
>      if (sif->state) {
> -        sif->state(state->chardev_sin, 0);
> +        sif->state(state->priv->chardev_sin, 0);
>      }
>  }
>  
>  static SpiceVmcState *spicevmc_red_channel_client_get_state(RedChannelClient
>  *rcc)
>  {
>      RedChannel *channel = red_channel_client_get_channel(rcc);
> -    return SPICE_CONTAINEROF(channel, SpiceVmcState, channel);
> +    return SPICE_VMC_STATE(channel);
>  }
>  
>  static int
>  spicevmc_channel_client_handle_migrate_flush_mark(RedChannelClient *rcc)
> @@ -283,7 +402,7 @@ static int
> spicevmc_channel_client_handle_migrate_data(RedChannelClient *rcc,
>          spice_error("bad header");
>          return FALSE;
>      }
> -    return red_char_device_restore(state->chardev, &mig_data->base);
> +    return red_char_device_restore(state->priv->chardev, &mig_data->base);
>  }
>  
>  static int spicevmc_red_channel_client_handle_message(RedChannelClient *rcc,
> @@ -295,14 +414,14 @@ static int
> spicevmc_red_channel_client_handle_message(RedChannelClient *rcc,
>      SpiceCharDeviceInterface *sif;
>  
>      state = spicevmc_red_channel_client_get_state(rcc);
> -    sif = spice_char_device_get_interface(state->chardev_sin);
> +    sif = spice_char_device_get_interface(state->priv->chardev_sin);
>  
>      switch (type) {
>      case SPICE_MSGC_SPICEVMC_DATA:
> -        spice_assert(state->recv_from_client_buf->buf == msg);
> -        state->recv_from_client_buf->buf_used = size;
> -        red_char_device_write_buffer_add(state->chardev,
> state->recv_from_client_buf);
> -        state->recv_from_client_buf = NULL;
> +        spice_assert(state->priv->recv_from_client_buf->buf == msg);
> +        state->priv->recv_from_client_buf->buf_used = size;
> +        red_char_device_write_buffer_add(state->priv->chardev,
> state->priv->recv_from_client_buf);
> +        state->priv->recv_from_client_buf = NULL;
>          break;
>      case SPICE_MSGC_PORT_EVENT:
>          if (size != sizeof(uint8_t)) {
> @@ -310,7 +429,7 @@ static int
> spicevmc_red_channel_client_handle_message(RedChannelClient *rcc,
>              return FALSE;
>          }
>          if (sif->base.minor_version >= 2 && sif->event != NULL)
> -            sif->event(state->chardev_sin, *msg);
> +            sif->event(state->priv->chardev_sin, *msg);
>          break;
>      default:
>          return red_channel_client_handle_message(rcc, size, type, msg);
> @@ -327,20 +446,20 @@ static uint8_t
> *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>      RedClient *client = red_channel_client_get_client(rcc);
>  
> -    state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel);
> +    state = SPICE_VMC_STATE(channel);
>  
>      switch (type) {
>      case SPICE_MSGC_SPICEVMC_DATA:
> -        assert(!state->recv_from_client_buf);
> +        assert(!state->priv->recv_from_client_buf);
>  
> -        state->recv_from_client_buf =
> red_char_device_write_buffer_get(state->chardev,
> -
> client,
> -
> size);
> -        if (!state->recv_from_client_buf) {
> +        state->priv->recv_from_client_buf =
> red_char_device_write_buffer_get(state->priv->chardev,
> +
> client,
> +
> size);
> +        if (!state->priv->recv_from_client_buf) {
>              spice_error("failed to allocate write buffer");
>              return NULL;
>          }
> -        return state->recv_from_client_buf->buf;
> +        return state->priv->recv_from_client_buf->buf;
>  
>      default:
>          return spice_malloc(size);
> @@ -356,13 +475,14 @@ static void
> spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc,
>      SpiceVmcState *state;
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>  
> -    state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel);
> +    state = SPICE_VMC_STATE(channel);
>  
>      switch (type) {
>      case SPICE_MSGC_SPICEVMC_DATA:
> -        if (state->recv_from_client_buf) { /* buffer wasn't pushed to device
> */
> -            red_char_device_write_buffer_release(state->chardev,
> state->recv_from_client_buf);
> -            state->recv_from_client_buf = NULL;
> +        if (state->priv->recv_from_client_buf) { /* buffer wasn't pushed to
> device */
> +            red_char_device_write_buffer_release(state->priv->chardev,
> +
> state->priv->recv_from_client_buf);
> +            state->priv->recv_from_client_buf = NULL;
>          }
>          break;
>      default:
> @@ -393,12 +513,12 @@ static void
> spicevmc_red_channel_send_migrate_data(RedChannelClient *rcc,
>      SpiceVmcState *state;
>      RedChannel *channel = red_channel_client_get_channel(rcc);
>  
> -    state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel);
> +    state = SPICE_VMC_STATE(channel);
>      red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
>      spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SPICEVMC_MAGIC);
>      spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SPICEVMC_VERSION);
>  
> -    red_char_device_migrate_data_marshall(state->chardev, m);
> +    red_char_device_migrate_data_marshall(state->priv->chardev, m);
>  }
>  
>  static void spicevmc_red_channel_send_port_init(RedChannelClient *rcc,
> @@ -464,6 +584,40 @@ static void
> spicevmc_red_channel_release_pipe_item(RedChannelClient *rcc,
>      }
>  }
>  
> +static void
> +spice_vmc_state_class_init(SpiceVmcStateClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
> +
> +    g_type_class_add_private(klass, sizeof(SpiceVmcStatePrivate));
> +
> +    object_class->get_property = spice_vmc_state_get_property;
> +    object_class->set_property = spice_vmc_state_set_property;
> +    object_class->constructed = spice_vmc_state_constructed;
> +
> +    channel_class->handle_message =
> spicevmc_red_channel_client_handle_message;
> +
> +    channel_class->config_socket =
> spicevmc_red_channel_client_config_socket;
> +    channel_class->on_disconnect =
> spicevmc_red_channel_client_on_disconnect;
> +    channel_class->send_item = spicevmc_red_channel_send_item;
> +    channel_class->hold_item = spicevmc_red_channel_hold_pipe_item;
> +    channel_class->release_item = spicevmc_red_channel_release_pipe_item;
> +    channel_class->alloc_recv_buf = spicevmc_red_channel_alloc_msg_rcv_buf;
> +    channel_class->release_recv_buf =
> spicevmc_red_channel_release_msg_rcv_buf;
> +    channel_class->handle_migrate_flush_mark =
> spicevmc_channel_client_handle_migrate_flush_mark;
> +    channel_class->handle_migrate_data =
> spicevmc_channel_client_handle_migrate_data;
> +
> +    g_object_class_install_property(object_class,
> +                                    PROP_DEVICE_INSTANCE,
> +                                    g_param_spec_pointer("device-instance",
> +                                                         "device instance",
> +                                                         "Device instance
> for this channel",
> +                                                         G_PARAM_READWRITE |
> +
> G_PARAM_CONSTRUCT_ONLY
> |
> +
> G_PARAM_STATIC_STRINGS));
> +}
> +
>  static void spicevmc_connect(RedChannel *channel, RedClient *client,
>      RedsStream *stream, int migration, int num_common_caps,
>      uint32_t *common_caps, int num_caps, uint32_t *caps)
> @@ -472,13 +626,15 @@ static void spicevmc_connect(RedChannel *channel,
> RedClient *client,
>      SpiceVmcState *state;
>      SpiceCharDeviceInstance *sin;
>      SpiceCharDeviceInterface *sif;
> +    uint32_t type, id;
>  
> -    state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel);
> -    sin = state->chardev_sin;
> +    state = SPICE_VMC_STATE(channel);
> +    sin = state->priv->chardev_sin;
> +    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
>  
> -    if (state->rcc) {
> +    if (state->priv->rcc) {
>          spice_printerr("channel client %d:%d (%p) already connected,
>          refusing second connection",
> -                       channel->type, channel->id, state->rcc);
> +                       type, id, state->priv->rcc);
>          // TODO: notify client in advance about the in use channel using
>          // SPICE_MSG_MAIN_CHANNEL_IN_USE (for example)
>          reds_stream_free(stream);
> @@ -490,21 +646,21 @@ static void spicevmc_connect(RedChannel *channel,
> RedClient *client,
>      if (!rcc) {
>          return;
>      }
> -    state->rcc = rcc;
> +    state->priv->rcc = rcc;
>      red_channel_client_ack_zero_messages_window(rcc);
>  
>      if (strcmp(sin->subtype, "port") == 0) {
>          spicevmc_port_send_init(rcc);
>      }
>  
> -    if (!red_char_device_client_add(state->chardev, client, FALSE, 0, ~0,
> ~0,
> +    if (!red_char_device_client_add(state->priv->chardev, client, FALSE, 0,
> ~0, ~0,
>                                      red_channel_client_is_waiting_for_migrate_data(rcc)))
>                                      {
>          spice_warning("failed to add client to spicevmc");
>          red_channel_client_disconnect(rcc);
>          return;
>      }
>  
> -    sif = spice_char_device_get_interface(state->chardev_sin);
> +    sif = spice_char_device_get_interface(state->priv->chardev_sin);
>      if (sif->state) {
>          sif->state(sin, 1);
>      }
> @@ -514,37 +670,9 @@ RedCharDevice *spicevmc_device_connect(RedsState *reds,
>                                         SpiceCharDeviceInstance *sin,
>                                         uint8_t channel_type)
>  {
> -    static uint8_t id[256] = { 0, };
> -    SpiceVmcState *state;
> -    ChannelCbs channel_cbs = { NULL, };
> -    ClientCbs client_cbs = { NULL, };
> -
> -    channel_cbs.config_socket = spicevmc_red_channel_client_config_socket;
> -    channel_cbs.on_disconnect = spicevmc_red_channel_client_on_disconnect;
> -    channel_cbs.send_item = spicevmc_red_channel_send_item;
> -    channel_cbs.hold_item = spicevmc_red_channel_hold_pipe_item;
> -    channel_cbs.release_item = spicevmc_red_channel_release_pipe_item;
> -    channel_cbs.alloc_recv_buf = spicevmc_red_channel_alloc_msg_rcv_buf;
> -    channel_cbs.release_recv_buf = spicevmc_red_channel_release_msg_rcv_buf;
> -    channel_cbs.handle_migrate_flush_mark =
> spicevmc_channel_client_handle_migrate_flush_mark;
> -    channel_cbs.handle_migrate_data =
> spicevmc_channel_client_handle_migrate_data;
> -
> -    state = (SpiceVmcState*)red_channel_create(sizeof(SpiceVmcState), reds,
> -                                   reds_get_core_interface(reds),
> channel_type, id[channel_type]++,
> -                                   FALSE /* handle_acks */,
> -
> spicevmc_red_channel_client_handle_message,
> -                                   &channel_cbs,
> -                                   SPICE_MIGRATE_NEED_FLUSH |
> SPICE_MIGRATE_NEED_DATA_TRANSFER);
> -    red_channel_init_outgoing_messages_window(&state->channel);
> -
> -    client_cbs.connect = spicevmc_connect;
> -    red_channel_register_client_cbs(&state->channel, &client_cbs, NULL);
> -
> -    state->chardev = red_char_device_spicevmc_new(sin, reds, state);
> -    state->chardev_sin = sin;
> +    SpiceVmcState *state = spice_vmc_state_new(reds, channel_type, sin);
>  
> -    reds_register_channel(reds, &state->channel);
> -    return state->chardev;
> +    return state->priv->chardev;
>  }
>  
>  /* Must be called from RedClient handling thread. */
> @@ -555,17 +683,18 @@ void spicevmc_device_disconnect(RedsState *reds,
> SpiceCharDeviceInstance *sin)
>      /* FIXME */
>      state = (SpiceVmcState
>      *)red_char_device_opaque_get((RedCharDevice*)sin->st);
>  
> -    if (state->recv_from_client_buf) {
> -        red_char_device_write_buffer_release(state->chardev,
> state->recv_from_client_buf);
> +    if (state->priv->recv_from_client_buf) {
> +        red_char_device_write_buffer_release(state->priv->chardev,
> +
> state->priv->recv_from_client_buf);
>      }
>      /* FIXME */
>      red_char_device_destroy((RedCharDevice*)sin->st);
> -    state->chardev = NULL;
> +    state->priv->chardev = NULL;
>      sin->st = NULL;
>  
> -    reds_unregister_channel(reds, &state->channel);
> -    free(state->pipe_item);
> -    red_channel_destroy(&state->channel);
> +    reds_unregister_channel(reds, RED_CHANNEL(state));
> +    free(state->priv->pipe_item);
> +    red_channel_destroy(RED_CHANNEL(state));
>  }
>  
>  SPICE_GNUC_VISIBLE void spice_server_port_event(SpiceCharDeviceInstance
>  *sin, uint8_t event)
> @@ -580,16 +709,16 @@ SPICE_GNUC_VISIBLE void
> spice_server_port_event(SpiceCharDeviceInstance *sin, ui
>      /* FIXME */
>      state = (SpiceVmcState
>      *)red_char_device_opaque_get((RedCharDevice*)sin->st);
>      if (event == SPICE_PORT_EVENT_OPENED) {
> -        state->port_opened = TRUE;
> +        state->priv->port_opened = TRUE;
>      } else if (event == SPICE_PORT_EVENT_CLOSED) {
> -        state->port_opened = FALSE;
> +        state->priv->port_opened = FALSE;
>      }
>  
> -    if (state->rcc == NULL) {
> +    if (state->priv->rcc == NULL) {
>          return;
>      }
>  
> -    spicevmc_port_send_event(state->rcc, event);
> +    spicevmc_port_send_event(state->priv->rcc, event);
>  }
>  
>  static void
> diff --git a/server/stream.c b/server/stream.c
> index 47a6924..004e886 100644
> --- a/server/stream.c
> +++ b/server/stream.c
> @@ -20,13 +20,14 @@
>  
>  #include "stream.h"
>  #include "display-channel.h"
> +#include "display-channel-private.h"
>  #include "main-channel-client.h"
>  
>  #define FPS_TEST_INTERVAL 1
>  #define FOREACH_STREAMS(display, item)                  \
> -    for (item = ring_get_head(&(display)->streams);     \
> +    for (item = ring_get_head(&(display)->priv->streams);     \
>           item != NULL;                                  \
> -         item = ring_next(&(display)->streams, item))
> +         item = ring_next(&(display)->priv->streams, item))
>  
>  void stream_agent_stats_print(StreamAgent *agent)
>  {
> @@ -72,11 +73,11 @@ void stream_stop(DisplayChannel *display, Stream *stream)
>      spice_return_if_fail(ring_item_is_linked(&stream->link));
>      spice_return_if_fail(!stream->current);
>  
> -    spice_debug("stream %d", get_stream_id(display, stream));
> +    spice_debug("stream %d", display_channel_get_stream_id(display,
> stream));
>      FOREACH_DCC(display, link, next, dcc) {
>          StreamAgent *stream_agent;
>  
> -        stream_agent = dcc_get_stream_agent(dcc, get_stream_id(display,
> stream));
> +        stream_agent = dcc_get_stream_agent(dcc,
> display_channel_get_stream_id(display, stream));
>          region_clear(&stream_agent->vis_region);
>          region_clear(&stream_agent->clip);
>          spice_assert(!red_pipe_item_is_linked(&stream_agent->destroy_item));
> @@ -94,25 +95,25 @@ void stream_stop(DisplayChannel *display, Stream *stream)
>          red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc),
>          &stream_agent->destroy_item);
>          stream_agent_stats_print(stream_agent);
>      }
> -    display->streams_size_total -= stream->width * stream->height;
> +    display->priv->streams_size_total -= stream->width * stream->height;
>      ring_remove(&stream->link);
>      stream_unref(display, stream);
>  }
>  
>  static void stream_free(DisplayChannel *display, Stream *stream)
>  {
> -    stream->next = display->free_streams;
> -    display->free_streams = stream;
> +    stream->next = display->priv->free_streams;
> +    display->priv->free_streams = stream;
>  }
>  
>  void display_channel_init_streams(DisplayChannel *display)
>  {
>      int i;
>  
> -    ring_init(&display->streams);
> -    display->free_streams = NULL;
> +    ring_init(&display->priv->streams);
> +    display->priv->free_streams = NULL;
>      for (i = 0; i < NUM_STREAMS; i++) {
> -        Stream *stream = &display->streams_buf[i];
> +        Stream *stream = &display->priv->streams_buf[i];
>          ring_item_init(&stream->link);
>          stream_free(display, stream);
>      }
> @@ -126,7 +127,7 @@ void stream_unref(DisplayChannel *display, Stream
> *stream)
>      spice_warn_if_fail(!ring_item_is_linked(&stream->link));
>  
>      stream_free(display, stream);
> -    display->stream_count--;
> +    display->priv->stream_count--;
>  }
>  
>  void stream_agent_unref(DisplayChannel *display, StreamAgent *agent)
> @@ -169,7 +170,7 @@ static void update_copy_graduality(DisplayChannel
> *display, Drawable *drawable)
>      SpiceBitmap *bitmap;
>      spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY);
>  
> -    if (display->stream_video != SPICE_STREAM_VIDEO_FILTER) {
> +    if (display_channel_get_stream_video(display) !=
> SPICE_STREAM_VIDEO_FILTER) {
>          drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID;
>          return;
>      }
> @@ -284,7 +285,7 @@ static void attach_stream(DisplayChannel *display,
> Drawable *drawable, Stream *s
>          StreamAgent *agent;
>          QRegion clip_in_draw_dest;
>  
> -        agent = dcc_get_stream_agent(dcc, get_stream_id(display, stream));
> +        agent = dcc_get_stream_agent(dcc,
> display_channel_get_stream_id(display, stream));
>          region_or(&agent->vis_region, &drawable->tree_item.base.rgn);
>  
>          region_init(&clip_in_draw_dest);
> @@ -335,7 +336,7 @@ static void before_reattach_stream(DisplayChannel
> *display,
>          return;
>      }
>  
> -    index = get_stream_id(display, stream);
> +    index = display_channel_get_stream_id(display, stream);
>      DRAWABLE_FOREACH_DPI_SAFE(stream->current, ring_item, next, dpi) {
>          dcc = dpi->dcc;
>          agent = dcc_get_stream_agent(dcc, index);
> @@ -392,11 +393,11 @@ static void before_reattach_stream(DisplayChannel
> *display,
>  static Stream *display_channel_stream_try_new(DisplayChannel *display)
>  {
>      Stream *stream;
> -    if (!display->free_streams) {
> +    if (!display->priv->free_streams) {
>          return NULL;
>      }
> -    stream = display->free_streams;
> -    display->free_streams = display->free_streams->next;
> +    stream = display->priv->free_streams;
> +    display->priv->free_streams = display->priv->free_streams->next;
>      return stream;
>  }
>  
> @@ -416,7 +417,7 @@ static void display_channel_create_stream(DisplayChannel
> *display, Drawable *dra
>      spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY);
>      src_rect = &drawable->red_drawable->u.copy.src_area;
>  
> -    ring_add(&display->streams, &stream->link);
> +    ring_add(&display->priv->streams, &stream->link);
>      stream->current = drawable;
>      stream->last_time = drawable->creation_time;
>      stream->width = src_rect->right - src_rect->left;
> @@ -438,13 +439,13 @@ static void
> display_channel_create_stream(DisplayChannel *display, Drawable *dra
>      }
>      stream->num_input_frames = 0;
>      stream->input_fps_start_time = drawable->creation_time;
> -    display->streams_size_total += stream->width * stream->height;
> -    display->stream_count++;
> +    display->priv->streams_size_total += stream->width * stream->height;
> +    display->priv->stream_count++;
>      FOREACH_DCC(display, link, next, dcc) {
>          dcc_create_stream(dcc, stream);
>      }
>      spice_debug("stream %d %dx%d (%d, %d) (%d, %d) %u fps",
> -                (int)(stream - display->streams_buf), stream->width,
> +                (int)(stream - display->priv->streams_buf), stream->width,
>                  stream->height, stream->dest_area.left,
>                  stream->dest_area.top,
>                  stream->dest_area.right, stream->dest_area.bottom,
>                  stream->input_fps);
> @@ -520,7 +521,7 @@ void stream_trace_update(DisplayChannel *display,
> Drawable *drawable)
>          }
>      }
>  
> -    trace = display->items_trace;
> +    trace = display->priv->items_trace;
>      trace_end = trace + NUM_TRACE_ITEMS;
>      for (; trace < trace_end; trace++) {
>          if (is_next_stream_frame(display, drawable, trace->width,
>          trace->height,
> @@ -591,7 +592,7 @@ static void
> dcc_update_streams_max_latency(DisplayChannelClient *dcc, StreamAgen
>      }
>  
>      dcc_set_max_stream_latency(dcc, 0);
> -    if (DCC_TO_DC(dcc)->stream_count == 1) {
> +    if (DCC_TO_DC(dcc)->priv->stream_count == 1) {
>          return;
>      }
>      for (i = 0; i < NUM_STREAMS; i++) {
> @@ -650,7 +651,7 @@ static uint64_t get_initial_bit_rate(DisplayChannelClient
> *dcc, Stream *stream)
>      /* dividing the available bandwidth among the active streams, and saving
>       * (1-RED_STREAM_CHANNEL_CAPACITY) of it for other messages */
>      return (RED_STREAM_CHANNEL_CAPACITY * bit_rate *
> -            stream->width * stream->height) /
> DCC_TO_DC(dcc)->streams_size_total;
> +            stream->width * stream->height) /
> DCC_TO_DC(dcc)->priv->streams_size_total;
>  }
>  
>  static uint32_t get_roundtrip_ms(void *opaque)
> @@ -703,7 +704,7 @@ static void update_client_playback_delay(void *opaque,
> uint32_t delay_ms)
>  
>  void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream)
>  {
> -    StreamAgent *agent = dcc_get_stream_agent(dcc,
> get_stream_id(DCC_TO_DC(dcc), stream));
> +    StreamAgent *agent = dcc_get_stream_agent(dcc,
> display_channel_get_stream_id(DCC_TO_DC(dcc), stream));
>  
>      spice_return_if_fail(region_is_empty(&agent->vis_region));
>  
> @@ -741,7 +742,7 @@ void dcc_create_stream(DisplayChannelClient *dcc, Stream
> *stream)
>          agent->report_id = rand();
>          red_pipe_item_init(&report_pipe_item->pipe_item,
>                             RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT);
> -        report_pipe_item->stream_id = get_stream_id(DCC_TO_DC(dcc), stream);
> +        report_pipe_item->stream_id =
> display_channel_get_stream_id(DCC_TO_DC(dcc), stream);
>          red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc),
>          &report_pipe_item->pipe_item);
>      }
>  #ifdef STREAM_STATS
> @@ -782,7 +783,7 @@ static void
> dcc_detach_stream_gracefully(DisplayChannelClient *dcc,
>                                           Drawable *update_area_limit)
>  {
>      DisplayChannel *display = DCC_TO_DC(dcc);
> -    int stream_id = get_stream_id(display, stream);
> +    int stream_id = display_channel_get_stream_id(display, stream);
>      StreamAgent *agent = dcc_get_stream_agent(dcc, stream_id);
>  
>      /* stopping the client from playing older frames at once*/
> @@ -868,7 +869,7 @@ static void detach_stream_gracefully(DisplayChannel
> *display, Stream *stream,
>   */
>  void stream_detach_behind(DisplayChannel *display, QRegion *region, Drawable
>  *drawable)
>  {
> -    Ring *ring = &display->streams;
> +    Ring *ring = &display->priv->streams;
>      RingItem *item = ring_get_head(ring);
>      GList *link, *next;
>      DisplayChannelClient *dcc;
> @@ -880,12 +881,12 @@ void stream_detach_behind(DisplayChannel *display,
> QRegion *region, Drawable *dr
>          item = ring_next(ring, item);
>  
>          FOREACH_DCC(display, link, next, dcc) {
> -            StreamAgent *agent = dcc_get_stream_agent(dcc,
> get_stream_id(display, stream));
> +            StreamAgent *agent = dcc_get_stream_agent(dcc,
> display_channel_get_stream_id(display, stream));
>  
>              if (region_intersects(&agent->vis_region, region)) {
>                  dcc_detach_stream_gracefully(dcc, stream, drawable);
>                  detach = 1;
> -                spice_debug("stream %d", get_stream_id(display, stream));
> +                spice_debug("stream %d",
> display_channel_get_stream_id(display, stream));
>              }
>          }
>          if (detach && stream->current) {
> @@ -904,7 +905,7 @@ void stream_detach_and_stop(DisplayChannel *display)
>      RingItem *stream_item;
>  
>      spice_debug(NULL);
> -    while ((stream_item = ring_get_head(&display->streams))) {
> +    while ((stream_item = ring_get_head(&display->priv->streams))) {
>          Stream *stream = SPICE_CONTAINEROF(stream_item, Stream, link);
>  
>          detach_stream_gracefully(display, stream, NULL);
> @@ -914,7 +915,7 @@ void stream_detach_and_stop(DisplayChannel *display)
>  
>  void stream_timeout(DisplayChannel *display)
>  {
> -    Ring *ring = &display->streams;
> +    Ring *ring = &display->priv->streams;
>      RingItem *item;
>  
>      red_time_t now = spice_get_monotonic_time_ns();
> @@ -937,7 +938,7 @@ void stream_trace_add_drawable(DisplayChannel *display,
> Drawable *item)
>          return;
>      }
>  
> -    trace = &display->items_trace[display->next_item_trace++ &
> ITEMS_TRACE_MASK];
> +    trace = &display->priv->items_trace[display->priv->next_item_trace++ &
> ITEMS_TRACE_MASK];
>      trace->time = item->creation_time;
>      trace->first_frame_time = item->first_frame_time;
>      trace->frames_count = item->frames_count;
> diff --git a/server/stream.h b/server/stream.h
> index 29071f5..f337059 100644
> --- a/server/stream.h
> +++ b/server/stream.h
> @@ -41,9 +41,6 @@
>  #define RED_STREAM_DEFAULT_LOW_START_BIT_RATE (2.5 * 1024 * 1024) // 2.5Mbps
>  #define MAX_FPS 30
>  
> -/* move back to display_channel once struct private */
> -typedef struct DisplayChannel DisplayChannel;
> -
>  typedef struct Stream Stream;
>  
>  typedef struct RedStreamActivateReportItem {



More information about the Spice-devel mailing list