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

Jonathon Jongsma jjongsma at redhat.com
Tue May 3 20:00:30 UTC 2016


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 {
-- 
2.4.11



More information about the Spice-devel mailing list