[Spice-commits] 62 commits - common/ring.h common/spice_common.h server/inputs_channel.c server/main_channel.c server/main_channel.h server/red_channel.c server/red_channel.h server/red_client_cache.h server/red_client_shared_cache.h server/red_dispatcher.c server/red_dispatcher.h server/red_parse_qxl.h server/reds.c server/reds.h server/red_tunnel_worker.c server/red_worker.c server/red_worker.h server/smartcard.c server/snd_worker.c server/spice.h server/spice-server.syms server/tests server/usbredir.c spice.proto tests/migrate.py TODO.multiclient

Alon Levy alon at kemper.freedesktop.org
Tue Aug 23 09:21:09 PDT 2011


 TODO.multiclient                 |   16 
 common/ring.h                    |    8 
 common/spice_common.h            |   10 
 server/inputs_channel.c          |  193 +-
 server/main_channel.c            |  651 ++++---
 server/main_channel.h            |   50 
 server/red_channel.c             | 1142 +++++++++----
 server/red_channel.h             |  312 ++-
 server/red_client_cache.h        |   58 
 server/red_client_shared_cache.h |   48 
 server/red_dispatcher.c          |  134 +
 server/red_dispatcher.h          |    3 
 server/red_parse_qxl.h           |    1 
 server/red_tunnel_worker.c       |  549 +++---
 server/red_worker.c              | 3347 ++++++++++++++++++++++++---------------
 server/red_worker.h              |    5 
 server/reds.c                    |  318 ++-
 server/reds.h                    |   38 
 server/smartcard.c               |  242 +-
 server/snd_worker.c              |  197 +-
 server/spice-server.syms         |    1 
 server/spice.h                   |    3 
 server/tests/Makefile.am         |   11 
 server/tests/basic_event_loop.c  |  151 -
 server/tests/test_display_base.c |    6 
 server/tests/test_multiple.py    |  121 +
 server/tests/test_playback.c     |   41 
 server/tests/test_util.h         |    3 
 server/usbredir.c                |  140 -
 spice.proto                      |    5 
 tests/migrate.py                 |   16 
 31 files changed, 4894 insertions(+), 2926 deletions(-)

New commits:
commit 51ac99dbf4b270ab70a78b361cc34ff7077012c8
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Aug 23 19:18:23 2011 +0300

    server: add tester and todo for multiple client support

diff --git a/TODO.multiclient b/TODO.multiclient
new file mode 100644
index 0000000..92d8a7d
--- /dev/null
+++ b/TODO.multiclient
@@ -0,0 +1,16 @@
+Real secondary clients:
+ currently the "main" is the client that connects when there are no clients. Main
+ should be one of the clients, determined by any means - seniority is fine.
+
+SPICE_MSG_MAIN_CHANNEL_IN_USE
+SPICE_MSG_MAIN_CHANNEL_AVAILABLE
+SPICE_MSG_MAIN_CHANNEL_NEW
+ - allow channels to become available or not. Useful for USB redirection
+ and later chardev remoting in the case where there chardev/usbredir channel is
+ single user only, so only one of the clients can use it.
+
+Chardev channel: allow adding a chardev channel without any server changes:
+ qemu -spicevmc name=generic,channelname=bla,id=bla -mon chardev=bla 
+ Note: client will still need to decide what to do with it of course. So the channelname
+ will have to have a predefined meaning.
+
diff --git a/server/tests/test_multiple.py b/server/tests/test_multiple.py
new file mode 100755
index 0000000..ce03c94
--- /dev/null
+++ b/server/tests/test_multiple.py
@@ -0,0 +1,121 @@
+#!/usr/bin/python
+
+"""
+Example usage:
+./test_multiple.py test_display_no_ssl --log test.log
+
+Resulting test.log file (single test, stopped with Ctrl-C with 15 clients):
+
+0 162212
+1 154156
+2 154424
+3 154952
+4 155352
+5 155616
+6 156280
+7 222084
+8 222612
+9 230820
+10 230820
+11 230820
+12 230820
+13 296356
+14 296356
+
+"""
+
+import argparse
+import os
+import sys
+import subprocess
+import atexit
+import socket
+import time
+
+def killall(procs):
+    for p in procs:
+        print "killing %d" % p.pid
+        p.kill()
+        p.wait()
+
+def cleanup():
+    killall(clients + [test_process])
+
+def wait_for_port(port):
+    if not port:
+        return
+    # TODO: do this without actually opening the port - maybe just look at /proc/qemu_process_id/fd?
+    s = socket.socket(socket.AF_INET)
+    while True:
+        try:
+            s.connect(('localhost', port))
+            s.close()
+            break
+        except:
+            time.sleep(1)
+            pass
+
+def get_vm_size(pid):
+    """ read from /proc/<pid>/status, VmSize, in KiloBytes  """
+    return int([x for x in open('/proc/%s/status' % pid).readlines() if 'VmSize' in x][0].split()[1])
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--client', default='spicy')
+parser.add_argument('--start-count', default=1, type=int)
+parser.add_argument('--end-count', default=50, type=int)
+parser.add_argument('--log', default='-')
+parser.add_argument('--sleep', default=3, type=int)
+args, rest = parser.parse_known_args(sys.argv[1:])
+client = os.popen('which %s' % args.client).read().strip()
+if not os.path.exists(client):
+    print "supply a valid client. %s does not exist" % (args.client)
+    sys.exit(1)
+
+if not rest or len(rest) < 1 or not os.path.exists(rest[0]):
+    print "supply one argument that is the tester you wish to run"
+    sys.exit(1)
+
+prog = rest[0]
+port = {
+'test_display_no_ssl': 5912,
+'test_display_streaming': 5912,
+'test_just_sockets_no_ssl': 5912,
+'test_playback': 5701,
+}.get(prog, None)
+
+if args.log == '-':
+    log = sys.stdout
+else:
+    log = open(args.log, 'a+')
+
+log.write('#%s\n' % time.time())
+
+# kill leftovers from previous invocation
+os.system('killall lt-%s' % prog)
+
+if prog[0] != '/':
+    prog = os.path.join('.', prog)
+
+if not port:
+    print "unknown port for %r" % prog
+
+print "prog = %r" % prog
+print "client = %r" % client
+print "range = %d..%d" % (args.start_count, args.end_count)
+atexit.register(cleanup)
+os.environ['SPICE_DEBUG_ALLOW_MC'] = '1'
+test_process = subprocess.Popen([prog], executable=prog)
+wait_for_port(port)
+for count in xrange(args.start_count, args.end_count):
+    print "starting %d clients" % count
+    clients = [subprocess.Popen(args=[client, '-h', 'localhost', '-p', str(port)],
+                                executable=client) for i in xrange(count)]
+    print "sleeping %d" % (args.sleep * count)
+    time.sleep(args.sleep * count)
+    vmsize = "%d %d" % (i, get_vm_size(test_process.pid))
+    print vmsize
+    log.write(vmsize + '\n')
+    log.flush()
+    killall(clients)
+
+test_process.wait()
commit 0b169b7014d657da6a64848168db7136ecbb191f
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Aug 23 14:14:22 2011 +0300

    server/snd_worker.c: add reference counting to SndChannel
    
    Fixes a valgrind discovered possible bug in spice-server - valgrind on
    test_playback saw it, didn't see it happen with qemu.
    
    The problem is that the frames buffers returned by spice_server_playback_get_buffer
    are part of the malloc'ed SndChannel, whose lifetime is smaller then that of SndWorker.
    As a result a pointer to a previously returned spice_server_playback_get_buffer could
    remain and be used after SndChannel has been freed because the client disconnected.

diff --git a/server/snd_worker.c b/server/snd_worker.c
index 0894785..98f3cd1 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -84,6 +84,7 @@ struct SndChannel {
     RedsStream *stream;
     SndWorker *worker;
     spice_parse_channel_func_t parser;
+    int refs;
 
     RedChannelClient *channel_client;
 
@@ -207,6 +208,23 @@ static int check_cap(uint32_t *caps, int num_caps, uint32_t cap)
     return caps[i] & cap;
 }
 
+static SndChannel *snd_channel_get(SndChannel *channel)
+{
+    channel->refs++;
+    return channel;
+}
+
+static SndChannel *snd_channel_put(SndChannel *channel)
+{
+    if (!--channel->refs) {
+        channel->worker->connection = NULL;
+        free(channel);
+        red_printf("sound channel freed\n");
+        return NULL;
+    }
+    return channel;
+}
+
 static void snd_disconnect_channel(SndChannel *channel)
 {
     SndWorker *worker;
@@ -217,13 +235,12 @@ static void snd_disconnect_channel(SndChannel *channel)
     channel->cleanup(channel);
     worker = channel->worker;
     red_channel_client_destroy_dummy(worker->connection->channel_client);
-    worker->connection = NULL;
     core->watch_remove(channel->stream->watch);
     channel->stream->watch = NULL;
     reds_stream_free(channel->stream);
     spice_marshaller_destroy(channel->send_data.marshaller);
     free(channel->caps);
-    free(channel);
+    snd_channel_put(channel);
 }
 
 static void snd_playback_free_frame(PlaybackChannel *playback_channel, AudioFrame *frame)
@@ -903,6 +920,7 @@ static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_i
 
     ASSERT(size >= sizeof(*channel));
     channel = spice_malloc0(size);
+    channel->refs = 1;
     channel->parser = spice_get_client_channel_parser(channel_id, NULL);
     channel->stream = stream;
     channel->worker = worker;
@@ -949,6 +967,7 @@ static void snd_disconnect_channel_client(RedChannelClient *rcc)
 
     ASSERT(worker->connection->channel_client == rcc);
     snd_disconnect_channel(worker->connection);
+    ASSERT(worker->connection == NULL);
 }
 
 static void snd_set_command(SndChannel *channel, uint32_t command)
@@ -1049,6 +1068,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *
         return;
     }
     ASSERT(playback_channel->base.active);
+    snd_channel_get(channel);
 
     *frame = playback_channel->free_frames->samples;
     playback_channel->free_frames = playback_channel->free_frames->next;
@@ -1061,8 +1081,13 @@ SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance
     PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
     AudioFrame *frame;
 
-    if (!channel)
+    if (!channel) {
         return;
+    }
+    if (!snd_channel_put(channel)) {
+        /* lost last reference, channel has been destroyed previously */
+        return;
+    }
     ASSERT(playback_channel->base.active);
 
     if (playback_channel->pending_frame) {
commit 1078dc04edc406950e5f6d91bae456411eaa4a47
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Aug 23 14:13:16 2011 +0300

    server/reds: reds_client_disconnect: remove wrong check for reds_main_channel_connected
    
    The "channel->disconnecting" parameter already protects against recursion.
    
    Removed fixed TODOs.

diff --git a/server/reds.c b/server/reds.c
index f629471..d140afd 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -606,18 +606,15 @@ static int reds_main_channel_connected(void)
 
 void reds_client_disconnect(RedClient *client)
 {
-    // TODO: rename reds_main_channel_connected, or really set main_channel to NULL on disconnect,
-    // though still, it is not a reason not to disconnect the rest of the channels
-    if (!reds_main_channel_connected()  || client->disconnecting) { 
-        /* case of recursion (main_channel_client_on_disconnect->
-         *                    reds_client_disconnect->red_client_destroy-<main_channel...
-         */
+    if (!client || client->disconnecting) {
         return;
     }
 
     red_printf("");
-    // why is "disconnecting" even needed? it is synchronic, even in the dispatcher we are now waiting for disconnection
-    // Are there recursive calls? Maybe from main_channel?
+    /* disconnecting is set to prevent recursion because of the following:
+     * main_channel_client_on_disconnect->
+     *  reds_client_disconnect->red_client_destroy->main_channel...
+     */
     client->disconnecting = TRUE;
 
     // TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how?)
@@ -629,7 +626,6 @@ void reds_client_disconnect(RedClient *client)
 
    // TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how? Maybe throw away messages
    // if we are in the middle of one from another client)
-   // We shouldn't initialize the agent when there are still clients connected
     if (reds->num_clients == 0) {
        /* Reset write filter to start with clean state on client reconnect */
         agent_msg_filter_init(&reds->agent_state.write_filter, agent_copypaste,
commit f15cac7d7d757fed2a8ce36e8800616e905b128c
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Aug 23 14:00:11 2011 +0300

    server/reds: fix reds_main_channel_connected
    
    instead of checking just for reds->main_channel check that there is at least
    one client as well.

diff --git a/server/main_channel.c b/server/main_channel.c
index e793e48..43c0f3f 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -155,6 +155,11 @@ enum NetTestStage {
     NET_TEST_STAGE_RATE,
 };
 
+int main_channel_is_connected(MainChannel *main_chan)
+{
+    return red_channel_is_connected(&main_chan->base);
+}
+
 // when disconnection occurs, let reds shutdown all channels. This will trigger the
 // real disconnection of main channel
 static void main_channel_client_on_disconnect(RedChannelClient *rcc)
diff --git a/server/main_channel.h b/server/main_channel.h
index 8047535..713bcd0 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -80,6 +80,7 @@ uint32_t main_channel_client_get_link_id(MainChannelClient *mcc);
 
 int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
 uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
+int main_channel_is_connected(MainChannel *main_chan);
 
 // 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.
diff --git a/server/reds.c b/server/reds.c
index c6528e9..f629471 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -601,7 +601,7 @@ static void reds_reset_vdp()
 
 static int reds_main_channel_connected(void)
 {
-    return !!reds->main_channel;
+    return main_channel_is_connected(reds->main_channel);
 }
 
 void reds_client_disconnect(RedClient *client)
commit 0410cccde378cb418281560d47940a3e7100a3c4
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Aug 23 13:55:38 2011 +0300

    server: add public spice_server_get_num_clients

diff --git a/server/reds.c b/server/reds.c
index 0387a5c..c6528e9 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -978,6 +978,17 @@ int reds_num_of_channels()
     return reds ? reds->num_of_channels : 0;
 }
 
+int reds_num_of_clients(void)
+{
+    return reds ? reds->num_clients : 0;
+}
+
+SPICE_GNUC_VISIBLE int spice_server_get_num_clients(SpiceServer *s)
+{
+    ASSERT(reds == s);
+    return reds_num_of_clients();
+}
+
 static int secondary_channels[] = {
     SPICE_CHANNEL_MAIN, SPICE_CHANNEL_DISPLAY, SPICE_CHANNEL_CURSOR, SPICE_CHANNEL_INPUTS};
 
diff --git a/server/reds.h b/server/reds.h
index 0bbda14..81da46d 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -125,6 +125,7 @@ void reds_fill_channels(SpiceMsgChannels *channels_info);
 void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate);
 void reds_mig_release(void);
 int reds_num_of_channels(void);
+int reds_num_of_clients(void);
 #ifdef RED_STATISTICS
 void reds_update_stat_value(uint32_t value);
 #endif
diff --git a/server/spice-server.syms b/server/spice-server.syms
index b908d85..f1374bd 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -88,5 +88,6 @@ global:
     spice_server_playback_set_volume;
     spice_server_record_set_mute;
     spice_server_record_set_volume;
+    spice_server_get_num_clients;
 } SPICE_SERVER_0.8.2;
 
diff --git a/server/spice.h b/server/spice.h
index 5f331fb..74f9fdb 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -482,4 +482,7 @@ int spice_server_migrate_info(SpiceServer *s, const char* dest,
                               const char* cert_subject);
 int spice_server_migrate_switch(SpiceServer *s);
 
+/* server status */
+int spice_server_get_num_clients(SpiceServer *s);
+
 #endif
commit 9255515c15e4c322c35395b31e056a807b389289
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Aug 22 15:36:56 2011 +0100

    server/snd_worker.c: add red_channel_client_destroy_dummy

diff --git a/server/red_channel.c b/server/red_channel.c
index 3f47f7f..87320b4 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -403,21 +403,6 @@ error:
     return NULL;
 }
 
-
-RedChannelClient *red_channel_client_create_dummy(int size,
-                                                  RedChannel *channel,
-                                                  RedClient  *client)
-{
-    RedChannelClient *rcc;
-
-    ASSERT(size >= sizeof(RedChannelClient));
-    rcc = spice_malloc0(size);
-    rcc->client = client;
-    rcc->channel = channel;
-    red_channel_add_client(channel, rcc);
-    return rcc;
-}
-
 static void red_channel_client_default_connect(RedChannel *channel, RedClient *client,
                                                RedsStream *stream,
                                                int migration,
@@ -951,6 +936,7 @@ static void red_channel_remove_client(RedChannelClient *rcc)
 {
     ASSERT(pthread_equal(pthread_self(), rcc->channel->thread_id));
     ring_remove(&rcc->channel_link);
+    ASSERT(rcc->channel->clients_num > 0);
     rcc->channel->clients_num--;
     // TODO: should we set rcc->channel to NULL???
 }
@@ -990,6 +976,26 @@ void red_channel_disconnect(RedChannel *channel)
     }
 }
 
+RedChannelClient *red_channel_client_create_dummy(int size,
+                                                  RedChannel *channel,
+                                                  RedClient  *client)
+{
+    RedChannelClient *rcc;
+
+    ASSERT(size >= sizeof(RedChannelClient));
+    rcc = spice_malloc0(size);
+    rcc->client = client;
+    rcc->channel = channel;
+    red_channel_add_client(channel, rcc);
+    return rcc;
+}
+
+void red_channel_client_destroy_dummy(RedChannelClient *rcc)
+{
+    red_channel_remove_client(rcc);
+    free(rcc);
+}
+
 void red_channel_apply_clients(RedChannel *channel, channel_client_callback cb)
 {
     RingItem *link;
diff --git a/server/red_channel.h b/server/red_channel.h
index daee8ba..a24ff7d 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -278,6 +278,7 @@ RedChannel *red_channel_create_dummy(int size, uint32_t type, uint32_t id);
 RedChannelClient *red_channel_client_create_dummy(int size,
                                                   RedChannel *channel,
                                                   RedClient  *client);
+void red_channel_client_destroy_dummy(RedChannelClient *rcc);
 
 
 int red_channel_is_connected(RedChannel *channel);
diff --git a/server/snd_worker.c b/server/snd_worker.c
index 4c9a6f0..0894785 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -216,6 +216,7 @@ static void snd_disconnect_channel(SndChannel *channel)
     }
     channel->cleanup(channel);
     worker = channel->worker;
+    red_channel_client_destroy_dummy(worker->connection->channel_client);
     worker->connection = NULL;
     core->watch_remove(channel->stream->watch);
     channel->stream->watch = NULL;
commit 439993d7882bc5a9b1055553f30b8b18140b7f0d
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Aug 15 14:42:45 2011 -0700

    server/red_channel: release channel allocated message  buffer
    
    handler->cb->release_msg_buf was not being called except in the error path,
    causing a memory leak.

diff --git a/server/red_channel.c b/server/red_channel.c
index ae333aa..3f47f7f 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -141,6 +141,7 @@ static void red_peer_handle_incoming(RedsStream *stream, IncomingHandler *handle
                                                  handler->msg);
         }
         handler->msg_pos = 0;
+        handler->cb->release_msg_buf(handler->opaque, &handler->header, handler->msg);
         handler->msg = NULL;
         handler->header_pos = 0;
 
commit 6ef8d1e844950f6b4099569682618ff2590a096b
Author: Alon Levy <alevy at redhat.com>
Date:   Fri Aug 12 21:50:42 2011 +0300

    server/main_channel: reduce verbose agent data command
    
    by using the new SPICE_DEBUG_LEVEL.

diff --git a/server/main_channel.c b/server/main_channel.c
index 6d77f71..e793e48 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -724,7 +724,8 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
         case SPICE_MSG_MAIN_AGENT_DATA: {
             AgentDataPipeItem *data = (AgentDataPipeItem*)base;
             if (!--data->refs->refs) {
-                red_printf("SPICE_MSG_MAIN_AGENT_DATA %p %p, %d", data, data->refs, data->refs->refs);
+                red_printf_debug(1, "SPICE_MSG_MAIN_AGENT_DATA %p %p, %d",
+                                 data, data->refs, data->refs->refs);
                 free(data->refs);
                 data->free_data(data->data, data->opaque);
             }
commit 16ed3f9f25ce9e069d4a95562fc6d04bddd779cf
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Thu Aug 4 16:39:09 2011 +0300

    drawables count for debug

diff --git a/server/red_worker.c b/server/red_worker.c
index e767623..43d0bae 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -885,6 +885,8 @@ typedef struct RedWorker {
     Ring current_list;
     uint32_t current_size;
     uint32_t drawable_count;
+    uint32_t red_drawable_count;
+    uint32_t glz_drawable_count;
     uint32_t transparent_count;
 
     uint32_t shadows_count;
@@ -1674,6 +1676,7 @@ static inline void put_red_drawable(RedWorker *worker, RedDrawable *drawable, ui
         return;
     }
 
+    worker->red_drawable_count--;
     release_info_ext.group_id = group_id;
     release_info_ext.info = drawable->release_info;
     worker->qxl->st->qif->release_resource(worker->qxl, release_info_ext);
@@ -1737,6 +1740,7 @@ static inline void release_drawable(RedWorker *worker, Drawable *drawable)
         put_red_drawable(worker, drawable->red_drawable,
                           drawable->group_id, drawable->self_bitmap);
         free_drawable(worker, drawable);
+        worker->drawable_count--;
     }
 }
 
@@ -1820,8 +1824,6 @@ static void red_flush_source_surfaces(RedWorker *worker, Drawable *drawable)
 
 static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
 {
-    worker->drawable_count--;
-
     if (item->tree_item.effect != QXL_EFFECT_OPAQUE) {
         worker->transparent_count--;
     }
@@ -2300,8 +2302,8 @@ static inline void __current_add_drawable(RedWorker *worker, Drawable *drawable,
     surface = &worker->surfaces[surface_id];
     ring_add_after(&drawable->tree_item.base.siblings_link, pos);
     ring_add(&worker->current_list, &drawable->list_link);
-    worker->drawable_count++;
     ring_add(&surface->current_list, &drawable->surface_list_link);
+    worker->current_size++;
     drawable->refs++;
 }
 
@@ -3220,7 +3222,6 @@ static inline int red_current_add(RedWorker *worker, Ring *ring, Drawable *drawa
 
     print_base_item("ADD", &item->base);
     ASSERT(!region_is_empty(&item->base.rgn));
-    worker->current_size++;
     region_init(&exclude_rgn);
     now = ring_next(ring, ring);
 
@@ -3365,7 +3366,6 @@ static inline int red_current_add_with_shadow(RedWorker *worker, Ring *ring, Dra
         return FALSE;
     }
     print_base_item("ADDSHADOW", &item->tree_item.base);
-    worker->current_size++;
     // item and his shadow must initially be placed in the same container.
     // for now putting them on root.
 
@@ -3629,6 +3629,8 @@ static Drawable *get_drawable(RedWorker *worker, uint8_t effect, RedDrawable *re
     while (!(drawable = alloc_drawable(worker))) {
         free_one_drawable(worker, FALSE);
     }
+    worker->drawable_count++;
+    worker->red_drawable_count++;
     memset(drawable, 0, sizeof(Drawable));
     drawable->refs = 1;
     clock_gettime(CLOCK_MONOTONIC, &time);
@@ -4689,6 +4691,7 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
 
     *ring_is_empty = FALSE;
     while (!display_is_connected(worker) ||
+           // TODO: change to average pipe size?
            red_channel_min_pipe_size(&worker->display_channel->common.base) <= max_pipe_size) {
         if (!worker->qxl->st->qif->get_command(worker->qxl, &ext_cmd)) {
             *ring_is_empty = TRUE;;
@@ -4778,6 +4781,9 @@ static void red_free_some(RedWorker *worker)
     DisplayChannelClient *dcc;
     RingItem *item;
 
+    red_printf_debug(3, "#draw=%d, #red_draw=%d, #glz_draw=%d", worker->drawable_count,
+                                                                worker->red_drawable_count,
+                                                                worker->glz_drawable_count);
     WORKER_FOREACH_DCC(worker, item, dcc) {
         GlzSharedDictionary *glz_dict = dcc ? dcc->glz_dict : NULL;
 
@@ -5065,7 +5071,7 @@ static RedGlzDrawable *red_display_get_glz_drawable(DisplayChannelClient *dcc, D
     ring_item_init(&ret->drawable_link);
     ring_add_before(&ret->link, &dcc->glz_drawables);
     ring_add(&drawable->glz_ring, &ret->drawable_link);
-
+    dcc->common.worker->glz_drawable_count++;
     return ret;
 }
 
@@ -5125,7 +5131,7 @@ static void red_display_free_glz_drawable_instance(DisplayChannelClient *dcc,
         }
         put_red_drawable(worker, glz_drawable->red_drawable,
                           glz_drawable->group_id, glz_drawable->self_bitmap);
-
+        worker->glz_drawable_count--;
         if (ring_item_is_linked(&glz_drawable->link)) {
             ring_remove(&glz_drawable->link);
         }
@@ -5476,6 +5482,7 @@ static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *im
     if (this_cc == drawable_cc) {
         red_display_free_glz_drawable_instance(drawable_cc, glz_drawable_instance);
     } else {
+        red_printf("error");
         pthread_mutex_lock(&drawable_cc->glz_drawables_inst_to_free_lock);
         ring_add_before(&glz_drawable_instance->free_link,
                         &drawable_cc->glz_drawables_inst_to_free);
@@ -8639,6 +8646,9 @@ static void display_channel_client_on_disconnect(RedChannelClient *rcc)
     if (!red_channel_is_connected(rcc->channel)) {
         red_display_destroy_compress_bufs(display_channel);
     }
+    red_printf_debug(3, "#draw=%d, #red_draw=%d, #glz_draw=%d", worker->drawable_count,
+                                                                worker->red_drawable_count,
+                                                                worker->glz_drawable_count);
 }
 
 void red_disconnect_all_display_TODO_remove_me(RedChannel *channel)
@@ -10411,16 +10421,28 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
         break;
     case RED_WORKER_MESSAGE_OOM:
         ASSERT(worker->running);
+        // streams? but without streams also leak
+        red_printf_debug(1, "OOM1 #draw=%u, #red_draw=%u, #glz_draw=%u current %u pipes %u",
+                         worker->drawable_count,
+                         worker->red_drawable_count,
+                         worker->glz_drawable_count,
+                         worker->current_size,
+                         worker->display_channel ?
+                         red_channel_sum_pipes_size(display_red_channel) : 0);
         while (red_process_commands(worker, MAX_PIPE_SIZE, &ring_is_empty)) {
             red_channel_push(&worker->display_channel->common.base);
         }
         if (worker->qxl->st->qif->flush_resources(worker->qxl) == 0) {
-            red_printf("oom current %u pipes %u", worker->current_size,
-                       worker->display_channel ?
-                       red_channel_sum_pipes_size(display_red_channel) : 0);
             red_free_some(worker);
             worker->qxl->st->qif->flush_resources(worker->qxl);
         }
+        red_printf_debug(1, "OOM2 #draw=%u, #red_draw=%u, #glz_draw=%u current %u pipes %u",
+                         worker->drawable_count,
+                         worker->red_drawable_count,
+                         worker->glz_drawable_count,
+                         worker->current_size,
+                         worker->display_channel ?
+                         red_channel_sum_pipes_size(display_red_channel) : 0);
         clear_bit(RED_WORKER_PENDING_OOM, worker->pending);
         break;
     case RED_WORKER_MESSAGE_RESET_CURSOR:
commit f84dfeb0aac4b6b49bc447a2140cbc7c2882de51
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed Aug 3 18:36:04 2011 +0300

    server: registering RedChannel in reds, instead of Channel
    
    Merging the functionality of reds::channel, into RedChannel.
    In addition, cleanup and fix disconnection code: before this patch,
    red_dispatcher_disconnect_display_client
    could have been called from the red_worker thread
    (and it must be called only from the io thread).
    
    RedChannel holds only connected channel clients. RedClient holds all the
    channel clients that were created till it is destroyed
    (and then it destroys them as well).
    
    Note: snd_channel still doesn't use red_channel, however it
    creates dummy channel and channel clients, in order to register itself
    in reds.
    
    server/red_channel.c: a channel is connected if it holds at least one channel client
    
    Previously I changed RedChannel to hold only connected channel clients and
    RedClient, to hold all the channel clients as long as it is not destroyed.
    
    usbredir: multichannel has not been tested, it just compiles.

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index e6af0d5..653910e 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -434,37 +434,18 @@ static void inputs_relase_keys(void)
     kbd_push_scan(keyboard, 0x38 | 0x80); //LALT
 }
 
-static void inputs_channel_disconnect(RedChannelClient *rcc)
+static void inputs_channel_on_disconnect(RedChannelClient *rcc)
 {
-    inputs_relase_keys();
-    red_channel_client_disconnect(rcc);
-}
-
-static void inputs_channel_on_error(RedChannelClient *rcc)
-{
-    red_printf("");
-    inputs_channel_disconnect(rcc);
-}
-
-static void inputs_shutdown(Channel *channel)
-{
-    InputsChannel *inputs_channel = (InputsChannel *)channel->data;
-    ASSERT(g_inputs_channel == inputs_channel);
-
-    if (inputs_channel) {
-        red_channel_shutdown(&inputs_channel->base);
-        red_channel_destroy(&inputs_channel->base);
-        channel->data = NULL;
-        g_inputs_channel = NULL;
+    if (!rcc) {
+        return;
     }
+    inputs_relase_keys();
 }
 
-static void inputs_migrate(Channel *channel)
+static void inputs_migrate(RedChannelClient *rcc)
 {
-    InputsChannel *inputs_channel = channel->data;
-
-    ASSERT(g_inputs_channel == (InputsChannel *)channel->data);
-    red_channel_pipes_add_type(&inputs_channel->base, PIPE_ITEM_MIGRATE);
+    ASSERT(g_inputs_channel == (InputsChannel *)rcc->channel);
+    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MIGRATE);
 }
 
 static void inputs_pipe_add_init(RedChannelClient *rcc)
@@ -501,40 +482,21 @@ static void inputs_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
 }
 
-static void inputs_link(Channel *channel, RedClient *client,
-                        RedsStream *stream, int migration,
-                        int num_common_caps, uint32_t *common_caps,
-                        int num_caps, uint32_t *caps)
+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)
 {
     InputsChannelClient *icc;
 
-    ASSERT(channel->data == g_inputs_channel);
-    if (channel->data == NULL) {
-        ChannelCbs channel_cbs;
-
-        memset(&channel_cbs, sizeof(channel_cbs), 0);
-
-        channel_cbs.config_socket = inputs_channel_config_socket;
-        channel_cbs.disconnect = inputs_channel_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;
-
-        red_printf("inputs channel create");
-        channel->data = g_inputs_channel = (InputsChannel*)red_channel_create_parser(
-            sizeof(InputsChannel), core, migration, FALSE /* handle_acks */
-            ,spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL)
-            ,inputs_channel_handle_parsed
-            ,inputs_channel_on_error
-            ,inputs_channel_on_error
-            ,&channel_cbs);
-        ASSERT(channel->data);
-    }
+    ASSERT(g_inputs_channel);
+    ASSERT(channel == &g_inputs_channel->base);
+
     red_printf("inputs channel client create");
     icc = (InputsChannelClient*)red_channel_client_create(sizeof(InputsChannelClient),
-                              channel->data, client, stream);
+                                                          channel,
+                                                          client,
+                                                          stream);
     icc->motion_count = 0;
     inputs_pipe_add_init(&icc->base);
 }
@@ -560,14 +522,41 @@ static void key_modifiers_sender(void *opaque)
 
 void inputs_init(void)
 {
-    Channel *channel;
-
-    channel = spice_new0(Channel, 1);
-    channel->type = SPICE_CHANNEL_INPUTS;
-    channel->link = inputs_link;
-    channel->shutdown = inputs_shutdown;
-    channel->migrate = inputs_migrate;
-    reds_register_channel(channel);
+    ChannelCbs channel_cbs;
+    ClientCbs client_cbs = {0,};
+
+    ASSERT(!g_inputs_channel);
+
+    memset(&channel_cbs, sizeof(channel_cbs), 0);
+    memset(&client_cbs, sizeof(client_cbs), 0);
+
+    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;
+
+    g_inputs_channel = (InputsChannel *)red_channel_create_parser(
+                                    sizeof(InputsChannel),
+                                    core,
+                                    SPICE_CHANNEL_INPUTS, 0,
+                                    FALSE, // TODO: set migration?
+                                    FALSE, /* handle_acks */
+                                    spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL),
+                                    inputs_channel_handle_parsed,
+                                    &channel_cbs);
+
+    if (!g_inputs_channel) {
+        red_error("failed to allocate Inputs Channel");
+    }
+
+    client_cbs.connect = inputs_connect;
+    client_cbs.migrate = inputs_migrate;
+    red_channel_register_client_cbs(&g_inputs_channel->base, &client_cbs);
+
+    reds_register_channel(&g_inputs_channel->base);
 
     if (!(key_modifiers_timer = core->timer_add(key_modifiers_sender, NULL))) {
         red_error("key modifiers timer create failed");
diff --git a/server/main_channel.c b/server/main_channel.c
index 54b3dff..6d77f71 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -155,14 +155,13 @@ enum NetTestStage {
     NET_TEST_STAGE_RATE,
 };
 
-static void main_channel_client_disconnect(RedChannelClient *rcc)
+// when disconnection occurs, let reds shutdown all channels. This will trigger the
+// real disconnection of main channel
+static void main_channel_client_on_disconnect(RedChannelClient *rcc)
 {
-    red_channel_client_disconnect(rcc);
-}
-
-static void main_disconnect(MainChannel *main_chan)
-{
-    red_channel_destroy(&main_chan->base);
+    red_printf("rcc=%p", rcc);
+    reds_client_disconnect(rcc->client);
+//    red_channel_client_disconnect(rcc);
 }
 
 RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
@@ -840,12 +839,6 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
     return TRUE;
 }
 
-static void main_channel_on_error(RedChannelClient *rcc)
-{
-    red_printf("");
-    reds_client_disconnect(rcc->client);
-}
-
 static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header)
 {
     MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
@@ -912,11 +905,12 @@ uint32_t main_channel_client_get_link_id(MainChannelClient *mcc)
     return mcc->connection_id;
 }
 
-MainChannelClient *main_channel_client_create(MainChannel *main_chan,
-    RedClient *client, RedsStream *stream, uint32_t connection_id)
+MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
+                                              RedsStream *stream, uint32_t connection_id)
 {
-    MainChannelClient *mcc = (MainChannelClient*)red_channel_client_create(
-        sizeof(MainChannelClient), &main_chan->base, client, stream);
+    MainChannelClient *mcc = (MainChannelClient*)red_channel_client_create(sizeof(MainChannelClient),
+                                                                           &main_chan->base,
+                                                                           client, stream);
 
     mcc->connection_id = connection_id;
     mcc->bitrate_per_sec = ~0;
@@ -929,42 +923,20 @@ MainChannelClient *main_channel_client_create(MainChannel *main_chan,
     return mcc;
 }
 
-MainChannelClient *main_channel_link(Channel *channel, RedClient *client,
-                        RedsStream *stream, uint32_t connection_id, int migration,
-                        int num_common_caps, uint32_t *common_caps, int num_caps,
-                        uint32_t *caps)
+MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
+                                     RedsStream *stream, uint32_t connection_id, int migration,
+                                     int num_common_caps, uint32_t *common_caps, int num_caps,
+                                     uint32_t *caps)
 {
     MainChannelClient *mcc;
 
-    if (channel->data == NULL) {
-        ChannelCbs channel_cbs;
-
-        channel_cbs.config_socket = main_channel_config_socket;
-        channel_cbs.disconnect = main_channel_client_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;
-        channel_cbs.handle_migrate_data_get_serial = main_channel_handle_migrate_data_get_serial;
-
-        red_printf("create main channel");
-        channel->data = red_channel_create_parser(
-            sizeof(MainChannel), core, migration, FALSE /* handle_acks */
-            ,spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL)
-            ,main_channel_handle_parsed
-            ,main_channel_on_error
-            ,main_channel_on_error
-            ,&channel_cbs);
-        ASSERT(channel->data);
-    }
+    ASSERT(channel);
+
     // TODO - migration - I removed it from channel creation, now put it
     // into usage somewhere (not an issue until we return migration to it's
     // former glory)
     red_printf("add main channel client");
-    mcc = main_channel_client_create(channel->data, client, stream, connection_id);
+    mcc = main_channel_client_create(channel, client, stream, connection_id);
     return mcc;
 }
 
@@ -978,6 +950,7 @@ int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, sockle
     return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
 }
 
+// TODO: ? shouldn't it disonnect all clients? or shutdown all main_channels?
 void main_channel_close(MainChannel *main_chan)
 {
     int socketfd;
@@ -998,27 +971,31 @@ uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
     return mcc->bitrate_per_sec;
 }
 
-static void main_channel_shutdown(Channel *channel)
+MainChannel* main_channel_init(void)
 {
-    MainChannel *main_chan = channel->data;
+    RedChannel *channel;
+    ChannelCbs channel_cbs;
 
-    if (main_chan != NULL) {
-        main_disconnect(main_chan);
-    }
-}
 
-static void main_channel_migrate()
-{
-}
+    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;
+    channel_cbs.handle_migrate_data_get_serial = main_channel_handle_migrate_data_get_serial;
 
-Channel* main_channel_init(void)
-{
-    Channel *channel;
-
-    channel = spice_new0(Channel, 1);
-    channel->type = SPICE_CHANNEL_MAIN;
-    channel->link = NULL; /* the main channel client is created by reds.c explicitly */
-    channel->shutdown = main_channel_shutdown;
-    channel->migrate = main_channel_migrate;
-    return channel;
+    // TODO: set the migration flag of the channel
+    channel = red_channel_create_parser(sizeof(MainChannel), core,
+                                        SPICE_CHANNEL_MAIN, 0,
+                                        FALSE, FALSE, /* handle_acks */
+                                        spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL),
+                                        main_channel_handle_parsed,
+                                        &channel_cbs);
+    ASSERT(channel);
+
+    return (MainChannel *)channel;
 }
diff --git a/server/main_channel.h b/server/main_channel.h
index 0dd3797..8047535 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -47,10 +47,10 @@ struct MainMigrateData {
 
 typedef struct MainChannel MainChannel;
 
-Channel *main_channel_init(void);
+MainChannel *main_channel_init(void);
 RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
 /* This is a 'clone' from the reds.h Channel.link callback to allow passing link_id */
-MainChannelClient *main_channel_link(struct Channel *, RedClient *client,
+MainChannelClient *main_channel_link(MainChannel *, RedClient *client,
      RedsStream *stream, uint32_t link_id, int migration, int num_common_caps,
      uint32_t *common_caps, int num_caps, uint32_t *caps);
 void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
diff --git a/server/red_channel.c b/server/red_channel.c
index 80aa667..ae333aa 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -32,10 +32,12 @@
 #include "ring.h"
 #include "stat.h"
 #include "red_channel.h"
+#include "reds.h"
 #include "generated_marshallers.h"
 
 static void red_channel_client_event(int fd, int event, void *data);
 static void red_client_add_channel(RedClient *client, RedChannelClient *rcc);
+static void red_client_remove_channel(RedChannelClient *rcc);
 
 /* return the number of bytes read. -1 in case of error */
 static int red_peer_receive(RedsStream *stream, uint8_t *buf, uint32_t size)
@@ -138,10 +140,6 @@ static void red_peer_handle_incoming(RedsStream *stream, IncomingHandler *handle
             ret_handle = handler->cb->handle_message(handler->opaque, &handler->header,
                                                  handler->msg);
         }
-        if (handler->shut) {
-            handler->cb->on_error(handler->opaque);
-            return;
-        }
         handler->msg_pos = 0;
         handler->msg = NULL;
         handler->header_pos = 0;
@@ -221,22 +219,9 @@ static void red_channel_client_on_output(void *opaque, int n)
     stat_inc_counter(rcc->channel->out_bytes_counter, n);
 }
 
-void red_channel_client_default_peer_on_error(RedChannelClient *rcc)
-{
-    rcc->channel->channel_cbs.disconnect(rcc);
-}
-
-static void red_channel_peer_on_incoming_error(RedChannelClient *rcc)
-{
-    if (rcc->stream->shutdown) {
-        return; // assume error has already been handled which caused the shutdown.
-    }
-    rcc->channel->on_incoming_error(rcc);
-}
-
-static void red_channel_peer_on_outgoing_error(RedChannelClient *rcc)
+static void red_channel_client_default_peer_on_error(RedChannelClient *rcc)
 {
-    rcc->channel->on_outgoing_error(rcc);
+    red_channel_client_disconnect(rcc);
 }
 
 static int red_channel_client_peer_get_out_msg_size(void *opaque)
@@ -417,6 +402,21 @@ error:
     return NULL;
 }
 
+
+RedChannelClient *red_channel_client_create_dummy(int size,
+                                                  RedChannel *channel,
+                                                  RedClient  *client)
+{
+    RedChannelClient *rcc;
+
+    ASSERT(size >= sizeof(RedChannelClient));
+    rcc = spice_malloc0(size);
+    rcc->client = client;
+    rcc->channel = channel;
+    red_channel_add_client(channel, rcc);
+    return rcc;
+}
+
 static void red_channel_client_default_connect(RedChannel *channel, RedClient *client,
                                                RedsStream *stream,
                                                int migration,
@@ -437,6 +437,7 @@ static void red_channel_client_default_migrate(RedChannelClient *base)
 
 RedChannel *red_channel_create(int size,
                                SpiceCoreInterface *core,
+                               uint32_t type, uint32_t id,
                                int migrate, int handle_acks,
                                channel_handle_message_proc handle_message,
                                ChannelCbs *channel_cbs)
@@ -445,11 +446,13 @@ RedChannel *red_channel_create(int size,
     ClientCbs client_cbs;
 
     ASSERT(size >= sizeof(*channel));
-    ASSERT(channel_cbs->config_socket && channel_cbs->disconnect && handle_message &&
+    ASSERT(channel_cbs->config_socket && channel_cbs->on_disconnect && handle_message &&
            channel_cbs->alloc_recv_buf && channel_cbs->release_item);
     channel = spice_malloc0(size);
+    channel->type = type;
+    channel->id = id;
     channel->handle_acks = handle_acks;
-    channel->channel_cbs.disconnect = channel_cbs->disconnect;
+    channel->channel_cbs.on_disconnect = channel_cbs->on_disconnect;
     channel->channel_cbs.send_item = channel_cbs->send_item;
     channel->channel_cbs.release_item = channel_cbs->release_item;
     channel->channel_cbs.hold_item = channel_cbs->hold_item;
@@ -484,8 +487,53 @@ RedChannel *red_channel_create(int size,
 
     channel->thread_id = pthread_self();
 
-    channel->shut = 0; // came here from inputs, perhaps can be removed? XXX
     channel->out_bytes_counter = 0;
+
+    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(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
+SpiceCoreInterface 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, uint32_t type, uint32_t id)
+{
+    RedChannel *channel;
+    ClientCbs client_cbs;
+
+    ASSERT(size >= sizeof(*channel));
+    channel = spice_malloc0(size);
+    channel->type = type;
+    channel->id = id;
+    channel->core = &dummy_core;
+    ring_init(&channel->clients);
+    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);
+
+    channel->thread_id = pthread_self();
+
+    channel->out_bytes_counter = 0;
+
     return channel;
 }
 
@@ -496,26 +544,22 @@ static int do_nothing_handle_message(RedChannelClient *rcc, SpiceDataHeader *hea
 
 RedChannel *red_channel_create_parser(int size,
                                SpiceCoreInterface *core,
+                               uint32_t type, uint32_t id,
                                int migrate, int handle_acks,
                                spice_parse_channel_func_t parser,
-                               channel_handle_parsed_proc handle_parsed, 
-                               channel_on_incoming_error_proc incoming_error,
-                               channel_on_outgoing_error_proc outgoing_error,
+                               channel_handle_parsed_proc handle_parsed,
                                ChannelCbs *channel_cbs)
 {
-    RedChannel *channel = red_channel_create(size,
-        core, migrate, handle_acks, do_nothing_handle_message,
-        channel_cbs);
+    RedChannel *channel = red_channel_create(size, core, type, id,
+                                             migrate, handle_acks,
+                                             do_nothing_handle_message,
+                                             channel_cbs);
 
     if (channel == NULL) {
         return NULL;
     }
     channel->incoming_cb.handle_parsed = (handle_parsed_proc)handle_parsed;
     channel->incoming_cb.parser = parser;
-    channel->incoming_cb.on_error = (on_incoming_error_proc)red_channel_peer_on_incoming_error;
-    channel->outgoing_cb.on_error = (on_outgoing_error_proc)red_channel_peer_on_outgoing_error;
-    channel->on_incoming_error = incoming_error;
-    channel->on_outgoing_error = outgoing_error;
     return channel;
 }
 
@@ -533,10 +577,27 @@ void red_channel_register_client_cbs(RedChannel *channel, ClientCbs *client_cbs)
     }
 }
 
+void red_channel_set_caps(RedChannel *channel, int num_caps, uint32_t *caps)
+{
+    channel->num_caps = num_caps;
+    channel->caps = caps;
+}
+
+void red_channel_set_data(RedChannel *channel, void *data)
+{
+    ASSERT(channel);
+    channel->data = data;
+}
+
 void red_channel_client_destroy(RedChannelClient *rcc)
 {
-    red_channel_client_disconnect(rcc);
-    spice_marshaller_destroy(rcc->send_data.marshaller);
+    if (red_channel_client_is_connected(rcc)) {
+        red_channel_client_disconnect(rcc);
+    }
+    red_client_remove_channel(rcc);
+    if (rcc->send_data.marshaller) {
+        spice_marshaller_destroy(rcc->send_data.marshaller);
+    }
     free(rcc);
 }
 
@@ -548,11 +609,13 @@ void red_channel_destroy(RedChannel *channel)
     if (!channel) {
         return;
     }
-    red_channel_pipes_clear(channel);
     RING_FOREACH_SAFE(link, next, &channel->clients) {
         red_channel_client_destroy(
             SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
     }
+    if (channel->caps) {
+        free(channel->caps);
+    }
     free(channel);
 }
 
@@ -563,21 +626,7 @@ void red_channel_client_shutdown(RedChannelClient *rcc)
         rcc->stream->watch = NULL;
         shutdown(rcc->stream->socket, SHUT_RDWR);
         rcc->stream->shutdown = TRUE;
-        rcc->incoming.shut = TRUE;
     }
-    red_channel_client_release_sent_item(rcc);
-}
-
-void red_channel_shutdown(RedChannel *channel)
-{
-    RingItem *link;
-    RingItem *next;
-
-    red_printf("%d", channel->clients_num);
-    RING_FOREACH_SAFE(link, next, &channel->clients) {
-        red_channel_client_shutdown(SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
-    }
-    red_channel_pipes_clear(channel);
 }
 
 void red_channel_client_send(RedChannelClient *rcc)
@@ -650,11 +699,7 @@ void red_channel_push(RedChannel *channel)
     }
     RING_FOREACH_SAFE(link, next, &channel->clients) {
         rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
-        if (rcc->stream == NULL) {
-            rcc->channel->channel_cbs.disconnect(rcc);
-        } else {
-            red_channel_client_push(rcc);
-        }
+        red_channel_client_push(rcc);
     }
 }
 
@@ -861,25 +906,9 @@ int red_channel_client_is_connected(RedChannelClient *rcc)
     return rcc->stream != NULL;
 }
 
-void red_channel_pipe_item_remove(RedChannel *channel, PipeItem *item)
-{
-    ring_remove(&item->link);
-}
-
 int red_channel_is_connected(RedChannel *channel)
 {
-    RingItem *link;
-
-    if (!channel || channel->clients_num == 0) {
-        return FALSE;
-    }
-    RING_FOREACH(link, &channel->clients) {
-        if (red_channel_client_is_connected(
-                SPICE_CONTAINEROF(link, RedChannelClient, channel_link))) {
-            return TRUE;
-        }
-    }
-    return FALSE;
+    return channel && (channel->clients_num > 0);
 }
 
 void red_channel_client_clear_sent_item(RedChannelClient *rcc)
@@ -906,21 +935,6 @@ void red_channel_client_pipe_clear(RedChannelClient *rcc)
     rcc->pipe_size = 0;
 }
 
-void red_channel_pipes_clear(RedChannel *channel)
-{
-    RingItem *link;
-    RingItem *next;
-    RedChannelClient *rcc;
-
-    if (!channel) {
-        return;
-    }
-    RING_FOREACH_SAFE(link, next, &channel->clients) {
-        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
-        red_channel_client_pipe_clear(rcc);
-    }
-}
-
 void red_channel_client_ack_zero_messages_window(RedChannelClient *rcc)
 {
     rcc->ack_data.messages_window = 0;
@@ -931,27 +945,37 @@ void red_channel_client_ack_set_client_window(RedChannelClient *rcc, int client_
     rcc->ack_data.client_window = client_window;
 }
 
-static void red_channel_client_remove(RedChannelClient *rcc)
+
+static void red_channel_remove_client(RedChannelClient *rcc)
 {
-    ring_remove(&rcc->client_link);
-    rcc->client->channels_num--;
+    ASSERT(pthread_equal(pthread_self(), rcc->channel->thread_id));
     ring_remove(&rcc->channel_link);
     rcc->channel->clients_num--;
+    // TODO: should we set rcc->channel to NULL???
 }
 
-void red_channel_client_disconnect(RedChannelClient *rcc)
+static void red_client_remove_channel(RedChannelClient *rcc)
 {
-    red_printf("%p (channel %p)", rcc, rcc->channel);
+    pthread_mutex_lock(&rcc->client->lock);
+    ring_remove(&rcc->client_link);
+    rcc->client->channels_num--;
+    pthread_mutex_unlock(&rcc->client->lock);
+}
 
-    if (rcc->send_data.item) {
-        rcc->channel->channel_cbs.release_item(rcc, rcc->send_data.item, FALSE);
+void red_channel_client_disconnect(RedChannelClient *rcc)
+{
+    red_printf("%p (channel %p type %d id %d)", rcc, rcc->channel,
+                                                rcc->channel->type, rcc->channel->id);
+    if (!red_channel_client_is_connected(rcc)) {
+        return;
     }
     red_channel_client_pipe_clear(rcc);
     reds_stream_free(rcc->stream);
-    rcc->send_data.item = NULL;
-    rcc->send_data.blocked = FALSE;
-    rcc->send_data.size = 0;
-    red_channel_client_remove(rcc);
+    rcc->stream = NULL;
+    red_channel_remove_client(rcc);
+    // TODO: not do it till destroyed?
+//    red_channel_client_remove(rcc);
+    rcc->channel->channel_cbs.on_disconnect(rcc);
 }
 
 void red_channel_disconnect(RedChannel *channel)
@@ -959,27 +983,12 @@ void red_channel_disconnect(RedChannel *channel)
     RingItem *link;
     RingItem *next;
 
-    red_channel_pipes_clear(channel);
     RING_FOREACH_SAFE(link, next, &channel->clients) {
         red_channel_client_disconnect(
             SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
     }
 }
 
-int red_channel_all_clients_serials_are_zero(RedChannel *channel)
-{
-    RingItem *link;
-    RedChannelClient *rcc;
-
-    RING_FOREACH(link, &channel->clients) {
-        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
-        if (rcc->send_data.serial != 0) {
-            return FALSE;
-        }
-    }
-    return TRUE;
-}
-
 void red_channel_apply_clients(RedChannel *channel, channel_client_callback cb)
 {
     RingItem *link;
@@ -1004,17 +1013,6 @@ void red_channel_apply_clients_data(RedChannel *channel, channel_client_callback
     }
 }
 
-void red_channel_set_shut(RedChannel *channel)
-{
-    RingItem *link;
-    RedChannelClient *rcc;
-
-    RING_FOREACH(link, &channel->clients) {
-        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
-        rcc->incoming.shut = TRUE;
-    }
-}
-
 int red_channel_all_blocked(RedChannel *channel)
 {
     RingItem *link;
@@ -1143,18 +1141,24 @@ RedClient *red_client_new()
 
     client = spice_malloc0(sizeof(RedClient));
     ring_init(&client->channels);
+    pthread_mutex_init(&client->lock, NULL);
     client->thread_id = pthread_self();
 
     return client;
 }
 
-void red_client_shutdown(RedClient *client)
+void red_client_migrate(RedClient *client)
 {
     RingItem *link, *next;
+    RedChannelClient *rcc;
 
-    red_printf("#channels %d", client->channels_num);
+    red_printf("migrate client with #channels %d", client->channels_num);
+    ASSERT(pthread_equal(pthread_self(), client->thread_id));
     RING_FOREACH_SAFE(link, next, &client->channels) {
-        red_channel_client_shutdown(SPICE_CONTAINEROF(link, RedChannelClient, client_link));
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, client_link);
+        if (red_channel_client_is_connected(rcc)) {
+            rcc->channel->client_cbs.migrate(rcc);
+        }
     }
 }
 
@@ -1175,29 +1179,23 @@ void red_client_destroy(RedClient *client)
         // TODO: should we go back to async. For this we need to use
         // ref count for channel clients.
         rcc->channel->client_cbs.disconnect(rcc);
+        ASSERT(ring_is_empty(&rcc->pipe));
+        ASSERT(rcc->pipe_size == 0);
+        ASSERT(rcc->send_data.size == 0);
+        red_channel_client_destroy(rcc);
     }
-    free(client);
-}
 
-void red_client_disconnect(RedClient *client)
-{
-    RingItem *link, *next;
-    RedChannelClient *rcc;
-
-    red_printf("#channels %d", client->channels_num);
-    RING_FOREACH_SAFE(link, next, &client->channels) {
-        // some channels may be in other threads, so disconnection
-        // is not synchronous.
-        rcc = SPICE_CONTAINEROF(link, RedChannelClient, client_link);
-        rcc->channel->client_cbs.disconnect(rcc);
-    }
+    pthread_mutex_destroy(&client->lock);
+    free(client);
 }
 
 static void red_client_add_channel(RedClient *client, RedChannelClient *rcc)
 {
     ASSERT(rcc && client);
+    pthread_mutex_lock(&client->lock);
     ring_add(&client->channels, &rcc->client_link);
     client->channels_num++;
+    pthread_mutex_unlock(&client->lock);
 }
 
 MainChannelClient *red_client_get_main(RedClient *client) {
diff --git a/server/red_channel.h b/server/red_channel.h
index cb33fcb..daee8ba 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -24,9 +24,9 @@
 
 #include "red_common.h"
 #include <pthread.h>
-#include "reds.h"
 #include "spice.h"
 #include "ring.h"
+#include "common/marshaller.h"
 #include "server/demarshallers.h"
 
 #define MAX_SEND_BUFS 1000
@@ -62,7 +62,6 @@ typedef struct IncomingHandler {
     uint32_t header_pos;
     uint8_t *msg; // data of the msg following the header. allocated by alloc_msg_buf.
     uint32_t msg_pos;
-    int shut; // came here from inputs_channel. Not sure if it is really required or can be removed. XXX
 } IncomingHandler;
 
 typedef int (*get_outgoing_msg_size_proc)(void *opaque);
@@ -98,8 +97,11 @@ typedef struct BufDescriptor {
     uint8_t *data;
 } BufDescriptor;
 
+typedef struct RedsStream RedsStream;
 typedef struct RedChannel RedChannel;
 typedef struct RedChannelClient RedChannelClient;
+typedef struct RedClient RedClient;
+typedef struct MainChannelClient MainChannelClient;
 
 /* Messages handled by red_channel
  * SET_ACK - sent to client on channel connection
@@ -154,7 +156,7 @@ typedef void (*channel_client_migrate_proc)(RedChannelClient *base);
  */
 typedef struct {
     channel_configure_socket_proc config_socket;
-    channel_disconnect_proc disconnect;
+    channel_disconnect_proc on_disconnect;
     channel_send_pipe_item_proc send_item;
     channel_hold_pipe_item_proc hold_item;
     channel_release_pipe_item_proc release_item;
@@ -207,10 +209,20 @@ struct RedChannelClient {
 };
 
 struct RedChannel {
+    uint32_t type;
+    uint32_t id;
+
     SpiceCoreInterface *core;
     int migrate;
     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'?
     Ring clients;
     uint32_t clients_num;
 
@@ -220,11 +232,10 @@ struct RedChannel {
     ChannelCbs channel_cbs;
     ClientCbs client_cbs;
 
-    /* Stuff below added for Main and Inputs channels switch to RedChannel
-     * (might be removed later) */
-    channel_on_incoming_error_proc on_incoming_error; /* alternative to disconnect */
-    channel_on_outgoing_error_proc on_outgoing_error;
-    int shut; /* signal channel is to be closed */
+    int num_caps;
+    uint32_t *caps;
+
+    void *data;
 
     // TODO: when different channel_clients are in different threads from Channel -> need to protect!
     pthread_t thread_id;
@@ -237,6 +248,7 @@ struct RedChannel {
  * explicitly destroy the channel */
 RedChannel *red_channel_create(int size,
                                SpiceCoreInterface *core,
+                               uint32_t type, uint32_t id,
                                int migrate, int handle_acks,
                                channel_handle_message_proc handle_message,
                                ChannelCbs *channel_cbs);
@@ -245,21 +257,39 @@ RedChannel *red_channel_create(int size,
  * will become default eventually */
 RedChannel *red_channel_create_parser(int size,
                                SpiceCoreInterface *core,
+                               uint32_t type, uint32_t id,
                                int migrate, int handle_acks,
                                spice_parse_channel_func_t parser,
                                channel_handle_parsed_proc handle_parsed,
-                               channel_on_incoming_error_proc incoming_error,
-                               channel_on_outgoing_error_proc outgoing_error,
                                ChannelCbs *channel_cbs);
 
 void red_channel_register_client_cbs(RedChannel *channel, ClientCbs *client_cbs);
+// caps are freed when the channel is destroyed
+void red_channel_set_caps(RedChannel *channel, int num_caps, uint32_t *caps);
+void red_channel_set_data(RedChannel *channel, void *data);
 
 RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
                                             RedsStream *stream);
 
+// 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, uint32_t type, uint32_t id);
+RedChannelClient *red_channel_client_create_dummy(int size,
+                                                  RedChannel *channel,
+                                                  RedClient  *client);
+
+
 int red_channel_is_connected(RedChannel *channel);
 int red_channel_client_is_connected(RedChannelClient *rcc);
 
+/*
+ * the disconnect callback is called from the channel's thread,
+ * i.e., for display channels - red worker thread, for all the other - from the main thread.
+ * RedClient is managed from the main thread. red_channel_client_destroy can be called only
+ * from red_client_destroy.
+ */
+
 void red_channel_client_destroy(RedChannelClient *rcc);
 void red_channel_destroy(RedChannel *channel);
 
@@ -267,7 +297,6 @@ void red_channel_destroy(RedChannel *channel);
  * thread. It will not touch the rings, just shutdown the socket.
  * It should be followed by some way to gurantee a disconnection. */
 void red_channel_client_shutdown(RedChannelClient *rcc);
-void red_channel_shutdown(RedChannel *channel);
 
 /* should be called when a new channel is ready to send messages */
 void red_channel_init_outgoing_messages_window(RedChannel *channel);
@@ -276,9 +305,6 @@ void red_channel_init_outgoing_messages_window(RedChannel *channel);
 int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size,
                                uint16_t type, void *message);
 
-/* default error handler that disconnects channel */
-void red_channel_client_default_peer_on_error(RedChannelClient *rcc);
-
 /* when preparing send_data: should call init and then use marshaller */
 void red_channel_client_init_send_data(RedChannelClient *rcc, uint16_t msg_type, PipeItem *item);
 
@@ -303,7 +329,6 @@ void red_channel_client_pipe_add_push(RedChannelClient *rcc, PipeItem *item);
 void red_channel_client_pipe_add(RedChannelClient *rcc, PipeItem *item);
 void red_channel_client_pipe_add_after(RedChannelClient *rcc, PipeItem *item, PipeItem *pos);
 int red_channel_client_pipe_item_is_linked(RedChannelClient *rcc, PipeItem *item);
-void red_channel_pipe_item_remove(RedChannel *channel, PipeItem *item);
 void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc, PipeItem *item);
 void red_channel_client_pipe_add_tail(RedChannelClient *rcc, PipeItem *item);
 /* for types that use this routine -> the pipe item should be freed */
@@ -315,9 +340,6 @@ void red_channel_client_ack_set_client_window(RedChannelClient *rcc, int client_
 void red_channel_client_push_set_ack(RedChannelClient *rcc);
 void red_channel_push_set_ack(RedChannel *channel);
 
-/* TODO: This sets all clients to shut state - probably we want to close per channel */
-void red_channel_shutdown(RedChannel *channel);
-
 int red_channel_get_first_socket(RedChannel *channel);
 
 /* return TRUE if all of the connected clients to this channel are blocked */
@@ -354,7 +376,6 @@ void red_channel_client_push(RedChannelClient *rcc);
 // TODO: again - what is the context exactly? this happens in channel disconnect. but our
 // current red_channel_shutdown also closes the socket - is there a socket to close?
 // are we reading from an fd here? arghh
-void red_channel_pipes_clear(RedChannel *channel);
 void red_channel_client_pipe_clear(RedChannelClient *rcc);
 // Again, used in various places outside of event handler context (or in other event handler
 // contexts):
@@ -403,17 +424,22 @@ struct RedClient {
     RingItem link;
     Ring channels;
     int channels_num;
-    int disconnecting;
     MainChannelClient *mcc;
+    pthread_mutex_t lock; // different channels can be in different threads
 
     pthread_t thread_id;
+
+    int disconnecting;
 };
 
 RedClient *red_client_new();
 MainChannelClient *red_client_get_main(RedClient *client);
+// main should be set once before all the other channels are created
 void red_client_set_main(RedClient *client, MainChannelClient *mcc);
+
+
+void red_client_migrate(RedClient *client);
+// disconnects all the client's channels (should be called from the client's thread)
 void red_client_destroy(RedClient *client);
-void red_client_disconnect(RedClient *client);
-void red_client_remove_channel(RedClient *client, RedChannelClient *rcc);
 
 #endif
diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
index 8cbdec9..c00dc58 100644
--- a/server/red_dispatcher.c
+++ b/server/red_dispatcher.c
@@ -76,10 +76,10 @@ extern spice_wan_compression_t zlib_glz_state;
 
 static RedDispatcher *dispatchers = NULL;
 
-static void red_dispatcher_set_peer(Channel *channel, RedClient *client,
-                                    RedsStream *stream, int migration,
-                                    int num_common_caps, uint32_t *common_caps, int num_caps,
-                                    uint32_t *caps)
+static void red_dispatcher_set_display_peer(RedChannel *channel, RedClient *client,
+                                            RedsStream *stream, int migration,
+                                            int num_common_caps, uint32_t *common_caps, int num_caps,
+                                            uint32_t *caps)
 {
     RedDispatcher *dispatcher;
 
@@ -92,23 +92,41 @@ static void red_dispatcher_set_peer(Channel *channel, RedClient *client,
     send_data(dispatcher->channel, &migration, sizeof(int));
 }
 
-static void red_dispatcher_shutdown_peer(Channel *channel)
+static void red_dispatcher_disconnect_display_peer(RedChannelClient *rcc)
 {
-    RedDispatcher *dispatcher = (RedDispatcher *)channel->data;
+    RedDispatcher *dispatcher;
+
+    if (!rcc->channel) {
+        return;
+    }
+
+    dispatcher = (RedDispatcher *)rcc->channel->data;
+
     red_printf("");
     RedWorkerMessage message = RED_WORKER_MESSAGE_DISPLAY_DISCONNECT;
     write_message(dispatcher->channel, &message);
+    send_data(dispatcher->channel, &rcc, sizeof(RedChannelClient *));
+
+    // TODO: we turned it to be sync, due to client_destroy . Should we support async? - for this we will need ref count
+    // for channels
+    read_message(dispatcher->channel, &message);
+    ASSERT(message == RED_WORKER_MESSAGE_READY);
 }
 
-static void red_dispatcher_migrate(Channel *channel)
+static void red_dispatcher_display_migrate(RedChannelClient *rcc)
 {
-    RedDispatcher *dispatcher = (RedDispatcher *)channel->data;
-    red_printf("channel type %u id %u", channel->type, channel->id);
+    RedDispatcher *dispatcher;
+    if (!rcc->channel) {
+        return;
+    }
+    dispatcher = (RedDispatcher *)rcc->channel->data;
+    red_printf("channel type %u id %u", rcc->channel->type, rcc->channel->id);
     RedWorkerMessage message = RED_WORKER_MESSAGE_DISPLAY_MIGRATE;
     write_message(dispatcher->channel, &message);
+    send_data(dispatcher->channel, &rcc, sizeof(RedChannelClient *));
 }
 
-static void red_dispatcher_set_cursor_peer(Channel *channel, RedClient *client, RedsStream *stream,
+static void red_dispatcher_set_cursor_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
                                            int migration, int num_common_caps,
                                            uint32_t *common_caps, int num_caps,
                                            uint32_t *caps)
@@ -122,18 +140,33 @@ static void red_dispatcher_set_cursor_peer(Channel *channel, RedClient *client,
     send_data(dispatcher->channel, &migration, sizeof(int));
 }
 
-static void red_dispatcher_shutdown_cursor_peer(Channel *channel)
+static void red_dispatcher_disconnect_cursor_peer(RedChannelClient *rcc)
 {
-    RedDispatcher *dispatcher = (RedDispatcher *)channel->data;
+    RedDispatcher *dispatcher;
+
+    if (!rcc->channel) {
+    return;
+    }
+
+    dispatcher = (RedDispatcher *)rcc->channel->data;
     red_printf("");
     RedWorkerMessage message = RED_WORKER_MESSAGE_CURSOR_DISCONNECT;
     write_message(dispatcher->channel, &message);
+    send_data(dispatcher->channel, &rcc, sizeof(RedChannelClient *));
+
+    read_message(dispatcher->channel, &message);
+    ASSERT(message == RED_WORKER_MESSAGE_READY);
 }
 
-static void red_dispatcher_cursor_migrate(Channel *channel)
+static void red_dispatcher_cursor_migrate(RedChannelClient *rcc)
 {
-    RedDispatcher *dispatcher = (RedDispatcher *)channel->data;
-    red_printf("channel type %u id %u", channel->type, channel->id);
+    RedDispatcher *dispatcher;
+
+    if (!rcc->channel) {
+        return;
+    }
+    dispatcher = (RedDispatcher *)rcc->channel->data;
+    red_printf("channel type %u id %u", rcc->channel->type, rcc->channel->id);
     RedWorkerMessage message = RED_WORKER_MESSAGE_CURSOR_MIGRATE;
     write_message(dispatcher->channel, &message);
 }
@@ -591,36 +624,6 @@ static void qxl_worker_loadvm_commands(QXLWorker *qxl_worker,
     red_dispatcher_loadvm_commands((RedDispatcher*)qxl_worker, ext, count);
 }
 
-static void red_dispatcher_send_disconnect(RedDispatcher *dispatcher,
-                    struct RedChannelClient *rcc, RedWorkerMessage message)
-{
-    write_message(dispatcher->channel, &message);
-    send_data(dispatcher->channel, &rcc, sizeof(struct RedChannelClient *));
-}
-
-void red_dispatcher_disconnect_display_client(RedDispatcher *dispatcher,
-                                      struct RedChannelClient *rcc)
-{
-    RedWorkerMessage message = RED_WORKER_MESSAGE_STOP;
-
-    red_dispatcher_send_disconnect(dispatcher, rcc,
-            RED_WORKER_MESSAGE_DISPLAY_DISCONNECT_CLIENT);
-    read_message(dispatcher->channel, &message);
-    ASSERT(message == RED_WORKER_MESSAGE_READY);
-}
-
-void red_dispatcher_disconnect_cursor_client(RedDispatcher *dispatcher,
-                                      struct RedChannelClient *rcc)
-{
-    RedWorkerMessage message = RED_WORKER_MESSAGE_STOP;
-
-    red_dispatcher_send_disconnect(dispatcher, rcc,
-            RED_WORKER_MESSAGE_CURSOR_DISCONNECT_CLIENT);
-    read_message(dispatcher->channel, &message);
-    ASSERT(message == RED_WORKER_MESSAGE_READY);
-}
-
-
 void red_dispatcher_set_mm_time(uint32_t mm_time)
 {
     RedDispatcher *now = dispatchers;
@@ -867,6 +870,32 @@ void red_dispatcher_async_complete(struct RedDispatcher *dispatcher, uint64_t co
     dispatcher->qxl->st->qif->async_complete(dispatcher->qxl, cookie);
 }
 
+static RedChannel *red_dispatcher_display_channel_create(RedDispatcher *dispatcher)
+{
+    RedWorkerMessage message = RED_WORKER_MESSAGE_DISPLAY_CHANNEL_CREATE;
+    RedChannel *display_channel;
+
+    write_message(dispatcher->channel, &message);
+
+    receive_data(dispatcher->channel, &display_channel, sizeof(RedChannel *));
+    read_message(dispatcher->channel, &message);
+    ASSERT(message == RED_WORKER_MESSAGE_READY);
+    return display_channel;
+}
+
+static RedChannel *red_dispatcher_cursor_channel_create(RedDispatcher *dispatcher)
+{
+    RedWorkerMessage message = RED_WORKER_MESSAGE_CURSOR_CHANNEL_CREATE;
+    RedChannel *cursor_channel;
+
+    write_message(dispatcher->channel, &message);
+
+    receive_data(dispatcher->channel, &cursor_channel, sizeof(RedChannel *));
+    read_message(dispatcher->channel, &message);
+    ASSERT(message == RED_WORKER_MESSAGE_READY);
+    return cursor_channel;
+}
+
 RedDispatcher *red_dispatcher_init(QXLInstance *qxl)
 {
     RedDispatcher *dispatcher;
@@ -875,10 +904,11 @@ RedDispatcher *red_dispatcher_init(QXLInstance *qxl)
     WorkerInitData init_data;
     QXLDevInitInfo init_info;
     int r;
-    Channel *reds_channel;
-    Channel *cursor_channel;
+    RedChannel *display_channel;
+    RedChannel *cursor_channel;
     sigset_t thread_sig_mask;
     sigset_t curr_sig_mask;
+    ClientCbs client_cbs = {0,};
 
     quic_init();
     sw_canvas_init();
@@ -950,23 +980,28 @@ RedDispatcher *red_dispatcher_init(QXLInstance *qxl)
     read_message(dispatcher->channel, &message);
     ASSERT(message == RED_WORKER_MESSAGE_READY);
 
-    reds_channel = spice_new0(Channel, 1);
-    reds_channel->type = SPICE_CHANNEL_DISPLAY;
-    reds_channel->id = qxl->id;
-    reds_channel->link = red_dispatcher_set_peer;
-    reds_channel->shutdown = red_dispatcher_shutdown_peer;
-    reds_channel->migrate = red_dispatcher_migrate;
-    reds_channel->data = dispatcher;
-    reds_register_channel(reds_channel);
-
-    cursor_channel = spice_new0(Channel, 1);
-    cursor_channel->type = SPICE_CHANNEL_CURSOR;
-    cursor_channel->id = qxl->id;
-    cursor_channel->link = red_dispatcher_set_cursor_peer;
-    cursor_channel->shutdown = red_dispatcher_shutdown_cursor_peer;
-    cursor_channel->migrate = red_dispatcher_cursor_migrate;
-    cursor_channel->data = dispatcher;
-    reds_register_channel(cursor_channel);
+    display_channel = red_dispatcher_display_channel_create(dispatcher);
+
+    if (display_channel) {
+        client_cbs.connect = red_dispatcher_set_display_peer;
+        client_cbs.disconnect = red_dispatcher_disconnect_display_peer;
+        client_cbs.migrate = red_dispatcher_display_migrate;
+        red_channel_register_client_cbs(display_channel, &client_cbs);
+        red_channel_set_data(display_channel, dispatcher);
+        reds_register_channel(display_channel);
+    }
+
+    cursor_channel = red_dispatcher_cursor_channel_create(dispatcher);
+
+    if (cursor_channel) {
+        client_cbs.connect = red_dispatcher_set_cursor_peer;
+        client_cbs.disconnect = red_dispatcher_disconnect_cursor_peer;
+        client_cbs.migrate = red_dispatcher_cursor_migrate;
+        red_channel_register_client_cbs(cursor_channel, &client_cbs);
+        red_channel_set_data(cursor_channel, dispatcher);
+        reds_register_channel(cursor_channel);
+    }
+
     qxl->st->qif->attache_worker(qxl, &dispatcher->base);
     qxl->st->qif->set_compression_level(qxl, calc_compression_level());
 
diff --git a/server/red_dispatcher.h b/server/red_dispatcher.h
index 07a95ae..144a40e 100644
--- a/server/red_dispatcher.h
+++ b/server/red_dispatcher.h
@@ -32,9 +32,4 @@ uint32_t red_dispatcher_qxl_ram_size(void);
 int red_dispatcher_qxl_count(void);
 void red_dispatcher_async_complete(struct RedDispatcher*, uint64_t);
 
-void red_dispatcher_disconnect_display_client(struct RedDispatcher *dispatcher,
-                                      struct RedChannelClient *rcc);
-void red_dispatcher_disconnect_cursor_client(struct RedDispatcher *dispatcher,
-                                      struct RedChannelClient *rcc);
-
 #endif
diff --git a/server/red_tunnel_worker.c b/server/red_tunnel_worker.c
index 79cb854..6c36fec 100644
--- a/server/red_tunnel_worker.c
+++ b/server/red_tunnel_worker.c
@@ -537,8 +537,8 @@ typedef struct TunnelPrintService {
 } TunnelPrintService;
 
 struct TunnelWorker {
-    Channel channel_interface; // for reds
-    TunnelChannelClient *channel;
+    RedChannel *channel;
+    TunnelChannelClient *channel_client;
 
     SpiceCoreInterface *core_interface;
     SpiceNetWireInstance *sin;
@@ -564,7 +564,7 @@ struct TunnelWorker {
 /*********************************************************************
  * Tunnel interface
  *********************************************************************/
-static void tunnel_channel_disconnect(RedChannel *channel);
+static void tunnel_channel_on_disconnect(RedChannel *channel);
 
 /* networking interface for slirp */
 static int  qemu_can_output(SlirpUsrNetworkInterface *usr_interface);
@@ -601,23 +601,23 @@ static UserTimer *create_timer(SlirpUsrNetworkInterface *usr_interface,
 static void arm_timer(SlirpUsrNetworkInterface *usr_interface, UserTimer *timer, uint32_t ms);
 
 
-/* reds interface */
-static void handle_tunnel_channel_link(Channel *channel, RedClient *client,
+/* RedChannel interface */
+
+static void handle_tunnel_channel_link(RedChannel *channel, RedClient *client,
                                        RedsStream *stream, int migration,
                                        int num_common_caps,
                                        uint32_t *common_caps, int num_caps,
                                        uint32_t *caps);
-static void handle_tunnel_channel_shutdown(struct Channel *channel);
-static void handle_tunnel_channel_migrate(struct Channel *channel);
-
+static void handle_tunnel_channel_client_migrate(RedChannelClient *rcc);
+static void red_tunnel_channel_create(TunnelWorker *worker);
 
 static void tunnel_shutdown(TunnelWorker *worker)
 {
     int i;
     red_printf("");
     /* shutdown input from channel */
-    if (worker->channel) {
-        red_channel_shutdown(worker->channel->base.channel);
+    if (worker->channel_client) {
+        red_channel_client_shutdown(&worker->channel_client->base);
     }
 
     /* shutdown socket pipe items */
@@ -745,7 +745,7 @@ static void tunnel_socket_free_rcv_buf(RedSocket *sckt, RedSocketRawRcvBuf *rcv_
     --sckt->in_data.num_buffers;
     __tunnel_worker_free_socket_rcv_buf(sckt->worker, rcv_buf);
     ++sckt->in_data.num_tokens;
-    __process_rcv_buf_tokens(sckt->worker->channel, sckt);
+    __process_rcv_buf_tokens(sckt->worker->channel_client, sckt);
 }
 
 static inline void __tunnel_worker_free_socket_rcv_buf(TunnelWorker *worker,
@@ -973,7 +973,7 @@ SPICE_GNUC_VISIBLE void spice_server_net_wire_recv_packet(SpiceNetWireInstance *
     TunnelWorker *worker = sin->st->worker;
     ASSERT(worker);
 
-    if (worker->channel && worker->channel->base.channel->migrate) {
+    if (worker->channel_client && worker->channel_client->base.channel->migrate) {
         return; // during migration and the tunnel state hasn't been restored yet.
     }
 
@@ -1016,15 +1016,9 @@ void *red_tunnel_attach(SpiceCoreInterface *core_interface,
 
     worker->null_interface.worker = worker;
 
-    worker->channel_interface.type = SPICE_CHANNEL_TUNNEL;
-    worker->channel_interface.id = 0;
-    worker->channel_interface.link = handle_tunnel_channel_link;
-    worker->channel_interface.shutdown = handle_tunnel_channel_shutdown;
-    worker->channel_interface.migrate = handle_tunnel_channel_migrate;
-    worker->channel_interface.data = worker;
+    red_tunnel_channel_create(worker);
 
-    ring_init(&worker->services);
-    reds_register_channel(&worker->channel_interface);
+   ring_init(&worker->services);
 
     net_slirp_init(worker->sif->get_ip(worker->sin),
                    TRUE,
@@ -1096,7 +1090,7 @@ static inline TunnelService *__tunnel_worker_add_service(TunnelWorker *worker, u
 #endif
     if (!virt_ip) {
         new_service->pipe_item.type = PIPE_ITEM_TYPE_SERVICE_IP_MAP;
-        red_channel_client_pipe_add(&worker->channel->base, &new_service->pipe_item);
+        red_channel_client_pipe_add(&worker->channel_client->base, &new_service->pipe_item);
     }
 
     return new_service;
@@ -1292,24 +1286,24 @@ static RedSocket *tunnel_worker_create_socket(TunnelWorker *worker, uint16_t loc
 
 static void tunnel_worker_free_socket(TunnelWorker *worker, RedSocket *sckt)
 {
-    if (worker->channel) {
-        if (red_channel_client_pipe_item_is_linked(&worker->channel->base,
+    if (worker->channel_client) {
+        if (red_channel_client_pipe_item_is_linked(&worker->channel_client->base,
                                             &sckt->out_data.data_pipe_item)) {
-            red_channel_client_pipe_remove_and_release(&worker->channel->base,
+            red_channel_client_pipe_remove_and_release(&worker->channel_client->base,
                                          &sckt->out_data.data_pipe_item);
             return;
         }
 
-        if (red_channel_client_pipe_item_is_linked(&worker->channel->base,
+        if (red_channel_client_pipe_item_is_linked(&worker->channel_client->base,
                                             &sckt->out_data.status_pipe_item)) {
-            red_channel_client_pipe_remove_and_release(&worker->channel->base,
+            red_channel_client_pipe_remove_and_release(&worker->channel_client->base,
                                          &sckt->out_data.status_pipe_item);
             return;
         }
 
-        if (red_channel_client_pipe_item_is_linked(&worker->channel->base,
+        if (red_channel_client_pipe_item_is_linked(&worker->channel_client->base,
                                             &sckt->out_data.token_pipe_item)) {
-            red_channel_client_pipe_remove_and_release(&worker->channel->base,
+            red_channel_client_pipe_remove_and_release(&worker->channel_client->base,
                                          &sckt->out_data.token_pipe_item);
             return;
         }
@@ -1631,7 +1625,7 @@ static inline int __client_socket_can_receive(RedSocket *sckt)
 {
     return (((sckt->client_status == CLIENT_SCKT_STATUS_OPEN) ||
              (sckt->client_status == CLIENT_SCKT_STATUS_SHUTDOWN_SEND)) &&
-            !sckt->worker->channel->mig_inprogress);
+            !sckt->worker->channel_client->mig_inprogress);
 }
 
 static int tunnel_channel_handle_socket_token(TunnelChannelClient *channel, RedSocket *sckt,
@@ -1870,7 +1864,7 @@ static void restored_rcv_buf_release(RawTunneledBuffer *buf)
     --sckt->in_data.num_buffers;
     __tunnel_worker_free_socket_rcv_buf(sckt->worker, (RedSocketRawRcvBuf *)buf);
     // for case that ready queue is empty and the client has no tokens
-    __process_rcv_buf_tokens(sckt->worker->channel, sckt);
+    __process_rcv_buf_tokens(sckt->worker->channel_client, sckt);
 }
 
 RawTunneledBuffer *tunnel_socket_alloc_restored_rcv_buf(RedSocket *sckt)
@@ -1889,7 +1883,7 @@ static void restore_tokens_buf_release(RawTunneledBuffer *buf)
     RedSocket *sckt = (RedSocket *)buf->usr_opaque;
 
     sckt->in_data.num_tokens += tokens_buf->num_tokens;
-    __process_rcv_buf_tokens(sckt->worker->channel, sckt);
+    __process_rcv_buf_tokens(sckt->worker->channel_client, sckt);
 
     free(tokens_buf);
 }
@@ -2182,7 +2176,7 @@ static uint64_t tunnel_channel_handle_migrate_data_get_serial(RedChannelClient *
 static uint64_t tunnel_channel_handle_migrate_data(RedChannelClient *base,
                                               uint32_t size, void *msg)
 {
-    TunnelChannelClient *channel = SPICE_CONTAINEROF(base->channel, TunnelChannelClient, base);
+    TunnelChannelClient *channel = SPICE_CONTAINEROF(base, TunnelChannelClient, base);
     TunnelMigrateSocketList *sockets_list;
     TunnelMigrateServicesList *services_list;
     TunnelMigrateData *migrate_data = msg;
@@ -2790,22 +2784,22 @@ static void tunnel_worker_release_socket_out_data(TunnelWorker *worker, PipeItem
     sckt_out_data->push_tail = NULL;
     sckt_out_data->push_tail_size = 0;
 
-    if (worker->channel) {
+    if (worker->channel_client) {
         // can still send data to socket
         if (__client_socket_can_receive(sckt)) {
             if (sckt_out_data->ready_chunks_queue.head) {
                 // the pipe item may already be linked, if for example the send was
                 // blocked and before it finished and called release, tunnel_socket_send was called
                 if (!red_channel_client_pipe_item_is_linked(
-                        &worker->channel->base, &sckt_out_data->data_pipe_item)) {
+                        &worker->channel_client->base, &sckt_out_data->data_pipe_item)) {
                     sckt_out_data->data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-                    red_channel_client_pipe_add(&worker->channel->base, &sckt_out_data->data_pipe_item);
+                    red_channel_client_pipe_add(&worker->channel_client->base, &sckt_out_data->data_pipe_item);
                 }
             } else if ((sckt->slirp_status == SLIRP_SCKT_STATUS_SHUTDOWN_SEND) ||
                        (sckt->slirp_status == SLIRP_SCKT_STATUS_WAIT_CLOSE)) {
-                __tunnel_socket_add_fin_to_pipe(worker->channel, sckt);
+                __tunnel_socket_add_fin_to_pipe(worker->channel_client, sckt);
             } else if (sckt->slirp_status == SLIRP_SCKT_STATUS_CLOSED) {
-                __tunnel_socket_add_close_to_pipe(worker->channel, sckt);
+                __tunnel_socket_add_close_to_pipe(worker->channel_client, sckt);
             }
         }
     }
@@ -2813,7 +2807,7 @@ static void tunnel_worker_release_socket_out_data(TunnelWorker *worker, PipeItem
 
     if (((sckt->slirp_status == SLIRP_SCKT_STATUS_OPEN) ||
          (sckt->slirp_status == SLIRP_SCKT_STATUS_SHUTDOWN_RECV)) &&
-        !sckt->in_slirp_send && !worker->channel->mig_inprogress) {
+        !sckt->in_slirp_send && !worker->channel_client->mig_inprogress) {
         // for cases that slirp couldn't write whole it data to our socket buffer
         net_slirp_socket_can_send_notify(sckt->slirp_sckt);
     }
@@ -2935,8 +2929,8 @@ static int tunnel_socket_connect(SlirpUsrNetworkInterface *usr_interface,
     red_printf("TUNNEL_DBG");
 #endif
     worker = ((RedSlirpNetworkInterface *)usr_interface)->worker;
-    ASSERT(worker->channel);
-    ASSERT(!worker->channel->mig_inprogress);
+    ASSERT(worker->channel_client);
+    ASSERT(!worker->channel_client->mig_inprogress);
 
     far_service = tunnel_worker_find_service_by_addr(worker, &dst_addr, (uint32_t)ntohs(dst_port));
 
@@ -2964,7 +2958,7 @@ static int tunnel_socket_connect(SlirpUsrNetworkInterface *usr_interface,
 #endif
     *o_usr_s = sckt;
     sckt->out_data.status_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_OPEN;
-    red_channel_client_pipe_add(&worker->channel->base, &sckt->out_data.status_pipe_item);
+    red_channel_client_pipe_add(&worker->channel_client->base, &sckt->out_data.status_pipe_item);
 
     errno = EINPROGRESS;
     return -1;
@@ -2989,7 +2983,7 @@ static int tunnel_socket_send(SlirpUsrNetworkInterface *usr_interface, UserSocke
 
     worker = ((RedSlirpNetworkInterface *)usr_interface)->worker;
 
-    ASSERT(!worker->channel->mig_inprogress);
+    ASSERT(!worker->channel_client->mig_inprogress);
 
     sckt = (RedSocket *)opaque;
 
@@ -3014,7 +3008,7 @@ static int tunnel_socket_send(SlirpUsrNetworkInterface *usr_interface, UserSocke
     }
 
     if (urgent) {
-        SET_TUNNEL_ERROR(worker->channel, "urgent msgs not supported");
+        SET_TUNNEL_ERROR(worker->channel_client, "urgent msgs not supported");
         tunnel_shutdown(worker);
         errno = ECONNRESET;
         return -1;
@@ -3037,7 +3031,7 @@ static int tunnel_socket_send(SlirpUsrNetworkInterface *usr_interface, UserSocke
                 red_printf("socket out buffers overflow, socket will be closed"
                            " (local_port=%d, service_id=%d)",
                            ntohs(sckt->local_port), sckt->far_service->id);
-                tunnel_socket_force_close(worker->channel, sckt);
+                tunnel_socket_force_close(worker->channel_client, sckt);
                 size_to_send = 0;
             } else {
                 size_to_send = len;
@@ -3050,10 +3044,10 @@ static int tunnel_socket_send(SlirpUsrNetworkInterface *usr_interface, UserSocke
         sckt->out_data.data_size += size_to_send;
 
         if (sckt->out_data.ready_chunks_queue.head &&
-            !red_channel_client_pipe_item_is_linked(&worker->channel->base,
+            !red_channel_client_pipe_item_is_linked(&worker->channel_client->base,
                                              &sckt->out_data.data_pipe_item)) {
             sckt->out_data.data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-            red_channel_client_pipe_add(&worker->channel->base, &sckt->out_data.data_pipe_item);
+            red_channel_client_pipe_add(&worker->channel_client->base, &sckt->out_data.data_pipe_item);
         }
     }
 
@@ -3093,7 +3087,7 @@ static int tunnel_socket_recv(SlirpUsrNetworkInterface *usr_interface, UserSocke
     ASSERT(opaque);
     worker = ((RedSlirpNetworkInterface *)usr_interface)->worker;
 
-    ASSERT(!worker->channel->mig_inprogress);
+    ASSERT(!worker->channel_client->mig_inprogress);
 
     sckt = (RedSocket *)opaque;
 
@@ -3104,14 +3098,14 @@ static int tunnel_socket_recv(SlirpUsrNetworkInterface *usr_interface, UserSocke
 
     if ((sckt->slirp_status == SLIRP_SCKT_STATUS_SHUTDOWN_RECV) ||
         (sckt->slirp_status == SLIRP_SCKT_STATUS_WAIT_CLOSE)) {
-        SET_TUNNEL_ERROR(worker->channel, "receive was shutdown");
+        SET_TUNNEL_ERROR(worker->channel_client, "receive was shutdown");
         tunnel_shutdown(worker);
         errno = ECONNRESET;
         return -1;
     }
 
     if (sckt->slirp_status == SLIRP_SCKT_STATUS_CLOSED) {
-        SET_TUNNEL_ERROR(worker->channel, "slirp socket not connected");
+        SET_TUNNEL_ERROR(worker->channel_client, "slirp socket not connected");
         tunnel_shutdown(worker);
         errno = ECONNRESET;
         return -1;
@@ -3177,7 +3171,7 @@ static void tunnel_socket_shutdown_send(SlirpUsrNetworkInterface *usr_interface,
 #ifdef DEBUG_NETWORK
     PRINT_SCKT(sckt);
 #endif
-    ASSERT(!worker->channel->mig_inprogress);
+    ASSERT(!worker->channel_client->mig_inprogress);
 
     if (sckt->slirp_status == SLIRP_SCKT_STATUS_DELAY_ABORT) {
         return;
@@ -3189,7 +3183,7 @@ static void tunnel_socket_shutdown_send(SlirpUsrNetworkInterface *usr_interface,
         ASSERT(sckt->client_status == CLIENT_SCKT_STATUS_SHUTDOWN_SEND);
         sckt->slirp_status = SLIRP_SCKT_STATUS_WAIT_CLOSE;
     } else {
-        SET_TUNNEL_ERROR(worker->channel, "unexpected tunnel_socket_shutdown_send slirp_status=%d",
+        SET_TUNNEL_ERROR(worker->channel_client, "unexpected tunnel_socket_shutdown_send slirp_status=%d",
                          sckt->slirp_status);
         tunnel_shutdown(worker);
         return;
@@ -3200,11 +3194,11 @@ static void tunnel_socket_shutdown_send(SlirpUsrNetworkInterface *usr_interface,
         // check if there is still data to send. the fin will be sent after data is released
         // channel is alive, otherwise the sockets would have been aborted
         if (!sckt->out_data.ready_chunks_queue.head) {
-            __tunnel_socket_add_fin_to_pipe(worker->channel, sckt);
+            __tunnel_socket_add_fin_to_pipe(worker->channel_client, sckt);
         }
     } else { // if client is closed, it means the connection was aborted since we didn't
              // received fin from guest
-        SET_TUNNEL_ERROR(worker->channel,
+        SET_TUNNEL_ERROR(worker->channel_client,
                          "unexpected tunnel_socket_shutdown_send client_status=%d",
                          sckt->client_status);
         tunnel_shutdown(worker);
@@ -3229,12 +3223,12 @@ static void tunnel_socket_shutdown_recv(SlirpUsrNetworkInterface *usr_interface,
 #ifdef DEBUG_NETWORK
     PRINT_SCKT(sckt);
 #endif
-    ASSERT(!worker->channel->mig_inprogress);
+    ASSERT(!worker->channel_client->mig_inprogress);
 
     /* failure in recv can happen after the client sckt was shutdown
       (after client sent FIN, or after slirp sent FIN and client socket was closed */
     if (!__should_send_fin_to_guest(sckt)) {
-        SET_TUNNEL_ERROR(worker->channel,
+        SET_TUNNEL_ERROR(worker->channel_client,
                          "unexpected tunnel_socket_shutdown_recv client_status=%d slirp_status=%d",
                          sckt->client_status, sckt->slirp_status);
         tunnel_shutdown(worker);
@@ -3246,7 +3240,7 @@ static void tunnel_socket_shutdown_recv(SlirpUsrNetworkInterface *usr_interface,
     } else if (sckt->slirp_status == SLIRP_SCKT_STATUS_SHUTDOWN_SEND) {
         sckt->slirp_status = SLIRP_SCKT_STATUS_WAIT_CLOSE;
     } else {
-        SET_TUNNEL_ERROR(worker->channel,
+        SET_TUNNEL_ERROR(worker->channel_client,
                          "unexpected tunnel_socket_shutdown_recv slirp_status=%d",
                          sckt->slirp_status);
         tunnel_shutdown(worker);
@@ -3302,11 +3296,11 @@ static void tunnel_socket_close(SlirpUsrNetworkInterface *usr_interface, UserSoc
         // check if there is still data to send. the close will be sent after data is released.
         // close may already been pushed if it is a forced close
         if (!sckt->out_data.ready_chunks_queue.head && !sckt->pushed_close) {
-            __tunnel_socket_add_close_to_pipe(worker->channel, sckt);
+            __tunnel_socket_add_close_to_pipe(worker->channel_client, sckt);
         }
     } else if (sckt->client_status == CLIENT_SCKT_STATUS_CLOSED) {
         if (sckt->client_waits_close_ack) {
-            __tunnel_socket_add_close_ack_to_pipe(worker->channel, sckt);
+            __tunnel_socket_add_close_ack_to_pipe(worker->channel_client, sckt);
         } else {
             tunnel_worker_free_socket(worker, sckt);
         }
@@ -3333,12 +3327,12 @@ static void arm_timer(SlirpUsrNetworkInterface *usr_interface, UserTimer *timer,
 
     worker = ((RedSlirpNetworkInterface *)usr_interface)->worker;
 #ifdef DEBUG_NETWORK
-    if (!worker->channel) {
+    if (!worker->channel_client) {
         red_printf("channel not connected");
     }
 #endif
-    if (worker->channel && worker->channel->mig_inprogress) {
-        SET_TUNNEL_ERROR(worker->channel, "during migration");
+    if (worker->channel_client && worker->channel_client->mig_inprogress) {
+        SET_TUNNEL_ERROR(worker->channel_client, "during migration");
         tunnel_shutdown(worker);
         return;
     }
@@ -3398,27 +3392,25 @@ static void tunnel_worker_disconnect_slirp(TunnelWorker *worker)
 
 /* don't call disconnect from functions that might be called by slirp
    since it closes all its sockets and slirp is not aware of it */
-static void tunnel_channel_disconnect(RedChannel *channel)
+static void tunnel_channel_on_disconnect(RedChannel *channel)
 {
-    TunnelChannelClient *tunnel_channel = (TunnelChannelClient *)channel;
     TunnelWorker *worker;
     if (!channel) {
         return;
     }
     red_printf("");
-    worker = tunnel_channel->worker;
+    worker = (TunnelWorker *)channel->data;
 
     tunnel_worker_disconnect_slirp(worker);
 
     tunnel_worker_clear_routed_network(worker);
-    red_channel_destroy(channel);
-    worker->channel = NULL;
+    worker->channel_client = NULL;
 }
 
 // TODO - not MC friendly, remove
-static void tunnel_channel_disconnect_client(RedChannelClient *rcc)
+static void tunnel_channel_client_on_disconnect(RedChannelClient *rcc)
 {
-    tunnel_channel_disconnect(rcc->channel);
+    tunnel_channel_on_disconnect(rcc->channel);
 }
 
 /* interface for reds */
@@ -3439,7 +3431,7 @@ static void tunnel_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
 }
 
-static void handle_tunnel_channel_link(Channel *channel, RedClient *client,
+static void handle_tunnel_channel_link(RedChannel *channel, RedClient *client,
                                        RedsStream *stream, int migration,
                                        int num_common_caps,
                                        uint32_t *common_caps, int num_caps,
@@ -3447,15 +3439,42 @@ static void handle_tunnel_channel_link(Channel *channel, RedClient *client,
 {
     TunnelChannelClient *tcc;
     TunnelWorker *worker = (TunnelWorker *)channel->data;
-    RedChannel *tunnel_channel;
-    ChannelCbs channel_cbs = {0,};
 
-    if (worker->channel) {
-        tunnel_channel_disconnect(worker->channel->base.channel);
+    if (worker->channel_client) {
+        red_error("tunnel does not support multiple client");
     }
 
+    tcc = (TunnelChannelClient*)red_channel_client_create(sizeof(TunnelChannelClient),
+                                                          channel, client, stream);
+
+    tcc->worker = worker;
+    tcc->worker->channel_client = tcc;
+    net_slirp_set_net_interface(&worker->tunnel_interface.base);
+
+    on_new_tunnel_channel(tcc);
+}
+
+static void handle_tunnel_channel_client_migrate(RedChannelClient *rcc)
+{
+    TunnelChannelClient *tunnel_channel;
+#ifdef DEBUG_NETWORK
+    red_printf("TUNNEL_DBG: MIGRATE STARTED");
+#endif
+    tunnel_channel = (TunnelChannelClient *)rcc;
+    ASSERT(tunnel_channel == tunnel_channel->worker->channel_client);
+    tunnel_channel->mig_inprogress = TRUE;
+    net_slirp_freeze();
+    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_MIGRATE);
+}
+
+static void red_tunnel_channel_create(TunnelWorker *worker)
+{
+    RedChannel *channel;
+    ChannelCbs channel_cbs;
+    ClientCbs client_cbs = {0,};
+
     channel_cbs.config_socket = tunnel_channel_config_socket;
-    channel_cbs.disconnect = tunnel_channel_disconnect_client;
+    channel_cbs.on_disconnect = tunnel_channel_client_on_disconnect;
     channel_cbs.alloc_recv_buf = tunnel_channel_alloc_msg_rcv_buf;
     channel_cbs.release_recv_buf = tunnel_channel_release_msg_rcv_buf;
     channel_cbs.hold_item = tunnel_channel_hold_pipe_item;
@@ -3465,39 +3484,23 @@ static void handle_tunnel_channel_link(Channel *channel, RedClient *client,
     channel_cbs.handle_migrate_data = tunnel_channel_handle_migrate_data;
     channel_cbs.handle_migrate_data_get_serial = tunnel_channel_handle_migrate_data_get_serial;
 
-    tunnel_channel = red_channel_create(sizeof(RedChannel),
-                                            worker->core_interface,
-                                            migration, TRUE,
-                                            tunnel_channel_handle_message,
-                                            &channel_cbs);
-
-    if (!tunnel_channel) {
+    channel = red_channel_create(sizeof(RedChannel),
+                                 worker->core_interface,
+                                 SPICE_CHANNEL_TUNNEL, 0,
+                                 FALSE, // TODO: handle migration=TRUE
+                                 TRUE,
+                                 tunnel_channel_handle_message,
+                                 &channel_cbs);
+    if (!channel) {
         return;
     }
-    tcc = (TunnelChannelClient*)red_channel_client_create(
-                    sizeof(TunnelChannelClient),
-                    tunnel_channel, client, stream);
 
-    tcc->worker = worker;
-    tcc->worker->channel = tcc;
-    net_slirp_set_net_interface(&worker->tunnel_interface.base);
-
-    on_new_tunnel_channel(tcc);
-}
+    client_cbs.connect = handle_tunnel_channel_link;
+    client_cbs.migrate = handle_tunnel_channel_client_migrate;
+    red_channel_register_client_cbs(channel, &client_cbs);
 
-static void handle_tunnel_channel_shutdown(struct Channel *channel)
-{
-    tunnel_channel_disconnect(((TunnelWorker *)channel->data)->channel->base.channel);
-}
-
-static void handle_tunnel_channel_migrate(struct Channel *channel)
-{
-#ifdef DEBUG_NETWORK
-    red_printf("TUNNEL_DBG: MIGRATE STARTED");
-#endif
-    TunnelChannelClient *tunnel_channel = ((TunnelWorker *)channel->data)->channel;
-    tunnel_channel->mig_inprogress = TRUE;
-    net_slirp_freeze();
-    red_channel_client_pipe_add_type(&tunnel_channel->base, PIPE_ITEM_TYPE_MIGRATE);
+    worker->channel = channel;
+    red_channel_set_data(channel, worker);
+    reds_register_channel(worker->channel);
 }
 
diff --git a/server/red_worker.c b/server/red_worker.c
index 4347d24..e767623 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -8606,15 +8606,20 @@ static void red_disconnect_channel(RedChannel *channel)
 
 static void display_channel_client_disconnect(RedChannelClient *rcc)
 {
-    // TODO: MC: right now we assume single channel
+    red_channel_client_disconnect(rcc);
+}
+
+static void display_channel_client_on_disconnect(RedChannelClient *rcc)
+{
     DisplayChannel *display_channel;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     CommonChannel *common;
     RedWorker *worker;
 
-    if (!rcc || !red_channel_is_connected(rcc->channel)) {
+    if (!rcc) {
         return;
     }
+    red_printf("");
     common = SPICE_CONTAINEROF(rcc->channel, CommonChannel, base);
     worker = common->worker;
     display_channel = (DisplayChannel *)rcc->channel;
@@ -8629,8 +8634,6 @@ static void display_channel_client_disconnect(RedChannelClient *rcc)
     red_display_reset_compress_buf(dcc);
     free(dcc->send_data.free_list.res);
     red_display_destroy_streams(dcc);
-    red_channel_client_pipe_clear(rcc); // do this before deleting surfaces
-    red_channel_client_disconnect(rcc);
 
     // this was the last channel client
     if (!red_channel_is_connected(rcc->channel)) {
@@ -8654,12 +8657,15 @@ void red_disconnect_all_display_TODO_remove_me(RedChannel *channel)
     worker->display_channel = NULL;
 }
 
-static void red_migrate_display(RedWorker *worker)
+static void red_migrate_display(RedWorker *worker, RedChannelClient *rcc)
 {
-    if (worker->display_channel) {
-        red_pipes_add_verb(&worker->display_channel->common.base,
-                           SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL);
-        red_channel_pipes_add_type(&worker->display_channel->common.base, PIPE_ITEM_TYPE_MIGRATE);
+    // TODO: replace all worker->display_channel tests with
+    // is_connected
+    if (red_channel_client_is_connected(rcc)) {
+        red_pipe_add_verb(rcc, PIPE_ITEM_TYPE_MIGRATE);
+//        red_pipes_add_verb(&worker->display_channel->common.base,
+//                           SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL);
+//        red_channel_pipes_add_type(&worker->display_channel->common.base, PIPE_ITEM_TYPE_MIGRATE);
     }
 }
 
@@ -8900,7 +8906,7 @@ static inline void flush_display_commands(RedWorker *worker)
         for (;;) {
             red_channel_push(&worker->display_channel->common.base);
             if (!display_is_connected(worker) ||
-                red_channel_min_pipe_size(display_red_channel) <= MAX_PIPE_SIZE) {
+                red_channel_max_pipe_size(display_red_channel) <= MAX_PIPE_SIZE) {
                 break;
             }
             RedChannel *channel = (RedChannel *)worker->display_channel;
@@ -8961,6 +8967,9 @@ static inline void flush_cursor_commands(RedWorker *worker)
     }
 }
 
+// TODO: on timeout, don't disconnect all channeld immeduiatly - try to disconnect the slowest ones first
+// and maybe turn timeouts to several timeouts in order to disconnect channels gradually.
+// Should use disconnect or shutdown?
 static inline void flush_all_qxl_commands(RedWorker *worker)
 {
     flush_display_commands(worker);
@@ -9487,15 +9496,13 @@ static int listen_to_new_client_channel(CommonChannel *common,
     return TRUE;
 }
 
-static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_id, int migrate,
+static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_type, int migrate,
                                  event_listener_action_proc handler,
-                                 channel_disconnect_proc disconnect,
+                                 channel_disconnect_proc on_disconnect,
                                  channel_send_pipe_item_proc send_item,
                                  channel_hold_pipe_item_proc hold_item,
                                  channel_release_pipe_item_proc release_item,
                                  channel_handle_parsed_proc handle_parsed,
-                                 channel_on_incoming_error_proc on_incoming_error,
-                                 channel_on_outgoing_error_proc on_outgoing_error,
                                  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)
@@ -9505,7 +9512,7 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
     ChannelCbs channel_cbs;
 
     channel_cbs.config_socket = common_channel_config_socket;
-    channel_cbs.disconnect = disconnect;
+    channel_cbs.on_disconnect = on_disconnect;
     channel_cbs.send_item = send_item;
     channel_cbs.hold_item = hold_item;
     channel_cbs.release_item = release_item;
@@ -9515,12 +9522,12 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
     channel_cbs.handle_migrate_data = handle_migrate_data;
     channel_cbs.handle_migrate_data_get_serial = handle_migrate_data_get_serial;
 
-    channel = red_channel_create_parser(size, &worker_core, migrate,
+    channel = red_channel_create_parser(size, &worker_core,
+                                        channel_type, worker->id,
+                                        migrate,
                                         TRUE /* handle_acks */,
-                                        spice_get_client_channel_parser(channel_id, NULL),
+                                        spice_get_client_channel_parser(channel_type, NULL),
                                         handle_parsed,
-                                        on_incoming_error,
-                                        on_outgoing_error,
                                         &channel_cbs);
     common = (CommonChannel *)channel;
     if (!channel) {
@@ -9682,50 +9689,6 @@ static void display_channel_release_item(RedChannelClient *rcc, PipeItem *item,
     }
 }
 
-static void display_channel_on_incoming_error(RedChannelClient *rcc)
-{
-    red_printf("");
-    red_channel_client_shutdown(rcc);
-}
-
-static void display_channel_on_outgoing_error(RedChannelClient *rcc)
-{
-    red_printf("");
-    red_channel_client_shutdown(rcc);
-}
-
-static void cursor_channel_on_incoming_error(RedChannelClient *rcc)
-{
-    red_printf("");
-    red_channel_client_shutdown(rcc);
-}
-
-static void cursor_channel_on_outgoing_error(RedChannelClient *rcc)
-{
-    red_printf("");
-    red_channel_client_shutdown(rcc);
-}
-
-// call this from dispatcher thread context
-static void dispatch_display_channel_client_disconnect(RedChannelClient *rcc)
-{
-    RedWorker *worker = ((DisplayChannel*)rcc->channel)->common.worker;
-    struct RedDispatcher *dispatcher = worker->qxl->st->dispatcher;
-
-    red_printf("");
-    red_dispatcher_disconnect_display_client(dispatcher, rcc);
-}
-
-// call this from dispatcher thread context
-static void dispatch_cursor_channel_client_disconnect(RedChannelClient *rcc)
-{
-    RedWorker *worker = ((CursorChannel*)rcc->channel)->common.worker;
-    struct RedDispatcher *dispatcher = worker->qxl->st->dispatcher;
-
-    red_printf("");
-    red_dispatcher_disconnect_cursor_client(dispatcher, rcc);
-}
-
 static void ensure_display_channel_created(RedWorker *worker, int migrate)
 {
     DisplayChannel *display_channel;
@@ -9739,17 +9702,16 @@ static void ensure_display_channel_created(RedWorker *worker, int migrate)
             worker, sizeof(*display_channel),
             SPICE_CHANNEL_DISPLAY, migrate,
             handle_channel_events,
-            dispatch_display_channel_client_disconnect,
+            display_channel_client_on_disconnect,
             display_channel_send_item,
             display_channel_hold_pipe_item,
             display_channel_release_item,
             display_channel_handle_message,
-            display_channel_on_incoming_error,
-            display_channel_on_outgoing_error,
             display_channel_handle_migrate_mark,
             display_channel_handle_migrate_data,
             display_channel_handle_migrate_data_get_serial
             ))) {
+        red_printf("failed to create display channel");
         return;
     }
     display_channel = worker->display_channel;
@@ -9791,7 +9753,7 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
     if (!dcc) {
         return;
     }
-
+    red_printf("New display (client %p) dcc %p stream %p", client, dcc, stream);
     stream_buf_size = 32*1024;
     dcc->send_data.stream_outbuf = spice_malloc(stream_buf_size);
     dcc->send_data.stream_outbuf_size = stream_buf_size;
@@ -9836,11 +9798,15 @@ error:
 
 static void cursor_channel_client_disconnect(RedChannelClient *rcc)
 {
-    if (!red_channel_is_connected(rcc->channel)) {
+    red_channel_client_disconnect(rcc);
+}
+
+static void cursor_channel_client_on_disconnect(RedChannelClient *rcc)
+{
+    if (!rcc) {
         return;
     }
     red_reset_cursor_cache(rcc);
-    red_channel_client_disconnect(rcc);
 }
 
 static void red_disconnect_cursor(RedChannel *channel)
@@ -9857,13 +9823,14 @@ static void red_disconnect_cursor(RedChannel *channel)
     red_disconnect_channel(channel);
 }
 
-static void red_migrate_cursor(RedWorker *worker)
+static void red_migrate_cursor(RedWorker *worker, RedChannelClient *rcc)
 {
-    if (cursor_is_connected(worker)) {
-        red_channel_pipes_add_type(&worker->cursor_channel->common.base,
-                                   PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
-        red_channel_pipes_add_type(&worker->cursor_channel->common.base,
-                                   PIPE_ITEM_TYPE_MIGRATE);
+//    if (cursor_is_connected(worker)) {
+    if (red_channel_client_is_connected(rcc)) {
+        red_channel_client_pipe_add_type(rcc,
+                                         PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
+        red_channel_client_pipe_add_type(rcc,
+                                         PIPE_ITEM_TYPE_MIGRATE);
     }
 }
 
@@ -9950,13 +9917,11 @@ static void ensure_cursor_channel_created(RedWorker *worker, int migrate)
         worker, sizeof(*worker->cursor_channel),
         SPICE_CHANNEL_CURSOR, migrate,
         handle_channel_events,
-        dispatch_cursor_channel_client_disconnect,
+        cursor_channel_client_on_disconnect,
         cursor_channel_send_item,
         cursor_channel_hold_pipe_item,
         cursor_channel_release_item,
         red_channel_client_handle_message,
-        cursor_channel_on_incoming_error,
-        cursor_channel_on_outgoing_error,
         NULL,
         NULL,
         NULL);
@@ -10424,6 +10389,10 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
     case RED_WORKER_MESSAGE_RESET_IMAGE_CACHE:
     case RED_WORKER_MESSAGE_STOP:
     case RED_WORKER_MESSAGE_LOADVM_COMMANDS:
+    case RED_WORKER_MESSAGE_DISPLAY_CHANNEL_CREATE:
+    case RED_WORKER_MESSAGE_CURSOR_CHANNEL_CREATE:
+    case RED_WORKER_MESSAGE_DISPLAY_DISCONNECT:
+    case RED_WORKER_MESSAGE_CURSOR_DISCONNECT:
         write_ready = 1;
     default:
         break;
@@ -10476,6 +10445,14 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
     case RED_WORKER_MESSAGE_DESTROY_PRIMARY_SURFACE:
         handle_dev_destroy_primary_surface(worker);
         break;
+    case RED_WORKER_MESSAGE_DISPLAY_CHANNEL_CREATE: {
+        RedChannel *red_channel;
+        // TODO: handle seemless migration. Temp, setting migrate to FALSE
+        ensure_display_channel_created(worker, FALSE);
+        red_channel = &worker->display_channel->common.base;
+        send_data(worker->channel, &red_channel, sizeof(RedChannel *));
+        break;
+    }
     case RED_WORKER_MESSAGE_DISPLAY_CONNECT: {
         RedsStream *stream;
         RedClient *client;
@@ -10488,20 +10465,15 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
         handle_new_display_channel(worker, client, stream, migrate);
         break;
     }
-    case RED_WORKER_MESSAGE_DISPLAY_DISCONNECT_CLIENT: {
+    case RED_WORKER_MESSAGE_DISPLAY_DISCONNECT: {
         RedChannelClient *rcc;
 
         red_printf("disconnect display client");
         receive_data(worker->channel, &rcc, sizeof(RedChannelClient *));
+        ASSERT(rcc);
         display_channel_client_disconnect(rcc);
-        message = RED_WORKER_MESSAGE_READY;
-        write_message(worker->channel, &message);
         break;
     }
-    case RED_WORKER_MESSAGE_DISPLAY_DISCONNECT:
-        red_printf("disconnect");
-        red_disconnect_all_display_TODO_remove_me((RedChannel *)worker->display_channel);
-        break;
     case RED_WORKER_MESSAGE_STOP: {
         red_printf("stop");
         handle_dev_stop(worker);
@@ -10511,10 +10483,22 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
         red_printf("start");
         handle_dev_start(worker);
         break;
-    case RED_WORKER_MESSAGE_DISPLAY_MIGRATE:
-        red_printf("migrate");
-        red_migrate_display(worker);
+    case RED_WORKER_MESSAGE_DISPLAY_MIGRATE: {
+        RedChannelClient *rcc;
+        red_printf("migrate display client");
+        receive_data(worker->channel, &rcc, sizeof(RedChannelClient *));
+        ASSERT(rcc);
+        red_migrate_display(worker, rcc);
+        break;
+    }
+    case RED_WORKER_MESSAGE_CURSOR_CHANNEL_CREATE: {
+        RedChannel *red_channel;
+        // TODO: handle seemless migration. Temp, setting migrate to FALSE
+        ensure_cursor_channel_created(worker, FALSE);
+        red_channel = &worker->cursor_channel->common.base;
+        send_data(worker->channel, &red_channel, sizeof(RedChannel *));
         break;
+    }
     case RED_WORKER_MESSAGE_CURSOR_CONNECT: {
         RedsStream *stream;
         RedClient *client;
@@ -10527,24 +10511,23 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
         red_connect_cursor(worker, client, stream, migrate);
         break;
     }
-    case RED_WORKER_MESSAGE_CURSOR_DISCONNECT_CLIENT: {
+    case RED_WORKER_MESSAGE_CURSOR_DISCONNECT: {
         RedChannelClient *rcc;
 
         red_printf("disconnect cursor client");
         receive_data(worker->channel, &rcc, sizeof(RedChannelClient *));
+        ASSERT(rcc);
         cursor_channel_client_disconnect(rcc);
-        message = RED_WORKER_MESSAGE_READY;
-        write_message(worker->channel, &message);
         break;
     }
-    case RED_WORKER_MESSAGE_CURSOR_DISCONNECT:
-        red_printf("cursor disconnect");
-        red_disconnect_cursor((RedChannel *)worker->cursor_channel);
-        break;
-    case RED_WORKER_MESSAGE_CURSOR_MIGRATE:
-        red_printf("cursor migrate");
-        red_migrate_cursor(worker);
+    case RED_WORKER_MESSAGE_CURSOR_MIGRATE: {
+        RedChannelClient *rcc;
+        red_printf("migrate cursor client");
+        receive_data(worker->channel, &rcc, sizeof(RedChannelClient *));
+        ASSERT(rcc);
+        red_migrate_cursor(worker, rcc);
         break;
+    }
     case RED_WORKER_MESSAGE_SET_COMPRESSION:
         receive_data(worker->channel, &worker->image_compression,
                      sizeof(spice_image_compression_t));
diff --git a/server/red_worker.h b/server/red_worker.h
index 6fbe061..26c43ad 100644
--- a/server/red_worker.h
+++ b/server/red_worker.h
@@ -53,13 +53,11 @@ enum {
     RED_WORKER_MESSAGE_READY,
     RED_WORKER_MESSAGE_DISPLAY_CONNECT,
     RED_WORKER_MESSAGE_DISPLAY_DISCONNECT,
-    RED_WORKER_MESSAGE_DISPLAY_DISCONNECT_CLIENT,
     RED_WORKER_MESSAGE_DISPLAY_MIGRATE,
     RED_WORKER_MESSAGE_START,
     RED_WORKER_MESSAGE_STOP,
     RED_WORKER_MESSAGE_CURSOR_CONNECT,
     RED_WORKER_MESSAGE_CURSOR_DISCONNECT,
-    RED_WORKER_MESSAGE_CURSOR_DISCONNECT_CLIENT,
     RED_WORKER_MESSAGE_CURSOR_MIGRATE,
     RED_WORKER_MESSAGE_SET_COMPRESSION,
     RED_WORKER_MESSAGE_SET_STREAMING_VIDEO,
@@ -83,6 +81,9 @@ enum {
     RED_WORKER_MESSAGE_DESTROY_SURFACE_WAIT_ASYNC,
     /* suspend/windows resolution change command */
     RED_WORKER_MESSAGE_FLUSH_SURFACES_ASYNC,
+
+    RED_WORKER_MESSAGE_DISPLAY_CHANNEL_CREATE,
+    RED_WORKER_MESSAGE_CURSOR_CHANNEL_CREATE,
 };
 
 typedef uint32_t RedWorkerMessage;
diff --git a/server/reds.c b/server/reds.c
index b8b4d26..0387a5c 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -193,17 +193,23 @@ typedef struct RedsStatValue {
 
 typedef struct RedsMigSpice RedsMigSpice;
 
+typedef struct RedsChannel {
+    struct RedsChannel *next;
+    RedChannel *base;
+
+    int num_common_caps;
+    uint32_t *common_caps;
+} RedsChannel;
+
 typedef struct RedsState {
     int listen_socket;
     int secure_listen_socket;
     SpiceWatch *listen_watch;
     SpiceWatch *secure_listen_watch;
-    int disconnecting;
     VDIPortState agent_state;
     int pending_mouse_event;
     Ring clients;
     int num_clients;
-    Channel *main_channel_factory;
     MainChannel *main_channel;
 
     int mig_wait_connect;
@@ -212,7 +218,7 @@ typedef struct RedsState {
     int mig_target;
     RedsMigSpice *mig_spice;
     int num_of_channels;
-    Channel *channels;
+    RedsChannel *channels;
     int mouse_mode;
     int is_client_mouse_allowed;
     int dispatcher_allows_client_mouse;
@@ -297,6 +303,8 @@ struct ChannelSecurityOptions {
     ChannelSecurityOptions *next;
 };
 
+static void reds_dispose_channel(RedsChannel *channel);
+
 static ChannelSecurityOptions *channels_security = NULL;
 static int default_channel_security =
     SPICE_CHANNEL_SECURITY_NONE | SPICE_CHANNEL_SECURITY_SSL;
@@ -506,21 +514,29 @@ void reds_update_stat_value(uint32_t value)
 
 #endif
 
-void reds_register_channel(Channel *channel)
+void reds_register_channel(RedChannel *channel)
 {
+    RedsChannel *reds_channel;
+
     ASSERT(reds);
-    channel->next = reds->channels;
-    reds->channels = channel;
+    // TODO: should channels be released upon some destructor?
+    reds_channel = spice_malloc0(sizeof(RedsChannel));
+    reds_channel->base = channel;
+    reds_channel->next = reds->channels;
+    reds->channels = reds_channel;
     reds->num_of_channels++;
 }
 
-void reds_unregister_channel(Channel *channel)
+void reds_unregister_channel(RedChannel *channel)
 {
-    Channel **now = &reds->channels;
+    RedsChannel **now = &reds->channels;
 
     while (*now) {
-        if (*now == channel) {
-            *now = channel->next;
+        if ((*now)->base == channel) {
+            RedsChannel *free_channel = *now;
+            *now = free_channel->next;
+            reds_dispose_channel(free_channel);
+            free(free_channel);
             reds->num_of_channels--;
             return;
         }
@@ -529,10 +545,10 @@ void reds_unregister_channel(Channel *channel)
     red_printf("not found");
 }
 
-static Channel *reds_find_channel(uint32_t type, uint32_t id)
+static RedsChannel *reds_find_channel(uint32_t type, uint32_t id)
 {
-    Channel *channel = reds->channels;
-    while (channel && !(channel->type == type && channel->id == id)) {
+    RedsChannel *channel = reds->channels;
+    while (channel && !(channel->base->type == type && channel->base->id == id)) {
         channel = channel->next;
     }
     return channel;
@@ -590,27 +606,42 @@ static int reds_main_channel_connected(void)
 
 void reds_client_disconnect(RedClient *client)
 {
-    if (!reds_main_channel_connected() || client->disconnecting) {
+    // TODO: rename reds_main_channel_connected, or really set main_channel to NULL on disconnect,
+    // though still, it is not a reason not to disconnect the rest of the channels
+    if (!reds_main_channel_connected()  || client->disconnecting) { 
+        /* case of recursion (main_channel_client_on_disconnect->
+         *                    reds_client_disconnect->red_client_destroy-<main_channel...
+         */
         return;
     }
 
     red_printf("");
+    // why is "disconnecting" even needed? it is synchronic, even in the dispatcher we are now waiting for disconnection
+    // Are there recursive calls? Maybe from main_channel?
     client->disconnecting = TRUE;
 
-    /* Reset write filter to start with clean state on client reconnect */
-    agent_msg_filter_init(&reds->agent_state.write_filter, agent_copypaste,
-                          TRUE);
-    /* Throw away pending chunks from the current (if any) and future
-       messages read from the agent */
-    reds->agent_state.read_filter.result = AGENT_MSG_FILTER_DISCARD;
-    reds->agent_state.read_filter.discard_all = TRUE;
+    // TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how?)
+    // We shouldn't initialize the agent when there are still clients connected
 
     ring_remove(&client->link);
     reds->num_clients--;
     red_client_destroy(client);
 
-    reds_mig_cleanup();
-    reds->disconnecting = FALSE;
+   // TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how? Maybe throw away messages
+   // if we are in the middle of one from another client)
+   // We shouldn't initialize the agent when there are still clients connected
+    if (reds->num_clients == 0) {
+       /* Reset write filter to start with clean state on client reconnect */
+        agent_msg_filter_init(&reds->agent_state.write_filter, agent_copypaste,
+                              TRUE);
+
+        /* Throw away pending chunks from the current (if any) and future
+         *  messages read from the agent */
+        reds->agent_state.read_filter.result = AGENT_MSG_FILTER_DISCARD;
+        reds->agent_state.read_filter.discard_all = TRUE;
+
+        reds_mig_cleanup();
+    }
 }
 
 // TODO: go over all usage of reds_disconnect, most/some of it should be converted to
@@ -623,6 +654,7 @@ static void reds_disconnect(void)
     RING_FOREACH_SAFE(link, next, &reds->clients) {
         reds_client_disconnect(SPICE_CONTAINEROF(link, RedClient, link));
     }
+    reds_mig_cleanup();
 }
 
 static void reds_mig_disconnect()
@@ -949,11 +981,11 @@ int reds_num_of_channels()
 static int secondary_channels[] = {
     SPICE_CHANNEL_MAIN, SPICE_CHANNEL_DISPLAY, SPICE_CHANNEL_CURSOR, SPICE_CHANNEL_INPUTS};
 
-static int channel_is_secondary(Channel *channel)
+static int channel_is_secondary(RedsChannel *channel)
 {
     int i;
     for (i = 0 ; i < sizeof(secondary_channels)/sizeof(secondary_channels[0]); ++i) {
-        if (channel->type == secondary_channels[i]) {
+        if (channel->base->type == secondary_channels[i]) {
             return TRUE;
         }
     }
@@ -962,7 +994,7 @@ static int channel_is_secondary(Channel *channel)
 
 void reds_fill_channels(SpiceMsgChannels *channels_info)
 {
-    Channel *channel;
+    RedsChannel *channel;
     int i;
     int used_channels = 0;
 
@@ -973,8 +1005,8 @@ void reds_fill_channels(SpiceMsgChannels *channels_info)
         if (reds->num_clients > 1 && !channel_is_secondary(channel)) {
             continue;
         }
-        channels_info->channels[used_channels].type = channel->type;
-        channels_info->channels[used_channels].id = channel->id;
+        channels_info->channels[used_channels].type = channel->base->type;
+        channels_info->channels[used_channels].id = channel->base->id;
         used_channels++;
     }
     channels_info->num_of_channels = used_channels;
@@ -1350,7 +1382,7 @@ static int sync_write(RedsStream *stream, const void *in_buf, size_t n)
     return TRUE;
 }
 
-static void reds_channel_set_common_caps(Channel *channel, int cap, int active)
+static void reds_channel_set_common_caps(RedsChannel *channel, int cap, int active)
 {
     int nbefore, n;
 
@@ -1367,7 +1399,7 @@ static void reds_channel_set_common_caps(Channel *channel, int cap, int active)
     }
 }
 
-static void reds_channel_init_auth_caps(Channel *channel)
+static void reds_channel_init_auth_caps(RedsChannel *channel)
 {
     if (sasl_enabled) {
         reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_AUTH_SASL, TRUE);
@@ -1377,12 +1409,8 @@ static void reds_channel_init_auth_caps(Channel *channel)
     reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION, TRUE);
 }
 
-void reds_channel_dispose(Channel *channel)
+static void reds_dispose_channel(RedsChannel *channel)
 {
-    free(channel->caps);
-    channel->caps = NULL;
-    channel->num_caps = 0;
-
     free(channel->common_caps);
     channel->common_caps = NULL;
     channel->num_common_caps = 0;
@@ -1392,8 +1420,8 @@ static int reds_send_link_ack(RedLinkInfo *link)
 {
     SpiceLinkHeader header;
     SpiceLinkReply ack;
-    Channel caps = { 0, };
-    Channel *channel;
+    RedsChannel common_caps = { 0, };
+    RedsChannel *channel;
     BUF_MEM *bmBuf;
     BIO *bio;
     int ret = FALSE;
@@ -1407,13 +1435,13 @@ static int reds_send_link_ack(RedLinkInfo *link)
 
     channel = reds_find_channel(link->link_mess->channel_type, 0);
     if (!channel) {
-        channel = &caps;
+        channel = &common_caps;
     }
 
     reds_channel_init_auth_caps(channel); /* make sure common caps are set */
 
     ack.num_common_caps = channel->num_common_caps;
-    ack.num_channel_caps = channel->num_caps;
+    ack.num_channel_caps = channel->base ? channel->base->num_caps : 0;
     header.size += (ack.num_common_caps + ack.num_channel_caps) * sizeof(uint32_t);
     ack.caps_offset = sizeof(SpiceLinkReply);
 
@@ -1441,13 +1469,15 @@ static int reds_send_link_ack(RedLinkInfo *link)
         goto end;
     if (!sync_write(link->stream, channel->common_caps, channel->num_common_caps * sizeof(uint32_t)))
         goto end;
-    if (!sync_write(link->stream, channel->caps, channel->num_caps * sizeof(uint32_t)))
-        goto end;
+    if (channel->base) {
+        if (!sync_write(link->stream, channel->base->caps, channel->base->num_caps * sizeof(uint32_t)))
+            goto end;
+    }
 
     ret = TRUE;
 
 end:
-    reds_channel_dispose(&caps);
+    reds_dispose_channel(&common_caps);
     BIO_free(bio);
     return ret;
 }
@@ -1540,18 +1570,19 @@ static void reds_handle_main_link(RedLinkInfo *link)
     link->link_mess = NULL;
     reds_link_free(link);
     caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
-    if (!reds->main_channel_factory) {
-        reds->main_channel_factory = main_channel_init();
+    if (!reds->main_channel) {
+        reds->main_channel = main_channel_init();
+        ASSERT(reds->main_channel);
     }
     client = red_client_new();
     ring_add(&reds->clients, &client->link);
     reds->num_clients++;
-    mcc = main_channel_link(reds->main_channel_factory, client,
-                  stream, connection_id, reds->mig_target, link_mess->num_common_caps,
-                  link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
-                  link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
-    reds->main_channel = (MainChannel*)reds->main_channel_factory->data;
-    ASSERT(reds->main_channel);
+    mcc = main_channel_link(reds->main_channel, client,
+                            stream, connection_id, reds->mig_target,
+                            link_mess->num_common_caps,
+                            link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
+                            link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
+    red_printf("NEW Client %p mcc %p connect-id %d", client, mcc, connection_id);
     free(link_mess);
     red_client_set_main(client, mcc);
 
@@ -1608,7 +1639,7 @@ static void openssl_init(RedLinkInfo *link)
 
 static void reds_handle_other_links(RedLinkInfo *link)
 {
-    Channel *channel;
+    RedsChannel *channel;
     RedClient *client = NULL;
     RedsStream *stream;
     SpiceLinkMess *link_mess;
@@ -1617,7 +1648,7 @@ static void reds_handle_other_links(RedLinkInfo *link)
     link_mess = link->link_mess;
     if (reds->main_channel) {
         client = main_channel_get_client_by_link_id(reds->main_channel,
-            link_mess->connection_id);
+                                                    link_mess->connection_id);
     }
 
     // TODO: MC: broke migration (at least for the dont-drop-connection kind).
@@ -1650,9 +1681,12 @@ static void reds_handle_other_links(RedLinkInfo *link)
     link->link_mess = NULL;
     reds_link_free(link);
     caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
-    channel->link(channel, client, stream, reds->mig_target, link_mess->num_common_caps,
-                  link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
-                  link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
+    channel->base->client_cbs.connect(channel->base, client, stream, reds->mig_target,
+                                      link_mess->num_common_caps,
+                                      link_mess->num_common_caps ? caps : NULL,
+                                      link_mess->num_channel_caps,
+                                      link_mess->num_channel_caps ?
+                                          caps + link_mess->num_common_caps : NULL);
     free(link_mess);
 }
 
@@ -3077,7 +3111,7 @@ static void reds_mig_finished(int completed)
     reds->mig_inprogress = TRUE;
 
     if (completed) {
-        Channel *channel;
+        RingItem *link, *next;
 
         reds->mig_wait_disconnect = TRUE;
         core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
@@ -3086,14 +3120,16 @@ static void reds_mig_finished(int completed)
         //  - it can have an empty migrate - that seems ok
         //  - I can try to fill it's migrate, then move stuff from reds.c there, but a lot of data
         //    is in reds state right now.
+        //    currently the migrate callback of main_channel does nothing
         main_channel_push_migrate(reds->main_channel);
-        channel = reds->channels;
-        while (channel) {
-            channel->migrate(channel);
-            channel = channel->next;
+
+        RING_FOREACH_SAFE(link, next, &reds->clients) {
+            red_client_migrate(SPICE_CONTAINEROF(link, RedClient, link));
         }
     } else {
         main_channel_push_migrate_cancel(reds->main_channel);
+        // TODO: all the seemless migration is broken. Before MC we waited for disconection of one client,
+        // no we need to wait to all the clients (see mig_timer)?
         reds_mig_cleanup();
     }
 }
diff --git a/server/reds.h b/server/reds.h
index 6930fe6..0bbda14 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -31,13 +31,10 @@
 #include "common/marshaller.h"
 #include "common/messages.h"
 #include "spice.h"
+#include "red_channel.h"
 
 #define SPICE_GNUC_VISIBLE __attribute__ ((visibility ("default")))
 
-typedef struct RedsStream RedsStream;
-typedef struct RedClient RedClient;
-typedef struct MainChannelClient MainChannelClient;
-
 #if HAVE_SASL
 typedef struct RedsSASL {
     sasl_conn_t *conn;
@@ -88,22 +85,6 @@ struct RedsStream {
     ssize_t (*writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
 };
 
-typedef struct Channel {
-    struct Channel *next;
-    uint32_t type;
-    uint32_t id;
-    int num_common_caps;
-    uint32_t *common_caps;
-    int num_caps;
-    uint32_t *caps;
-    void (*link)(struct Channel *, RedClient *client, RedsStream *stream,
-                 int migration, int num_common_caps,
-                 uint32_t *common_caps, int num_caps, uint32_t *caps);
-    void (*shutdown)(struct Channel *);
-    void (*migrate)(struct Channel *);
-    void *data;
-} Channel;
-
 struct QXLState {
     QXLInterface          *qif;
     struct RedDispatcher  *dispatcher;
@@ -114,8 +95,6 @@ struct SpiceNetWireState {
     struct TunnelWorker *worker;
 };
 
-void reds_channel_dispose(Channel *channel);
-
 ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
 ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte);
 ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
@@ -127,8 +106,8 @@ void reds_update_mm_timer(uint32_t mm_time);
 uint32_t reds_get_mm_time(void);
 void reds_set_client_mouse_allowed(int is_client_mouse_allowed,
                                    int x_res, int y_res);
-void reds_register_channel(Channel *channel);
-void reds_unregister_channel(Channel *channel);
+void reds_register_channel(RedChannel *channel);
+void reds_unregister_channel(RedChannel *channel);
 int reds_get_mouse_mode(void); // used by inputs_channel
 int reds_get_agent_mouse(void); // used by inputs_channel
 int reds_has_vdagent(void); // used by inputs channel
diff --git a/server/smartcard.c b/server/smartcard.c
index 36004f7..2ff1310 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -22,6 +22,7 @@
 #include <arpa/inet.h>
 
 #include <vscard_common.h>
+#include "server/reds.h"
 #include "server/char_device.h"
 #include "server/red_channel.h"
 #include "server/smartcard.h"
@@ -78,7 +79,7 @@ static void smartcard_on_message_from_device(
     RedChannelClient *rcc, VSCMsgHeader *vheader);
 static SmartCardDeviceState* smartcard_device_state_new();
 static void smartcard_device_state_free(SmartCardDeviceState* st);
-static void smartcard_register_channel(void);
+static void smartcard_init(void);
 
 void smartcard_char_device_wakeup(SpiceCharDeviceInstance *sin)
 {
@@ -169,7 +170,7 @@ static int smartcard_char_device_add_to_readers(SpiceCharDeviceInstance *char_de
     }
     state->reader_id = g_smartcard_readers.num;
     g_smartcard_readers.sin[g_smartcard_readers.num++] = char_device;
-    smartcard_register_channel();
+    smartcard_init();
     return 0;
 }
 
@@ -274,7 +275,6 @@ static int smartcard_channel_client_config_socket(RedChannelClient *rcc)
 static uint8_t *smartcard_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
                                                     SpiceDataHeader *msg_header)
 {
-    //red_printf("allocing %d bytes", msg_header->size);
     return spice_malloc(msg_header->size);
 }
 
@@ -337,10 +337,9 @@ static void smartcard_channel_release_pipe_item(RedChannelClient *rcc,
     free(item);
 }
 
-static void smartcard_channel_client_disconnect(RedChannelClient *rcc)
+static void smartcard_channel_on_disconnect(RedChannelClient *rcc)
 {
     smartcard_readers_detach_all(rcc);
-    red_channel_client_destroy(rcc);
 }
 
 /* this is called from both device input and client input. since the device is
@@ -487,63 +486,55 @@ static void smartcard_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *it
 {
 }
 
-static void smartcard_link(Channel *channel, RedClient *client,
+static void smartcard_connect(RedChannel *channel, RedClient *client,
                         RedsStream *stream, int migration,
                         int num_common_caps, uint32_t *common_caps,
                         int num_caps, uint32_t *caps)
 {
     RedChannelClient *rcc;
-    ChannelCbs channel_cbs;
 
-    if (!channel->data) {
-        memset(&channel_cbs, sizeof(channel_cbs), 0);
-        channel_cbs.config_socket = smartcard_channel_client_config_socket;
-        channel_cbs.disconnect = smartcard_channel_client_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_alloc_msg_rcv_buf;
-        channel_cbs.release_recv_buf = smartcard_channel_release_msg_rcv_buf;
-        channel->data =
-            red_channel_create(sizeof(SmartCardChannel),
-                                        core, migration,
-                                        FALSE /* handle_acks */,
-                                        smartcard_channel_handle_message,
-                                        &channel_cbs);
-        if (channel->data) {
-            red_channel_init_outgoing_messages_window((RedChannel*)channel->data);
-        } else {
-            red_printf("ERROR: smartcard channel creation failed");
-            return;
-        }
-    }
-    rcc = red_channel_client_create(sizeof(RedChannelClient),
-                                    (RedChannel*)channel->data, client, stream);
+    rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream);
     red_channel_client_ack_zero_messages_window(rcc);
 }
 
-static void smartcard_shutdown(Channel *channel)
+static void smartcard_migrate(RedChannelClient *rcc)
 {
 }
 
-static void smartcard_migrate(Channel *channel)
-{
-}
+SmartCardChannel *g_smartcard_channel;
 
-static void smartcard_register_channel(void)
+static void smartcard_init(void)
 {
-    Channel *channel;
-    static int registered = 0;
-
-    if (registered) {
-        return;
+    ChannelCbs channel_cbs;
+    ClientCbs client_cbs = {0,};
+
+    ASSERT(!g_smartcard_channel);
+
+    memset(&channel_cbs, sizeof(channel_cbs), 0);
+    memset(&client_cbs, sizeof(client_cbs), 0);
+
+    channel_cbs.config_socket = smartcard_channel_client_config_socket;
+    channel_cbs.on_disconnect = smartcard_channel_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_alloc_msg_rcv_buf;
+    channel_cbs.release_recv_buf = smartcard_channel_release_msg_rcv_buf;
+
+    g_smartcard_channel = (SmartCardChannel*)red_channel_create(sizeof(SmartCardChannel),
+                                             core, SPICE_CHANNEL_SMARTCARD, 0,
+                                             FALSE /* migration - TODO?*/,
+                                             FALSE /* handle_acks */,
+                                             smartcard_channel_handle_message,
+                                             &channel_cbs);
+
+    if (!g_smartcard_channel) {
+        red_error("failed to allocate Inputs Channel");
     }
-    red_printf("registering smartcard channel");
-    registered = 1;
-    channel = spice_new0(Channel, 1);
-    channel->type = SPICE_CHANNEL_SMARTCARD;
-    channel->link = smartcard_link;
-    channel->shutdown = smartcard_shutdown;
-    channel->migrate = smartcard_migrate;
-    reds_register_channel(channel);
+
+    client_cbs.connect = smartcard_connect;
+    client_cbs.migrate = smartcard_migrate;
+    red_channel_register_client_cbs(&g_smartcard_channel->base, &client_cbs);
+
+    reds_register_channel(&g_smartcard_channel->base);
 }
diff --git a/server/snd_worker.c b/server/snd_worker.c
index 332612f..4c9a6f0 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -28,6 +28,7 @@
 
 #include "spice.h"
 #include "red_common.h"
+#include "main_channel.h"
 #include "reds.h"
 #include "red_dispatcher.h"
 #include "snd_worker.h"
@@ -35,12 +36,6 @@
 #include "generated_marshallers.h"
 #include "demarshallers.h"
 
-/* main_channel.h inclusion drags red_channel.h which has conflicting types.
- * until the channels here are defined in terms of red_channel.h we have some
- * duplicate declarations */
-MainChannelClient *red_client_get_main(RedClient *client);
-int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
-
 #define MAX_SEND_VEC 100
 
 #define RECIVE_BUF_SIZE (16 * 1024 * 2)
@@ -78,10 +73,10 @@ enum RecordCommand {
 #define SND_RECORD_VOLUME_MASK (1 << SND_RECORD_VOLUME)
 
 typedef struct SndChannel SndChannel;
-typedef void (*send_messages_proc)(void *in_channel);
-typedef int (*handle_message_proc)(SndChannel *channel, size_t size, uint32_t type, void *message);
-typedef void (*on_message_done_proc)(SndChannel *channel);
-typedef void (*cleanup_channel_proc)(SndChannel *channel);
+typedef void (*snd_channel_send_messages_proc)(void *in_channel);
+typedef int (*snd_channel_handle_message_proc)(SndChannel *channel, size_t size, uint32_t type, void *message);
+typedef void (*snd_channel_on_message_done_proc)(SndChannel *channel);
+typedef void (*snd_channel_cleanup_channel_proc)(SndChannel *channel);
 
 typedef struct SndWorker SndWorker;
 
@@ -90,6 +85,8 @@ struct SndChannel {
     SndWorker *worker;
     spice_parse_channel_func_t parser;
 
+    RedChannelClient *channel_client;
+
     int active;
     int client_active;
     int blocked;
@@ -116,10 +113,10 @@ struct SndChannel {
         uint8_t *end;
     } recive_data;
 
-    send_messages_proc send_messages;
-    handle_message_proc handle_message;
-    on_message_done_proc on_message_done;
-    cleanup_channel_proc cleanup;
+    snd_channel_send_messages_proc send_messages;
+    snd_channel_handle_message_proc handle_message;
+    snd_channel_on_message_done_proc on_message_done;
+    snd_channel_cleanup_channel_proc cleanup;
     int num_caps;
     uint32_t *caps;
 };
@@ -146,7 +143,7 @@ typedef struct PlaybackChannel {
 } PlaybackChannel;
 
 struct SndWorker {
-    Channel base;
+    RedChannel *base_channel;
     SndChannel *connection;
     SndWorker *next;
     int active;
@@ -193,7 +190,7 @@ typedef struct RecordChannel {
     uint32_t celt_buf[FRAME_SIZE];
 } RecordChannel;
 
-static SndWorker *workers = NULL;
+static SndWorker *workers;
 static uint32_t playback_compression = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
 
 static void snd_receive(void* data);
@@ -863,10 +860,11 @@ static void snd_record_send(void* data)
 static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_id,
                                  RedClient *client,
                                  RedsStream *stream,
-                                 int migrate, send_messages_proc send_messages,
-                                 handle_message_proc handle_message,
-                                 on_message_done_proc on_message_done,
-                                 cleanup_channel_proc cleanup,
+                                 int migrate,
+                                 snd_channel_send_messages_proc send_messages,
+                                 snd_channel_handle_message_proc handle_message,
+                                 snd_channel_on_message_done_proc on_message_done,
+                                 snd_channel_cleanup_channel_proc cleanup,
                                  uint32_t *caps, int num_caps)
 {
     SndChannel *channel;
@@ -926,6 +924,10 @@ static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_i
     channel->cleanup = cleanup;
     channel->num_caps = num_caps;
     channel->caps = spice_memdup(caps, num_caps * sizeof(uint32_t));
+
+    channel->channel_client = red_channel_client_create_dummy(sizeof(RedChannelClient),
+                                                              worker->base_channel,
+                                                              client);
     return channel;
 
 error2:
@@ -936,9 +938,15 @@ error1:
     return NULL;
 }
 
-static void snd_shutdown(Channel *channel)
+static void snd_disconnect_channel_client(RedChannelClient *rcc)
 {
-    SndWorker *worker = (SndWorker *)channel;
+    SndWorker *worker;
+
+    ASSERT(rcc->channel);
+    ASSERT(rcc->channel->data);
+    worker = (SndWorker *)rcc->channel->data;
+
+    ASSERT(worker->connection->channel_client == rcc);
     snd_disconnect_channel(worker->connection);
 }
 
@@ -1096,13 +1104,13 @@ static void snd_playback_cleanup(SndChannel *channel)
     celt051_mode_destroy(playback_channel->celt_mode);
 }
 
-static void snd_set_playback_peer(Channel *channel, RedClient *client, RedsStream *stream,
+static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
                                   int migration, int num_common_caps, uint32_t *common_caps,
                                   int num_caps, uint32_t *caps)
 {
-    SndWorker *worker = (SndWorker *)channel;
-    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
+    SndWorker *worker = channel->data;
     PlaybackChannel *playback_channel;
+    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
     CELTEncoder *celt_encoder;
     CELTMode *celt_mode;
     int celt_error;
@@ -1158,10 +1166,16 @@ error_1:
     celt051_mode_destroy(celt_mode);
 }
 
-static void snd_record_migrate(Channel *channel)
+static void snd_record_migrate_channel_client(RedChannelClient *rcc)
 {
-    SndWorker *worker = (SndWorker *)channel;
+    SndWorker *worker;
+
+    ASSERT(rcc->channel);
+    ASSERT(rcc->channel->data);
+    worker = (SndWorker *)rcc->channel->data;
+
     if (worker->connection) {
+        ASSERT(worker->connection->channel_client == rcc);
         snd_set_command(worker->connection, SND_RECORD_MIGRATE_MASK);
         snd_record_send(worker->connection);
     }
@@ -1290,19 +1304,19 @@ static void on_new_record_channel(SndWorker *worker)
 
 static void snd_record_cleanup(SndChannel *channel)
 {
-    RecordChannel *record_channel = (RecordChannel *)channel;
+    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
 
     celt051_decoder_destroy(record_channel->celt_decoder);
     celt051_mode_destroy(record_channel->celt_mode);
 }
 
-static void snd_set_record_peer(Channel *channel, RedClient *client, RedsStream *stream,
+static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
                                 int migration, int num_common_caps, uint32_t *common_caps,
                                 int num_caps, uint32_t *caps)
 {
-    SndWorker *worker = (SndWorker *)channel;
-    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
+    SndWorker *worker = channel->data;
     RecordChannel *record_channel;
+    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
     CELTDecoder *celt_decoder;
     CELTMode *celt_mode;
     int celt_error;
@@ -1354,11 +1368,16 @@ error_2:
     celt051_mode_destroy(celt_mode);
 }
 
-static void snd_playback_migrate(Channel *channel)
+static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
 {
-    SndWorker *worker = (SndWorker *)channel;
+    SndWorker *worker;
+
+    ASSERT(rcc->channel);
+    ASSERT(rcc->channel->data);
+    worker = (SndWorker *)rcc->channel->data;
 
     if (worker->connection) {
+        ASSERT(worker->connection->channel_client == rcc);
         snd_set_command(worker->connection, SND_PLAYBACK_MIGRATE_MASK);
         snd_playback_send(worker->connection);
     }
@@ -1386,48 +1405,67 @@ static void remove_worker(SndWorker *worker)
 void snd_attach_playback(SpicePlaybackInstance *sin)
 {
     SndWorker *playback_worker;
+    int num_caps;
+    uint32_t *caps;
+    RedChannel *channel;
+    ClientCbs client_cbs = {0,};
 
     sin->st = spice_new0(SpicePlaybackState, 1);
     sin->st->sin = sin;
     playback_worker = &sin->st->worker;
 
-    playback_worker->base.type = SPICE_CHANNEL_PLAYBACK;
-    playback_worker->base.link = snd_set_playback_peer;
-    playback_worker->base.shutdown = snd_shutdown;
-    playback_worker->base.migrate = snd_playback_migrate;
-    playback_worker->base.data = NULL;
+    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
+    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_PLAYBACK, 0);
+
+    channel->data = playback_worker;
+    client_cbs.connect = snd_set_playback_peer;
+    client_cbs.disconnect = snd_disconnect_channel_client;
+    client_cbs.migrate = snd_playback_migrate_channel_client;
+    red_channel_register_client_cbs(channel, &client_cbs);
+    red_channel_set_data(channel, playback_worker);
 
-    playback_worker->base.num_caps = 1;
-    playback_worker->base.caps = spice_new(uint32_t, 1);
-    playback_worker->base.caps[0] =
-        (1 << SPICE_PLAYBACK_CAP_CELT_0_5_1) |
-        (1 << SPICE_PLAYBACK_CAP_VOLUME);
+    num_caps = 1;
+    caps = spice_new(uint32_t, 1);
+    caps[0] = (1 << SPICE_PLAYBACK_CAP_CELT_0_5_1) |
+              (1 << SPICE_PLAYBACK_CAP_VOLUME);
+    red_channel_set_caps(channel, num_caps, caps);
 
+    playback_worker->base_channel = channel;
     add_worker(playback_worker);
-    reds_register_channel(&playback_worker->base);
+    reds_register_channel(playback_worker->base_channel);
 }
 
 void snd_attach_record(SpiceRecordInstance *sin)
 {
     SndWorker *record_worker;
+    int num_caps;
+    uint32_t *caps;
+    RedChannel *channel;
+    ClientCbs client_cbs = {0,};
 
     sin->st = spice_new0(SpiceRecordState, 1);
     sin->st->sin = sin;
     record_worker = &sin->st->worker;
 
-    record_worker->base.type = SPICE_CHANNEL_RECORD;
-    record_worker->base.link = snd_set_record_peer;
-    record_worker->base.shutdown = snd_shutdown;
-    record_worker->base.migrate = snd_record_migrate;
-    record_worker->base.data = NULL;
-
-    record_worker->base.num_caps = 1;
-    record_worker->base.caps = spice_new(uint32_t, 1);
-    record_worker->base.caps[0] =
-        (1 << SPICE_RECORD_CAP_CELT_0_5_1) |
-        (1 << SPICE_RECORD_CAP_VOLUME);
+    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
+    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_RECORD, 0);
+
+    channel->data = record_worker;
+    client_cbs.connect = snd_set_record_peer;
+    client_cbs.disconnect = snd_disconnect_channel_client;
+    client_cbs.migrate = snd_record_migrate_channel_client;
+    red_channel_register_client_cbs(channel, &client_cbs);
+    red_channel_set_data(channel, record_worker);
+
+    num_caps = 1;
+    caps = spice_new(uint32_t, 1);
+    caps[0] = (1 << SPICE_RECORD_CAP_CELT_0_5_1) |
+              (1 << SPICE_RECORD_CAP_VOLUME);
+    red_channel_set_caps(channel, num_caps, caps);
+
+    record_worker->base_channel = channel;
     add_worker(record_worker);
-    reds_register_channel(&record_worker->base);
+    reds_register_channel(record_worker->base_channel);
 }
 
 static void snd_detach_common(SndWorker *worker)
@@ -1437,9 +1475,8 @@ static void snd_detach_common(SndWorker *worker)
     }
     remove_worker(worker);
     snd_disconnect_channel(worker->connection);
-    reds_unregister_channel(&worker->base);
-
-    reds_channel_dispose(&worker->base);
+    reds_unregister_channel(worker->base_channel);
+    red_channel_destroy(worker->base_channel);
 }
 
 static void spice_playback_state_free(SpicePlaybackState *st)
@@ -1472,7 +1509,7 @@ void snd_set_playback_compression(int on)
 
     playback_compression = on ? SPICE_AUDIO_DATA_MODE_CELT_0_5_1 : SPICE_AUDIO_DATA_MODE_RAW;
     for (; now; now = now->next) {
-        if (now->base.type == SPICE_CHANNEL_PLAYBACK && now->connection) {
+        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
             SndChannel* sndchannel = now->connection;
             PlaybackChannel* playback = (PlaybackChannel*)now->connection;
             if (!check_cap(sndchannel->caps, sndchannel->num_caps,
diff --git a/server/usbredir.c b/server/usbredir.c
index 50a6095..8daab7d 100644
--- a/server/usbredir.c
+++ b/server/usbredir.c
@@ -24,6 +24,7 @@
 
 #include "server/char_device.h"
 #include "server/red_channel.h"
+#include "server/reds.h"
 
 /* 64K should be enough for all but the largest bulk xfers + 32 bytes hdr */
 #define BUF_SIZE (64 * 1024 + 32)
@@ -36,8 +37,7 @@ typedef struct UsbRedirPipeItem {
 } UsbRedirPipeItem;
 
 typedef struct UsbRedirState {
-    Channel channel;
-    RedChannel *red_channel;
+    RedChannel *channel;
     RedChannelClient *rcc;
     SpiceCharDeviceState chardev_st;
     SpiceCharDeviceInstance *chardev_sin;
@@ -60,14 +60,14 @@ static void usbredir_chardev_wakeup(SpiceCharDeviceInstance *sin)
     state = SPICE_CONTAINEROF(sin->st, UsbRedirState, chardev_st);
     sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
 
-    if (!state->red_channel) {
+    if (!state->rcc) {
         return;
     }
 
     do {
         if (!state->pipe_item) {
             state->pipe_item = spice_malloc(sizeof(UsbRedirPipeItem));
-            red_channel_pipe_item_init(state->red_channel,
+            red_channel_pipe_item_init(state->rcc->channel,
                                        &state->pipe_item->base, 0);
         }
 
@@ -82,12 +82,12 @@ static void usbredir_chardev_wakeup(SpiceCharDeviceInstance *sin)
     } while (n > 0);
 }
 
-static int usbredir_red_channel_config_socket(RedChannelClient *rcc)
+static int usbredir_red_channel_client_config_socket(RedChannelClient *rcc)
 {
     return TRUE;
 }
 
-static void usbredir_red_channel_disconnect(RedChannelClient *rcc)
+static void usbredir_red_channel_client_on_disconnect(RedChannelClient *rcc)
 {
     UsbRedirState *state;
     SpiceCharDeviceInstance *sin;
@@ -177,16 +177,18 @@ static void usbredir_red_channel_release_pipe_item(RedChannelClient *rcc,
     free(item);
 }
 
-static void usbredir_link(Channel *channel, RedClient *client, RedsStream *stream, int migration,
-    int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps)
+static void usbredir_connect(RedChannel *channel, RedClient *client,
+    RedsStream *stream, int migration, int num_common_caps,
+    uint32_t *common_caps, int num_caps, uint32_t *caps)
 {
+    RedChannelClient *rcc;
     UsbRedirState *state;
     UsbRedirChannel *redir_chan;
     SpiceCharDeviceInstance *sin;
     SpiceCharDeviceInterface *sif;
-    ChannelCbs channel_cbs;
 
-    state = SPICE_CONTAINEROF(channel, UsbRedirState, channel);
+    redir_chan = SPICE_CONTAINEROF(channel, UsbRedirChannel, base);
+    state = redir_chan->state;
     sin = state->chardev_sin;
     sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
 
@@ -199,86 +201,78 @@ static void usbredir_link(Channel *channel, RedClient *client, RedsStream *strea
         return;
     }
 
-    if (!state->red_channel) {
-        memset(&channel_cbs, sizeof(channel_cbs), 0);
-        channel_cbs.config_socket = usbredir_red_channel_config_socket;
-        channel_cbs.disconnect = usbredir_red_channel_disconnect;
-        channel_cbs.send_item = usbredir_red_channel_send_item;
-        channel_cbs.hold_item = usbredir_red_channel_hold_pipe_item;
-        channel_cbs.release_item = usbredir_red_channel_release_pipe_item;
-        channel_cbs.alloc_recv_buf = usbredir_red_channel_alloc_msg_rcv_buf;
-        channel_cbs.release_recv_buf = usbredir_red_channel_release_msg_rcv_buf;
-        state->red_channel = red_channel_create(sizeof(UsbRedirChannel),
-                                        core, migration, FALSE /* handle_acks */,
-                                        usbredir_red_channel_client_handle_message,
-                                        &channel_cbs);
-    }
-    if (!state->red_channel) {
-        return;
-    }
-    state->rcc = red_channel_client_create(sizeof(RedChannelClient), state->red_channel,
-                                           client, stream);
-    if (!state->rcc) {
-        red_printf("failed to create usbredir channel client\n");
+    rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream);
+    if (!rcc) {
         return;
     }
-    red_channel_init_outgoing_messages_window(state->red_channel);
-    redir_chan = SPICE_CONTAINEROF(state->red_channel, UsbRedirChannel, base);
-    redir_chan->state = state;
+    state->rcc = rcc;
+    red_channel_client_ack_zero_messages_window(rcc);
 
     if (sif->state) {
         sif->state(sin, 1);
     }
 }
 
-static void usbredir_shutdown(Channel *channel)
-{
-    UsbRedirState *state = SPICE_CONTAINEROF(channel, UsbRedirState, channel);
-
-    usbredir_red_channel_disconnect(state->rcc);
-    red_channel_destroy(state->red_channel);
-    state->red_channel = NULL;
-}
-
-static void usbredir_migrate(Channel *channel)
+static void usbredir_migrate(RedChannelClient *rcc)
 {
     /* NOOP */
 }
 
 int usbredir_device_connect(SpiceCharDeviceInstance *sin)
 {
-    UsbRedirState *state;
     static int id = 0;
-
+    UsbRedirState *state;
+    UsbRedirChannel *redir_chan;
+    ChannelCbs channel_cbs = {0,};
+    ClientCbs client_cbs = {0,};
+
+    channel_cbs.config_socket = usbredir_red_channel_client_config_socket;
+    channel_cbs.on_disconnect = usbredir_red_channel_client_on_disconnect;
+    channel_cbs.send_item = usbredir_red_channel_send_item;
+    channel_cbs.hold_item = usbredir_red_channel_hold_pipe_item;
+    channel_cbs.release_item = usbredir_red_channel_release_pipe_item;
+    channel_cbs.alloc_recv_buf = usbredir_red_channel_alloc_msg_rcv_buf;
+    channel_cbs.release_recv_buf = usbredir_red_channel_release_msg_rcv_buf;
+
+    redir_chan = (UsbRedirChannel*)red_channel_create(sizeof(UsbRedirChannel),
+                                             core, SPICE_CHANNEL_USBREDIR, id++,
+                                             FALSE /* migration - TODO?*/,
+                                             FALSE /* handle_acks */,
+                                             usbredir_red_channel_client_handle_message,
+                                             &channel_cbs);
+    red_channel_init_outgoing_messages_window(&redir_chan->base);
     state = spice_new0(UsbRedirState, 1);
-    state->channel.type = SPICE_CHANNEL_USBREDIR;
-    state->channel.id = id++;
-    state->channel.link = usbredir_link;
-    state->channel.shutdown = usbredir_shutdown;
-    state->channel.migrate = usbredir_migrate;
+    state->channel = &redir_chan->base;
     state->chardev_st.wakeup = usbredir_chardev_wakeup;
     state->chardev_sin = sin;
     state->rcv_buf = spice_malloc(BUF_SIZE);
     state->rcv_buf_size = BUF_SIZE;
 
+    client_cbs.connect = usbredir_connect;
+    client_cbs.migrate = usbredir_migrate;
+    red_channel_register_client_cbs(&redir_chan->base, &client_cbs);
+
     sin->st = &state->chardev_st;
 
-    reds_register_channel(&state->channel);
+    reds_register_channel(&redir_chan->base);
 
     return 0;
 }
 
+/* Must be called from RedClient handling thread. */
 void usbredir_device_disconnect(SpiceCharDeviceInstance *sin)
 {
     UsbRedirState *state;
 
     state = SPICE_CONTAINEROF(sin->st, UsbRedirState, chardev_st);
 
-    reds_unregister_channel(&state->channel);
+    reds_unregister_channel(state->channel);
 
-    usbredir_red_channel_disconnect(state->rcc);
+    red_channel_client_destroy(state->rcc);
+    red_channel_disconnect(state->channel);
 
     free(state->pipe_item);
     free(state->rcv_buf);
+    free(state->channel);
     free(state);
 }
commit 1db936e64cfe955a757d9d77302f104f68a58bfd
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed Aug 3 16:04:20 2011 +0300

    server/red_channel.c inroducing client_cbs
    
    client_cbs are supposed to be called from client context (reds). This patch will be used
    in future patches for relacing reds::Channel with RedChannel in order to eliminate redundancy.

diff --git a/server/red_channel.c b/server/red_channel.c
index e31f148..80aa667 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -417,6 +417,24 @@ error:
     return NULL;
 }
 
+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)
+{
+    red_error("not implemented");
+}
+
+static void red_channel_client_default_disconnect(RedChannelClient *base)
+{
+    red_channel_client_disconnect(base);
+}
+
+static void red_channel_client_default_migrate(RedChannelClient *base)
+{
+}
+
 RedChannel *red_channel_create(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
@@ -424,6 +442,7 @@ RedChannel *red_channel_create(int size,
                                ChannelCbs *channel_cbs)
 {
     RedChannel *channel;
+    ClientCbs client_cbs;
 
     ASSERT(size >= sizeof(*channel));
     ASSERT(channel_cbs->config_socket && channel_cbs->disconnect && handle_message &&
@@ -457,6 +476,14 @@ RedChannel *red_channel_create(int size,
     channel->outgoing_cb.on_msg_done = red_channel_peer_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);
+
+    channel->thread_id = pthread_self();
+
     channel->shut = 0; // came here from inputs, perhaps can be removed? XXX
     channel->out_bytes_counter = 0;
     return channel;
@@ -492,6 +519,20 @@ RedChannel *red_channel_create_parser(int size,
     return channel;
 }
 
+void red_channel_register_client_cbs(RedChannel *channel, ClientCbs *client_cbs)
+{
+    ASSERT(client_cbs->connect);
+    channel->client_cbs.connect = client_cbs->connect;
+
+    if (client_cbs->disconnect) {
+        channel->client_cbs.disconnect = client_cbs->disconnect;
+    }
+
+    if (client_cbs->migrate) {
+        channel->client_cbs.migrate = client_cbs->migrate;
+    }
+}
+
 void red_channel_client_destroy(RedChannelClient *rcc)
 {
     red_channel_client_disconnect(rcc);
@@ -1102,6 +1143,8 @@ RedClient *red_client_new()
 
     client = spice_malloc0(sizeof(RedClient));
     ring_init(&client->channels);
+    client->thread_id = pthread_self();
+
     return client;
 }
 
@@ -1121,11 +1164,17 @@ void red_client_destroy(RedClient *client)
     RedChannelClient *rcc;
 
     red_printf("destroy client with #channels %d", client->channels_num);
+    ASSERT(pthread_equal(pthread_self(), client->thread_id));
     RING_FOREACH_SAFE(link, next, &client->channels) {
         // some channels may be in other threads, so disconnection
         // is not synchronous.
         rcc = SPICE_CONTAINEROF(link, RedChannelClient, client_link);
-        rcc->channel->channel_cbs.disconnect(rcc); // this may call another thread. it also frees. (eventually - doesn't have to be in sync)
+        // some channels may be in other threads. However we currently
+        // assume disconnect is synchronous (we changed the dispatcher
+        // to wait for disconnection)
+        // TODO: should we go back to async. For this we need to use
+        // ref count for channel clients.
+        rcc->channel->client_cbs.disconnect(rcc);
     }
     free(client);
 }
@@ -1140,7 +1189,7 @@ void red_client_disconnect(RedClient *client)
         // some channels may be in other threads, so disconnection
         // is not synchronous.
         rcc = SPICE_CONTAINEROF(link, RedChannelClient, client_link);
-        rcc->channel->channel_cbs.disconnect(rcc);
+        rcc->channel->client_cbs.disconnect(rcc);
     }
 }
 
diff --git a/server/red_channel.h b/server/red_channel.h
index 996623b..cb33fcb 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -23,6 +23,7 @@
 #define _H_RED_CHANNEL
 
 #include "red_common.h"
+#include <pthread.h>
 #include "reds.h"
 #include "spice.h"
 #include "ring.h"
@@ -138,6 +139,15 @@ typedef uint64_t (*channel_handle_migrate_data_proc)(RedChannelClient *base,
 typedef uint64_t (*channel_handle_migrate_data_get_serial_proc)(RedChannelClient *base,
                                             uint32_t size, void *message);
 
+
+typedef void (*channel_client_connect_proc)(RedChannel *channel, RedClient *client, RedsStream *stream,
+                                            int migration, int num_common_caps, uint32_t *common_caps,
+                                            int num_caps, uint32_t *caps);
+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.
@@ -155,6 +165,17 @@ typedef struct {
     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;
+
 struct RedChannelClient {
     RingItem channel_link;
     RingItem client_link;
@@ -197,6 +218,7 @@ struct RedChannel {
     IncomingHandlerInterface incoming_cb;
 
     ChannelCbs channel_cbs;
+    ClientCbs client_cbs;
 
     /* Stuff below added for Main and Inputs channels switch to RedChannel
      * (might be removed later) */
@@ -204,6 +226,8 @@ struct RedChannel {
     channel_on_outgoing_error_proc on_outgoing_error;
     int shut; /* signal channel is to be closed */
 
+    // TODO: when different channel_clients are in different threads from Channel -> need to protect!
+    pthread_t thread_id;
 #ifdef RED_STATISTICS
     uint64_t *out_bytes_counter;
 #endif
@@ -228,6 +252,8 @@ RedChannel *red_channel_create_parser(int size,
                                channel_on_outgoing_error_proc outgoing_error,
                                ChannelCbs *channel_cbs);
 
+void red_channel_register_client_cbs(RedChannel *channel, ClientCbs *client_cbs);
+
 RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
                                             RedsStream *stream);
 
@@ -379,6 +405,8 @@ struct RedClient {
     int channels_num;
     int disconnecting;
     MainChannelClient *mcc;
+
+    pthread_t thread_id;
 };
 
 RedClient *red_client_new();
commit 812d01c06090f14ccb65e9a3b81df311c51e4b26
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed Aug 3 15:43:02 2011 +0300

    server/red_channel.c: pack all channel callbacks to ChannelCbs

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index b8d7801..e6af0d5 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -510,23 +510,26 @@ static void inputs_link(Channel *channel, RedClient *client,
 
     ASSERT(channel->data == g_inputs_channel);
     if (channel->data == NULL) {
+        ChannelCbs channel_cbs;
+
+        memset(&channel_cbs, sizeof(channel_cbs), 0);
+
+        channel_cbs.config_socket = inputs_channel_config_socket;
+        channel_cbs.disconnect = inputs_channel_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;
+
         red_printf("inputs channel create");
         channel->data = g_inputs_channel = (InputsChannel*)red_channel_create_parser(
             sizeof(InputsChannel), core, migration, FALSE /* handle_acks */
-            ,inputs_channel_config_socket
-            ,inputs_channel_disconnect
             ,spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL)
             ,inputs_channel_handle_parsed
-            ,inputs_channel_alloc_msg_rcv_buf
-            ,inputs_channel_release_msg_rcv_buf
-            ,inputs_channel_hold_pipe_item
-            ,inputs_channel_send_item
-            ,inputs_channel_release_pipe_item
             ,inputs_channel_on_error
             ,inputs_channel_on_error
-            ,NULL
-            ,NULL
-            ,NULL);
+            ,&channel_cbs);
         ASSERT(channel->data);
     }
     red_printf("inputs channel client create");
diff --git a/server/main_channel.c b/server/main_channel.c
index 3337c46..54b3dff 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -937,23 +937,27 @@ MainChannelClient *main_channel_link(Channel *channel, RedClient *client,
     MainChannelClient *mcc;
 
     if (channel->data == NULL) {
+        ChannelCbs channel_cbs;
+
+        channel_cbs.config_socket = main_channel_config_socket;
+        channel_cbs.disconnect = main_channel_client_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;
+        channel_cbs.handle_migrate_data_get_serial = main_channel_handle_migrate_data_get_serial;
+
         red_printf("create main channel");
         channel->data = red_channel_create_parser(
             sizeof(MainChannel), core, migration, FALSE /* handle_acks */
-            ,main_channel_config_socket
-            ,main_channel_client_disconnect
             ,spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL)
             ,main_channel_handle_parsed
-            ,main_channel_alloc_msg_rcv_buf
-            ,main_channel_release_msg_rcv_buf
-            ,main_channel_hold_pipe_item
-            ,main_channel_send_item
-            ,main_channel_release_pipe_item
             ,main_channel_on_error
             ,main_channel_on_error
-            ,main_channel_handle_migrate_flush_mark
-            ,main_channel_handle_migrate_data
-            ,main_channel_handle_migrate_data_get_serial);
+            ,&channel_cbs);
         ASSERT(channel->data);
     }
     // TODO - migration - I removed it from channel creation, now put it
diff --git a/server/red_channel.c b/server/red_channel.c
index b5a0330..e31f148 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -223,7 +223,7 @@ static void red_channel_client_on_output(void *opaque, int n)
 
 void red_channel_client_default_peer_on_error(RedChannelClient *rcc)
 {
-    rcc->channel->disconnect(rcc);
+    rcc->channel->channel_cbs.disconnect(rcc);
 }
 
 static void red_channel_peer_on_incoming_error(RedChannelClient *rcc)
@@ -316,7 +316,7 @@ static void red_channel_client_send_item(RedChannelClient *rcc, PipeItem *item)
             handled = FALSE;
     }
     if (!handled) {
-        rcc->channel->send_item(rcc, item);
+        rcc->channel->channel_cbs.send_item(rcc, item);
     }
 }
 
@@ -332,7 +332,7 @@ static void red_channel_client_release_item(RedChannelClient *rcc, PipeItem *ite
             handled = FALSE;
     }
     if (!handled) {
-        rcc->channel->release_item(rcc, item, item_pushed);
+        rcc->channel->channel_cbs.release_item(rcc, item, item_pushed);
     }
 }
 
@@ -397,7 +397,7 @@ RedChannelClient *red_channel_client_create(
     rcc->outgoing.cb = &channel->outgoing_cb;
     rcc->outgoing.pos = 0;
     rcc->outgoing.size = 0;
-    if (!channel->config_socket(rcc)) {
+    if (!channel->channel_cbs.config_socket(rcc)) {
         goto error;
     }
 
@@ -420,40 +420,32 @@ error:
 RedChannel *red_channel_create(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
-                               channel_configure_socket_proc config_socket,
-                               channel_disconnect_proc disconnect,
                                channel_handle_message_proc handle_message,
-                               channel_alloc_msg_recv_buf_proc alloc_recv_buf,
-                               channel_release_msg_recv_buf_proc release_recv_buf,
-                               channel_hold_pipe_item_proc hold_item,
-                               channel_send_pipe_item_proc send_item,
-                               channel_release_pipe_item_proc release_item,
-                               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 *channel_cbs)
 {
     RedChannel *channel;
 
     ASSERT(size >= sizeof(*channel));
-    ASSERT(config_socket && disconnect && handle_message && alloc_recv_buf &&
-           release_item);
+    ASSERT(channel_cbs->config_socket && channel_cbs->disconnect && handle_message &&
+           channel_cbs->alloc_recv_buf && channel_cbs->release_item);
     channel = spice_malloc0(size);
     channel->handle_acks = handle_acks;
-    channel->disconnect = disconnect;
-    channel->send_item = send_item;
-    channel->release_item = release_item;
-    channel->hold_item = hold_item;
-    channel->handle_migrate_flush_mark = handle_migrate_flush_mark;
-    channel->handle_migrate_data = handle_migrate_data;
-    channel->handle_migrate_data_get_serial = handle_migrate_data_get_serial;
-    channel->config_socket = config_socket;
+    channel->channel_cbs.disconnect = channel_cbs->disconnect;
+    channel->channel_cbs.send_item = channel_cbs->send_item;
+    channel->channel_cbs.release_item = channel_cbs->release_item;
+    channel->channel_cbs.hold_item = channel_cbs->hold_item;
+    channel->channel_cbs.handle_migrate_flush_mark = channel_cbs->handle_migrate_flush_mark;
+    channel->channel_cbs.handle_migrate_data = channel_cbs->handle_migrate_data;
+    channel->channel_cbs.handle_migrate_data_get_serial = channel_cbs->handle_migrate_data_get_serial;
+    channel->channel_cbs.config_socket = channel_cbs->config_socket;
 
     channel->core = core;
     channel->migrate = migrate;
     ring_init(&channel->clients);
 
-    channel->incoming_cb.alloc_msg_buf = (alloc_msg_recv_buf_proc)alloc_recv_buf;
-    channel->incoming_cb.release_msg_buf = (release_msg_recv_buf_proc)release_recv_buf;
+    // 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;
@@ -478,26 +470,15 @@ static int do_nothing_handle_message(RedChannelClient *rcc, SpiceDataHeader *hea
 RedChannel *red_channel_create_parser(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
-                               channel_configure_socket_proc config_socket,
-                               channel_disconnect_proc disconnect,
                                spice_parse_channel_func_t parser,
-                               channel_handle_parsed_proc handle_parsed,
-                               channel_alloc_msg_recv_buf_proc alloc_recv_buf,
-                               channel_release_msg_recv_buf_proc release_recv_buf,
-                               channel_hold_pipe_item_proc hold_item,
-                               channel_send_pipe_item_proc send_item,
-                               channel_release_pipe_item_proc release_item,
+                               channel_handle_parsed_proc handle_parsed, 
                                channel_on_incoming_error_proc incoming_error,
                                channel_on_outgoing_error_proc outgoing_error,
-                               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 *channel_cbs)
 {
     RedChannel *channel = red_channel_create(size,
-        core, migrate, handle_acks, config_socket, disconnect,
-        do_nothing_handle_message, alloc_recv_buf, release_recv_buf, hold_item,
-        send_item, release_item, handle_migrate_flush_mark, handle_migrate_data,
-        handle_migrate_data_get_serial);
+        core, migrate, handle_acks, do_nothing_handle_message,
+        channel_cbs);
 
     if (channel == NULL) {
         return NULL;
@@ -629,7 +610,7 @@ void red_channel_push(RedChannel *channel)
     RING_FOREACH_SAFE(link, next, &channel->clients) {
         rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
         if (rcc->stream == NULL) {
-            rcc->channel->disconnect(rcc);
+            rcc->channel->channel_cbs.disconnect(rcc);
         } else {
             red_channel_client_push(rcc);
         }
@@ -657,8 +638,8 @@ void red_channel_init_outgoing_messages_window(RedChannel *channel)
 
 static void red_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
 {
-    if (rcc->channel->handle_migrate_flush_mark) {
-        rcc->channel->handle_migrate_flush_mark(rcc);
+    if (rcc->channel->channel_cbs.handle_migrate_flush_mark) {
+        rcc->channel->channel_cbs.handle_migrate_flush_mark(rcc);
     }
 }
 
@@ -671,13 +652,13 @@ static void red_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
 // 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)
 {
-    if (!rcc->channel->handle_migrate_data) {
+    if (!rcc->channel->channel_cbs.handle_migrate_data) {
         return;
     }
     ASSERT(red_channel_client_get_message_serial(rcc) == 0);
     red_channel_client_set_message_serial(rcc,
-        rcc->channel->handle_migrate_data_get_serial(rcc, size, message));
-    rcc->channel->handle_migrate_data(rcc, size, message);
+        rcc->channel->channel_cbs.handle_migrate_data_get_serial(rcc, size, message));
+    rcc->channel->channel_cbs.handle_migrate_data(rcc, size, message);
 }
 
 int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size,
@@ -731,7 +712,7 @@ void red_channel_client_init_send_data(RedChannelClient *rcc, uint16_t msg_type,
     rcc->send_data.header->type = msg_type;
     rcc->send_data.item = item;
     if (item) {
-        rcc->channel->hold_item(rcc, item);
+        rcc->channel->channel_cbs.hold_item(rcc, item);
     }
 }
 
@@ -922,7 +903,7 @@ void red_channel_client_disconnect(RedChannelClient *rcc)
     red_printf("%p (channel %p)", rcc, rcc->channel);
 
     if (rcc->send_data.item) {
-        rcc->channel->release_item(rcc, rcc->send_data.item, FALSE);
+        rcc->channel->channel_cbs.release_item(rcc, rcc->send_data.item, FALSE);
     }
     red_channel_client_pipe_clear(rcc);
     reds_stream_free(rcc->stream);
@@ -1144,7 +1125,7 @@ void red_client_destroy(RedClient *client)
         // some channels may be in other threads, so disconnection
         // is not synchronous.
         rcc = SPICE_CONTAINEROF(link, RedChannelClient, client_link);
-        rcc->channel->disconnect(rcc); // this may call another thread. it also frees. (eventually - doesn't have to be in sync)
+        rcc->channel->channel_cbs.disconnect(rcc); // this may call another thread. it also frees. (eventually - doesn't have to be in sync)
     }
     free(client);
 }
@@ -1159,7 +1140,7 @@ void red_client_disconnect(RedClient *client)
         // some channels may be in other threads, so disconnection
         // is not synchronous.
         rcc = SPICE_CONTAINEROF(link, RedChannelClient, client_link);
-        rcc->channel->disconnect(rcc);
+        rcc->channel->channel_cbs.disconnect(rcc);
     }
 }
 
diff --git a/server/red_channel.h b/server/red_channel.h
index 652673b..996623b 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -138,6 +138,23 @@ typedef uint64_t (*channel_handle_migrate_data_proc)(RedChannelClient *base,
 typedef uint64_t (*channel_handle_migrate_data_get_serial_proc)(RedChannelClient *base,
                                             uint32_t size, void *message);
 
+/*
+ * callbacks that are triggered from channel client stream events.
+ * They are called from the thread that listen to the stream events.
+ */
+typedef struct {
+    channel_configure_socket_proc config_socket;
+    channel_disconnect_proc disconnect;
+    channel_send_pipe_item_proc send_item;
+    channel_hold_pipe_item_proc hold_item;
+    channel_release_pipe_item_proc release_item;
+    channel_alloc_msg_recv_buf_proc alloc_recv_buf;
+    channel_release_msg_recv_buf_proc release_recv_buf;
+    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;
+
 struct RedChannelClient {
     RingItem channel_link;
     RingItem client_link;
@@ -179,11 +196,7 @@ struct RedChannel {
     OutgoingHandlerInterface outgoing_cb;
     IncomingHandlerInterface incoming_cb;
 
-    channel_configure_socket_proc config_socket;
-    channel_disconnect_proc disconnect;
-    channel_send_pipe_item_proc send_item;
-    channel_hold_pipe_item_proc hold_item;
-    channel_release_pipe_item_proc release_item;
+    ChannelCbs channel_cbs;
 
     /* Stuff below added for Main and Inputs channels switch to RedChannel
      * (might be removed later) */
@@ -191,9 +204,6 @@ struct RedChannel {
     channel_on_outgoing_error_proc on_outgoing_error;
     int shut; /* signal channel is to be closed */
 
-    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;
 #ifdef RED_STATISTICS
     uint64_t *out_bytes_counter;
 #endif
@@ -204,37 +214,19 @@ struct RedChannel {
 RedChannel *red_channel_create(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
-                               channel_configure_socket_proc config_socket,
-                               channel_disconnect_proc disconnect,
                                channel_handle_message_proc handle_message,
-                               channel_alloc_msg_recv_buf_proc alloc_recv_buf,
-                               channel_release_msg_recv_buf_proc release_recv_buf,
-                               channel_hold_pipe_item_proc hold_item,
-                               channel_send_pipe_item_proc send_item,
-                               channel_release_pipe_item_proc release_item,
-                               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 *channel_cbs);
 
 /* alternative constructor, meant for marshaller based (inputs,main) channels,
  * will become default eventually */
 RedChannel *red_channel_create_parser(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
-                               channel_configure_socket_proc config_socket,
-                               channel_disconnect_proc disconnect,
                                spice_parse_channel_func_t parser,
                                channel_handle_parsed_proc handle_parsed,
-                               channel_alloc_msg_recv_buf_proc alloc_recv_buf,
-                               channel_release_msg_recv_buf_proc release_recv_buf,
-                               channel_hold_pipe_item_proc hold_item,
-                               channel_send_pipe_item_proc send_item,
-                               channel_release_pipe_item_proc release_item,
                                channel_on_incoming_error_proc incoming_error,
                                channel_on_outgoing_error_proc outgoing_error,
-                               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 *channel_cbs);
 
 RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
                                             RedsStream *stream);
diff --git a/server/red_tunnel_worker.c b/server/red_tunnel_worker.c
index 38e654e..79cb854 100644
--- a/server/red_tunnel_worker.c
+++ b/server/red_tunnel_worker.c
@@ -3448,25 +3448,28 @@ static void handle_tunnel_channel_link(Channel *channel, RedClient *client,
     TunnelChannelClient *tcc;
     TunnelWorker *worker = (TunnelWorker *)channel->data;
     RedChannel *tunnel_channel;
+    ChannelCbs channel_cbs = {0,};
 
     if (worker->channel) {
         tunnel_channel_disconnect(worker->channel->base.channel);
     }
 
+    channel_cbs.config_socket = tunnel_channel_config_socket;
+    channel_cbs.disconnect = tunnel_channel_disconnect_client;
+    channel_cbs.alloc_recv_buf = tunnel_channel_alloc_msg_rcv_buf;
+    channel_cbs.release_recv_buf = tunnel_channel_release_msg_rcv_buf;
+    channel_cbs.hold_item = tunnel_channel_hold_pipe_item;
+    channel_cbs.send_item = tunnel_channel_send_item;
+    channel_cbs.release_item = tunnel_channel_release_pipe_item;
+    channel_cbs.handle_migrate_flush_mark = tunnel_channel_handle_migrate_mark;
+    channel_cbs.handle_migrate_data = tunnel_channel_handle_migrate_data;
+    channel_cbs.handle_migrate_data_get_serial = tunnel_channel_handle_migrate_data_get_serial;
+
     tunnel_channel = red_channel_create(sizeof(RedChannel),
                                             worker->core_interface,
                                             migration, TRUE,
-                                            tunnel_channel_config_socket,
-                                            tunnel_channel_disconnect_client,
                                             tunnel_channel_handle_message,
-                                            tunnel_channel_alloc_msg_rcv_buf,
-                                            tunnel_channel_release_msg_rcv_buf,
-                                            tunnel_channel_hold_pipe_item,
-                                            tunnel_channel_send_item,
-                                            tunnel_channel_release_pipe_item,
-                                            tunnel_channel_handle_migrate_mark,
-                                            tunnel_channel_handle_migrate_data,
-                                            tunnel_channel_handle_migrate_data_get_serial);
+                                            &channel_cbs);
 
     if (!tunnel_channel) {
         return;
diff --git a/server/red_worker.c b/server/red_worker.c
index dc51aeb..4347d24 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -9502,23 +9502,26 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
 {
     RedChannel *channel = NULL;
     CommonChannel *common;
+    ChannelCbs channel_cbs;
+
+    channel_cbs.config_socket = common_channel_config_socket;
+    channel_cbs.disconnect = disconnect;
+    channel_cbs.send_item = send_item;
+    channel_cbs.hold_item = hold_item;
+    channel_cbs.release_item = release_item;
+    channel_cbs.alloc_recv_buf = common_alloc_recv_buf;
+    channel_cbs.release_recv_buf = common_release_recv_buf;
+    channel_cbs.handle_migrate_flush_mark = handle_migrate_flush_mark;
+    channel_cbs.handle_migrate_data = handle_migrate_data;
+    channel_cbs.handle_migrate_data_get_serial = handle_migrate_data_get_serial;
 
     channel = red_channel_create_parser(size, &worker_core, migrate,
                                         TRUE /* handle_acks */,
-                                        common_channel_config_socket,
-                                        disconnect,
                                         spice_get_client_channel_parser(channel_id, NULL),
                                         handle_parsed,
-                                        common_alloc_recv_buf,
-                                        common_release_recv_buf,
-                                        hold_item,
-                                        send_item,
-                                        release_item,
                                         on_incoming_error,
                                         on_outgoing_error,
-                                        handle_migrate_flush_mark,
-                                        handle_migrate_data,
-                                        handle_migrate_data_get_serial);
+                                        &channel_cbs);
     common = (CommonChannel *)channel;
     if (!channel) {
         goto error;
@@ -10061,7 +10064,7 @@ static void red_wait_pipe_item_sent(RedChannelClient *rcc, PipeItem *item)
     }
 
     red_printf("");
-    channel->hold_item(rcc, item);
+    channel->channel_cbs.hold_item(rcc, item);
 
     end_time = red_now() + CHANNEL_PUSH_TIMEOUT;
 
@@ -10086,7 +10089,7 @@ static void red_wait_pipe_item_sent(RedChannelClient *rcc, PipeItem *item)
             red_wait_outgoing_item(rcc);
         }
     }
-    channel->release_item(rcc, item, FALSE);
+    channel->channel_cbs.release_item(rcc, item, FALSE);
 }
 
 static void surface_dirty_region_to_rects(RedSurface *surface,
diff --git a/server/smartcard.c b/server/smartcard.c
index 2a3332c..36004f7 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -493,23 +493,23 @@ static void smartcard_link(Channel *channel, RedClient *client,
                         int num_caps, uint32_t *caps)
 {
     RedChannelClient *rcc;
+    ChannelCbs channel_cbs;
 
     if (!channel->data) {
+        memset(&channel_cbs, sizeof(channel_cbs), 0);
+        channel_cbs.config_socket = smartcard_channel_client_config_socket;
+        channel_cbs.disconnect = smartcard_channel_client_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_alloc_msg_rcv_buf;
+        channel_cbs.release_recv_buf = smartcard_channel_release_msg_rcv_buf;
         channel->data =
             red_channel_create(sizeof(SmartCardChannel),
                                         core, migration,
                                         FALSE /* handle_acks */,
-                                        smartcard_channel_client_config_socket,
-                                        smartcard_channel_client_disconnect,
                                         smartcard_channel_handle_message,
-                                        smartcard_channel_alloc_msg_rcv_buf,
-                                        smartcard_channel_release_msg_rcv_buf,
-                                        smartcard_channel_hold_pipe_item,
-                                        smartcard_channel_send_item,
-                                        smartcard_channel_release_pipe_item,
-                                        NULL,
-                                        NULL,
-                                        NULL);
+                                        &channel_cbs);
         if (channel->data) {
             red_channel_init_outgoing_messages_window((RedChannel*)channel->data);
         } else {
diff --git a/server/usbredir.c b/server/usbredir.c
index 1aa89b2..50a6095 100644
--- a/server/usbredir.c
+++ b/server/usbredir.c
@@ -184,6 +184,7 @@ static void usbredir_link(Channel *channel, RedClient *client, RedsStream *strea
     UsbRedirChannel *redir_chan;
     SpiceCharDeviceInstance *sin;
     SpiceCharDeviceInterface *sif;
+    ChannelCbs channel_cbs;
 
     state = SPICE_CONTAINEROF(channel, UsbRedirState, channel);
     sin = state->chardev_sin;
@@ -199,19 +200,18 @@ static void usbredir_link(Channel *channel, RedClient *client, RedsStream *strea
     }
 
     if (!state->red_channel) {
+        memset(&channel_cbs, sizeof(channel_cbs), 0);
+        channel_cbs.config_socket = usbredir_red_channel_config_socket;
+        channel_cbs.disconnect = usbredir_red_channel_disconnect;
+        channel_cbs.send_item = usbredir_red_channel_send_item;
+        channel_cbs.hold_item = usbredir_red_channel_hold_pipe_item;
+        channel_cbs.release_item = usbredir_red_channel_release_pipe_item;
+        channel_cbs.alloc_recv_buf = usbredir_red_channel_alloc_msg_rcv_buf;
+        channel_cbs.release_recv_buf = usbredir_red_channel_release_msg_rcv_buf;
         state->red_channel = red_channel_create(sizeof(UsbRedirChannel),
                                         core, migration, FALSE /* handle_acks */,
-                                        usbredir_red_channel_config_socket,
-                                        usbredir_red_channel_disconnect,
                                         usbredir_red_channel_client_handle_message,
-                                        usbredir_red_channel_alloc_msg_rcv_buf,
-                                        usbredir_red_channel_release_msg_rcv_buf,
-                                        usbredir_red_channel_hold_pipe_item,
-                                        usbredir_red_channel_send_item,
-                                        usbredir_red_channel_release_pipe_item,
-                                        NULL,
-                                        NULL,
-                                        NULL);
+                                        &channel_cbs);
     }
     if (!state->red_channel) {
         return;
commit 67671b26097a0d629a42356b22690c8b34db51a5
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Apr 21 22:21:36 2011 +0300

    server/red_worker: add ref counting to RedDrawable
    
    introduces ref_red_drawable and put_red_drawable (rename from free_red_drawable)
    
    RedDrawable is already references by Drawable and RedGlzDrawable, with
    a hack to NULL the drawable field in RedGlzDrawable to indicate RedGlzDrawable
    is the last reference holder. Using an explicit reference count instead.

diff --git a/server/red_parse_qxl.h b/server/red_parse_qxl.h
index a713dcf..9397852 100644
--- a/server/red_parse_qxl.h
+++ b/server/red_parse_qxl.h
@@ -24,6 +24,7 @@
 #include "red_memslots.h"
 
 typedef struct RedDrawable {
+    int refs;
     QXLReleaseInfo *release_info;
     uint32_t surface_id;
     uint8_t effect;
diff --git a/server/red_worker.c b/server/red_worker.c
index dc641d4..dc51aeb 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -1655,7 +1655,14 @@ static inline void set_surface_release_info(RedWorker *worker, uint32_t surface_
     }
 }
 
-static inline void free_red_drawable(RedWorker *worker, RedDrawable *drawable, uint32_t group_id,
+static RedDrawable *ref_red_drawable(RedDrawable *drawable)
+{
+    drawable->refs++;
+    return drawable;
+}
+
+
+static inline void put_red_drawable(RedWorker *worker, RedDrawable *drawable, uint32_t group_id,
                                      SpiceImage *self_bitmap)
 {
     QXLReleaseInfoExt release_info_ext;
@@ -1663,6 +1670,10 @@ static inline void free_red_drawable(RedWorker *worker, RedDrawable *drawable, u
     if (self_bitmap) {
         red_put_image(self_bitmap);
     }
+    if (--drawable->refs) {
+        return;
+    }
+
     release_info_ext.group_id = group_id;
     release_info_ext.info = drawable->release_info;
     worker->qxl->st->qif->release_resource(worker->qxl, release_info_ext);
@@ -1707,7 +1718,6 @@ static void remove_drawable_dependencies(RedWorker *worker, Drawable *drawable)
 
 static inline void release_drawable(RedWorker *worker, Drawable *drawable)
 {
-    int glz_count = 0;
     RingItem *item, *next;
 
     if (!--drawable->refs) {
@@ -1723,12 +1733,9 @@ static inline void release_drawable(RedWorker *worker, Drawable *drawable)
         RING_FOREACH_SAFE(item, next, &drawable->glz_ring) {
             SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->drawable = NULL;
             ring_remove(item);
-            glz_count++;
-        }
-        if (!glz_count) { // no reference to the qxl drawable left
-            free_red_drawable(worker, drawable->red_drawable,
-                              drawable->group_id, drawable->self_bitmap);
         }
+        put_red_drawable(worker, drawable->red_drawable,
+                          drawable->group_id, drawable->self_bitmap);
         free_drawable(worker, drawable);
     }
 }
@@ -3638,7 +3645,7 @@ static Drawable *get_drawable(RedWorker *worker, uint8_t effect, RedDrawable *re
     drawable->tree_item.base.type = TREE_ITEM_TYPE_DRAWABLE;
     region_init(&drawable->tree_item.base.rgn);
     drawable->tree_item.effect = effect;
-    drawable->red_drawable = red_drawable;
+    drawable->red_drawable = ref_red_drawable(red_drawable);
     drawable->group_id = group_id;
 
     drawable->surface_id = red_drawable->surface_id;
@@ -4661,6 +4668,14 @@ static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ri
     return n;
 }
 
+static RedDrawable *red_drawable_new(void)
+{
+    RedDrawable * red = spice_new0(RedDrawable, 1);
+
+    red->refs = 1;
+    return red;
+}
+
 static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *ring_is_empty)
 {
     QXLCommandExt ext_cmd;
@@ -4693,11 +4708,13 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
         worker->repoll_cmd_ring = 0;
         switch (ext_cmd.cmd.type) {
         case QXL_CMD_DRAW: {
-            RedDrawable *drawable = spice_new0(RedDrawable, 1);
+            RedDrawable *drawable = red_drawable_new(); // returns with 1 ref
 
             red_get_drawable(&worker->mem_slots, ext_cmd.group_id,
                              drawable, ext_cmd.cmd.data, ext_cmd.flags);
             red_process_drawable(worker, drawable, ext_cmd.group_id);
+            // release the red_drawable
+            put_red_drawable(worker, drawable, ext_cmd.group_id, NULL);
             break;
         }
         case QXL_CMD_UPDATE: {
@@ -5037,7 +5054,7 @@ static RedGlzDrawable *red_display_get_glz_drawable(DisplayChannelClient *dcc, D
     ret = spice_new(RedGlzDrawable, 1);
 
     ret->dcc = dcc;
-    ret->red_drawable = drawable->red_drawable;
+    ret->red_drawable = ref_red_drawable(drawable->red_drawable);
     ret->drawable = drawable;
     ret->group_id = drawable->group_id;
     ret->self_bitmap = drawable->self_bitmap;
@@ -5105,10 +5122,9 @@ static void red_display_free_glz_drawable_instance(DisplayChannelClient *dcc,
 
         if (drawable) {
             ring_remove(&glz_drawable->drawable_link);
-        } else { // no reference to the qxl drawable left
-            free_red_drawable(worker, glz_drawable->red_drawable,
-                              glz_drawable->group_id, glz_drawable->self_bitmap);
         }
+        put_red_drawable(worker, glz_drawable->red_drawable,
+                          glz_drawable->group_id, glz_drawable->self_bitmap);
 
         if (ring_item_is_linked(&glz_drawable->link)) {
             ring_remove(&glz_drawable->link);
commit 74ce23a0e88e16476b019c42cee397b97e5ed6a1
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Jun 15 14:48:46 2011 +0200

    server/red_worker: add stream_count (for debug purposes)

diff --git a/server/red_worker.c b/server/red_worker.c
index d1f1176..dc641d4 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -889,6 +889,7 @@ typedef struct RedWorker {
 
     uint32_t shadows_count;
     uint32_t containers_count;
+    uint32_t stream_count;
 
     uint32_t bits_unique;
 
@@ -2388,6 +2389,7 @@ static void red_release_stream(RedWorker *worker, Stream *stream)
             mjpeg_encoder_destroy(stream->mjpeg_encoder);
         }
         red_free_stream(worker, stream);
+        worker->stream_count--;
     }
 }
 
@@ -2770,6 +2772,7 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
     WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
         red_display_create_stream(dcc, stream);
     }
+    worker->stream_count++;
 
     return;
 }
commit 11f2ad60ea9a6e6f6dc546b8a8a05d971129e6cb
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Jun 5 11:49:48 2011 +0300

    server/red_worker: validate_surface: print paniced surface_id

diff --git a/server/red_worker.c b/server/red_worker.c
index 30b9658..d1f1176 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -1185,7 +1185,10 @@ static inline void __validate_surface(RedWorker *worker, uint32_t surface_id)
 static inline void validate_surface(RedWorker *worker, uint32_t surface_id)
 {
     PANIC_ON(surface_id >= worker->n_surfaces);
-    PANIC_ON(!worker->surfaces[surface_id].context.canvas);
+    if (!worker->surfaces[surface_id].context.canvas) {
+        red_printf("failed on %d", surface_id);
+        PANIC_ON(!worker->surfaces[surface_id].context.canvas);
+    }
 }
 
 static char *draw_type_to_str(uint8_t type)
commit d0e740eeaa2ff293edad2bf7df959540208f5572
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Jun 22 13:43:13 2011 +0200

    server/red_worker: no panic on double destroy primary

diff --git a/server/red_worker.c b/server/red_worker.c
index ddd34c6..30b9658 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -10304,7 +10304,11 @@ static inline void handle_dev_destroy_primary_surface(RedWorker *worker)
     receive_data(worker->channel, &surface_id, sizeof(uint32_t));
 
     PANIC_ON(surface_id != 0);
-    PANIC_ON(!worker->surfaces[surface_id].context.canvas);
+
+    if (!worker->surfaces[surface_id].context.canvas) {
+        red_printf("double destroy of primary surface\n");
+        return;
+    }
 
     flush_all_qxl_commands(worker);
     destroy_surface_wait(worker, 0);
commit c820b252c1a7a9ac6574ed5c461c90a7bc569ba9
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 24 13:42:03 2011 +0300

    server/red_worker: DEBUG_CURSORS
    
    Add cursor allocation debugging code that is turned off as long as
    DEBUG_CURSORS is not defined.

diff --git a/server/red_worker.c b/server/red_worker.c
index c264a41..ddd34c6 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -81,6 +81,7 @@
 //#define DRAW_ALL
 //#define COMPRESS_DEBUG
 //#define ACYCLIC_SURFACE_DEBUG
+//#define DEBUG_CURSORS
 
 //#define UPDATE_AREA_BY_TREE
 
@@ -4480,12 +4481,20 @@ static void red_set_cursor(RedWorker *worker, CursorItem *cursor)
     worker->cursor = cursor;
 }
 
+#ifdef DEBUG_CURSORS
+static int _cursor_count = 0;
+#endif
+
 static inline CursorItem *alloc_cursor_item(RedWorker *worker)
 {
     CursorItem *cursor;
+
     if (!worker->free_cursor_items) {
         return NULL;
     }
+#ifdef DEBUG_CURSORS
+    --_cursor_count;
+#endif
     cursor = &worker->free_cursor_items->u.cursor_item;
     worker->free_cursor_items = worker->free_cursor_items->u.next;
     return cursor;
@@ -4495,6 +4504,10 @@ static inline void free_cursor_item(RedWorker *worker, CursorItem *item)
 {
     ((_CursorItem *)item)->u.next = worker->free_cursor_items;
     worker->free_cursor_items = (_CursorItem *)item;
+#ifdef DEBUG_CURSORS
+    ++_cursor_count;
+    ASSERT(_cursor_count <= NUM_CURSORS);
+#endif
 }
 
 static void cursor_items_init(RedWorker *worker)
commit f4d07791cc0c85abf9e886878eddf070b6f838ca
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 24 12:00:43 2011 +0300

    server/red_worker: on_new_display_channel_client: push ack, cleanup
    
    small cleanup patch, only functional change is sending a set ack message.

diff --git a/server/red_worker.c b/server/red_worker.c
index 9fb8dad..c264a41 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -8991,14 +8991,13 @@ static void on_new_display_channel_client(DisplayChannelClient *dcc)
         return;
     }
     red_channel_client_ack_zero_messages_window(&dcc->common.base);
+    red_channel_client_push_set_ack(&dcc->common.base);
     if (worker->surfaces[0].context.canvas) {
         red_current_flush(worker, 0);
         push_new_primary_surface(dcc);
         red_push_surface_image(dcc, 0);
-        if (red_channel_is_connected(&display_channel->common.base)) {
-            red_pipe_add_verb(rcc, SPICE_MSG_DISPLAY_MARK);
-            red_disply_start_streams(dcc);
-        }
+        red_pipe_add_verb(rcc, SPICE_MSG_DISPLAY_MARK);
+        red_disply_start_streams(dcc);
     }
 }
 
commit 969ab70413de331e9b7c3e12e7e3ce06da77742a
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 24 09:44:21 2011 +0300

    server/red_worker: add cursor_channel_client_disconnect
    
    makes RED_WORKER_MESSAGE_CURSOR_DISCONNECT_CLIENT disconnect only a
    single client.

diff --git a/server/red_worker.c b/server/red_worker.c
index 8892b88..9fb8dad 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -9797,6 +9797,15 @@ error:
     red_channel_client_destroy(&dcc->common.base);
 }
 
+static void cursor_channel_client_disconnect(RedChannelClient *rcc)
+{
+    if (!red_channel_is_connected(rcc->channel)) {
+        return;
+    }
+    red_reset_cursor_cache(rcc);
+    red_channel_client_disconnect(rcc);
+}
+
 static void red_disconnect_cursor(RedChannel *channel)
 {
     CommonChannel *common;
@@ -10482,7 +10491,7 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
 
         red_printf("disconnect cursor client");
         receive_data(worker->channel, &rcc, sizeof(RedChannelClient *));
-        red_disconnect_cursor(rcc->channel); /* TODO - assumes a single client */
+        cursor_channel_client_disconnect(rcc);
         message = RED_WORKER_MESSAGE_READY;
         write_message(worker->channel, &message);
         break;
commit c78e951bff941d9975c3e8b2689a1a52cd2c7d33
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 24 09:43:35 2011 +0300

    server/red_worker: remove forced disconnect on connect

diff --git a/server/red_worker.c b/server/red_worker.c
index 7593ce9..8892b88 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -9744,7 +9744,6 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
     size_t stream_buf_size;
     int is_low_bandwidth = main_channel_client_is_low_bandwidth(red_client_get_main(client));
 
-    red_disconnect_all_display_TODO_remove_me((RedChannel *)worker->display_channel);
     ensure_display_channel_created(worker, migrate);
     if (!worker->display_channel) {
         return;
@@ -9923,7 +9922,6 @@ static void red_connect_cursor(RedWorker *worker, RedClient *client, RedsStream
     CursorChannel *channel;
     CursorChannelClient *ccc;
 
-    red_disconnect_cursor((RedChannel *)worker->cursor_channel);
     ensure_cursor_channel_created(worker, migrate);
     if (worker->cursor_channel == NULL) {
         red_printf("failed to create cursor channel");
commit bed8c9d4f6e62165452fe1082492eb43d2c7c5bc
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Tue Aug 2 11:01:16 2011 +0300

    server/red_worker.c: fix CursorPipeItem leak
    
    CursorPipeItems and their corresponding cursor_item were not
    freed when they were removed from the pipe without sending them.
    In addition cursor_channel_hold_pipe_item used wrong conversion
    to (CursorItem*) for a (CursorPipeItem*).

diff --git a/server/red_worker.c b/server/red_worker.c
index 2988164..7593ce9 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -320,6 +320,7 @@ typedef struct CursorItem {
 typedef struct CursorPipeItem {
     PipeItem base;
     CursorItem *cursor_item;
+    int refs;
 } CursorPipeItem;
 
 typedef struct LocalCursor {
@@ -4519,16 +4520,38 @@ static CursorItem *get_cursor_item(RedWorker *worker, RedCursorCmd *cmd, uint32_
     return cursor_item;
 }
 
+static CursorPipeItem *ref_cursor_pipe_item(CursorPipeItem *item)
+{
+    ASSERT(item);
+    item->refs++;
+    return item;
+}
+
 static PipeItem *new_cursor_pipe_item(RedChannelClient *rcc, void *data, int num)
 {
     CursorPipeItem *item = spice_malloc0(sizeof(CursorPipeItem));
 
     red_channel_pipe_item_init(rcc->channel, &item->base, PIPE_ITEM_TYPE_CURSOR);
+    item->refs = 1;
     item->cursor_item = data;
     item->cursor_item->refs++;
     return &item->base;
 }
 
+static void put_cursor_pipe_item(CursorChannelClient *ccc, CursorPipeItem *pipe_item)
+{
+    ASSERT(pipe_item);
+
+    if (--pipe_item->refs) {
+        return;
+    }
+
+    ASSERT(!pipe_item_is_linked(&pipe_item->base));
+
+    red_release_cursor(ccc->common.worker, pipe_item->cursor_item);
+    free(pipe_item);
+}
+
 static void qxl_process_cursor(RedWorker *worker, RedCursorCmd *cursor_cmd, uint32_t group_id)
 {
     CursorItem *cursor_item;
@@ -9550,6 +9573,8 @@ static void display_channel_client_release_item_after_push(DisplayChannelClient
     }
 }
 
+// TODO: share code between before/after_push since most of the items need the same
+// release
 static void display_channel_client_release_item_before_push(DisplayChannelClient *dcc,
                                                             PipeItem *item)
 {
@@ -9813,16 +9838,23 @@ static void on_new_cursor_channel(RedWorker *worker, RedChannelClient *rcc)
 
 static void cursor_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
+    CursorPipeItem *cursor_pipe_item;
     ASSERT(item);
-    ((CursorItem *)item)->refs++;
+    cursor_pipe_item = SPICE_CONTAINEROF(item, CursorPipeItem, base);
+    ref_cursor_pipe_item(cursor_pipe_item);
 }
 
+// TODO: share code between before/after_push since most of the items need the same
+// release
 static void cursor_channel_client_release_item_before_push(CursorChannelClient *ccc,
                                                            PipeItem *item)
 {
     switch (item->type) {
-    case PIPE_ITEM_TYPE_CURSOR:
+    case PIPE_ITEM_TYPE_CURSOR: {
+        CursorPipeItem *cursor_pipe_item = SPICE_CONTAINEROF(item, CursorPipeItem, base);
+        put_cursor_pipe_item(ccc, cursor_pipe_item);
         break;
+    }
     case PIPE_ITEM_TYPE_INVAL_ONE:
     case PIPE_ITEM_TYPE_VERB:
     case PIPE_ITEM_TYPE_MIGRATE:
@@ -9841,8 +9873,7 @@ static void cursor_channel_client_release_item_after_push(CursorChannelClient *c
     switch (item->type) {
         case PIPE_ITEM_TYPE_CURSOR: {
             CursorPipeItem *cursor_pipe_item = SPICE_CONTAINEROF(item, CursorPipeItem, base);
-            red_release_cursor(ccc->common.worker, cursor_pipe_item->cursor_item);
-            free(cursor_pipe_item);
+            put_cursor_pipe_item(ccc, cursor_pipe_item);
             break;
         }
         default:
commit 52bff67935f453f8cf7ccaa3add41f8f122cf4eb
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 24 13:39:41 2011 +0300

    server/red_worker: split cursor pipe item from cursor item
    
    Required to support multiple clients.
    Also changes somewhat the way we produce PIPE_ITEM_TYPE_LOCAL_CURSOR. Btw,
    I haven't managed to see when we actually produce such an item during my
    tests.
    
    Previously we had a single pipe item per CursorItem, this is impossible
    with two pipes, which happens when we have two clients.

diff --git a/server/red_worker.c b/server/red_worker.c
index 896f91b..2988164 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -312,12 +312,16 @@ typedef struct SurfaceDestroyItem {
 } SurfaceDestroyItem;
 
 typedef struct CursorItem {
-    PipeItem pipe_data;
     uint32_t group_id;
     int refs;
     RedCursorCmd *red_cursor;
 } CursorItem;
 
+typedef struct CursorPipeItem {
+    PipeItem base;
+    CursorItem *cursor_item;
+} CursorPipeItem;
+
 typedef struct LocalCursor {
     CursorItem base;
     SpicePoint16 position;
@@ -4509,33 +4513,33 @@ static CursorItem *get_cursor_item(RedWorker *worker, RedCursorCmd *cmd, uint32_
     PANIC_ON(!(cursor_item = alloc_cursor_item(worker)));
 
     cursor_item->refs = 1;
-    red_channel_pipe_item_init(&worker->cursor_channel->common.base,
-                        &cursor_item->pipe_data, PIPE_ITEM_TYPE_CURSOR);
     cursor_item->group_id = group_id;
     cursor_item->red_cursor = cmd;
 
     return cursor_item;
 }
 
-static PipeItem *ref_cursor_pipe_item(RedChannelClient *rcc, void *data, int num)
+static PipeItem *new_cursor_pipe_item(RedChannelClient *rcc, void *data, int num)
 {
-    CursorItem *cursor_item = data;
+    CursorPipeItem *item = spice_malloc0(sizeof(CursorPipeItem));
 
-    cursor_item->refs += (num != 0); /* we already reference the first use */
-    return &cursor_item->pipe_data;
+    red_channel_pipe_item_init(rcc->channel, &item->base, PIPE_ITEM_TYPE_CURSOR);
+    item->cursor_item = data;
+    item->cursor_item->refs++;
+    return &item->base;
 }
 
 static void qxl_process_cursor(RedWorker *worker, RedCursorCmd *cursor_cmd, uint32_t group_id)
 {
-    CursorItem *item;
+    CursorItem *cursor_item;
     int cursor_show = FALSE;
 
-    item = get_cursor_item(worker, cursor_cmd, group_id);
+    cursor_item = get_cursor_item(worker, cursor_cmd, group_id);
 
     switch (cursor_cmd->type) {
     case QXL_CURSOR_SET:
         worker->cursor_visible = cursor_cmd->u.set.visible;
-        red_set_cursor(worker, item);
+        red_set_cursor(worker, cursor_item);
         break;
     case QXL_CURSOR_MOVE:
         cursor_show = !worker->cursor_visible;
@@ -4555,11 +4559,10 @@ static void qxl_process_cursor(RedWorker *worker, RedCursorCmd *cursor_cmd, uint
 
     if (cursor_is_connected(worker) && (worker->mouse_mode == SPICE_MOUSE_MODE_SERVER ||
                                    cursor_cmd->type != QXL_CURSOR_MOVE || cursor_show)) {
-        red_channel_pipes_new_add(&worker->cursor_channel->common.base, ref_cursor_pipe_item,
-                                  (void*)item);
-    } else {
-        red_release_cursor(worker, item);
+        red_channel_pipes_new_add(&worker->cursor_channel->common.base, new_cursor_pipe_item,
+                                  (void*)cursor_item);
     }
+    red_release_cursor(worker, cursor_item);
 }
 
 static inline uint64_t red_now()
@@ -8229,7 +8232,8 @@ static void red_cursor_marshall_inval(RedChannelClient *rcc,
     red_marshall_inval(rcc, m, cach_item);
 }
 
-static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
+static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *base_marshaller,
+                                     PipeItem *pipe_item)
 {
     CursorChannel *cursor_channel;
     CursorChannelClient *ccc = RCC_TO_CCC(rcc);
@@ -8241,7 +8245,7 @@ static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *bas
     cursor_channel = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base);
     worker = cursor_channel->common.worker;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_INIT, &worker->cursor->pipe_data);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_INIT, NULL);
     msg.visible = worker->cursor_visible;
     msg.position = worker->cursor_position;
     msg.trail_length = worker->cursor_trail_length;
@@ -8264,10 +8268,12 @@ static void cursor_channel_marshall_migrate(RedChannelClient *rcc,
 }
 
 static void red_marshall_cursor(RedChannelClient *rcc,
-                   SpiceMarshaller *m, CursorItem *cursor)
+                   SpiceMarshaller *m, CursorPipeItem *cursor_pipe_item)
 {
     CursorChannel *cursor_channel = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base);
     CursorChannelClient *ccc = RCC_TO_CCC(rcc);
+    CursorItem *cursor = cursor_pipe_item->cursor_item;
+    PipeItem *pipe_item = &cursor_pipe_item->base;
     RedCursorCmd *cmd;
     RedWorker *worker;
 
@@ -8280,7 +8286,7 @@ static void red_marshall_cursor(RedChannelClient *rcc,
     case QXL_CURSOR_MOVE:
         {
             SpiceMsgCursorMove cursor_move;
-            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_MOVE, &cursor->pipe_data);
+            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_MOVE, pipe_item);
             cursor_move.position = cmd->u.position;
             spice_marshall_msg_cursor_move(m, &cursor_move);
             break;
@@ -8290,7 +8296,7 @@ static void red_marshall_cursor(RedChannelClient *rcc,
             SpiceMsgCursorSet cursor_set;
             AddBufInfo info;
 
-            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_SET, &cursor->pipe_data);
+            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_SET, pipe_item);
             cursor_set.position = cmd->u.set.position;
             cursor_set.visible = worker->cursor_visible;
 
@@ -8300,13 +8306,13 @@ static void red_marshall_cursor(RedChannelClient *rcc,
             break;
         }
     case QXL_CURSOR_HIDE:
-        red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_HIDE, &cursor->pipe_data);
+        red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_HIDE, pipe_item);
         break;
     case QXL_CURSOR_TRAIL:
         {
             SpiceMsgCursorTrail cursor_trail;
 
-            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_TRAIL, &cursor->pipe_data);
+            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_TRAIL, pipe_item);
             cursor_trail.length = cmd->u.trail.length;
             cursor_trail.frequency = cmd->u.trail.frequency;
             spice_marshall_msg_cursor_trail(m, &cursor_trail);
@@ -8315,7 +8321,6 @@ static void red_marshall_cursor(RedChannelClient *rcc,
     default:
         red_error("bad cursor command %d", cmd->type);
     }
-    red_release_cursor(worker, cursor);
 }
 
 static void red_marshall_surface_create(RedChannelClient *rcc,
@@ -8430,7 +8435,7 @@ static void cursor_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
 
     switch (pipe_item->type) {
     case PIPE_ITEM_TYPE_CURSOR:
-        red_marshall_cursor(rcc, m, (CursorItem *)pipe_item);
+        red_marshall_cursor(rcc, m, SPICE_CONTAINEROF(pipe_item, CursorPipeItem, base));
         break;
     case PIPE_ITEM_TYPE_INVAL_ONE:
         red_cursor_marshall_inval(rcc, m, (CacheItem *)pipe_item);
@@ -8444,7 +8449,7 @@ static void cursor_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
         break;
     case PIPE_ITEM_TYPE_CURSOR_INIT:
         red_reset_cursor_cache(rcc);
-        red_marshall_cursor_init(rcc, m);
+        red_marshall_cursor_init(rcc, m, pipe_item);
         break;
     case PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE:
         red_reset_cursor_cache(rcc);
@@ -9834,9 +9839,12 @@ static void cursor_channel_client_release_item_after_push(CursorChannelClient *c
                                                           PipeItem *item)
 {
     switch (item->type) {
-        case PIPE_ITEM_TYPE_CURSOR:
-            red_release_cursor(ccc->common.worker, SPICE_CONTAINEROF(item, CursorItem, pipe_data));
+        case PIPE_ITEM_TYPE_CURSOR: {
+            CursorPipeItem *cursor_pipe_item = SPICE_CONTAINEROF(item, CursorPipeItem, base);
+            red_release_cursor(ccc->common.worker, cursor_pipe_item->cursor_item);
+            free(cursor_pipe_item);
             break;
+        }
         default:
             PANIC("invalid item type");
     }
commit 24d8340b71bd853fb5e2490a53d8369a4682b370
Author: Alon Levy <alevy at redhat.com>
Date:   Fri Apr 22 23:04:27 2011 +0300

    server/red_worker: whitespace fixes

diff --git a/server/red_worker.c b/server/red_worker.c
index 4225d0e..896f91b 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -2794,6 +2794,7 @@ static void red_display_client_init_streams(DisplayChannelClient *dcc)
 static void red_display_destroy_streams(DisplayChannelClient *dcc)
 {
     int i;
+
     for (i = 0; i < NUM_STREAMS; i++) {
         StreamAgent *agent = &dcc->stream_agents[i];
         region_destroy(&agent->vis_region);
@@ -6993,13 +6994,11 @@ static void red_lossy_marshall_qxl_draw_copy(RedWorker *worker,
                                    &drawable->u.copy.src_area, item, &src_bitmap_data);
 
     src_send_type = red_marshall_qxl_draw_copy(worker, rcc, base_marshaller, dpi, TRUE);
-
     if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) {
         src_is_lossy = TRUE;
     } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) {
         src_is_lossy = FALSE;
     }
-
     surface_lossy_region_update(worker, dcc, item, has_mask,
                                 src_is_lossy);
 }
commit 5117efcff7b61cd9ac9db88ab36a325b26f773ec
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 24 10:31:41 2011 +0300

    server/reds: add RedsState.allow_multiple_clients
    
    Currently set by environment variable SPICE_DEBUG_ALLOW_MC (any value means
    to allow multiple connections). Later will be set by spice api from qemu.

diff --git a/server/reds.c b/server/reds.c
index e078594..b8b4d26 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -74,6 +74,10 @@
 SpiceCoreInterface *core = NULL;
 static SpiceCharDeviceInstance *vdagent = NULL;
 
+/* Debugging only variable: allow multiple client connections to the spice
+ * server */
+#define SPICE_DEBUG_ALLOW_MC_ENV "SPICE_DEBUG_ALLOW_MC"
+
 #define MIGRATION_NOTIFY_SPICE_KEY "spice_mig_ext"
 
 #define REDS_MIG_VERSION 3
@@ -228,6 +232,7 @@ typedef struct RedsState {
     RedsStatValue roundtrip_stat;
 #endif
     int peer_minor_version;
+    int allow_multiple_clients;
 } RedsState;
 
 static RedsState *reds = NULL;
@@ -1502,7 +1507,9 @@ static void reds_handle_main_link(RedLinkInfo *link)
 
     red_printf("");
     link_mess = link->link_mess;
-    reds_disconnect();
+    if (!reds->allow_multiple_clients) {
+        reds_disconnect();
+    }
 
     if (link_mess->connection_id == 0) {
         reds_send_link_result(link, SPICE_LINK_ERR_OK);
@@ -3533,6 +3540,10 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
     inputs_init();
 
     reds->mouse_mode = SPICE_MOUSE_MODE_SERVER;
+    reds->allow_multiple_clients = getenv(SPICE_DEBUG_ALLOW_MC_ENV) != NULL;
+    if (reds->allow_multiple_clients) {
+        red_printf("spice: allowing multiple client connections");
+    }
     atexit(reds_exit);
     return 0;
 
commit 4db9f1d1a9df95e84312f142cf5a0fa233376e21
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Apr 13 09:24:31 2011 +0300

    server/red_channel: introduce client ring in RedChannel
    
    Also adds Drawable pipes and glz rings.
    
    main_channel and red_worker had several locations that still accessed rcc
    directly, so they had to be touched too, but the changes are minimal.
    
    Most changes are in red_channel: drop the single client reference in RedChannel
    and add a ring of channels.
    
    Things missing / not done right in red_worker:
     * StreamAgents are in DCC - right/wrong?
     * GlzDict is multiplied - multiple compressions.
    
    We still are missing:
     * remove the disconnect calls on new connections

diff --git a/server/main_channel.c b/server/main_channel.c
index 1c80105..3337c46 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -165,15 +165,13 @@ static void main_disconnect(MainChannel *main_chan)
     red_channel_destroy(&main_chan->base);
 }
 
-#define MAIN_FOREACH(_link, _main, _mcc) \
-    if ((_main) && ((_mcc) = \
-        SPICE_CONTAINEROF((_main)->base.rcc, MainChannelClient, base)))
-
 RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
 {
+    RingItem *link;
     MainChannelClient *mcc;
 
-    MAIN_FOREACH(link, main_chan, mcc) {
+    RING_FOREACH(link, &main_chan->base.clients) {
+        mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link);
         if (mcc->connection_id == connection_id) {
             return mcc->base.client;
         }
diff --git a/server/red_channel.c b/server/red_channel.c
index eb00aba..b5a0330 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -29,6 +29,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include "ring.h"
 #include "stat.h"
 #include "red_channel.h"
 #include "generated_marshallers.h"
@@ -159,7 +160,14 @@ void red_channel_client_receive(RedChannelClient *rcc)
 
 void red_channel_receive(RedChannel *channel)
 {
-    red_channel_client_receive(channel->rcc);
+    RingItem *link;
+    RingItem *next;
+    RedChannelClient *rcc;
+
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        red_channel_client_receive(rcc);
+    }
 }
 
 static void red_peer_handle_outgoing(RedsStream *stream, OutgoingHandler *handler)
@@ -358,8 +366,8 @@ static void red_channel_client_pipe_remove(RedChannelClient *rcc, PipeItem *item
 
 static void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc)
 {
-    ASSERT(rcc && !channel->rcc);
-	channel->rcc = rcc;
+    ASSERT(rcc);
+    ring_add(&channel->clients, &rcc->channel_link);
     channel->clients_num++;
 }
 
@@ -442,6 +450,7 @@ RedChannel *red_channel_create(int size,
 
     channel->core = core;
     channel->migrate = migrate;
+    ring_init(&channel->clients);
 
     channel->incoming_cb.alloc_msg_buf = (alloc_msg_recv_buf_proc)alloc_recv_buf;
     channel->incoming_cb.release_msg_buf = (release_msg_recv_buf_proc)release_recv_buf;
@@ -511,12 +520,16 @@ void red_channel_client_destroy(RedChannelClient *rcc)
 
 void red_channel_destroy(RedChannel *channel)
 {
+    RingItem *link;
+    RingItem *next;
+
     if (!channel) {
         return;
     }
     red_channel_pipes_clear(channel);
-    if (channel->rcc) {
-        red_channel_client_destroy(channel->rcc);
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        red_channel_client_destroy(
+            SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
     }
     free(channel);
 }
@@ -535,8 +548,12 @@ void red_channel_client_shutdown(RedChannelClient *rcc)
 
 void red_channel_shutdown(RedChannel *channel)
 {
-    if (channel->rcc) {
-        red_channel_client_shutdown(channel->rcc);
+    RingItem *link;
+    RingItem *next;
+
+    red_printf("%d", channel->clients_num);
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        red_channel_client_shutdown(SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
     }
     red_channel_pipes_clear(channel);
 }
@@ -548,8 +565,11 @@ void red_channel_client_send(RedChannelClient *rcc)
 
 void red_channel_send(RedChannel *channel)
 {
-    if (channel->rcc) {
-        red_channel_client_send(channel->rcc);
+    RingItem *link;
+    RingItem *next;
+
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        red_channel_client_send(SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
     }
 }
 
@@ -586,7 +606,7 @@ void red_channel_client_push(RedChannelClient *rcc)
         red_channel_client_send(rcc);
     }
 
-    if (rcc->send_data.item && !rcc->send_data.blocked) {
+    if (!red_channel_client_no_item_being_sent(rcc) && !rcc->send_data.blocked) {
         rcc->send_data.blocked = TRUE;
         red_printf("ERROR: an item waiting to be sent and not blocked");
     }
@@ -599,10 +619,21 @@ void red_channel_client_push(RedChannelClient *rcc)
 
 void red_channel_push(RedChannel *channel)
 {
-    if (!channel || !channel->rcc) {
+    RingItem *link;
+    RingItem *next;
+    RedChannelClient *rcc;
+
+    if (!channel) {
         return;
     }
-    red_channel_client_push(channel->rcc);
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        if (rcc->stream == NULL) {
+            rcc->channel->disconnect(rcc);
+        } else {
+            red_channel_client_push(rcc);
+        }
+    }
 }
 
 static void red_channel_client_init_outgoing_messages_window(RedChannelClient *rcc)
@@ -615,8 +646,12 @@ static void red_channel_client_init_outgoing_messages_window(RedChannelClient *r
 // specific
 void red_channel_init_outgoing_messages_window(RedChannel *channel)
 {
-    if (channel->rcc) {
-        red_channel_client_init_outgoing_messages_window(channel->rcc);
+    RingItem *link;
+    RingItem *next;
+
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        red_channel_client_init_outgoing_messages_window(
+            SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
     }
 }
 
@@ -790,8 +825,12 @@ void red_channel_client_pipe_add_type(RedChannelClient *rcc, int pipe_item_type)
 
 void red_channel_pipes_add_type(RedChannel *channel, int pipe_item_type)
 {
-    if (channel->rcc) {
-        red_channel_client_pipe_add_type(channel->rcc, pipe_item_type);
+    RingItem *link;
+
+    RING_FOREACH(link, &channel->clients) {
+        red_channel_client_pipe_add_type(
+            SPICE_CONTAINEROF(link, RedChannelClient, channel_link),
+            pipe_item_type);
     }
 }
 
@@ -807,7 +846,18 @@ void red_channel_pipe_item_remove(RedChannel *channel, PipeItem *item)
 
 int red_channel_is_connected(RedChannel *channel)
 {
-    return (channel->rcc != NULL) && red_channel_client_is_connected(channel->rcc);
+    RingItem *link;
+
+    if (!channel || channel->clients_num == 0) {
+        return FALSE;
+    }
+    RING_FOREACH(link, &channel->clients) {
+        if (red_channel_client_is_connected(
+                SPICE_CONTAINEROF(link, RedChannelClient, channel_link))) {
+            return TRUE;
+        }
+    }
+    return FALSE;
 }
 
 void red_channel_client_clear_sent_item(RedChannelClient *rcc)
@@ -836,10 +886,17 @@ void red_channel_client_pipe_clear(RedChannelClient *rcc)
 
 void red_channel_pipes_clear(RedChannel *channel)
 {
-    if (!channel || !channel->rcc) {
+    RingItem *link;
+    RingItem *next;
+    RedChannelClient *rcc;
+
+    if (!channel) {
         return;
     }
-    red_channel_client_pipe_clear(channel->rcc);
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        red_channel_client_pipe_clear(rcc);
+    }
 }
 
 void red_channel_client_ack_zero_messages_window(RedChannelClient *rcc)
@@ -856,8 +913,7 @@ static void red_channel_client_remove(RedChannelClient *rcc)
 {
     ring_remove(&rcc->client_link);
     rcc->client->channels_num--;
-    ASSERT(rcc->channel->rcc == rcc);
-    rcc->channel->rcc = NULL;
+    ring_remove(&rcc->channel_link);
     rcc->channel->clients_num--;
 }
 
@@ -878,46 +934,94 @@ void red_channel_client_disconnect(RedChannelClient *rcc)
 
 void red_channel_disconnect(RedChannel *channel)
 {
+    RingItem *link;
+    RingItem *next;
+
     red_channel_pipes_clear(channel);
-    if (channel->rcc) {
-        red_channel_client_disconnect(channel->rcc);
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        red_channel_client_disconnect(
+            SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
     }
 }
 
 int red_channel_all_clients_serials_are_zero(RedChannel *channel)
 {
-    return (!channel->rcc || channel->rcc->send_data.serial == 0);
+    RingItem *link;
+    RedChannelClient *rcc;
+
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        if (rcc->send_data.serial != 0) {
+            return FALSE;
+        }
+    }
+    return TRUE;
 }
 
 void red_channel_apply_clients(RedChannel *channel, channel_client_callback cb)
 {
-    if (channel->rcc) {
-        cb(channel->rcc);
+    RingItem *link;
+    RingItem *next;
+    RedChannelClient *rcc;
+
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        cb(rcc);
     }
 }
 
 void red_channel_apply_clients_data(RedChannel *channel, channel_client_callback_data cb, void *data)
 {
-    if (channel->rcc) {
-        cb(channel->rcc, data);
+    RingItem *link;
+    RingItem *next;
+    RedChannelClient *rcc;
+
+    RING_FOREACH_SAFE(link, next, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        cb(rcc, data);
     }
 }
 
 void red_channel_set_shut(RedChannel *channel)
 {
-    if (channel->rcc) {
-        channel->rcc->incoming.shut = TRUE;
+    RingItem *link;
+    RedChannelClient *rcc;
+
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        rcc->incoming.shut = TRUE;
     }
 }
 
 int red_channel_all_blocked(RedChannel *channel)
 {
-    return !channel || !channel->rcc || channel->rcc->send_data.blocked;
+    RingItem *link;
+    RedChannelClient *rcc;
+
+    if (!channel || channel->clients_num == 0) {
+        return FALSE;
+    }
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        if (!rcc->send_data.blocked) {
+            return FALSE;
+        }
+    }
+    return TRUE;
 }
 
 int red_channel_any_blocked(RedChannel *channel)
 {
-    return !channel || !channel->rcc || channel->rcc->send_data.blocked;
+    RingItem *link;
+    RedChannelClient *rcc;
+
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        if (rcc->send_data.blocked) {
+            return TRUE;
+        }
+    }
+    return FALSE;
 }
 
 int red_channel_client_blocked(RedChannelClient *rcc)
@@ -954,10 +1058,11 @@ SpiceDataHeader *red_channel_client_get_header(RedChannelClient *rcc)
 
 int red_channel_get_first_socket(RedChannel *channel)
 {
-    if (!channel->rcc || !channel->rcc->stream) {
+    if (!channel || channel->clients_num == 0) {
         return -1;
     }
-    return channel->rcc->stream->socket;
+    return SPICE_CONTAINEROF(ring_get_head(&channel->clients),
+                             RedChannelClient, channel_link)->stream->socket;
 }
 
 int red_channel_client_item_being_sent(RedChannelClient *rcc, PipeItem *item)
@@ -967,12 +1072,30 @@ int red_channel_client_item_being_sent(RedChannelClient *rcc, PipeItem *item)
 
 int red_channel_item_being_sent(RedChannel *channel, PipeItem *item)
 {
-    return channel->rcc && red_channel_client_item_being_sent(channel->rcc, item);
+    RingItem *link;
+    RedChannelClient *rcc;
+
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        if (rcc->send_data.item == item) {
+            return TRUE;
+        }
+    }
+    return FALSE;
 }
 
 int red_channel_no_item_being_sent(RedChannel *channel)
 {
-    return !channel->rcc || red_channel_client_no_item_being_sent(channel->rcc);
+    RingItem *link;
+    RedChannelClient *rcc;
+
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        if (!red_channel_client_no_item_being_sent(rcc)) {
+            return FALSE;
+        }
+    }
+    return TRUE;
 }
 
 int red_channel_client_no_item_being_sent(RedChannelClient *rcc)
@@ -1073,16 +1196,17 @@ static void red_channel_pipes_create_batch(RedChannel *channel,
                                 new_pipe_item_t creator, void *data,
                                 rcc_item_t callback)
 {
+    RingItem *link;
     RedChannelClient *rcc;
     PipeItem *item;
     int num = 0;
 
-    if (!(rcc = channel->rcc)) {
-        return;
-    }
-    item = (*creator)(rcc, data, num++);
-    if (callback) {
-        (*callback)(rcc, item);
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        item = (*creator)(rcc, data, num++);
+        if (callback) {
+            (*callback)(rcc, item);
+        }
     }
 }
 
@@ -1108,15 +1232,39 @@ void red_channel_pipes_new_add_tail(RedChannel *channel, new_pipe_item_t creator
 
 uint32_t red_channel_max_pipe_size(RedChannel *channel)
 {
-    return channel->rcc ? channel->rcc->pipe_size : 0;
+    RingItem *link;
+    RedChannelClient *rcc;
+    uint32_t pipe_size = 0;
+
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        pipe_size = pipe_size > rcc->pipe_size ? pipe_size : rcc->pipe_size;
+    }
+    return pipe_size;
 }
 
 uint32_t red_channel_min_pipe_size(RedChannel *channel)
 {
-    return channel->rcc ? channel->rcc->pipe_size : 0;
+    RingItem *link;
+    RedChannelClient *rcc;
+    uint32_t pipe_size = ~0;
+
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        pipe_size = pipe_size < rcc->pipe_size ? pipe_size : rcc->pipe_size;
+    }
+    return pipe_size == ~0 ? 0 : pipe_size;
 }
 
 uint32_t red_channel_sum_pipes_size(RedChannel *channel)
 {
-    return channel->rcc ? channel->rcc->pipe_size : 0;
+    RingItem *link;
+    RedChannelClient *rcc;
+    uint32_t sum = 0;
+
+    RING_FOREACH(link, &channel->clients) {
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        sum += rcc->pipe_size;
+    }
+    return sum;
 }
diff --git a/server/red_channel.h b/server/red_channel.h
index 4b29b0c..652673b 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -173,7 +173,7 @@ struct RedChannel {
     int migrate;
     int handle_acks;
 
-    RedChannelClient *rcc;
+    Ring clients;
     uint32_t clients_num;
 
     OutgoingHandlerInterface outgoing_cb;
@@ -200,7 +200,7 @@ struct RedChannel {
 };
 
 /* if one of the callbacks should cause disconnect, use red_channel_shutdown and don't
-   explicitly destroy the channel */
+ * explicitly destroy the channel */
 RedChannel *red_channel_create(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
@@ -235,6 +235,7 @@ RedChannel *red_channel_create_parser(int size,
                                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);
+
 RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
                                             RedsStream *stream);
 
@@ -314,8 +315,10 @@ int red_channel_client_send_message_pending(RedChannelClient *rcc);
 
 /* returns TRUE if item is being sent by one of the channel clients. This will
  * be true if someone called init_send_data but send has not completed (or perhaps
- * hasn't even begun, i.e. no one called begin_send_)
- * */
+ * hasn't even begun, i.e. no one called begin_send_).
+ * However, note that red_channel_client_init_send_data can also be called with
+ * item==NULL, thus not all pipe items can be tracked.
+ */
 int red_channel_item_being_sent(RedChannel *channel, PipeItem *item);
 int red_channel_client_item_being_sent(RedChannelClient *rcc, PipeItem *item);
 
@@ -387,8 +390,10 @@ struct RedClient {
 };
 
 RedClient *red_client_new();
-void red_client_destroy(RedClient *client);
-void red_client_set_main(RedClient *client, MainChannelClient *mcc);
 MainChannelClient *red_client_get_main(RedClient *client);
+void red_client_set_main(RedClient *client, MainChannelClient *mcc);
+void red_client_destroy(RedClient *client);
+void red_client_disconnect(RedClient *client);
+void red_client_remove_channel(RedClient *client, RedChannelClient *rcc);
 
 #endif
diff --git a/server/red_worker.c b/server/red_worker.c
index cbd9001..4225d0e 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -530,6 +530,12 @@ typedef struct {
 typedef struct RedGlzDrawable RedGlzDrawable;
 
 /* for each qxl drawable, there may be several instances of lz drawables */
+/* TODO - reuse this stuff for the top level. I just added a second level of multiplicity
+ * at the Drawable by keeping a ring, so:
+ * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem
+ * and it should probably (but need to be sure...) be
+ * Drawable -> ring of GlzDrawableInstanceItem.
+ */
 typedef struct GlzDrawableInstanceItem {
     RingItem glz_link;
     RingItem free_link;
@@ -539,6 +545,7 @@ typedef struct GlzDrawableInstanceItem {
 
 struct RedGlzDrawable {
     RingItem link;    // ordered by the time it was encoded
+    RingItem drawable_link;
     RedDrawable *red_drawable;
     Drawable    *drawable;
     uint32_t     group_id;
@@ -739,12 +746,20 @@ typedef struct DependItem {
     RingItem ring_item;
 } DependItem;
 
+typedef struct DrawablePipeItem {
+    RingItem base;  /* link for a list of pipe items held by Drawable */
+    PipeItem dpi_pipe_item; /* link for the client's pipe itself */
+    Drawable *drawable;
+    DisplayChannelClient *dcc;
+    uint8_t refs;
+} DrawablePipeItem;
+
 struct Drawable {
     uint8_t refs;
     RingItem surface_list_link;
     RingItem list_link;
     DrawItem tree_item;
-    PipeItem pipe_item;
+    Ring pipes;
     PipeItem *pipe_item_rest;
     uint32_t size_pipe_item_rest;
 #ifdef UPDATE_AREA_BY_TREE
@@ -752,7 +767,7 @@ struct Drawable {
 #endif
     RedDrawable *red_drawable;
 
-    RedGlzDrawable *red_glz_drawable;
+    Ring glz_ring;
 
     red_time_t creation_time;
     int frames_count;
@@ -989,6 +1004,87 @@ static void red_wait_pipe_item_sent(RedChannelClient *rcc, PipeItem *item);
 static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_id);
 #endif
 
+/*
+ * Macros to make iterating over stuff easier
+ * The two collections we iterate over:
+ *  given a channel, iterate over it's clients
+ */
+
+#define RCC_FOREACH(link, rcc, channel) \
+    for (link = ring_get_head(&(channel)->clients),\
+         rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);\
+            (link);                              \
+            (link) = ring_next(&(channel)->clients, link),\
+            rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link))
+
+#define RCC_FOREACH_SAFE(link, next, rcc, channel) \
+    for (link = ring_get_head(&(channel)->clients),                         \
+         rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link),     \
+         (next) = (link) ? ring_next(&(channel)->clients, (link)) : NULL;      \
+            (link);                                            \
+            (link) = (next),                                   \
+            (next) = (link) ? ring_next(&(channel)->clients, (link)) : NULL,    \
+            rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link))
+
+#define DCC_FOREACH(link, dcc, channel) \
+    for (link = channel ? ring_get_head(&(channel)->clients) : NULL,\
+         dcc = link ? SPICE_CONTAINEROF((link), DisplayChannelClient, common.base.channel_link) : NULL;\
+            (link);                              \
+            (link) = ring_next(&(channel)->clients, link),\
+            dcc = SPICE_CONTAINEROF((link), DisplayChannelClient, common.base.channel_link))
+
+#define WORKER_FOREACH_DCC(worker, link, dcc) \
+    for (link = ((worker) && (worker)->display_channel) ? ring_get_head(&(worker)->display_channel->common.base.clients) : NULL,\
+         dcc = link ? SPICE_CONTAINEROF((link), DisplayChannelClient, common.base.channel_link) : NULL;\
+            (link);                              \
+            (link) = ring_next(&(worker)->display_channel->common.base.clients, link),\
+            dcc = SPICE_CONTAINEROF((link), DisplayChannelClient, common.base.channel_link))
+
+#define DRAWABLE_FOREACH_DPI(drawable, link, dpi) \
+    for (link = (drawable) ? ring_get_head(&(drawable)->pipes) : NULL,\
+         dpi = (link) ? SPICE_CONTAINEROF((link), DrawablePipeItem, base) : NULL; \
+         (link);\
+         (link) = ring_next(&(drawable)->pipes, (link)),\
+         dpi = (link) ? SPICE_CONTAINEROF((link), DrawablePipeItem, base) : NULL)
+
+#define DRAWABLE_FOREACH_GLZ(drawable, link, glz) \
+    for (link = (drawable) ? ring_get_head(&drawable->glz_ring) : NULL,\
+        glz = (link) ? SPICE_CONTAINEROF((link), RedGlzDrawable, drawable_link) : NULL;\
+        (link);\
+        (link) = ring_next(&drawable->glz_ring, (link)),\
+        glz = (link) ? SPICE_CONTAINEROF((link), RedGlzDrawable, drawable_link) : NULL)
+
+#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
+    for (link = (drawable) ? ring_get_head(&drawable->glz_ring) : NULL,\
+        next = (link) ? ring_next(&drawable->glz_ring, link) : NULL,\
+        glz = (link) ? SPICE_CONTAINEROF((link), RedGlzDrawable, drawable_link) : NULL;\
+        (link);\
+        (link) = (next),\
+        (next) = (link) ? ring_next(&drawable->glz_ring, (link)) : NULL,\
+        glz = (link) ? SPICE_CONTAINEROF((link), RedGlzDrawable, drawable_link) : NULL)
+
+#define CCC_FOREACH(link, ccc, channel) \
+    for (link = ring_get_head(&(channel)->clients),\
+         ccc = SPICE_CONTAINEROF(link, CommonChannelClient, base.channel_link);\
+            (link);                              \
+            (link) = ring_next(&(channel)->clients, link),\
+            ccc = SPICE_CONTAINEROF(link, CommonChannelClient, base.channel_link))
+
+#define DCC_TO_WORKER(dcc) \
+    (SPICE_CONTAINEROF((dcc)->common.base.channel, CommonChannel, base)->worker)
+
+// TODO: replace with DCC_FOREACH when it is introduced
+#define WORKER_TO_DCC(worker) \
+    (worker->display_channel ? SPICE_CONTAINEROF(worker->display_channel->common.base.rcc, DisplayChannelClient, common.base) : NULL)
+
+#define DCC_TO_DC(dcc) SPICE_CONTAINEROF((dcc)->common.base.channel,\
+                                         DisplayChannel, common.base)
+
+#define RCC_TO_DCC(rcc) SPICE_CONTAINEROF((rcc), DisplayChannelClient, common.base)
+#define RCC_TO_CCC(rcc) SPICE_CONTAINEROF((rcc), CursorChannelClient, common.base)
+
+
+
 #ifdef COMPRESS_STAT
 static void print_compress_stats(DisplayChannel *display_channel)
 {
@@ -1189,30 +1285,17 @@ static void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb)
 
 static inline void red_create_surface_item(DisplayChannelClient *dcc, int surface_id);
 static void red_push_surface_image(DisplayChannelClient *dcc, int surface_id);
+
 static void red_pipes_add_verb(RedChannel *channel, uint16_t verb)
 {
-    RedChannelClient *rcc = channel->rcc;
+    RedChannelClient *rcc;
+    RingItem *link;
 
-    if (!rcc) {
-        return;
+    RCC_FOREACH(link, rcc, channel) {
+        red_pipe_add_verb(rcc, verb);
     }
-    red_pipe_add_verb(rcc, verb);
 }
 
-
-// TODO: replace with FOREACH_DCC when it is introduced
-#define WORKER_TO_DCC(worker) \
-    (worker->display_channel ? SPICE_CONTAINEROF(worker->display_channel->common.base.rcc, DisplayChannelClient, common.base) : NULL)
-
-#define DCC_TO_WORKER(dcc) \
-    (SPICE_CONTAINEROF((dcc)->common.base.channel, CommonChannel, base)->worker)
-
-#define DCC_TO_DC(dcc) SPICE_CONTAINEROF((dcc)->common.base.channel,\
-                                         DisplayChannel, common.base)
-
-#define RCC_TO_DCC(rcc) SPICE_CONTAINEROF((rcc), DisplayChannelClient, common.base)
-#define RCC_TO_CCC(rcc) SPICE_CONTAINEROF((rcc), CursorChannelClient, common.base)
-
 static inline void red_handle_drawable_surfaces_client_synced(
                         DisplayChannelClient *dcc, Drawable *drawable)
 {
@@ -1254,44 +1337,111 @@ static int cursor_is_connected(RedWorker *worker)
         &worker->cursor_channel->common.base));
 }
 
-// TODO: remove red_pipe_add_drawable, replace with red_clients_add_drawable
-static inline void red_pipe_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
+static void put_drawable_pipe_item(DrawablePipeItem *dpi)
 {
-    if (!dcc) {
+    RedWorker *worker = DCC_TO_WORKER(dpi->dcc);
+
+    if (--dpi->refs) {
         return;
     }
 
-    red_handle_drawable_surfaces_client_synced(dcc, drawable);
+    ASSERT(!ring_item_is_linked(&dpi->dpi_pipe_item.link));
+    ASSERT(!ring_item_is_linked(&dpi->base));
+    release_drawable(worker, dpi->drawable);
+    free(dpi);
+}
+
+static inline DrawablePipeItem *get_drawable_pipe_item(DisplayChannelClient *dcc,
+                                                       Drawable *drawable)
+{
+    DrawablePipeItem *dpi;
 
+    dpi = spice_malloc0(sizeof(*dpi));
+    dpi->drawable = drawable;
+    dpi->dcc = dcc;
+    ring_item_init(&dpi->base);
+    ring_add(&drawable->pipes, &dpi->base);
+    red_channel_pipe_item_init(dcc->common.base.channel, &dpi->dpi_pipe_item, PIPE_ITEM_TYPE_DRAW);
+    dpi->refs++;
     drawable->refs++;
-    red_channel_client_pipe_add(&dcc->common.base, &drawable->pipe_item);
+    return dpi;
+}
+
+static inline DrawablePipeItem *ref_drawable_pipe_item(DrawablePipeItem *dpi)
+{
+    ASSERT(dpi->drawable);
+    dpi->refs++;
+    return dpi;
+}
+
+static inline void red_pipe_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
+{
+    DrawablePipeItem *dpi;
+
+    red_handle_drawable_surfaces_client_synced(dcc, drawable);
+    dpi = get_drawable_pipe_item(dcc, drawable);
+    red_channel_client_pipe_add(&dcc->common.base, &dpi->dpi_pipe_item);
+}
+
+static inline void red_pipes_add_drawable(RedWorker *worker, Drawable *drawable)
+{
+    DisplayChannelClient *dcc;
+    RingItem *dcc_ring_item;
+
+    PANIC_ON(!ring_is_empty(&drawable->pipes));
+    WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
+        red_pipe_add_drawable(dcc, drawable);
+    }
 }
 
 static inline void red_pipe_add_drawable_to_tail(DisplayChannelClient *dcc, Drawable *drawable)
 {
+    DrawablePipeItem *dpi;
+
     if (!dcc) {
         return;
     }
     red_handle_drawable_surfaces_client_synced(dcc, drawable);
-    drawable->refs++;
-    red_channel_client_pipe_add_tail(&dcc->common.base, &drawable->pipe_item);
+    dpi = get_drawable_pipe_item(dcc, drawable);
+    red_channel_client_pipe_add_tail(&dcc->common.base, &dpi->dpi_pipe_item);
 }
 
-static inline void red_pipe_add_drawable_after(DisplayChannelClient *dcc, Drawable *drawable,
-                                               Drawable *pos_after)
+static inline void red_pipes_add_drawable_after(RedWorker *worker,
+                                                Drawable *drawable, Drawable *pos_after)
 {
-    if (!dcc) {
+    DrawablePipeItem *dpi, *dpi_pos_after;
+    RingItem *dpi_link;
+    DisplayChannelClient *dcc;
+    int num_other_linked = 0;
+
+    DRAWABLE_FOREACH_DPI(pos_after, dpi_link, dpi_pos_after) {
+        num_other_linked++;
+        dcc = dpi_pos_after->dcc;
+        red_handle_drawable_surfaces_client_synced(dcc, drawable);
+        dpi = get_drawable_pipe_item(dcc, drawable);
+        red_channel_client_pipe_add_after(&dcc->common.base, &dpi->dpi_pipe_item,
+                                          &dpi_pos_after->dpi_pipe_item);
+    }
+    if (num_other_linked == 0) {
+        red_pipes_add_drawable(worker, drawable);
         return;
     }
-
-    if (!pos_after || !pipe_item_is_linked(&pos_after->pipe_item)) {
-        red_pipe_add_drawable(dcc, drawable);
-        return;
+    if (num_other_linked != worker->display_channel->common.base.clients_num) {
+        RingItem *worker_item;
+        red_printf("TODO: not O(n^2)");
+        WORKER_FOREACH_DCC(worker, worker_item, dcc) {
+            int sent = 0;
+            DRAWABLE_FOREACH_DPI(pos_after, dpi_link, dpi_pos_after) {
+                if (dpi_pos_after->dcc == dcc) {
+                    sent = 1;
+                    break;
+                }
+            }
+            if (!sent) {
+                red_pipe_add_drawable(dcc, drawable);
+            }
+        }
     }
-    red_handle_drawable_surfaces_client_synced(dcc, drawable);
-    drawable->refs++;
-    red_channel_client_pipe_add_after(&dcc->common.base, &drawable->pipe_item,
-                                      &pos_after->pipe_item);
 }
 
 static inline PipeItem *red_pipe_get_tail(DisplayChannelClient *dcc)
@@ -1305,10 +1455,16 @@ static inline PipeItem *red_pipe_get_tail(DisplayChannelClient *dcc)
 
 static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id);
 
-static inline void red_pipe_remove_drawable(DisplayChannelClient *dcc, Drawable *drawable)
+static inline void red_pipes_remove_drawable(Drawable *drawable)
 {
-    if (pipe_item_is_linked(&drawable->pipe_item)) {
-        red_channel_client_pipe_remove_and_release(&dcc->common.base, &drawable->pipe_item);
+    DrawablePipeItem *dpi;
+    RingItem *item, *next;
+
+    RING_FOREACH_SAFE(item, next, &drawable->pipes) {
+        dpi = SPICE_CONTAINEROF(item, DrawablePipeItem, base);
+        if (pipe_item_is_linked(&dpi->dpi_pipe_item)) {
+            red_channel_client_pipe_remove_and_release(&dpi->dcc->common.base, &dpi->dpi_pipe_item);
+        }
     }
 }
 
@@ -1445,6 +1601,8 @@ static inline void red_destroy_surface_item(RedWorker *worker,
 static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
 {
     RedSurface *surface = &worker->surfaces[surface_id];
+    DisplayChannelClient *dcc;
+    RingItem *link;
 
     if (!--surface->refs) {
         // only primary surface streams are supported
@@ -1463,13 +1621,15 @@ static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
 
         region_destroy(&surface->draw_dirty_region);
         surface->context.canvas = NULL;
-        red_destroy_surface_item(worker, WORKER_TO_DCC(worker), surface_id);
+        WORKER_FOREACH_DCC(worker, link, dcc) {
+            red_destroy_surface_item(worker, dcc, surface_id);
+        }
 
         PANIC_ON(!ring_is_empty(&surface->depend_on_me));
     }
 }
 
-static inline void set_surface_release_info(RedWorker *worker, uint32_t surface_id, int is_create, 
+static inline void set_surface_release_info(RedWorker *worker, uint32_t surface_id, int is_create,
                                             QXLReleaseInfo *release_info, uint32_t group_id)
 {
     RedSurface *surface;
@@ -1535,23 +1695,31 @@ static void remove_drawable_dependencies(RedWorker *worker, Drawable *drawable)
     }
 }
 
-static inline void release_drawable(RedWorker *worker, Drawable *item)
+static inline void release_drawable(RedWorker *worker, Drawable *drawable)
 {
-    if (!--item->refs) {
-        ASSERT(!item->stream);
-        ASSERT(!item->tree_item.shadow);
-        region_destroy(&item->tree_item.base.rgn);
+    int glz_count = 0;
+    RingItem *item, *next;
 
-        remove_drawable_dependencies(worker, item);
-        red_dec_surfaces_drawable_dependencies(worker, item);
-        red_destroy_surface(worker, item->surface_id);
+    if (!--drawable->refs) {
+        ASSERT(!drawable->stream);
+        ASSERT(!drawable->tree_item.shadow);
+        ASSERT(ring_is_empty(&drawable->pipes));
+        region_destroy(&drawable->tree_item.base.rgn);
 
-        if (item->red_glz_drawable) {
-            item->red_glz_drawable->drawable = NULL;
-        } else { // no reference to the qxl drawable left
-            free_red_drawable(worker, item->red_drawable, item->group_id, item->self_bitmap);
+        remove_drawable_dependencies(worker, drawable);
+        red_dec_surfaces_drawable_dependencies(worker, drawable);
+        red_destroy_surface(worker, drawable->surface_id);
+
+        RING_FOREACH_SAFE(item, next, &drawable->glz_ring) {
+            SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->drawable = NULL;
+            ring_remove(item);
+            glz_count++;
+        }
+        if (!glz_count) { // no reference to the qxl drawable left
+            free_red_drawable(worker, drawable->red_drawable,
+                              drawable->group_id, drawable->self_bitmap);
         }
-        free_drawable(worker, item);
+        free_drawable(worker, drawable);
     }
 }
 
@@ -1653,10 +1821,10 @@ static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
     worker->current_size--;
 }
 
-static void remove_drawable(RedWorker *worker, Drawable *item)
+static void remove_drawable(RedWorker *worker, Drawable *drawable)
 {
-    red_pipe_remove_drawable(WORKER_TO_DCC(worker), item);
-    current_remove_drawable(worker, item);
+    red_pipes_remove_drawable(drawable);
+    current_remove_drawable(worker, drawable);
 }
 
 static inline void current_remove(RedWorker *worker, TreeItem *item)
@@ -1760,9 +1928,12 @@ static void red_clear_surface_drawables_from_pipe(DisplayChannelClient *dcc, int
     item = (PipeItem *) ring;
     while ((item = (PipeItem *)ring_next(ring, (RingItem *)item))) {
         Drawable *drawable;
+        DrawablePipeItem *dpi = NULL;
         int depend_found = FALSE;
+
         if (item->type == PIPE_ITEM_TYPE_DRAW) {
-            drawable = SPICE_CONTAINEROF(item, Drawable, pipe_item);
+            dpi = SPICE_CONTAINEROF(item, DrawablePipeItem, dpi_pipe_item);
+            drawable = dpi->drawable;
         } else if (item->type == PIPE_ITEM_TYPE_UPGRADE) {
             drawable = ((UpgradeItem *)item)->drawable;
         } else {
@@ -1800,6 +1971,28 @@ static void red_clear_surface_drawables_from_pipe(DisplayChannelClient *dcc, int
     }
 }
 
+static void red_wait_outgoing_item(RedChannelClient *rcc);
+static void red_wait_outgoing_items(RedChannel *channel);
+
+static void red_clear_surface_drawables_from_pipes(RedWorker *worker, int surface_id,
+    int force, int wait_for_outgoing_item)
+{
+    RingItem *item;
+    DisplayChannelClient *dcc;
+
+    WORKER_FOREACH_DCC(worker, item, dcc) {
+        red_clear_surface_drawables_from_pipe(dcc, surface_id, force);
+        if (wait_for_outgoing_item) {
+            // in case that the pipe didn't contain any item that is dependent on the surface, but
+            // there is one during sending.
+            red_wait_outgoing_item(&dcc->common.base);
+            if (dcc) {
+                ASSERT(red_channel_client_no_item_being_sent(&dcc->common.base));
+            }
+        }
+    }
+}
+
 #ifdef PIPE_DEBUG
 
 static void print_rgn(const char* prefix, const QRegion* rgn)
@@ -2267,6 +2460,7 @@ static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *str
 {
     DisplayChannelClient *dcc;
     StreamAgent *agent;
+    RingItem *item;
 
     ASSERT(!drawable->stream && !stream->current);
     ASSERT(drawable && stream);
@@ -2274,8 +2468,7 @@ static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *str
     drawable->stream = stream;
     stream->last_time = drawable->creation_time;
 
-    dcc = WORKER_TO_DCC(worker);
-    if (dcc) {
+    WORKER_FOREACH_DCC(worker, item, dcc) {
         agent = &dcc->stream_agents[stream - worker->streams_buf];
         if (!region_is_equal(&agent->vis_region, &drawable->tree_item.base.rgn)) {
             region_destroy(&agent->vis_region);
@@ -2287,11 +2480,12 @@ static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *str
 
 static void red_stop_stream(RedWorker *worker, Stream *stream)
 {
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    DisplayChannelClient *dcc;
+    RingItem *item;
+
     ASSERT(ring_item_is_linked(&stream->link));
     ASSERT(!stream->current);
-
-    if (dcc) {
+    WORKER_FOREACH_DCC(worker, item, dcc) {
         StreamAgent *stream_agent;
         stream_agent = &dcc->stream_agents[stream - worker->streams_buf];
         region_clear(&stream_agent->vis_region);
@@ -2303,18 +2497,27 @@ static void red_stop_stream(RedWorker *worker, Stream *stream)
     red_release_stream(worker, stream);
 }
 
+static int drawable_is_linked(Drawable *drawable)
+{
+    return !ring_is_empty(&drawable->pipes);
+}
+
 static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *stream)
 {
     RedChannel *channel;
+    RingItem *item;
     RedChannelClient *rcc;
+    DisplayChannelClient *dcc;
 
     ASSERT(stream->current);
-    if (WORKER_TO_DCC(worker) && !pipe_item_is_linked(&stream->current->pipe_item)) {
+    WORKER_FOREACH_DCC(worker, item, dcc) {
         UpgradeItem *upgrade_item;
         int n_rects;
-        rcc = &WORKER_TO_DCC(worker)->common.base;
+        if (drawable_is_linked(stream->current)) {
+            continue;
+        }
+        rcc = &dcc->common.base;
         channel = rcc->channel;
-
         upgrade_item = spice_new(UpgradeItem, 1);
         upgrade_item->refs = 1;
         red_channel_pipe_item_init(channel,
@@ -2336,24 +2539,33 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region)
 {
     Ring *ring = &worker->streams;
     RingItem *item = ring_get_head(ring);
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    RingItem *dcc_ring_item;
+    DisplayChannelClient *dcc;
+    int has_clients = display_is_connected(worker);
 
     while (item) {
         Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
+        int detach_stream = 0;
         item = ring_next(ring, item);
 
-        if (dcc) {
+        WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
             StreamAgent *agent = &dcc->stream_agents[stream - worker->streams_buf];
             if (region_intersects(&agent->vis_region, region)) {
                 region_clear(&agent->vis_region);
                 push_stream_clip(dcc, agent);
-                if (stream->current) {
-                    red_detach_stream_gracefully(worker, stream);
-                }
+                detach_stream = 1;
+            }
+        }
+        if (!has_clients) {
+            if (stream->current &&
+                region_intersects(&stream->current->tree_item.base.rgn, region)) {
+                red_detach_stream(worker, stream);
+            }
+        }
+        if (detach_stream) {
+            if (stream->current) {
+                red_detach_stream_gracefully(worker, stream);
             }
-        } else if (stream->current && region_intersects(&stream->current->tree_item.base.rgn,
-                                                        region)) {
-            red_detach_stream(worker, stream);
         }
     }
 }
@@ -2362,9 +2574,10 @@ static void red_streams_update_clip(RedWorker *worker, Drawable *drawable)
 {
     Ring *ring;
     RingItem *item;
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    RingItem *dcc_ring_item;
+    DisplayChannelClient *dcc;
 
-    if (!dcc) {
+    if (!display_is_connected(worker)) {
         return;
     }
 
@@ -2381,15 +2594,17 @@ static void red_streams_update_clip(RedWorker *worker, Drawable *drawable)
 
         item = ring_next(ring, item);
 
-        agent = &dcc->stream_agents[stream - worker->streams_buf];
-
         if (stream->current == drawable) {
             continue;
         }
 
-        if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) {
-            region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn);
-            push_stream_clip(dcc, agent);
+        WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
+            agent = &dcc->stream_agents[stream - worker->streams_buf];
+
+            if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) {
+                region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn);
+                push_stream_clip(dcc, agent);
+            }
         }
     }
 }
@@ -2474,6 +2689,21 @@ static int get_bit_rate(DisplayChannelClient *dcc,
     return bit_rate;
 }
 
+static int get_minimal_bit_rate(RedWorker *worker, int width, int height)
+{
+    RingItem *item;
+    DisplayChannelClient *dcc;
+    int ret = INT_MAX;
+
+    WORKER_FOREACH_DCC(worker, item, dcc) {
+        int bit_rate = get_bit_rate(dcc, width, height);
+        if (bit_rate < ret) {
+            ret = bit_rate;
+        }
+    }
+    return ret;
+}
+
 static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
 {
     StreamAgent *agent = &dcc->stream_agents[stream - dcc->common.worker->streams_buf];
@@ -2496,7 +2726,8 @@ static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
  * maybe we can't reach this function in that case? question: do we want to? */
 static void red_create_stream(RedWorker *worker, Drawable *drawable)
 {
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    DisplayChannelClient *dcc;
+    RingItem *dcc_ring_item;
     Stream *stream;
     SpiceRect* src_rect;
     int stream_width;
@@ -2522,12 +2753,12 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
     stream->height = src_rect->bottom - src_rect->top;
     stream->dest_area = drawable->red_drawable->bbox;
     stream->refs = 1;
-    stream->bit_rate = get_bit_rate(dcc, stream_width, stream_height);
+    stream->bit_rate = get_minimal_bit_rate(worker, stream_width, stream_height);
     SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
     stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
     drawable->stream = stream;
 
-    if (dcc) {
+    WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
         red_display_create_stream(dcc, stream);
     }
 
@@ -2645,51 +2876,57 @@ static void reset_rate(DisplayChannelClient *dcc, StreamAgent *stream_agent)
     /* MJpeg has no rate limiting anyway, so do nothing */
 }
 
-static int display_channel_is_low_bandwidth(DisplayChannelClient *dcc)
+static int display_channel_client_is_low_bandwidth(DisplayChannelClient *dcc)
 {
-    RedChannelClient *rcc = &dcc->common.base;
-
-    return dcc && main_channel_client_is_low_bandwidth(
-                red_client_get_main(red_channel_client_get_client(rcc)));
+    return main_channel_client_is_low_bandwidth(
+        red_client_get_main(red_channel_client_get_client(&dcc->common.base)));
 }
 
 static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
 {
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    DrawablePipeItem *dpi;
+    DisplayChannelClient *dcc;
     int index;
     StreamAgent *agent;
+    RingItem *ring_item;
 
     ASSERT(stream->current);
 
-    if (!dcc || !display_channel_is_low_bandwidth(dcc)) {
+    if (!display_is_connected(worker)) {
         return;
     }
 
     index = stream - worker->streams_buf;
-    agent = &dcc->stream_agents[index];
+    DRAWABLE_FOREACH_DPI(stream->current, ring_item, dpi) {
+        dcc = dpi->dcc;
+        if (!display_channel_client_is_low_bandwidth(dcc)) {
+            continue;
+        }
+        agent = &dcc->stream_agents[index];
 
-    if (pipe_item_is_linked(&stream->current->pipe_item)) {
-        ++agent->drops;
-    }
+        if (pipe_item_is_linked(&dpi->dpi_pipe_item)) {
+            ++agent->drops;
+        }
 
-    if (agent->frames / agent->fps < FPS_TEST_INTERVAL) {
-        agent->frames++;
-        return;
-    }
+        if (agent->frames / agent->fps < FPS_TEST_INTERVAL) {
+            agent->frames++;
+            return;
+        }
 
-    double drop_factor = ((double)agent->frames - (double)agent->drops) / (double)agent->frames;
+        double drop_factor = ((double)agent->frames - (double)agent->drops) / (double)agent->frames;
 
-    if (drop_factor == 1) {
-        if (agent->fps < MAX_FPS) {
-            agent->fps++;
-        }
-    } else if (drop_factor < 0.9) {
-        if (agent->fps > 1) {
-            agent->fps--;
+        if (drop_factor == 1) {
+            if (agent->fps < MAX_FPS) {
+                agent->fps++;
+            }
+        } else if (drop_factor < 0.9) {
+            if (agent->fps > 1) {
+                agent->fps--;
+            }
         }
+        agent->frames = 1;
+        agent->drops = 0;
     }
-    agent->frames = 1;
-    agent->drops = 0;
 }
 
 static inline void red_update_copy_graduality(RedWorker* worker, Drawable *drawable)
@@ -2798,7 +3035,6 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
     DrawItem *other_draw_item;
     Drawable *drawable;
     Drawable *other_drawable;
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
 
     if (other->type != TREE_ITEM_TYPE_DRAWABLE) {
         return FALSE;
@@ -2819,11 +3055,11 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
         other_drawable->refs++;
         current_remove_drawable(worker, other_drawable);
         if (add_after) {
-            red_pipe_add_drawable_after(dcc, drawable, other_drawable);
+            red_pipes_add_drawable_after(worker, drawable, other_drawable);
         } else {
-            red_pipe_add_drawable(dcc, drawable);
+            red_pipes_add_drawable(worker, drawable);
         }
-        red_pipe_remove_drawable(dcc, other_drawable);
+        red_pipes_remove_drawable(other_drawable);
         release_drawable(worker, other_drawable);
         return TRUE;
     }
@@ -2831,13 +3067,42 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
     switch (item->effect) {
     case QXL_EFFECT_REVERT_ON_DUP:
         if (is_same_drawable(worker, drawable, other_drawable)) {
+
+            DisplayChannelClient *dcc;
+            DrawablePipeItem *dpi;
+            RingItem *worker_ring_item, *dpi_ring_item;
+
             other_drawable->refs++;
             current_remove_drawable(worker, other_drawable);
-            if (!ring_item_is_linked(&other_drawable->pipe_item.link)) {
-                red_pipe_add_drawable(dcc, drawable);
-            } else {
-                red_pipe_remove_drawable(dcc, other_drawable);
+
+            /* sending the drawable to clients that already received 
+             * (or will receive) other_drawable */
+            worker_ring_item = ring_get_head(&worker->display_channel->common.base.clients);
+            dpi_ring_item = ring_get_head(&other_drawable->pipes);
+            /* dpi contains a sublist of dcc's, ordered the same */
+            while (worker_ring_item) {
+                dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
+                                        common.base.channel_link);
+                dpi = SPICE_CONTAINEROF(dpi_ring_item, DrawablePipeItem, base);
+                while (worker_ring_item && (!dpi || dcc != dpi->dcc)) {
+                    red_pipe_add_drawable(dcc, drawable);
+                    worker_ring_item = ring_next(&worker->display_channel->common.base.clients,
+                                                 worker_ring_item);
+                    dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
+                                            common.base.channel_link);
+                }
+
+                if (dpi_ring_item) {
+                    dpi_ring_item = ring_next(&other_drawable->pipes, dpi_ring_item);
+                }
+                if (worker_ring_item) {
+                    worker_ring_item = ring_next(&worker->display_channel->common.base.clients,
+                                                 worker_ring_item);
+                }
             }
+            /* not sending other_drawable where possible */
+            red_pipes_remove_drawable(other_drawable);
+
             release_drawable(worker, other_drawable);
             return TRUE;
         }
@@ -2846,7 +3111,7 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
         if (is_same_geometry(worker, drawable, other_drawable)) {
             __current_add_drawable(worker, drawable, &other->siblings_link);
             remove_drawable(worker, other_drawable);
-            red_pipe_add_drawable(dcc, drawable);
+            red_pipes_add_drawable(worker, drawable);
             return TRUE;
         }
         break;
@@ -3318,13 +3583,15 @@ static void free_one_drawable(RedWorker *worker, int force_glz_free)
     RingItem *ring_item = ring_get_tail(&worker->current_list);
     Drawable *drawable;
     Container *container;
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
 
     ASSERT(ring_item);
     drawable = SPICE_CONTAINEROF(ring_item, Drawable, list_link);
-    if (drawable->red_glz_drawable && force_glz_free) {
-        ASSERT(worker->display_channel);
-        red_display_free_glz_drawable(dcc, drawable->red_glz_drawable);
+    if (force_glz_free) {
+        RingItem *glz_item, *next_item;
+        RedGlzDrawable *glz;
+        DRAWABLE_FOREACH_GLZ_SAFE(drawable, glz_item, next_item, glz) {
+            red_display_free_glz_drawable(glz->dcc, glz);
+        }
     }
     red_draw_drawable(worker, drawable);
     container = drawable->tree_item.base.container;
@@ -3358,8 +3625,6 @@ static Drawable *get_drawable(RedWorker *worker, uint8_t effect, RedDrawable *re
     drawable->tree_item.base.type = TREE_ITEM_TYPE_DRAWABLE;
     region_init(&drawable->tree_item.base.rgn);
     drawable->tree_item.effect = effect;
-    red_channel_pipe_item_init(&worker->display_channel->common.base,
-        &drawable->pipe_item, PIPE_ITEM_TYPE_DRAW);
     drawable->red_drawable = red_drawable;
     drawable->group_id = group_id;
 
@@ -3371,6 +3636,8 @@ static Drawable *get_drawable(RedWorker *worker, uint8_t effect, RedDrawable *re
             validate_surface(worker, drawable->surfaces_dest[x]);
         }
     }
+    ring_init(&drawable->pipes);
+    ring_init(&drawable->glz_ring);
 
     return drawable;
 }
@@ -3451,7 +3718,6 @@ static inline void red_process_drawable(RedWorker *worker, RedDrawable *drawable
 {
     int surface_id;
     Drawable *item = get_drawable(worker, drawable->effect, drawable, group_id);
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
 
     ASSERT(item);
 
@@ -3509,7 +3775,7 @@ static inline void red_process_drawable(RedWorker *worker, RedDrawable *drawable
         if (item->tree_item.effect != QXL_EFFECT_OPAQUE) {
             worker->transparent_count++;
         }
-        red_pipe_add_drawable(dcc, item);
+        red_pipes_add_drawable(worker, item);
 #ifdef DRAW_ALL
         red_draw_qxl_drawable(worker, item);
 #endif
@@ -3558,7 +3824,7 @@ static inline void red_process_surface(RedWorker *worker, RedSurfaceCmd *surface
            otherwise "current" will hold items that other drawables may depend on, and then
            red_current_clear will remove them from the pipe. */
         red_current_clear(worker, surface_id);
-        red_clear_surface_drawables_from_pipe(WORKER_TO_DCC(worker), surface_id, FALSE);
+        red_clear_surface_drawables_from_pipes(worker, surface_id, FALSE, FALSE);
         red_destroy_surface(worker, surface_id);
         break;
     default:
@@ -4446,22 +4712,30 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
 static void red_free_some(RedWorker *worker)
 {
     int n = 0;
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
-    GlzSharedDictionary *glz_dict = dcc ? dcc->glz_dict : NULL;
+    DisplayChannelClient *dcc;
+    RingItem *item;
 
-    if (glz_dict) {
-        // encoding using the dictionary is prevented since the following operations might
-        // change the dictionary
-        pthread_rwlock_wrlock(&glz_dict->encode_lock);
-        n = red_display_free_some_independent_glz_drawables(dcc);
+    WORKER_FOREACH_DCC(worker, item, dcc) {
+        GlzSharedDictionary *glz_dict = dcc ? dcc->glz_dict : NULL;
+
+        if (glz_dict) {
+            // encoding using the dictionary is prevented since the following operations might
+            // change the dictionary
+            pthread_rwlock_wrlock(&glz_dict->encode_lock);
+            n = red_display_free_some_independent_glz_drawables(dcc);
+        }
     }
 
     while (!ring_is_empty(&worker->current_list) && n++ < RED_RELEASE_BUNCH_SIZE) {
         free_one_drawable(worker, TRUE);
     }
 
-    if (glz_dict) {
-        pthread_rwlock_unlock(&glz_dict->encode_lock);
+    WORKER_FOREACH_DCC(worker, item, dcc) {
+        GlzSharedDictionary *glz_dict = dcc ? dcc->glz_dict : NULL;
+
+        if (glz_dict) {
+            pthread_rwlock_unlock(&glz_dict->encode_lock);
+        }
     }
 }
 
@@ -4474,11 +4748,11 @@ static void red_current_flush(RedWorker *worker, int surface_id)
 }
 
 // adding the pipe item after pos. If pos == NULL, adding to head.
-static ImageItem *red_add_surface_area_image(RedWorker *worker, int surface_id, SpiceRect *area,
+static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surface_id, SpiceRect *area,
                                              PipeItem *pos, int can_lossy)
 {
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
-    DisplayChannel *display_channel = worker ? worker->display_channel : NULL;
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
+    RedWorker *worker = display_channel->common.worker;
     RedChannel *channel = &display_channel->common.base;
     RedSurface *surface = &worker->surfaces[surface_id];
     SpiceCanvas *canvas = surface->context.canvas;
@@ -4491,9 +4765,6 @@ static ImageItem *red_add_surface_area_image(RedWorker *worker, int surface_id,
 
     ASSERT(area);
 
-    if (!dcc) {
-        return NULL;
-    }
     width = area->right - area->left;
     height = area->bottom - area->top;
     bpp = SPICE_SURFACE_FMT_DEPTH(surface->context.format) / 8;
@@ -4561,7 +4832,7 @@ static void red_push_surface_image(DisplayChannelClient *dcc, int surface_id)
 
     /* not allowing lossy compression because probably, especially if it is a primary surface,
        it combines both "picture-like" areas with areas that are more "artificial"*/
-    red_add_surface_area_image(worker, surface_id, &area, NULL, FALSE);
+    red_add_surface_area_image(dcc, surface_id, &area, NULL, FALSE);
     red_channel_client_push(&dcc->common.base);
 }
 
@@ -4706,9 +4977,15 @@ static void red_display_destroy_compress_bufs(DisplayChannel *display_channel)
 static RedGlzDrawable *red_display_get_glz_drawable(DisplayChannelClient *dcc, Drawable *drawable)
 {
     RedGlzDrawable *ret;
+    RingItem *item;
 
-    if (drawable->red_glz_drawable) {
-        return drawable->red_glz_drawable;
+    // TODO - I don't really understand what's going on here, so just doing the technical equivalent
+    // now that we have multiple glz_dicts, so the only way to go from dcc to drawable glz is to go
+    // over the glz_ring (unless adding some better data structure then a ring)
+    DRAWABLE_FOREACH_GLZ(drawable, item, ret) {
+        if (ret->dcc == dcc) {
+            return ret;
+        }
     }
 
     ret = spice_new(RedGlzDrawable, 1);
@@ -4722,8 +4999,9 @@ static RedGlzDrawable *red_display_get_glz_drawable(DisplayChannelClient *dcc, D
     ring_init(&ret->instances);
 
     ring_item_init(&ret->link);
+    ring_item_init(&ret->drawable_link);
     ring_add_before(&ret->link, &dcc->glz_drawables);
-    drawable->red_glz_drawable = ret;
+    ring_add(&drawable->glz_ring, &ret->drawable_link);
 
     return ret;
 }
@@ -4780,7 +5058,7 @@ static void red_display_free_glz_drawable_instance(DisplayChannelClient *dcc,
         Drawable *drawable = glz_drawable->drawable;
 
         if (drawable) {
-            drawable->red_glz_drawable = NULL;
+            ring_remove(&glz_drawable->drawable_link);
         } else { // no reference to the qxl drawable left
             free_red_drawable(worker, glz_drawable->red_drawable,
                               glz_drawable->group_id, glz_drawable->self_bitmap);
@@ -4867,13 +5145,15 @@ static void red_display_client_clear_glz_drawables(DisplayChannelClient *dcc)
 
 static void red_display_clear_glz_drawables(DisplayChannel *display_channel)
 {
-    DisplayChannelClient *dcc = display_channel ?
-        RCC_TO_DCC(display_channel->common.base.rcc) : NULL;
+    RingItem *link;
+    DisplayChannelClient *dcc;
 
-    if (!dcc) {
+    if (!display_channel) {
         return;
     }
-    red_display_client_clear_glz_drawables(dcc);
+    DCC_FOREACH(link, dcc, &display_channel->common.base) {
+        red_display_client_clear_glz_drawables(dcc);
+    }
 }
 
 /*
@@ -5828,7 +6108,7 @@ static inline int red_compress_image(DisplayChannelClient *dcc,
         if (can_lossy && display_channel->enable_jpeg &&
             ((image_compression == SPICE_IMAGE_COMPRESS_AUTO_LZ) ||
             (image_compression == SPICE_IMAGE_COMPRESS_AUTO_GLZ))) {
-            // if we use lz for alpha, the stride can't be extra 
+            // if we use lz for alpha, the stride can't be extra
             if (src->format != SPICE_BITMAP_FMT_RGBA || !_stride_is_extra(src)) {
                 return red_jpeg_compress_image(dcc, dest,
                                                src, o_comp_data, drawable->group_id);
@@ -6344,7 +6624,7 @@ static int pipe_rendered_drawables_intersect_with_areas(RedWorker *worker,
 
         if (pipe_item->type != PIPE_ITEM_TYPE_DRAW)
             continue;
-        drawable = SPICE_CONTAINEROF(pipe_item, Drawable, pipe_item);
+        drawable = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item)->drawable;
 
         if (ring_item_is_linked(&drawable->list_link))
             continue; // item hasn't been rendered
@@ -6380,15 +6660,18 @@ static void red_pipe_replace_rendered_drawables_with_images(RedWorker *worker,
          pipe_item;
          pipe_item = (PipeItem *)ring_prev(pipe, &pipe_item->link)) {
         Drawable *drawable;
+        DrawablePipeItem *dpi;
         ImageItem *image;
+
         if (pipe_item->type != PIPE_ITEM_TYPE_DRAW)
             continue;
-        drawable = SPICE_CONTAINEROF(pipe_item, Drawable, pipe_item);
+        dpi = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item);
+        drawable = dpi->drawable;
         if (ring_item_is_linked(&drawable->list_link))
             continue; // item hasn't been rendered
 
         // When a drawable command, X, depends on bitmaps that were resent,
-        // these bitmaps state at the client might not be synchronized with X 
+        // these bitmaps state at the client might not be synchronized with X
         // (i.e., the bitmaps can be more futuristic w.r.t X). Thus, X shouldn't
         // be rendered at the client, and we replace it with an image as well.
         if (!drawable_depends_on_areas(drawable,
@@ -6398,14 +6681,14 @@ static void red_pipe_replace_rendered_drawables_with_images(RedWorker *worker,
             continue;
         }
 
-        image = red_add_surface_area_image(worker, drawable->red_drawable->surface_id,
+        image = red_add_surface_area_image(dcc, drawable->red_drawable->surface_id,
                                            &drawable->red_drawable->bbox, pipe_item, TRUE);
         resent_surface_ids[num_resent] = drawable->red_drawable->surface_id;
         resent_areas[num_resent] = drawable->red_drawable->bbox;
         num_resent++;
 
         ASSERT(image);
-        red_pipe_remove_drawable(dcc, drawable);
+        red_channel_client_pipe_remove_and_release(&dcc->common.base, &dpi->dpi_pipe_item);
         pipe_item = &image->link;
     }
 }
@@ -6454,7 +6737,7 @@ static void red_add_lossless_drawable_dependencies(RedWorker *worker,
         // the surfaces areas will be sent as DRAW_COPY commands, that
         // will be executed before the current drawable
         for (i = 0; i < num_deps; i++) {
-            red_add_surface_area_image(worker, deps_surfaces_ids[i], deps_areas[i],
+            red_add_surface_area_image(dcc, deps_surfaces_ids[i], deps_areas[i],
                                        red_pipe_get_tail(dcc), FALSE);
 
         }
@@ -6475,7 +6758,7 @@ static void red_add_lossless_drawable_dependencies(RedWorker *worker,
                                                             &drawable->bbox);
         }
 
-        red_add_surface_area_image(worker, drawable->surface_id, &drawable->bbox,
+        red_add_surface_area_image(dcc, drawable->surface_id, &drawable->bbox,
                                    red_pipe_get_tail(dcc), TRUE);
     }
 }
@@ -6483,15 +6766,16 @@ static void red_add_lossless_drawable_dependencies(RedWorker *worker,
 static void red_marshall_qxl_draw_fill(RedWorker *worker,
                                    RedChannelClient *rcc,
                                    SpiceMarshaller *base_marshaller,
-                                   Drawable *item)
+                                   DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     RedDrawable *drawable = item->red_drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *mask_bitmap_out;
     SpiceFill fill;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     fill = drawable->u.fill;
     spice_marshall_Fill(base_marshaller,
@@ -6510,8 +6794,9 @@ static void red_marshall_qxl_draw_fill(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_fill(RedWorker *worker,
                                          RedChannelClient *rcc,
                                          SpiceMarshaller *m,
-                                         Drawable *item)
+                                         DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
 
@@ -6539,7 +6824,7 @@ static void red_lossy_marshall_qxl_draw_fill(RedWorker *worker,
         !(brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE))) {
         int has_mask = !!drawable->u.fill.mask.bitmap;
 
-        red_marshall_qxl_draw_fill(worker, rcc, m, item);
+        red_marshall_qxl_draw_fill(worker, rcc, m, dpi);
         // either the brush operation is opaque, or the dest is not lossy
         surface_lossy_region_update(worker, dcc, item, has_mask, FALSE);
     } else {
@@ -6567,8 +6852,9 @@ static void red_lossy_marshall_qxl_draw_fill(RedWorker *worker,
 static FillBitsType red_marshall_qxl_draw_opaque(RedWorker *worker,
                                              RedChannelClient *rcc,
                                              SpiceMarshaller *base_marshaller,
-                                             Drawable *item, int src_allowed_lossy)
+                                             DrawablePipeItem *dpi, int src_allowed_lossy)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *brush_pat_out;
@@ -6577,7 +6863,7 @@ static FillBitsType red_marshall_qxl_draw_opaque(RedWorker *worker,
     SpiceOpaque opaque;
     FillBitsType src_send_type;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_OPAQUE, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_OPAQUE, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     opaque = drawable->u.opaque;
     spice_marshall_Opaque(base_marshaller,
@@ -6600,8 +6886,9 @@ static FillBitsType red_marshall_qxl_draw_opaque(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_opaque(RedWorker *worker,
                                            RedChannelClient *rcc,
                                            SpiceMarshaller *m,
-                                           Drawable *item)
+                                           DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
 
@@ -6632,7 +6919,7 @@ static void red_lossy_marshall_qxl_draw_opaque(RedWorker *worker,
         FillBitsType src_send_type;
         int has_mask = !!drawable->u.opaque.mask.bitmap;
 
-        src_send_type = red_marshall_qxl_draw_opaque(worker, rcc, m, item, src_allowed_lossy);
+        src_send_type = red_marshall_qxl_draw_opaque(worker, rcc, m, dpi, src_allowed_lossy);
         if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) {
             src_is_lossy = TRUE;
         } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) {
@@ -6665,8 +6952,9 @@ static void red_lossy_marshall_qxl_draw_opaque(RedWorker *worker,
 static FillBitsType red_marshall_qxl_draw_copy(RedWorker *worker,
                                            RedChannelClient *rcc,
                                            SpiceMarshaller *base_marshaller,
-                                           Drawable *item, int src_allowed_lossy)
+                                           DrawablePipeItem *dpi, int src_allowed_lossy)
 {
+    Drawable *item = dpi->drawable;
     RedDrawable *drawable = item->red_drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     SpiceMarshaller *src_bitmap_out;
@@ -6674,7 +6962,7 @@ static FillBitsType red_marshall_qxl_draw_copy(RedWorker *worker,
     SpiceCopy copy;
     FillBitsType src_send_type;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     copy = drawable->u.copy;
     spice_marshall_Copy(base_marshaller,
@@ -6691,8 +6979,9 @@ static FillBitsType red_marshall_qxl_draw_copy(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_copy(RedWorker *worker,
                                          RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
-                                         Drawable *item)
+                                         DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int has_mask = !!drawable->u.copy.mask.bitmap;
@@ -6703,7 +6992,7 @@ static void red_lossy_marshall_qxl_draw_copy(RedWorker *worker,
     src_is_lossy = is_bitmap_lossy(rcc, drawable->u.copy.src_bitmap,
                                    &drawable->u.copy.src_area, item, &src_bitmap_data);
 
-    src_send_type = red_marshall_qxl_draw_copy(worker, rcc, base_marshaller, item, TRUE);
+    src_send_type = red_marshall_qxl_draw_copy(worker, rcc, base_marshaller, dpi, TRUE);
 
     if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) {
         src_is_lossy = TRUE;
@@ -6718,14 +7007,15 @@ static void red_lossy_marshall_qxl_draw_copy(RedWorker *worker,
 static void red_marshall_qxl_draw_transparent(RedWorker *worker,
                                           RedChannelClient *rcc,
                                           SpiceMarshaller *base_marshaller,
-                                          Drawable *item)
+                                          DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceTransparent transparent;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TRANSPARENT, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TRANSPARENT, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     transparent = drawable->u.transparent;
     spice_marshall_Transparent(base_marshaller,
@@ -6737,8 +7027,9 @@ static void red_marshall_qxl_draw_transparent(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_transparent(RedWorker *worker,
                                                 RedChannelClient *rcc,
                                                 SpiceMarshaller *base_marshaller,
-                                                Drawable *item)
+                                                DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
     BitmapData src_bitmap_data;
@@ -6747,7 +7038,7 @@ static void red_lossy_marshall_qxl_draw_transparent(RedWorker *worker,
                                    &drawable->u.transparent.src_area, item, &src_bitmap_data);
 
     if (!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) {
-        red_marshall_qxl_draw_transparent(worker, rcc, base_marshaller, item);
+        red_marshall_qxl_draw_transparent(worker, rcc, base_marshaller, dpi);
         // don't update surface lossy region since transperent areas might be lossy
     } else {
         int resend_surface_ids[1];
@@ -6764,16 +7055,17 @@ static void red_lossy_marshall_qxl_draw_transparent(RedWorker *worker,
 static FillBitsType red_marshall_qxl_draw_alpha_blend(RedWorker *worker,
                                                   RedChannelClient *rcc,
                                                   SpiceMarshaller *base_marshaller,
-                                                  Drawable *item,
+                                                  DrawablePipeItem *dpi,
                                                   int src_allowed_lossy)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceAlphaBlend alpha_blend;
     FillBitsType src_send_type;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     alpha_blend = drawable->u.alpha_blend;
     spice_marshall_AlphaBlend(base_marshaller,
@@ -6787,8 +7079,9 @@ static FillBitsType red_marshall_qxl_draw_alpha_blend(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_alpha_blend(RedWorker *worker,
                                                 RedChannelClient *rcc,
                                                 SpiceMarshaller *base_marshaller,
-                                                Drawable *item)
+                                                DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
@@ -6798,7 +7091,7 @@ static void red_lossy_marshall_qxl_draw_alpha_blend(RedWorker *worker,
     src_is_lossy = is_bitmap_lossy(rcc, drawable->u.alpha_blend.src_bitmap,
                                    &drawable->u.alpha_blend.src_area, item, &src_bitmap_data);
 
-    src_send_type = red_marshall_qxl_draw_alpha_blend(worker, rcc, base_marshaller, item, TRUE);
+    src_send_type = red_marshall_qxl_draw_alpha_blend(worker, rcc, base_marshaller, dpi, TRUE);
 
     if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) {
         src_is_lossy = TRUE;
@@ -6814,12 +7107,13 @@ static void red_lossy_marshall_qxl_draw_alpha_blend(RedWorker *worker,
 static void red_marshall_qxl_copy_bits(RedWorker *worker,
                                    RedChannelClient *rcc,
                                    SpiceMarshaller *base_marshaller,
-                                   Drawable *item)
+                                   DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     RedDrawable *drawable = item->red_drawable;
     SpicePoint copy_bits;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_COPY_BITS, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_COPY_BITS, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     copy_bits = drawable->u.copy_bits.src_pos;
     spice_marshall_Point(base_marshaller,
@@ -6829,8 +7123,9 @@ static void red_marshall_qxl_copy_bits(RedWorker *worker,
 static void red_lossy_marshall_qxl_copy_bits(RedWorker *worker,
                                          RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
-                                         Drawable *item)
+                                         DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceRect src_rect;
@@ -6839,7 +7134,7 @@ static void red_lossy_marshall_qxl_copy_bits(RedWorker *worker,
     int src_is_lossy;
     SpiceRect src_lossy_area;
 
-    red_marshall_qxl_copy_bits(worker, rcc, base_marshaller, item);
+    red_marshall_qxl_copy_bits(worker, rcc, base_marshaller, dpi);
 
     horz_offset = drawable->u.copy_bits.src_pos.x - drawable->bbox.left;
     vert_offset = drawable->u.copy_bits.src_pos.y - drawable->bbox.top;
@@ -6859,15 +7154,16 @@ static void red_lossy_marshall_qxl_copy_bits(RedWorker *worker,
 static void red_marshall_qxl_draw_blend(RedWorker *worker,
                                     RedChannelClient *rcc,
                                     SpiceMarshaller *base_marshaller,
-                                    Drawable *item)
+                                    DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceMarshaller *mask_bitmap_out;
     SpiceBlend blend;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLEND, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLEND, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     blend = drawable->u.blend;
     spice_marshall_Blend(base_marshaller,
@@ -6883,8 +7179,9 @@ static void red_marshall_qxl_draw_blend(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_blend(RedWorker *worker,
                                           RedChannelClient *rcc,
                                           SpiceMarshaller *base_marshaller,
-                                          Drawable *item)
+                                          DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
@@ -6899,7 +7196,7 @@ static void red_lossy_marshall_qxl_draw_blend(RedWorker *worker,
 
     if (!dest_is_lossy &&
         (!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) {
-        red_marshall_qxl_draw_blend(worker, rcc, base_marshaller, item);
+        red_marshall_qxl_draw_blend(worker, rcc, base_marshaller, dpi);
     } else {
         int resend_surface_ids[2];
         SpiceRect *resend_areas[2];
@@ -6925,13 +7222,14 @@ static void red_lossy_marshall_qxl_draw_blend(RedWorker *worker,
 static void red_marshall_qxl_draw_blackness(RedWorker *worker,
                                         RedChannelClient *rcc,
                                         SpiceMarshaller *base_marshaller,
-                                        Drawable *item)
+                                        DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *mask_bitmap_out;
     SpiceBlackness blackness;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLACKNESS, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLACKNESS, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     blackness = drawable->u.blackness;
 
@@ -6945,13 +7243,14 @@ static void red_marshall_qxl_draw_blackness(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_blackness(RedWorker *worker,
                                               RedChannelClient *rcc,
                                               SpiceMarshaller *base_marshaller,
-                                              Drawable *item)
+                                              DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int has_mask = !!drawable->u.blackness.mask.bitmap;
 
-    red_marshall_qxl_draw_blackness(worker, rcc, base_marshaller, item);
+    red_marshall_qxl_draw_blackness(worker, rcc, base_marshaller, dpi);
 
     surface_lossy_region_update(worker, dcc, item, has_mask, FALSE);
 }
@@ -6959,13 +7258,14 @@ static void red_lossy_marshall_qxl_draw_blackness(RedWorker *worker,
 static void red_marshall_qxl_draw_whiteness(RedWorker *worker,
                                         RedChannelClient *rcc,
                                         SpiceMarshaller *base_marshaller,
-                                        Drawable *item)
+                                        DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *mask_bitmap_out;
     SpiceWhiteness whiteness;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_WHITENESS, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_WHITENESS, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     whiteness = drawable->u.whiteness;
 
@@ -6979,13 +7279,14 @@ static void red_marshall_qxl_draw_whiteness(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_whiteness(RedWorker *worker,
                                               RedChannelClient *rcc,
                                               SpiceMarshaller *base_marshaller,
-                                              Drawable *item)
+                                              DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int has_mask = !!drawable->u.whiteness.mask.bitmap;
 
-    red_marshall_qxl_draw_whiteness(worker, rcc, base_marshaller, item);
+    red_marshall_qxl_draw_whiteness(worker, rcc, base_marshaller, dpi);
 
     surface_lossy_region_update(worker, dcc, item, has_mask, FALSE);
 }
@@ -7021,8 +7322,9 @@ static void red_lossy_marshall_qxl_draw_inverse(RedWorker *worker,
 static void red_marshall_qxl_draw_rop3(RedWorker *worker,
                                    RedChannelClient *rcc,
                                    SpiceMarshaller *base_marshaller,
-                                   Drawable *item)
+                                   DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceRop3 rop3;
@@ -7030,7 +7332,7 @@ static void red_marshall_qxl_draw_rop3(RedWorker *worker,
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *mask_bitmap_out;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ROP3, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ROP3, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     rop3 = drawable->u.rop3;
     spice_marshall_Rop3(base_marshaller,
@@ -7050,8 +7352,9 @@ static void red_marshall_qxl_draw_rop3(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_rop3(RedWorker *worker,
                                          RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
-                                         Drawable *item)
+                                         DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
@@ -7072,7 +7375,7 @@ static void red_lossy_marshall_qxl_draw_rop3(RedWorker *worker,
         (!brush_is_lossy || (brush_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) &&
         !dest_is_lossy) {
         int has_mask = !!drawable->u.rop3.mask.bitmap;
-        red_marshall_qxl_draw_rop3(worker, rcc, base_marshaller, item);
+        red_marshall_qxl_draw_rop3(worker, rcc, base_marshaller, dpi);
         surface_lossy_region_update(worker, dcc, item, has_mask, FALSE);
     } else {
         int resend_surface_ids[3];
@@ -7105,15 +7408,16 @@ static void red_lossy_marshall_qxl_draw_rop3(RedWorker *worker,
 static void red_marshall_qxl_draw_stroke(RedWorker *worker,
                                      RedChannelClient *rcc,
                                      SpiceMarshaller *base_marshaller,
-                                     Drawable *item)
+                                     DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceStroke stroke;
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *style_out;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_STROKE, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_STROKE, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     stroke = drawable->u.stroke;
     spice_marshall_Stroke(base_marshaller,
@@ -7130,8 +7434,9 @@ static void red_marshall_qxl_draw_stroke(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_stroke(RedWorker *worker,
                                            RedChannelClient *rcc,
                                            SpiceMarshaller *base_marshaller,
-                                           Drawable *item)
+                                           DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int brush_is_lossy;
@@ -7158,7 +7463,7 @@ static void red_lossy_marshall_qxl_draw_stroke(RedWorker *worker,
     if (!dest_is_lossy &&
         (!brush_is_lossy || (brush_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)))
     {
-        red_marshall_qxl_draw_stroke(worker, rcc, base_marshaller, item);
+        red_marshall_qxl_draw_stroke(worker, rcc, base_marshaller, dpi);
     } else {
         int resend_surface_ids[2];
         SpiceRect *resend_areas[2];
@@ -7185,15 +7490,16 @@ static void red_lossy_marshall_qxl_draw_stroke(RedWorker *worker,
 static void red_marshall_qxl_draw_text(RedWorker *worker,
                                    RedChannelClient *rcc,
                                    SpiceMarshaller *base_marshaller,
-                                   Drawable *item)
+                                   DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceText text;
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *back_brush_pat_out;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TEXT, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TEXT, &dpi->dpi_pipe_item);
     fill_base(base_marshaller, item);
     text = drawable->u.text;
     spice_marshall_Text(base_marshaller,
@@ -7212,8 +7518,9 @@ static void red_marshall_qxl_draw_text(RedWorker *worker,
 static void red_lossy_marshall_qxl_draw_text(RedWorker *worker,
                                          RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
-                                         Drawable *item)
+                                         DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int fg_is_lossy;
@@ -7248,7 +7555,7 @@ static void red_lossy_marshall_qxl_draw_text(RedWorker *worker,
     if (!dest_is_lossy &&
         (!fg_is_lossy || (fg_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) &&
         (!bg_is_lossy || (bg_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) {
-        red_marshall_qxl_draw_text(worker, rcc, base_marshaller, item);
+        red_marshall_qxl_draw_text(worker, rcc, base_marshaller, dpi);
     } else {
         int resend_surface_ids[3];
         SpiceRect *resend_areas[3];
@@ -7277,47 +7584,48 @@ static void red_lossy_marshall_qxl_draw_text(RedWorker *worker,
 }
 
 static void red_lossy_marshall_qxl_drawable(RedWorker *worker, RedChannelClient *rcc,
-                                        SpiceMarshaller *base_marshaller, Drawable *item)
+                                        SpiceMarshaller *base_marshaller, DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     switch (item->red_drawable->type) {
     case QXL_DRAW_FILL:
-        red_lossy_marshall_qxl_draw_fill(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_fill(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_DRAW_OPAQUE:
-        red_lossy_marshall_qxl_draw_opaque(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_opaque(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_DRAW_COPY:
-        red_lossy_marshall_qxl_draw_copy(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_copy(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_DRAW_TRANSPARENT:
-        red_lossy_marshall_qxl_draw_transparent(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_transparent(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_DRAW_ALPHA_BLEND:
-        red_lossy_marshall_qxl_draw_alpha_blend(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_alpha_blend(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_COPY_BITS:
-        red_lossy_marshall_qxl_copy_bits(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_copy_bits(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_DRAW_BLEND:
-        red_lossy_marshall_qxl_draw_blend(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_blend(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_DRAW_BLACKNESS:
-        red_lossy_marshall_qxl_draw_blackness(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_blackness(worker, rcc, base_marshaller, dpi);
         break;
      case QXL_DRAW_WHITENESS:
-        red_lossy_marshall_qxl_draw_whiteness(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_whiteness(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_DRAW_INVERS:
         red_lossy_marshall_qxl_draw_inverse(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_ROP3:
-        red_lossy_marshall_qxl_draw_rop3(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_rop3(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_DRAW_STROKE:
-        red_lossy_marshall_qxl_draw_stroke(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_stroke(worker, rcc, base_marshaller, dpi);
         break;
     case QXL_DRAW_TEXT:
-        red_lossy_marshall_qxl_draw_text(worker, rcc, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_text(worker, rcc, base_marshaller, dpi);
         break;
     default:
         red_error("invalid type");
@@ -7325,49 +7633,50 @@ static void red_lossy_marshall_qxl_drawable(RedWorker *worker, RedChannelClient
 }
 
 static inline void red_marshall_qxl_drawable(RedWorker *worker, RedChannelClient *rcc,
-                                SpiceMarshaller *m, Drawable *item)
+                                SpiceMarshaller *m, DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     RedDrawable *drawable = item->red_drawable;
 
     switch (drawable->type) {
     case QXL_DRAW_FILL:
-        red_marshall_qxl_draw_fill(worker, rcc, m, item);
+        red_marshall_qxl_draw_fill(worker, rcc, m, dpi);
         break;
     case QXL_DRAW_OPAQUE:
-        red_marshall_qxl_draw_opaque(worker, rcc, m, item, FALSE);
+        red_marshall_qxl_draw_opaque(worker, rcc, m, dpi, FALSE);
         break;
     case QXL_DRAW_COPY:
-        red_marshall_qxl_draw_copy(worker, rcc, m, item, FALSE);
+        red_marshall_qxl_draw_copy(worker, rcc, m, dpi, FALSE);
         break;
     case QXL_DRAW_TRANSPARENT:
-        red_marshall_qxl_draw_transparent(worker, rcc, m, item);
+        red_marshall_qxl_draw_transparent(worker, rcc, m, dpi);
         break;
     case QXL_DRAW_ALPHA_BLEND:
-        red_marshall_qxl_draw_alpha_blend(worker, rcc, m, item, FALSE);
+        red_marshall_qxl_draw_alpha_blend(worker, rcc, m, dpi, FALSE);
         break;
     case QXL_COPY_BITS:
-        red_marshall_qxl_copy_bits(worker, rcc, m, item);
+        red_marshall_qxl_copy_bits(worker, rcc, m, dpi);
         break;
     case QXL_DRAW_BLEND:
-        red_marshall_qxl_draw_blend(worker, rcc, m, item);
+        red_marshall_qxl_draw_blend(worker, rcc, m, dpi);
         break;
     case QXL_DRAW_BLACKNESS:
-        red_marshall_qxl_draw_blackness(worker, rcc, m, item);
+        red_marshall_qxl_draw_blackness(worker, rcc, m, dpi);
         break;
     case QXL_DRAW_WHITENESS:
-        red_marshall_qxl_draw_whiteness(worker, rcc, m, item);
+        red_marshall_qxl_draw_whiteness(worker, rcc, m, dpi);
         break;
     case QXL_DRAW_INVERS:
         red_marshall_qxl_draw_inverse(worker, rcc, m, item);
         break;
     case QXL_DRAW_ROP3:
-        red_marshall_qxl_draw_rop3(worker, rcc, m, item);
+        red_marshall_qxl_draw_rop3(worker, rcc, m, dpi);
         break;
     case QXL_DRAW_STROKE:
-        red_marshall_qxl_draw_stroke(worker, rcc, m, item);
+        red_marshall_qxl_draw_stroke(worker, rcc, m, dpi);
         break;
     case QXL_DRAW_TEXT:
-        red_marshall_qxl_draw_text(worker, rcc, m, item);
+        red_marshall_qxl_draw_text(worker, rcc, m, dpi);
         break;
     default:
         red_error("invalid type");
@@ -7571,8 +7880,9 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
 }
 
 static inline void marshall_qxl_drawable(RedChannelClient *rcc,
-    SpiceMarshaller *m, Drawable *item)
+    SpiceMarshaller *m, DrawablePipeItem *dpi)
 {
+    Drawable *item = dpi->drawable;
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
 
     ASSERT(display_channel && rcc);
@@ -7580,9 +7890,9 @@ static inline void marshall_qxl_drawable(RedChannelClient *rcc,
         return;
     }
     if (!display_channel->enable_jpeg)
-        red_marshall_qxl_drawable(display_channel->common.worker, rcc, m, item);
+        red_marshall_qxl_drawable(display_channel->common.worker, rcc, m, dpi);
     else
-        red_lossy_marshall_qxl_drawable(display_channel->common.worker, rcc, m, item);
+        red_lossy_marshall_qxl_drawable(display_channel->common.worker, rcc, m, dpi);
 }
 
 static inline void red_marshall_verb(RedChannelClient *rcc, uint16_t verb)
@@ -7683,7 +7993,7 @@ static void display_channel_marshall_reset_cache(RedChannelClient *rcc,
                                                  &wait);
 }
 
-static void red_marshall_image( RedChannelClient *rcc, SpiceMarshaller *m, ImageItem *item)
+static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageItem *item)
 {
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     DisplayChannel *display_channel = DCC_TO_DC(dcc);
@@ -7854,8 +8164,7 @@ static void red_display_marshall_stream_start(RedChannelClient *rcc,
 
     agent->last_send_time = 0;
     ASSERT(stream);
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CREATE,
-        stream->current ? &stream->current->pipe_item : NULL);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CREATE, &agent->create_item);
     SpiceMsgDisplayStreamCreate stream_create;
     SpiceClipRects clip_rects;
 
@@ -8043,8 +8352,8 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
     red_display_reset_send_data(dcc);
     switch (pipe_item->type) {
     case PIPE_ITEM_TYPE_DRAW: {
-        Drawable *drawable = SPICE_CONTAINEROF(pipe_item, Drawable, pipe_item);
-        marshall_qxl_drawable(rcc, m, drawable);
+        DrawablePipeItem *dpi = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item);
+        marshall_qxl_drawable(rcc, m, dpi);
         break;
     }
     case PIPE_ITEM_TYPE_INVAL_ONE:
@@ -8419,13 +8728,34 @@ static inline void red_create_surface_item(DisplayChannelClient *dcc, int surfac
     red_channel_client_pipe_add(&dcc->common.base, &create->pipe_item);
 }
 
+static void red_worker_create_surface_item(RedWorker *worker, int surface_id)
+{
+    DisplayChannelClient *dcc;
+    RingItem *item;
+
+    WORKER_FOREACH_DCC(worker, item, dcc) {
+        red_create_surface_item(dcc, surface_id);
+    }
+}
+
+
+static void red_worker_push_surface_image(RedWorker *worker, int surface_id)
+{
+    DisplayChannelClient *dcc;
+    RingItem *item;
+
+    WORKER_FOREACH_DCC(worker, item, dcc) {
+        red_push_surface_image(dcc, surface_id);
+    }
+}
+
 static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, uint32_t width,
                                       uint32_t height, int32_t stride, uint32_t format,
                                       void *line_0, int data_is_valid, int send_client)
 {
     RedSurface *surface = &worker->surfaces[surface_id];
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
     uint32_t i;
+
     if (stride >= 0) {
         PANIC("Untested path stride >= 0");
     }
@@ -8456,9 +8786,9 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
         }
 
         if (send_client) {
-            red_create_surface_item(dcc, surface_id);
+            red_worker_create_surface_item(worker, surface_id);
             if (data_is_valid) {
-                red_push_surface_image(dcc, surface_id);
+                red_worker_push_surface_image(worker, surface_id);
             }
         }
         return;
@@ -8471,9 +8801,9 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
         if (surface->context.canvas) { //no need canvas check
             worker->renderer = worker->renderers[i];
             if (send_client) {
-                red_create_surface_item(dcc, surface_id);
+                red_worker_create_surface_item(worker, surface_id);
                 if (data_is_valid) {
-                    red_push_surface_image(dcc, surface_id);
+                    red_worker_push_surface_image(worker, surface_id);
                 }
             }
             return;
@@ -8483,9 +8813,6 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
     PANIC("unable to create drawing canvas");
 }
 
-static void red_wait_outgoing_item(RedChannelClient *rcc);
-static void red_wait_outgoing_items(RedChannel *channel);
-
 static inline void flush_display_commands(RedWorker *worker)
 {
     RedChannel *display_red_channel = &worker->display_channel->common.base;
@@ -9163,9 +9490,15 @@ static void display_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item
     ASSERT(item);
     switch (item->type) {
     case PIPE_ITEM_TYPE_DRAW:
-    case PIPE_ITEM_TYPE_STREAM_CREATE:
-        SPICE_CONTAINEROF(item, Drawable, pipe_item)->refs++;
+        ref_drawable_pipe_item(SPICE_CONTAINEROF(item, DrawablePipeItem, dpi_pipe_item));
         break;
+    case PIPE_ITEM_TYPE_STREAM_CREATE: {
+        StreamAgent *stream_agent = SPICE_CONTAINEROF(item, StreamAgent, create_item);
+        if (stream_agent->stream->current) {
+            stream_agent->stream->current->refs++;
+        }
+        break;
+    }
     case PIPE_ITEM_TYPE_STREAM_CLIP:
         ((StreamClipItem *)item)->refs++;
         break;
@@ -9187,9 +9520,15 @@ static void display_channel_client_release_item_after_push(DisplayChannelClient
 
     switch (item->type) {
     case PIPE_ITEM_TYPE_DRAW:
-    case PIPE_ITEM_TYPE_STREAM_CREATE:
-        release_drawable(worker, SPICE_CONTAINEROF(item, Drawable, pipe_item));
+        put_drawable_pipe_item(SPICE_CONTAINEROF(item, DrawablePipeItem, dpi_pipe_item));
         break;
+    case PIPE_ITEM_TYPE_STREAM_CREATE: {
+        StreamAgent *stream_agent = SPICE_CONTAINEROF(item, StreamAgent, create_item);
+        if (stream_agent->stream->current) {
+            release_drawable(worker, stream_agent->stream->current);
+        }
+        break;
+    }
     case PIPE_ITEM_TYPE_STREAM_CLIP:
         red_display_release_stream_clip(worker, (StreamClipItem *)item);
         break;
@@ -9213,9 +9552,12 @@ static void display_channel_client_release_item_before_push(DisplayChannelClient
     RedWorker *worker = dcc->common.worker;
 
     switch (item->type) {
-    case PIPE_ITEM_TYPE_DRAW:
-        release_drawable(worker, SPICE_CONTAINEROF(item, Drawable, pipe_item));
+    case PIPE_ITEM_TYPE_DRAW: {
+        DrawablePipeItem *dpi = SPICE_CONTAINEROF(item, DrawablePipeItem, dpi_pipe_item);
+        ring_remove(&dpi->base);
+        put_drawable_pipe_item(dpi);
         break;
+    }
     case PIPE_ITEM_TYPE_STREAM_CREATE: {
         StreamAgent *agent = SPICE_CONTAINEROF(item, StreamAgent, create_item);
         red_display_release_stream(worker, agent);
@@ -9407,7 +9749,7 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
     if (worker->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) {
         display_channel->enable_zlib_glz_wrap = is_low_bandwidth;
     } else {
-        display_channel->enable_zlib_glz_wrap = (worker->zlib_glz_state == 
+        display_channel->enable_zlib_glz_wrap = (worker->zlib_glz_state ==
                                                  SPICE_WAN_COMPRESSION_ALWAYS);
     }
 
@@ -9612,7 +9954,7 @@ static void red_wait_outgoing_items(RedChannel *channel)
     if (!red_channel_any_blocked(channel)) {
         return;
     }
- 
+
     end_time = red_now() + DETACH_TIMEOUT;
     red_printf("blocked");
 
@@ -9783,9 +10125,6 @@ static inline void handle_dev_del_memslot(RedWorker *worker)
 
 static inline void destroy_surface_wait(RedWorker *worker, int surface_id)
 {
-    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
-    RedChannelClient *rcc = &dcc->common.base;
-
     if (!worker->surfaces[surface_id].context.canvas) {
         return;
     }
@@ -9795,13 +10134,7 @@ static inline void destroy_surface_wait(RedWorker *worker, int surface_id)
        otherwise "current" will hold items that other drawables may depend on, and then
        red_current_clear will remove them from the pipe. */
     red_current_clear(worker, surface_id);
-    red_clear_surface_drawables_from_pipe(dcc, surface_id, TRUE);
-    // in case that the pipe didn't contain any item that is dependent on the surface, but
-    // there is one during sending.
-    red_wait_outgoing_item(rcc);
-    if (dcc) {
-        ASSERT(red_channel_client_no_item_being_sent(rcc));
-    }
+    red_clear_surface_drawables_from_pipes(worker, surface_id, TRUE, TRUE);
 }
 
 static inline void handle_dev_destroy_surface_wait(RedWorker *worker)
@@ -9811,7 +10144,7 @@ static inline void handle_dev_destroy_surface_wait(RedWorker *worker)
     receive_data(worker->channel, &surface_id, sizeof(uint32_t));
 
     ASSERT(surface_id == 0);
-    
+
     flush_all_qxl_commands(worker);
 
     if (worker->surfaces[0].context.canvas) {
@@ -9949,7 +10282,7 @@ static void handle_dev_stop(RedWorker *worker)
 {
     ASSERT(worker->running);
     worker->running = FALSE;
-    red_display_client_clear_glz_drawables(WORKER_TO_DCC(worker));
+    red_display_clear_glz_drawables(worker->display_channel);
     flush_all_surfaces(worker);
     red_wait_outgoing_items(&worker->display_channel->common.base);
     red_wait_outgoing_items(&worker->cursor_channel->common.base);
commit 6a1d65737300627b77bf0c5b813028146e40159b
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Apr 13 08:52:44 2011 +0300

    server/red_channel: add pipe_size helpers

diff --git a/server/red_channel.c b/server/red_channel.c
index 43b5788..eb00aba 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -1105,3 +1105,18 @@ void red_channel_pipes_new_add_tail(RedChannel *channel, new_pipe_item_t creator
     red_channel_pipes_create_batch(channel, creator, data,
                                      red_channel_client_pipe_add_tail_no_push);
 }
+
+uint32_t red_channel_max_pipe_size(RedChannel *channel)
+{
+    return channel->rcc ? channel->rcc->pipe_size : 0;
+}
+
+uint32_t red_channel_min_pipe_size(RedChannel *channel)
+{
+    return channel->rcc ? channel->rcc->pipe_size : 0;
+}
+
+uint32_t red_channel_sum_pipes_size(RedChannel *channel)
+{
+    return channel->rcc ? channel->rcc->pipe_size : 0;
+}
diff --git a/server/red_channel.h b/server/red_channel.h
index a48d3a5..4b29b0c 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -365,6 +365,13 @@ RedClient *red_channel_client_get_client(RedChannelClient *rcc);
  * not via the below accessor and direct header manipulation. */
 SpiceDataHeader *red_channel_client_get_header(RedChannelClient *rcc);
 
+/* return the sum of all the rcc pipe size */
+uint32_t red_channel_max_pipe_size(RedChannel *channel);
+/* return the min size of all the rcc pipe */
+uint32_t red_channel_min_pipe_size(RedChannel *channel);
+/* return the max size of all the rcc pipe */
+uint32_t red_channel_sum_pipes_size(RedChannel *channel);
+
 /* apply given function to all connected clients */
 typedef void (*channel_client_callback)(RedChannelClient *rcc);
 typedef void (*channel_client_callback_data)(RedChannelClient *rcc, void *data);
diff --git a/server/red_worker.c b/server/red_worker.c
index 54958fa..cbd9001 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -4316,7 +4316,7 @@ static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ri
 
     *ring_is_empty = FALSE;
     while (!cursor_is_connected(worker) ||
-           worker->cursor_channel->common.base.rcc->pipe_size <= max_pipe_size) {
+           red_channel_min_pipe_size(&worker->cursor_channel->common.base) <= max_pipe_size) {
         if (!worker->qxl->st->qif->get_cursor_command(worker->qxl, &ext_cmd)) {
             *ring_is_empty = TRUE;
             if (worker->repoll_cursor_ring < CMD_RING_POLL_RETRIES) {
@@ -4361,8 +4361,8 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
     }
 
     *ring_is_empty = FALSE;
-    while (!display_is_connected(worker)
-           || worker->display_channel->common.base.rcc->pipe_size <= max_pipe_size) {
+    while (!display_is_connected(worker) ||
+           red_channel_min_pipe_size(&worker->display_channel->common.base) <= max_pipe_size) {
         if (!worker->qxl->st->qif->get_command(worker->qxl, &ext_cmd)) {
             *ring_is_empty = TRUE;;
             if (worker->repoll_cmd_ring < CMD_RING_POLL_RETRIES) {
@@ -8488,6 +8488,8 @@ static void red_wait_outgoing_items(RedChannel *channel);
 
 static inline void flush_display_commands(RedWorker *worker)
 {
+    RedChannel *display_red_channel = &worker->display_channel->common.base;
+
     for (;;) {
         uint64_t end_time;
         int ring_is_empty;
@@ -8509,7 +8511,7 @@ static inline void flush_display_commands(RedWorker *worker)
         for (;;) {
             red_channel_push(&worker->display_channel->common.base);
             if (!display_is_connected(worker) ||
-                 worker->display_channel->common.base.rcc->pipe_size <= MAX_PIPE_SIZE) {
+                red_channel_min_pipe_size(display_red_channel) <= MAX_PIPE_SIZE) {
                 break;
             }
             RedChannel *channel = (RedChannel *)worker->display_channel;
@@ -8530,6 +8532,8 @@ static inline void flush_display_commands(RedWorker *worker)
 
 static inline void flush_cursor_commands(RedWorker *worker)
 {
+    RedChannel *cursor_red_channel = &worker->cursor_channel->common.base;
+
     for (;;) {
         uint64_t end_time;
         int ring_is_empty = FALSE;
@@ -8551,7 +8555,7 @@ static inline void flush_cursor_commands(RedWorker *worker)
         for (;;) {
             red_channel_push(&worker->cursor_channel->common.base);
             if (!cursor_is_connected(worker)
-                || worker->cursor_channel->common.base.rcc->pipe_size <= MAX_PIPE_SIZE) {
+                || red_channel_min_pipe_size(cursor_red_channel) <= MAX_PIPE_SIZE) {
                 break;
             }
             RedChannel *channel = (RedChannel *)worker->cursor_channel;
@@ -10023,9 +10027,9 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
             red_channel_push(&worker->display_channel->common.base);
         }
         if (worker->qxl->st->qif->flush_resources(worker->qxl) == 0) {
-            red_printf("oom current %u pipe %u", worker->current_size,
+            red_printf("oom current %u pipes %u", worker->current_size,
                        worker->display_channel ?
-                       display_red_channel->rcc->pipe_size : 0);
+                       red_channel_sum_pipes_size(display_red_channel) : 0);
             red_free_some(worker);
             worker->qxl->st->qif->flush_resources(worker->qxl);
         }
commit 9c192980e829633cf780e23e246c811119ef5ca1
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Apr 13 00:35:20 2011 +0300

    server/red_worker: remove more direct access to RedChannelClient.rcc

diff --git a/server/red_worker.c b/server/red_worker.c
index 908c17d..54958fa 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -1188,7 +1188,7 @@ static void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb)
 }
 
 static inline void red_create_surface_item(DisplayChannelClient *dcc, int surface_id);
-static void red_add_surface_image(RedWorker *worker, int surface_id);
+static void red_push_surface_image(DisplayChannelClient *dcc, int surface_id);
 static void red_pipes_add_verb(RedChannel *channel, uint16_t verb)
 {
     RedChannelClient *rcc = channel->rcc;
@@ -1229,7 +1229,7 @@ static inline void red_handle_drawable_surfaces_client_synced(
             }
             red_create_surface_item(dcc, surface_id);
             red_current_flush(worker, surface_id);
-            red_add_surface_image(worker, surface_id);
+            red_push_surface_image(dcc, surface_id);
         }
     }
 
@@ -1239,7 +1239,7 @@ static inline void red_handle_drawable_surfaces_client_synced(
 
     red_create_surface_item(dcc, drawable->surface_id);
     red_current_flush(worker, drawable->surface_id);
-    red_add_surface_image(worker, drawable->surface_id);
+    red_push_surface_image(dcc, drawable->surface_id);
 }
 
 static int display_is_connected(RedWorker *worker)
@@ -1312,23 +1312,23 @@ static inline void red_pipe_remove_drawable(DisplayChannelClient *dcc, Drawable
     }
 }
 
-static inline void red_pipe_add_image_item(RedWorker *worker, ImageItem *item)
+static inline void red_pipe_add_image_item(DisplayChannelClient *dcc, ImageItem *item)
 {
-    if (!display_is_connected(worker)) {
+    if (!dcc) {
         return;
     }
     item->refs++;
-    red_channel_client_pipe_add(worker->display_channel->common.base.rcc, &item->link);
+    red_channel_client_pipe_add(&dcc->common.base, &item->link);
 }
 
-static inline void red_pipe_add_image_item_after(RedWorker *worker, ImageItem *item,
+static inline void red_pipe_add_image_item_after(DisplayChannelClient *dcc, ImageItem *item,
                                                  PipeItem *pos)
 {
-    if (!display_is_connected(worker)) {
+    if (!dcc) {
         return;
     }
     item->refs++;
-    red_channel_client_pipe_add_after(worker->display_channel->common.base.rcc, &item->link, pos);
+    red_channel_client_pipe_add_after(&dcc->common.base, &item->link, pos);
 }
 
 static void release_image_item(ImageItem *item)
@@ -2645,15 +2645,12 @@ static void reset_rate(DisplayChannelClient *dcc, StreamAgent *stream_agent)
     /* MJpeg has no rate limiting anyway, so do nothing */
 }
 
-static int display_channel_is_low_bandwidth(DisplayChannel *display_channel)
+static int display_channel_is_low_bandwidth(DisplayChannelClient *dcc)
 {
-    if (display_channel->common.base.rcc) {
-        if (main_channel_client_is_low_bandwidth(
-                red_client_get_main(display_channel->common.base.rcc->client))) {
-            return TRUE;
-        }
-    }
-    return FALSE;
+    RedChannelClient *rcc = &dcc->common.base;
+
+    return dcc && main_channel_client_is_low_bandwidth(
+                red_client_get_main(red_channel_client_get_client(rcc)));
 }
 
 static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
@@ -2664,7 +2661,7 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
 
     ASSERT(stream->current);
 
-    if (!dcc || !display_channel_is_low_bandwidth(worker->display_channel)) {
+    if (!dcc || !display_channel_is_low_bandwidth(dcc)) {
         return;
     }
 
@@ -4253,6 +4250,14 @@ static CursorItem *get_cursor_item(RedWorker *worker, RedCursorCmd *cmd, uint32_
     return cursor_item;
 }
 
+static PipeItem *ref_cursor_pipe_item(RedChannelClient *rcc, void *data, int num)
+{
+    CursorItem *cursor_item = data;
+
+    cursor_item->refs += (num != 0); /* we already reference the first use */
+    return &cursor_item->pipe_data;
+}
+
 static void qxl_process_cursor(RedWorker *worker, RedCursorCmd *cursor_cmd, uint32_t group_id)
 {
     CursorItem *item;
@@ -4281,9 +4286,10 @@ static void qxl_process_cursor(RedWorker *worker, RedCursorCmd *cursor_cmd, uint
         red_error("invalid cursor command %u", cursor_cmd->type);
     }
 
-    if (worker->cursor_channel && (worker->mouse_mode == SPICE_MOUSE_MODE_SERVER ||
+    if (cursor_is_connected(worker) && (worker->mouse_mode == SPICE_MOUSE_MODE_SERVER ||
                                    cursor_cmd->type != QXL_CURSOR_MOVE || cursor_show)) {
-        red_channel_client_pipe_add(worker->cursor_channel->common.base.rcc, &item->pipe_data);
+        red_channel_pipes_new_add(&worker->cursor_channel->common.base, ref_cursor_pipe_item,
+                                  (void*)item);
     } else {
         red_release_cursor(worker, item);
     }
@@ -4471,26 +4477,31 @@ static void red_current_flush(RedWorker *worker, int surface_id)
 static ImageItem *red_add_surface_area_image(RedWorker *worker, int surface_id, SpiceRect *area,
                                              PipeItem *pos, int can_lossy)
 {
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    DisplayChannel *display_channel = worker ? worker->display_channel : NULL;
+    RedChannel *channel = &display_channel->common.base;
+    RedSurface *surface = &worker->surfaces[surface_id];
+    SpiceCanvas *canvas = surface->context.canvas;
     ImageItem *item;
     int stride;
     int width;
     int height;
-    RedSurface *surface = &worker->surfaces[surface_id];
-    SpiceCanvas *canvas = surface->context.canvas;
     int bpp;
     int all_set;
 
     ASSERT(area);
 
+    if (!dcc) {
+        return NULL;
+    }
     width = area->right - area->left;
     height = area->bottom - area->top;
     bpp = SPICE_SURFACE_FMT_DEPTH(surface->context.format) / 8;
-    stride = SPICE_ALIGN(width * bpp, 4); 
-                                      
+    stride = SPICE_ALIGN(width * bpp, 4);
+
     item = (ImageItem *)spice_malloc_n_m(height, stride, sizeof(ImageItem));
 
-    red_channel_pipe_item_init(&worker->display_channel->common.base,
-                               &item->link, PIPE_ITEM_TYPE_IMAGE);
+    red_channel_pipe_item_init(channel, &item->link, PIPE_ITEM_TYPE_IMAGE);
 
     item->refs = 1;
     item->surface_id = surface_id;
@@ -4520,9 +4531,9 @@ static ImageItem *red_add_surface_area_image(RedWorker *worker, int surface_id,
     }
 
     if (!pos) {
-        red_pipe_add_image_item(worker, item);
+        red_pipe_add_image_item(dcc, item);
     } else {
-        red_pipe_add_image_item_after(worker, item, pos);
+        red_pipe_add_image_item_after(dcc, item, pos);
     }
 
     release_image_item(item);
@@ -4530,17 +4541,20 @@ static ImageItem *red_add_surface_area_image(RedWorker *worker, int surface_id,
     return item;
 }
 
-static void red_add_surface_image(RedWorker *worker, int surface_id)
+static void red_push_surface_image(DisplayChannelClient *dcc, int surface_id)
 {
     SpiceRect area;
     RedSurface *surface;
+    RedWorker *worker;
 
+    if (!dcc) {
+        return;
+    }
+    worker = DCC_TO_WORKER(dcc);
     surface = &worker->surfaces[surface_id];
-
-    if (!display_is_connected(worker) || !surface->context.canvas) {
+    if (!surface->context.canvas) {
         return;
     }
-
     area.top = area.left = 0;
     area.right = surface->context.width;
     area.bottom = surface->context.height;
@@ -4548,7 +4562,7 @@ static void red_add_surface_image(RedWorker *worker, int surface_id)
     /* not allowing lossy compression because probably, especially if it is a primary surface,
        it combines both "picture-like" areas with areas that are more "artificial"*/
     red_add_surface_area_image(worker, surface_id, &area, NULL, FALSE);
-    red_channel_push(&worker->display_channel->common.base);
+    red_channel_client_push(&dcc->common.base);
 }
 
 typedef struct {
@@ -6348,6 +6362,7 @@ static void red_pipe_replace_rendered_drawables_with_images(RedWorker *worker,
                                                             int first_surface_id,
                                                             SpiceRect *first_area)
 {
+    /* TODO: can't have those statics with multiple clients */
     static int resent_surface_ids[MAX_PIPE_SIZE];
     static SpiceRect resent_areas[MAX_PIPE_SIZE]; // not pointers since drawbales may be released
     int num_resent;
@@ -8443,7 +8458,7 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
         if (send_client) {
             red_create_surface_item(dcc, surface_id);
             if (data_is_valid) {
-                red_add_surface_image(worker, surface_id);
+                red_push_surface_image(dcc, surface_id);
             }
         }
         return;
@@ -8458,7 +8473,7 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
             if (send_client) {
                 red_create_surface_item(dcc, surface_id);
                 if (data_is_valid) {
-                    red_add_surface_image(worker, surface_id);
+                    red_push_surface_image(dcc, surface_id);
                 }
             }
             return;
@@ -8621,7 +8636,7 @@ static void on_new_display_channel_client(DisplayChannelClient *dcc)
     if (worker->surfaces[0].context.canvas) {
         red_current_flush(worker, 0);
         push_new_primary_surface(dcc);
-        red_add_surface_image(worker, 0);
+        red_push_surface_image(dcc, 0);
         if (red_channel_is_connected(&display_channel->common.base)) {
             red_pipe_add_verb(rcc, SPICE_MSG_DISPLAY_MARK);
             red_disply_start_streams(dcc);
commit d42575feab3697657f9d68d91f50f6e7d6c4272c
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Apr 24 12:03:28 2011 +0300

    server/red_worker.c: make dictionary and cache different per client
    
    There is no inter-client shared dictionary and cache yet.
    
    At this point the display channel can be used by multiple clients.
    
    You can still crash on lack of Drawables or CursorItems due to the slower
    clients pipe growing uncontrollably.

diff --git a/server/red_worker.c b/server/red_worker.c
index bc2694d..908c17d 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -437,7 +437,9 @@ struct PixmapCache {
         uint8_t client;
         uint64_t message;
     } generation_initiator;
-    uint64_t sync[MAX_CACHE_CLIENTS];
+    uint64_t sync[MAX_CACHE_CLIENTS]; // here CLIENTS refer to different channel
+                                      // clients of the same client
+    RedClient *client;
 };
 
 #define NUM_STREAMS 50
@@ -557,6 +559,7 @@ typedef struct GlzSharedDictionary {
     uint8_t id;
     pthread_rwlock_t encode_lock;
     int migrate_freeze;
+    RedClient *client; // channel clients of the same client share the dict
 } GlzSharedDictionary;
 
 #define NUM_SURFACES 10000
@@ -8626,16 +8629,16 @@ static void on_new_display_channel_client(DisplayChannelClient *dcc)
     }
 }
 
-static GlzSharedDictionary *_red_find_glz_dictionary(uint8_t dict_id)
+static GlzSharedDictionary *_red_find_glz_dictionary(RedClient *client, uint8_t dict_id)
 {
     RingItem *now;
     GlzSharedDictionary *ret = NULL;
 
     now = &glz_dictionary_list;
-
     while ((now = ring_next(&glz_dictionary_list, now))) {
-        if (((GlzSharedDictionary *)now)->id == dict_id) {
-            ret = (GlzSharedDictionary *)now;
+        GlzSharedDictionary *dict = (GlzSharedDictionary *)now;
+        if ((dict->client == client) && (dict->id == dict_id)) {
+            ret = dict;
             break;
         }
     }
@@ -8643,15 +8646,15 @@ static GlzSharedDictionary *_red_find_glz_dictionary(uint8_t dict_id)
     return ret;
 }
 
-static GlzSharedDictionary *_red_create_glz_dictionary(uint8_t id,
+static GlzSharedDictionary *_red_create_glz_dictionary(RedClient *client, uint8_t id,
                                                        GlzEncDictContext *opaque_dict)
 {
     GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
-
     shared_dict->dict = opaque_dict;
     shared_dict->id = id;
     shared_dict->refs = 1;
     shared_dict->migrate_freeze = FALSE;
+    shared_dict->client = client;
     ring_item_init(&shared_dict->base);
     pthread_rwlock_init(&shared_dict->encode_lock, NULL);
     return shared_dict;
@@ -8670,7 +8673,7 @@ static GlzSharedDictionary *red_create_glz_dictionary(DisplayChannelClient *dcc,
         PANIC("failed creating lz dictionary");
         return NULL;
     }
-    return _red_create_glz_dictionary(id, glz_dict);
+    return _red_create_glz_dictionary(dcc->common.base.client, id, glz_dict);
 }
 
 static GlzSharedDictionary *red_create_restored_glz_dictionary(DisplayChannelClient *dcc,
@@ -8683,7 +8686,7 @@ static GlzSharedDictionary *red_create_restored_glz_dictionary(DisplayChannelCli
         PANIC("failed creating lz dictionary");
         return NULL;
     }
-    return _red_create_glz_dictionary(id, glz_dict);
+    return _red_create_glz_dictionary(dcc->common.base.client, id, glz_dict);
 }
 
 static GlzSharedDictionary *red_get_glz_dictionary(DisplayChannelClient *dcc,
@@ -8693,7 +8696,7 @@ static GlzSharedDictionary *red_get_glz_dictionary(DisplayChannelClient *dcc,
 
     pthread_mutex_lock(&glz_dictionary_list_lock);
 
-    shared_dict = _red_find_glz_dictionary(id);
+    shared_dict = _red_find_glz_dictionary(dcc->common.base.client, id);
 
     if (!shared_dict) {
         shared_dict = red_create_glz_dictionary(dcc, id, window_size);
@@ -8713,7 +8716,7 @@ static GlzSharedDictionary *red_restore_glz_dictionary(DisplayChannelClient *dcc
 
     pthread_mutex_lock(&glz_dictionary_list_lock);
 
-    shared_dict = _red_find_glz_dictionary(id);
+    shared_dict = _red_find_glz_dictionary(dcc->common.base.client, id);
 
     if (!shared_dict) {
         shared_dict = red_create_restored_glz_dictionary(dcc, id, restore_data);
@@ -8760,7 +8763,7 @@ static void red_release_glz(DisplayChannelClient *dcc)
     free(shared_dict);
 }
 
-static PixmapCache *red_create_pixmap_cache(uint8_t id, int64_t size)
+static PixmapCache *red_create_pixmap_cache(RedClient *client, uint8_t id, int64_t size)
 {
     PixmapCache *cache = spice_new0(PixmapCache, 1);
     ring_item_init(&cache->base);
@@ -8770,29 +8773,31 @@ static PixmapCache *red_create_pixmap_cache(uint8_t id, int64_t size)
     ring_init(&cache->lru);
     cache->available = size;
     cache->size = size;
+    cache->client = client;
     return cache;
 }
 
-static PixmapCache *red_get_pixmap_cache(uint8_t id, int64_t size)
+static PixmapCache *red_get_pixmap_cache(RedClient *client, uint8_t id, int64_t size)
 {
-    PixmapCache *cache = NULL;
+    PixmapCache *ret = NULL;
     RingItem *now;
     pthread_mutex_lock(&cache_lock);
 
     now = &pixmap_cache_list;
     while ((now = ring_next(&pixmap_cache_list, now))) {
-        if (((PixmapCache *)now)->id == id) {
-            cache = (PixmapCache *)now;
-            cache->refs++;
+        PixmapCache *cache = (PixmapCache *)now;
+        if ((cache->client == client) && (cache->id == id)) {
+            ret = cache;
+            ret->refs++;
             break;
         }
     }
-    if (!cache) {
-        cache = red_create_pixmap_cache(id, size);
-        ring_add(&pixmap_cache_list, &cache->base);
+    if (!ret) {
+        ret = red_create_pixmap_cache(client, id, size);
+        ring_add(&pixmap_cache_list, &ret->base);
     }
     pthread_mutex_unlock(&cache_lock);
-    return cache;
+    return ret;
 }
 
 static void red_release_pixmap_cache(DisplayChannelClient *dcc)
@@ -8816,7 +8821,8 @@ static void red_release_pixmap_cache(DisplayChannelClient *dcc)
 static int display_channel_init_cache(DisplayChannelClient *dcc, SpiceMsgcDisplayInit *init_info)
 {
     ASSERT(!dcc->pixmap_cache);
-    return !!(dcc->pixmap_cache = red_get_pixmap_cache(init_info->pixmap_cache_id,
+    return !!(dcc->pixmap_cache = red_get_pixmap_cache(dcc->common.base.client,
+                                                       init_info->pixmap_cache_id,
                                                        init_info->pixmap_cache_size));
 }
 
@@ -8904,7 +8910,9 @@ static uint64_t display_channel_handle_migrate_data(RedChannelClient *rcc, uint3
         return FALSE;
     }
     display_channel->expect_migrate_data = FALSE;
-    if (!(dcc->pixmap_cache = red_get_pixmap_cache(migrate_data->pixmap_cache_id, -1))) {
+    dcc->pixmap_cache = red_get_pixmap_cache(dcc->common.base.client,
+                                            migrate_data->pixmap_cache_id, -1);
+    if (!dcc->pixmap_cache) {
         return FALSE;
     }
     pthread_mutex_lock(&dcc->pixmap_cache->lock);
commit a32e90257e834e340075e633132b52c612be4578
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Jul 3 18:56:33 2011 +0200

    server/red_worker: multiple client support - base split
    
    This patch compiles but breaks spice.
    
    Split both display and cursor channels to a client part and channel part.
    
    Introduce DisplayChannelClient, CursorChannelClient, CommonChannelClient.
    don't disconnect channel on client disconnect.
    Move all caches to the ChannelClient's.
    
    Remove reference counting of the channel.
    
    No new functionality introduced.
    
    NOTE: Introduces a crash in disconnections, a regression, resulting from
    incorrect thread access, that is fixed in the patch titled:
    "server: registering RedChannel in reds, instead of Channel"

diff --git a/server/red_channel.c b/server/red_channel.c
index eab1384..43b5788 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -847,23 +847,11 @@ void red_channel_client_ack_zero_messages_window(RedChannelClient *rcc)
     rcc->ack_data.messages_window = 0;
 }
 
-void red_channel_ack_zero_messages_window(RedChannel *channel)
-{
-    red_channel_client_ack_zero_messages_window(channel->rcc);
-}
-
 void red_channel_client_ack_set_client_window(RedChannelClient *rcc, int client_window)
 {
     rcc->ack_data.client_window = client_window;
 }
 
-void red_channel_ack_set_client_window(RedChannel* channel, int client_window)
-{
-    if (channel->rcc) {
-        red_channel_client_ack_set_client_window(channel->rcc, client_window);
-    }
-}
-
 static void red_channel_client_remove(RedChannelClient *rcc)
 {
     ring_remove(&rcc->client_link);
diff --git a/server/red_channel.h b/server/red_channel.h
index 617ccfb..a48d3a5 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -294,8 +294,6 @@ void red_channel_pipes_add_type(RedChannel *channel, int pipe_item_type);
 void red_channel_client_ack_zero_messages_window(RedChannelClient *rcc);
 void red_channel_client_ack_set_client_window(RedChannelClient *rcc, int client_window);
 void red_channel_client_push_set_ack(RedChannelClient *rcc);
-void red_channel_ack_zero_messages_window(RedChannel *channel);
-void red_channel_ack_set_client_window(RedChannel *channel, int client_window);
 void red_channel_push_set_ack(RedChannel *channel);
 
 /* TODO: This sets all clients to shut state - probably we want to close per channel */
diff --git a/server/red_client_cache.h b/server/red_client_cache.h
index 6c17ba6..0da11a6 100644
--- a/server/red_client_cache.h
+++ b/server/red_client_cache.h
@@ -24,7 +24,7 @@
 #define FUNC_NAME(name) red_cursor_cache_##name
 #define VAR_NAME(name) cursor_cache_##name
 #define CHANNEL CursorChannel
-#define CHANNELCLIENT RedChannelClient
+#define CHANNELCLIENT CursorChannelClient
 
 #elif defined(CLIENT_PALETTE_CACHE)
 
@@ -35,7 +35,7 @@
 #define FUNC_NAME(name) red_palette_cache_##name
 #define VAR_NAME(name) palette_cache_##name
 #define CHANNEL DisplayChannel
-#define CHANNELCLIENT RedChannelClient
+#define CHANNELCLIENT DisplayChannelClient
 #else
 
 #error "no cache type."
@@ -44,14 +44,14 @@
 
 #define CHANNEL_FROM_RCC(rcc) SPICE_CONTAINEROF((rcc)->channel, CHANNEL, common.base);
 
-static CacheItem *FUNC_NAME(find)(CHANNEL *channel, uint64_t id)
+static CacheItem *FUNC_NAME(find)(CHANNELCLIENT *channel_client, uint64_t id)
 {
-    CacheItem *item = channel->CACHE_NAME[CACHE_HASH_KEY(id)];
+    CacheItem *item = channel_client->CACHE_NAME[CACHE_HASH_KEY(id)];
 
     while (item) {
         if (item->id == id) {
             ring_remove(&item->u.cache_data.lru_link);
-            ring_add(&channel->VAR_NAME(lru), &item->u.cache_data.lru_link);
+            ring_add(&channel_client->VAR_NAME(lru), &item->u.cache_data.lru_link);
             break;
         }
         item = item->u.cache_data.next;
@@ -62,10 +62,10 @@ static CacheItem *FUNC_NAME(find)(CHANNEL *channel, uint64_t id)
 static void FUNC_NAME(remove)(CHANNELCLIENT *channel_client, CacheItem *item)
 {
     CacheItem **now;
-    CHANNEL *channel = CHANNEL_FROM_RCC(channel_client);
+    CHANNEL *channel = CHANNEL_FROM_RCC(&channel_client->common.base);
     ASSERT(item);
 
-    now = &channel->CACHE_NAME[CACHE_HASH_KEY(item->id)];
+    now = &channel_client->CACHE_NAME[CACHE_HASH_KEY(item->id)];
     for (;;) {
         ASSERT(*now);
         if (*now == item) {
@@ -75,56 +75,55 @@ static void FUNC_NAME(remove)(CHANNELCLIENT *channel_client, CacheItem *item)
         now = &(*now)->u.cache_data.next;
     }
     ring_remove(&item->u.cache_data.lru_link);
-    channel->VAR_NAME(items)--;
-    channel->VAR_NAME(available) += item->size;
+    channel_client->VAR_NAME(items)--;
+    channel_client->VAR_NAME(available) += item->size;
 
     red_channel_pipe_item_init(&channel->common.base, &item->u.pipe_data, PIPE_ITEM_TYPE_INVAL_ONE);
-    red_channel_client_pipe_add_tail(channel_client, &item->u.pipe_data); // for now
+    red_channel_client_pipe_add_tail(&channel_client->common.base, &item->u.pipe_data); // for now
 }
 
 static int FUNC_NAME(add)(CHANNELCLIENT *channel_client, uint64_t id, size_t size)
 {
-    CHANNEL *channel = CHANNEL_FROM_RCC(channel_client);
     CacheItem *item;
     int key;
 
     item = spice_new(CacheItem, 1);
 
-    channel->VAR_NAME(available) -= size;
-    while (channel->VAR_NAME(available) < 0) {
-        CacheItem *tail = (CacheItem *)ring_get_tail(&channel->VAR_NAME(lru));
+    channel_client->VAR_NAME(available) -= size;
+    while (channel_client->VAR_NAME(available) < 0) {
+        CacheItem *tail = (CacheItem *)ring_get_tail(&channel_client->VAR_NAME(lru));
         if (!tail) {
-            channel->VAR_NAME(available) += size;
+            channel_client->VAR_NAME(available) += size;
             free(item);
             return FALSE;
         }
         FUNC_NAME(remove)(channel_client, tail);
     }
-    ++channel->VAR_NAME(items);
-    item->u.cache_data.next = channel->CACHE_NAME[(key = CACHE_HASH_KEY(id))];
-    channel->CACHE_NAME[key] = item;
+    ++channel_client->VAR_NAME(items);
+    item->u.cache_data.next = channel_client->CACHE_NAME[(key = CACHE_HASH_KEY(id))];
+    channel_client->CACHE_NAME[key] = item;
     ring_item_init(&item->u.cache_data.lru_link);
-    ring_add(&channel->VAR_NAME(lru), &item->u.cache_data.lru_link);
+    ring_add(&channel_client->VAR_NAME(lru), &item->u.cache_data.lru_link);
     item->id = id;
     item->size = size;
     item->inval_type = CACHE_INVAL_TYPE;
     return TRUE;
 }
 
-static void FUNC_NAME(reset)(CHANNEL *channel, long size)
+static void FUNC_NAME(reset)(CHANNELCLIENT *channel_client, long size)
 {
     int i;
 
     for (i = 0; i < CACHE_HASH_SIZE; i++) {
-        while (channel->CACHE_NAME[i]) {
-            CacheItem *item = channel->CACHE_NAME[i];
-            channel->CACHE_NAME[i] = item->u.cache_data.next;
+        while (channel_client->CACHE_NAME[i]) {
+            CacheItem *item = channel_client->CACHE_NAME[i];
+            channel_client->CACHE_NAME[i] = item->u.cache_data.next;
             free(item);
         }
     }
-    ring_init(&channel->VAR_NAME(lru));
-    channel->VAR_NAME(available) = size;
-    channel->VAR_NAME(items) = 0;
+    ring_init(&channel_client->VAR_NAME(lru));
+    channel_client->VAR_NAME(available) = size;
+    channel_client->VAR_NAME(items) = 0;
 }
 
 
@@ -136,4 +135,5 @@ static void FUNC_NAME(reset)(CHANNEL *channel, long size)
 #undef FUNC_NAME
 #undef VAR_NAME
 #undef CHANNEL
+#undef CHANNELCLIENT
 #undef CHANNEL_FROM_RCC
diff --git a/server/red_client_shared_cache.h b/server/red_client_shared_cache.h
index 6b1f975..ed3f1da 100644
--- a/server/red_client_shared_cache.h
+++ b/server/red_client_shared_cache.h
@@ -26,7 +26,6 @@
 #define FUNC_NAME(name) pixmap_cache_##name
 #define PRIVATE_FUNC_NAME(name) __pixmap_cache_##name
 #define CHANNEL DisplayChannel
-#define CHANNEL_FROM_RCC(rcc) SPICE_CONTAINEROF(rcc->channel, CHANNEL, common.base);
 #define CACH_GENERATION pixmap_cache_generation
 #define INVAL_ALL_VERB SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS
 #else
@@ -35,14 +34,14 @@
 
 #endif
 
+#define CHANNEL_FROM_RCC(rcc) SPICE_CONTAINEROF((rcc)->channel, CHANNEL, common.base);
 
-static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, RedChannelClient *rcc)
+static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelClient *dcc)
 {
-    CHANNEL *channel = CHANNEL_FROM_RCC(rcc);
     NewCacheItem *item;
     uint64_t serial;
 
-    serial = red_channel_client_get_message_serial(rcc);
+    serial = red_channel_client_get_message_serial(&dcc->common.base);
     pthread_mutex_lock(&cache->lock);
     item = cache->hash_table[CACHE_HASH_KEY(id)];
 
@@ -50,9 +49,9 @@ static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, RedChannelClien
         if (item->id == id) {
             ring_remove(&item->lru_link);
             ring_add(&cache->lru, &item->lru_link);
-            ASSERT(channel->common.id < MAX_CACHE_CLIENTS)
-            item->sync[channel->common.id] = serial;
-            cache->sync[channel->common.id] = serial;
+            ASSERT(dcc->common.id < MAX_CACHE_CLIENTS)
+            item->sync[dcc->common.id] = serial;
+            cache->sync[dcc->common.id] = serial;
             *lossy = item->lossy;
             break;
         }
@@ -81,9 +80,8 @@ static int FUNC_NAME(set_lossy)(CACHE *cache, uint64_t id, int lossy)
     return !!item;
 }
 
-static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, RedChannelClient *rcc)
+static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, DisplayChannelClient *dcc)
 {
-    CHANNEL *channel = CHANNEL_FROM_RCC(rcc);
     NewCacheItem *item;
     uint64_t serial;
     int key;
@@ -91,14 +89,15 @@ static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, R
     ASSERT(size > 0);
 
     item = spice_new(NewCacheItem, 1);
-    serial = red_channel_client_get_message_serial(rcc);
+    serial = red_channel_client_get_message_serial(&dcc->common.base);
 
     pthread_mutex_lock(&cache->lock);
 
-    if (cache->generation != channel->CACH_GENERATION) {
-        if (!channel->pending_pixmaps_sync) {
-            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_PIXMAP_SYNC);
-            channel->pending_pixmaps_sync = TRUE;
+    if (cache->generation != dcc->CACH_GENERATION) {
+        if (!dcc->pending_pixmaps_sync) {
+            red_channel_client_pipe_add_type(
+                &dcc->common.base, PIPE_ITEM_TYPE_PIXMAP_SYNC);
+            dcc->pending_pixmaps_sync = TRUE;
         }
         pthread_mutex_unlock(&cache->lock);
         free(item);
@@ -111,7 +110,7 @@ static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, R
         NewCacheItem **now;
 
         if (!(tail = (NewCacheItem *)ring_get_tail(&cache->lru)) ||
-                                                   tail->sync[channel->common.id] == serial) {
+                                                   tail->sync[dcc->common.id] == serial) {
             cache->available += size;
             pthread_mutex_unlock(&cache->lock);
             free(item);
@@ -130,8 +129,8 @@ static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, R
         ring_remove(&tail->lru_link);
         cache->items--;
         cache->available += tail->size;
-        cache->sync[channel->common.id] = serial;
-        display_channel_push_release(channel, SPICE_RES_TYPE_PIXMAP, tail->id, tail->sync);
+        cache->sync[dcc->common.id] = serial;
+        display_channel_push_release(dcc, SPICE_RES_TYPE_PIXMAP, tail->id, tail->sync);
         free(tail);
     }
     ++cache->items;
@@ -143,8 +142,8 @@ static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, R
     item->size = size;
     item->lossy = lossy;
     memset(item->sync, 0, sizeof(item->sync));
-    item->sync[channel->common.id] = serial;
-    cache->sync[channel->common.id] = serial;
+    item->sync[dcc->common.id] = serial;
+    cache->sync[dcc->common.id] = serial;
     pthread_mutex_unlock(&cache->lock);
     return TRUE;
 }
@@ -169,25 +168,24 @@ static void PRIVATE_FUNC_NAME(clear)(CACHE *cache)
     cache->items = 0;
 }
 
-static void FUNC_NAME(reset)(CACHE *cache, RedChannelClient *rcc, SpiceMsgWaitForChannels* sync_data)
+static void FUNC_NAME(reset)(CACHE *cache, DisplayChannelClient *dcc, SpiceMsgWaitForChannels* sync_data)
 {
-    CHANNEL *channel = CHANNEL_FROM_RCC(rcc);
     uint8_t wait_count;
     uint64_t serial;
     uint32_t i;
 
-    serial = red_channel_client_get_message_serial(rcc);
+    serial = red_channel_client_get_message_serial(&dcc->common.base);
     pthread_mutex_lock(&cache->lock);
     PRIVATE_FUNC_NAME(clear)(cache);
 
-    channel->CACH_GENERATION = ++cache->generation;
-    cache->generation_initiator.client = channel->common.id;
+    dcc->CACH_GENERATION = ++cache->generation;
+    cache->generation_initiator.client = dcc->common.id;
     cache->generation_initiator.message = serial;
-    cache->sync[channel->common.id] = serial;
+    cache->sync[dcc->common.id] = serial;
 
     wait_count = 0;
     for (i = 0; i < MAX_CACHE_CLIENTS; i++) {
-        if (cache->sync[i] && i != channel->common.id) {
+        if (cache->sync[i] && i != dcc->common.id) {
             sync_data->wait_list[wait_count].channel_type = SPICE_CHANNEL_DISPLAY;
             sync_data->wait_list[wait_count].channel_id = i;
             sync_data->wait_list[wait_count++].message_serial = cache->sync[i];
diff --git a/server/red_worker.c b/server/red_worker.c
index 22abe8d..bc2694d 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -19,6 +19,16 @@
 #include <config.h>
 #endif
 
+/* Common variable abberiviations:
+ *
+ * rcc - RedChannelClient
+ * ccc - CursorChannelClient (not to be confused with common_cc)
+ * common_cc - CommonChannelClient
+ * dcc - DisplayChannelClient
+ * cursor_red_channel - downcast of CursorChannel to RedChannel
+ * display_red_channel - downcast of DisplayChannel to RedChannel
+ */
+
 #include <stdio.h>
 #include <stdarg.h>
 #include <sys/epoll.h>
@@ -463,9 +473,10 @@ typedef struct FreeList {
 } FreeList;
 
 typedef struct DisplayChannel DisplayChannel;
+typedef struct DisplayChannelClient DisplayChannelClient;
 
 typedef struct  {
-    DisplayChannel *display_channel;
+    DisplayChannelClient *dcc;
     RedCompressBuf *bufs_head;
     RedCompressBuf *bufs_tail;
     jmp_buf jmp_env;
@@ -533,7 +544,7 @@ struct RedGlzDrawable {
     GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
     Ring instances;
     uint8_t instances_count;
-    DisplayChannel *display_channel;
+    DisplayChannelClient *dcc;
 };
 
 pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -552,15 +563,21 @@ typedef struct GlzSharedDictionary {
 
 typedef struct CommonChannel {
     RedChannel base; // Must be the first thing
-    EventListener listener;
-    uint32_t id;
+    event_listener_action_proc listener_action;
     struct RedWorker *worker;
     uint8_t recv_buf[RECIVE_BUF_SIZE];
+    uint32_t id_alloc; // bitfield. TODO - use this instead of shift scheme.
 } CommonChannel;
 
+typedef struct CommonChannelClient {
+    RedChannelClient base;
+    EventListener listener;
+    uint32_t id;
+    struct RedWorker *worker;
+} CommonChannelClient;
 
-struct DisplayChannel {
-    CommonChannel common; // Must be the first thing
+struct DisplayChannelClient {
+    CommonChannelClient common;
 
     int expect_init;
     int expect_migrate_mark;
@@ -575,7 +592,14 @@ struct DisplayChannel {
     long palette_cache_available;
     uint32_t palette_cache_items;
 
-    StreamAgent stream_agents[NUM_STREAMS];
+    struct {
+        uint32_t stream_outbuf_size;
+        uint8_t *stream_outbuf; // caution stream buffer is also used as compress bufs!!!
+
+        RedCompressBuf *used_compress_bufs;
+
+        FreeList free_list;
+    } send_data;
 
     /* global lz encoding entities */
     GlzSharedDictionary *glz_dict;
@@ -589,20 +613,24 @@ struct DisplayChannel {
     uint8_t surface_client_created[NUM_SURFACES];
     QRegion surface_client_lossy_region[NUM_SURFACES];
 
-    struct {
-        uint32_t stream_outbuf_size;
-        uint8_t *stream_outbuf; // caution stream buffer is also used as compress bufs!!!
+    StreamAgent stream_agents[NUM_STREAMS];
+};
 
-        RedCompressBuf *free_compress_bufs;
-        RedCompressBuf *used_compress_bufs;
+struct DisplayChannel {
+    CommonChannel common; // Must be the first thing
 
-        FreeList free_list;
-    } send_data;
+    // only required for one client, can be the first (or choose it by speed
+    // and keep a pointer to it here?)
+    int expect_migrate_mark;
+    int expect_migrate_data;
 
     int enable_jpeg;
     int jpeg_quality;
     int enable_zlib_glz_wrap;
     int zlib_level;
+
+    RedCompressBuf *free_compress_bufs;
+
 #ifdef RED_STATISTICS
     StatNodeRef stat;
     uint64_t *cache_hits_counter;
@@ -619,13 +647,17 @@ struct DisplayChannel {
 #endif
 };
 
-typedef struct CursorChannel {
-    CommonChannel common; // Must be the first thing
+typedef struct CursorChannelClient {
+    CommonChannelClient common;
 
     CacheItem *cursor_cache[CURSOR_CACHE_HASH_SIZE];
     Ring cursor_cache_lru;
     long cursor_cache_available;
     uint32_t cursor_cache_items;
+} CursorChannelClient;
+
+typedef struct CursorChannel {
+    CommonChannel common; // Must be the first thing
 
 #ifdef RED_STATISTICS
     StatNodeRef stat;
@@ -710,6 +742,8 @@ struct Drawable {
     RingItem list_link;
     DrawItem tree_item;
     PipeItem pipe_item;
+    PipeItem *pipe_item_rest;
+    uint32_t size_pipe_item_rest;
 #ifdef UPDATE_AREA_BY_TREE
     RingItem collect_link;
 #endif
@@ -919,29 +953,34 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac
 #endif
 static void red_release_cursor(RedWorker *worker, CursorItem *cursor);
 static inline void release_drawable(RedWorker *worker, Drawable *item);
-static void red_display_release_stream(DisplayChannel *display, StreamAgent *agent);
+static void red_display_release_stream(RedWorker *worker, StreamAgent *agent);
 static inline void red_detach_stream(RedWorker *worker, Stream *stream);
 static void red_stop_stream(RedWorker *worker, Stream *stream);
 static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate, Drawable *sect);
-static inline void display_begin_send_message(RedChannelClient *rcc, SpiceMarshaller *base_marshaller);
-static void red_release_pixmap_cache(DisplayChannel *channel);
-static void red_release_glz(DisplayChannel *channel);
-static void red_freeze_glz(DisplayChannel *channel);
-static void display_channel_push_release(DisplayChannel *channel, uint8_t type, uint64_t id,
+static inline void display_begin_send_message(RedChannelClient *rcc,
+                                              SpiceMarshaller *base_marshaller);
+static void red_release_pixmap_cache(DisplayChannelClient *dcc);
+static void red_release_glz(DisplayChannelClient *dcc);
+static void red_freeze_glz(DisplayChannelClient *dcc);
+static void display_channel_push_release(DisplayChannelClient *dcc, uint8_t type, uint64_t id,
                                          uint64_t* sync_data);
-static void red_display_release_stream_clip(DisplayChannel* channel, StreamClipItem *item);
-static int red_display_free_some_independent_glz_drawables(DisplayChannel *channel);
-static void red_display_free_glz_drawable(DisplayChannel *channel, RedGlzDrawable *drawable);
-static void reset_rate(RedWorker *worker, StreamAgent *stream_agent);
+static void red_display_release_stream_clip(RedWorker *worker, StreamClipItem *item);
+static int red_display_free_some_independent_glz_drawables(DisplayChannelClient *dcc);
+static void red_display_free_glz_drawable(DisplayChannelClient *dcc, RedGlzDrawable *drawable);
+static void reset_rate(DisplayChannelClient *dcc, StreamAgent *stream_agent);
 static BitmapGradualType _get_bitmap_graduality_level(RedWorker *worker, SpiceBitmap *bitmap,
                                                       uint32_t group_id);
 static inline int _stride_is_extra(SpiceBitmap *bitmap);
 static void red_disconnect_cursor(RedChannel *channel);
-static void red_wait_pipe_item_sent(RedChannel *channel, PipeItem *item);
-static void display_channel_release_item_before_push(DisplayChannel *display_channel, PipeItem *item);
-static void display_channel_release_item_after_push(DisplayChannel *display_channel, PipeItem *item);
-static void cursor_channel_release_item_before_push(CursorChannel *cursor_channel, PipeItem *item);
-static void cursor_channel_release_item_after_push(CursorChannel *cursor_channel, PipeItem *item);
+static void display_channel_client_release_item_before_push(DisplayChannelClient *dcc,
+                                                            PipeItem *item);
+static void display_channel_client_release_item_after_push(DisplayChannelClient *dcc,
+                                                           PipeItem *item);
+static void cursor_channel_client_release_item_before_push(CursorChannelClient *ccc,
+                                                           PipeItem *item);
+static void cursor_channel_client_release_item_after_push(CursorChannelClient *ccc,
+                                                          PipeItem *item);
+static void red_wait_pipe_item_sent(RedChannelClient *rcc, PipeItem *item);
 
 #ifdef DUMP_BITMAP
 static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_id);
@@ -1145,7 +1184,7 @@ static void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb)
     red_channel_client_pipe_add(rcc, &item->base);
 }
 
-static inline void red_create_surface_item(RedWorker *worker, int surface_id);
+static inline void red_create_surface_item(DisplayChannelClient *dcc, int surface_id);
 static void red_add_surface_image(RedWorker *worker, int surface_id);
 static void red_pipes_add_verb(RedChannel *channel, uint16_t verb)
 {
@@ -1157,8 +1196,24 @@ static void red_pipes_add_verb(RedChannel *channel, uint16_t verb)
     red_pipe_add_verb(rcc, verb);
 }
 
-static inline void red_handle_drawable_surfaces_client_synced(RedWorker *worker, Drawable *drawable)
+
+// TODO: replace with FOREACH_DCC when it is introduced
+#define WORKER_TO_DCC(worker) \
+    (worker->display_channel ? SPICE_CONTAINEROF(worker->display_channel->common.base.rcc, DisplayChannelClient, common.base) : NULL)
+
+#define DCC_TO_WORKER(dcc) \
+    (SPICE_CONTAINEROF((dcc)->common.base.channel, CommonChannel, base)->worker)
+
+#define DCC_TO_DC(dcc) SPICE_CONTAINEROF((dcc)->common.base.channel,\
+                                         DisplayChannel, common.base)
+
+#define RCC_TO_DCC(rcc) SPICE_CONTAINEROF((rcc), DisplayChannelClient, common.base)
+#define RCC_TO_CCC(rcc) SPICE_CONTAINEROF((rcc), CursorChannelClient, common.base)
+
+static inline void red_handle_drawable_surfaces_client_synced(
+                        DisplayChannelClient *dcc, Drawable *drawable)
 {
+    RedWorker *worker = DCC_TO_WORKER(dcc);
     int x;
 
     for (x = 0; x < 3; ++x) {
@@ -1166,20 +1221,20 @@ static inline void red_handle_drawable_surfaces_client_synced(RedWorker *worker,
 
         surface_id = drawable->surfaces_dest[x];
         if (surface_id != -1) {
-            if (worker->display_channel->surface_client_created[surface_id] == TRUE) {
+            if (dcc->surface_client_created[surface_id] == TRUE) {
                 continue;
             }
-            red_create_surface_item(worker, surface_id);
+            red_create_surface_item(dcc, surface_id);
             red_current_flush(worker, surface_id);
             red_add_surface_image(worker, surface_id);
         }
     }
 
-    if (worker->display_channel->surface_client_created[drawable->surface_id] == TRUE) {
+    if (dcc->surface_client_created[drawable->surface_id] == TRUE) {
         return;
     }
 
-    red_create_surface_item(worker, drawable->surface_id);
+    red_create_surface_item(dcc, drawable->surface_id);
     red_current_flush(worker, drawable->surface_id);
     red_add_surface_image(worker, drawable->surface_id);
 }
@@ -1196,62 +1251,61 @@ static int cursor_is_connected(RedWorker *worker)
         &worker->cursor_channel->common.base));
 }
 
-static inline void red_pipe_add_drawable(RedWorker *worker, Drawable *drawable)
+// TODO: remove red_pipe_add_drawable, replace with red_clients_add_drawable
+static inline void red_pipe_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
 {
-    if (!display_is_connected(worker)) {
+    if (!dcc) {
         return;
     }
 
-    red_handle_drawable_surfaces_client_synced(worker, drawable);
+    red_handle_drawable_surfaces_client_synced(dcc, drawable);
 
     drawable->refs++;
-    red_channel_client_pipe_add(worker->display_channel->common.base.rcc, &drawable->pipe_item);
+    red_channel_client_pipe_add(&dcc->common.base, &drawable->pipe_item);
 }
 
-static inline void red_pipe_add_drawable_to_tail(RedWorker *worker, Drawable *drawable)
+static inline void red_pipe_add_drawable_to_tail(DisplayChannelClient *dcc, Drawable *drawable)
 {
-    if (!display_is_connected(worker)) {
+    if (!dcc) {
         return;
     }
-    red_handle_drawable_surfaces_client_synced(worker, drawable);
+    red_handle_drawable_surfaces_client_synced(dcc, drawable);
     drawable->refs++;
-    red_channel_client_pipe_add_tail(worker->display_channel->common.base.rcc,
-                                     &drawable->pipe_item);
+    red_channel_client_pipe_add_tail(&dcc->common.base, &drawable->pipe_item);
 }
 
-static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *drawable,
+static inline void red_pipe_add_drawable_after(DisplayChannelClient *dcc, Drawable *drawable,
                                                Drawable *pos_after)
 {
-    if (!display_is_connected(worker)) {
+    if (!dcc) {
         return;
     }
 
     if (!pos_after || !pipe_item_is_linked(&pos_after->pipe_item)) {
-        red_pipe_add_drawable(worker, drawable);
+        red_pipe_add_drawable(dcc, drawable);
         return;
     }
-    red_handle_drawable_surfaces_client_synced(worker, drawable);
+    red_handle_drawable_surfaces_client_synced(dcc, drawable);
     drawable->refs++;
-    red_channel_client_pipe_add_after(worker->display_channel->common.base.rcc,
-                                      &drawable->pipe_item, &pos_after->pipe_item);
+    red_channel_client_pipe_add_after(&dcc->common.base, &drawable->pipe_item,
+                                      &pos_after->pipe_item);
 }
 
-static inline PipeItem *red_pipe_get_tail(RedWorker *worker)
+static inline PipeItem *red_pipe_get_tail(DisplayChannelClient *dcc)
 {
-    if (!display_is_connected(worker)) {
+    if (!dcc) {
         return NULL;
     }
 
-    return (PipeItem*)ring_get_tail(&worker->display_channel->common.base.rcc->pipe);
+    return (PipeItem*)ring_get_tail(&dcc->common.base.pipe);
 }
 
 static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id);
 
-static inline void red_pipe_remove_drawable(RedWorker *worker, Drawable *drawable)
+static inline void red_pipe_remove_drawable(DisplayChannelClient *dcc, Drawable *drawable)
 {
     if (pipe_item_is_linked(&drawable->pipe_item)) {
-        red_channel_client_pipe_remove_and_release(
-            worker->display_channel->common.base.rcc, &drawable->pipe_item);
+        red_channel_client_pipe_remove_and_release(&dcc->common.base, &drawable->pipe_item);
     }
 }
 
@@ -1314,14 +1368,14 @@ static void common_release_recv_buf(RedChannelClient *rcc,
 #include "red_client_cache.h"
 #undef CLIENT_PALETTE_CACHE
 
-static void red_reset_palette_cache(DisplayChannel *display_channel)
+static void red_reset_palette_cache(DisplayChannelClient *dcc)
 {
-    red_palette_cache_reset(display_channel, CLIENT_PALETTE_CACHE_SIZE);
+    red_palette_cache_reset(dcc, CLIENT_PALETTE_CACHE_SIZE);
 }
 
-static void red_reset_cursor_cache(CursorChannel *channel)
+static void red_reset_cursor_cache(RedChannelClient *rcc)
 {
-    red_cursor_cache_reset(channel, CLIENT_CURSOR_CACHE_SIZE);
+    red_cursor_cache_reset(RCC_TO_CCC(rcc), CLIENT_CURSOR_CACHE_SIZE);
 }
 
 static inline Drawable *alloc_drawable(RedWorker *worker)
@@ -1370,18 +1424,19 @@ static SurfaceDestroyItem *get_surface_destroy_item(RedChannel *channel,
     return destroy;
 }
 
-static inline void red_destroy_surface_item(RedWorker *worker, uint32_t surface_id)
+static inline void red_destroy_surface_item(RedWorker *worker,
+    DisplayChannelClient *dcc, uint32_t surface_id)
 {
     SurfaceDestroyItem *destroy;
     RedChannel *channel;
 
-    if (!display_is_connected(worker)) {
+    if (!dcc || !dcc->surface_client_created[surface_id]) {
         return;
     }
-    worker->display_channel->surface_client_created[surface_id] = FALSE;
+    dcc->surface_client_created[surface_id] = FALSE;
     channel = &worker->display_channel->common.base;
     destroy = get_surface_destroy_item(channel, surface_id);
-    red_channel_client_pipe_add(channel->rcc, &destroy->pipe_item);
+    red_channel_client_pipe_add(&dcc->common.base, &destroy->pipe_item);
 }
 
 static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
@@ -1405,7 +1460,7 @@ static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
 
         region_destroy(&surface->draw_dirty_region);
         surface->context.canvas = NULL;
-        red_destroy_surface_item(worker, surface_id);
+        red_destroy_surface_item(worker, WORKER_TO_DCC(worker), surface_id);
 
         PANIC_ON(!ring_is_empty(&surface->depend_on_me));
     }
@@ -1597,7 +1652,7 @@ static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
 
 static void remove_drawable(RedWorker *worker, Drawable *item)
 {
-    red_pipe_remove_drawable(worker, item);
+    red_pipe_remove_drawable(WORKER_TO_DCC(worker), item);
     current_remove_drawable(worker, item);
 }
 
@@ -1636,8 +1691,7 @@ static inline void current_remove(RedWorker *worker, TreeItem *item)
     }
 }
 
-static void current_tree_for_each(RedWorker *worker, Ring *ring, void (*f)(TreeItem *, void *),
-                                  void * data)
+static void current_tree_for_each(Ring *ring, void (*f)(TreeItem *, void *), void * data)
 {
     RingItem *ring_item;
     Ring *top_ring;
@@ -1684,20 +1738,22 @@ static void red_current_clear(RedWorker *worker, int surface_id)
     }
 }
 
-static void red_clear_surface_drawables_from_pipe(RedWorker *worker, int surface_id, int force)
+static void red_clear_surface_drawables_from_pipe(DisplayChannelClient *dcc, int surface_id, int force)
 {
     Ring *ring;
     PipeItem *item;
     int x;
+    RedChannelClient *rcc;
 
-    if (!display_is_connected(worker)) {
+    if (!dcc) {
         return;
     }
 
     /* removing the newest drawables that their destination is surface_id and
        no other drawable depends on them */
 
-    ring = &worker->display_channel->common.base.rcc->pipe;
+    rcc = &dcc->common.base;
+    ring = &dcc->common.base.pipe;
     item = (PipeItem *) ring;
     while ((item = (PipeItem *)ring_next(ring, (RingItem *)item))) {
         Drawable *drawable;
@@ -1713,11 +1769,7 @@ static void red_clear_surface_drawables_from_pipe(RedWorker *worker, int surface
         if (drawable->surface_id == surface_id) {
             PipeItem *tmp_item = item;
             item = (PipeItem *)ring_prev(ring, (RingItem *)item);
-            ring_remove(&tmp_item->link);
-            worker->display_channel->common.base.release_item(
-                worker->display_channel->common.base.rcc, tmp_item, FALSE);
-            worker->display_channel->common.base.rcc->pipe_size--;
-
+            red_channel_client_pipe_remove_and_release(rcc, tmp_item);
             if (!item) {
                 item = (PipeItem *)ring;
             }
@@ -1741,7 +1793,7 @@ static void red_clear_surface_drawables_from_pipe(RedWorker *worker, int surface
     }
 
     if (item) {
-        red_wait_pipe_item_sent(&worker->display_channel->common.base, item);
+        red_wait_pipe_item_sent(&dcc->common.base, item);
     }
 }
 
@@ -1823,7 +1875,7 @@ static void show_current(RedWorker *worker, Ring *ring)
         red_printf("TEST: TREE: EMPTY");
         return;
     }
-    current_tree_for_each(worker, ring, __show_current, NULL);
+    current_tree_for_each(ring, __show_current, NULL);
 }
 
 #else
@@ -2142,10 +2194,10 @@ static inline void red_detach_stream(RedWorker *worker, Stream *stream)
     stream->current = NULL;
 }
 
-static StreamClipItem *__new_stream_clip(DisplayChannel* channel, StreamAgent *agent)
+static StreamClipItem *__new_stream_clip(DisplayChannelClient* dcc, StreamAgent *agent)
 {
     StreamClipItem *item = spice_new(StreamClipItem, 1);
-    red_channel_pipe_item_init(&channel->common.base,
+    red_channel_pipe_item_init(dcc->common.base.channel,
                     (PipeItem *)item, PIPE_ITEM_TYPE_STREAM_CLIP);
 
     item->stream_agent = agent;
@@ -2154,10 +2206,10 @@ static StreamClipItem *__new_stream_clip(DisplayChannel* channel, StreamAgent *a
     return item;
 }
 
-static void push_stream_clip_by_drawable(DisplayChannel* channel, StreamAgent *agent,
+static void push_stream_clip_by_drawable(DisplayChannelClient* dcc, StreamAgent *agent,
                                          Drawable *drawable)
 {
-    StreamClipItem *item = __new_stream_clip(channel, agent);
+    StreamClipItem *item = __new_stream_clip(dcc, agent);
     int n_rects;
 
     if (!item) {
@@ -2176,12 +2228,12 @@ static void push_stream_clip_by_drawable(DisplayChannel* channel, StreamAgent *a
         item->rects->num_rects = n_rects;
         region_ret_rects(&drawable->tree_item.base.rgn, item->rects->rects, n_rects);
     }
-    red_channel_client_pipe_add(channel->common.base.rcc, (PipeItem *)item);
+    red_channel_client_pipe_add(&dcc->common.base, (PipeItem *)item);
 }
 
-static void push_stream_clip(DisplayChannel* channel, StreamAgent *agent)
+static void push_stream_clip(DisplayChannelClient* dcc, StreamAgent *agent)
 {
-    StreamClipItem *item = __new_stream_clip(channel, agent);
+    StreamClipItem *item = __new_stream_clip(dcc, agent);
     int n_rects;
 
     if (!item) {
@@ -2194,13 +2246,13 @@ static void push_stream_clip(DisplayChannel* channel, StreamAgent *agent)
     item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects));
     item->rects->num_rects = n_rects;
     region_ret_rects(&agent->vis_region, item->rects->rects, n_rects);
-    red_channel_client_pipe_add(channel->common.base.rcc, (PipeItem *)item);
+    red_channel_client_pipe_add(&dcc->common.base, (PipeItem *)item);
 }
 
-static void red_display_release_stream_clip(DisplayChannel* channel, StreamClipItem *item)
+static void red_display_release_stream_clip(RedWorker *worker, StreamClipItem *item)
 {
     if (!--item->refs) {
-        red_display_release_stream(channel, item->stream_agent);
+        red_display_release_stream(worker, item->stream_agent);
         if (item->rects) {
             free(item->rects);
         }
@@ -2210,36 +2262,39 @@ static void red_display_release_stream_clip(DisplayChannel* channel, StreamClipI
 
 static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *stream)
 {
-    DisplayChannel *channel;
+    DisplayChannelClient *dcc;
+    StreamAgent *agent;
+
     ASSERT(!drawable->stream && !stream->current);
     ASSERT(drawable && stream);
     stream->current = drawable;
     drawable->stream = stream;
     stream->last_time = drawable->creation_time;
 
-    if ((channel = worker->display_channel)) {
-        StreamAgent *agent = &channel->stream_agents[stream - worker->streams_buf];
+    dcc = WORKER_TO_DCC(worker);
+    if (dcc) {
+        agent = &dcc->stream_agents[stream - worker->streams_buf];
         if (!region_is_equal(&agent->vis_region, &drawable->tree_item.base.rgn)) {
             region_destroy(&agent->vis_region);
             region_clone(&agent->vis_region, &drawable->tree_item.base.rgn);
-            push_stream_clip_by_drawable(channel, agent, drawable);
+            push_stream_clip_by_drawable(dcc, agent, drawable);
         }
     }
 }
 
 static void red_stop_stream(RedWorker *worker, Stream *stream)
 {
-    DisplayChannel *channel;
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
     ASSERT(ring_item_is_linked(&stream->link));
     ASSERT(!stream->current);
 
-    if ((channel = worker->display_channel)) {
+    if (dcc) {
         StreamAgent *stream_agent;
-        stream_agent = &channel->stream_agents[stream - worker->streams_buf];
+        stream_agent = &dcc->stream_agents[stream - worker->streams_buf];
         region_clear(&stream_agent->vis_region);
         ASSERT(!pipe_item_is_linked(&stream_agent->destroy_item));
         stream->refs++;
-        red_channel_client_pipe_add(channel->common.base.rcc, &stream_agent->destroy_item);
+        red_channel_client_pipe_add(&dcc->common.base, &stream_agent->destroy_item);
     }
     ring_remove(&stream->link);
     red_release_stream(worker, stream);
@@ -2247,16 +2302,19 @@ static void red_stop_stream(RedWorker *worker, Stream *stream)
 
 static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *stream)
 {
-    DisplayChannel *channel;
-    ASSERT(stream->current);
+    RedChannel *channel;
+    RedChannelClient *rcc;
 
-    if ((channel = worker->display_channel) && !pipe_item_is_linked(&stream->current->pipe_item)) {
+    ASSERT(stream->current);
+    if (WORKER_TO_DCC(worker) && !pipe_item_is_linked(&stream->current->pipe_item)) {
         UpgradeItem *upgrade_item;
         int n_rects;
+        rcc = &WORKER_TO_DCC(worker)->common.base;
+        channel = rcc->channel;
 
         upgrade_item = spice_new(UpgradeItem, 1);
         upgrade_item->refs = 1;
-        red_channel_pipe_item_init(&channel->common.base,
+        red_channel_pipe_item_init(channel,
                 &upgrade_item->base, PIPE_ITEM_TYPE_UPGRADE);
         upgrade_item->drawable = stream->current;
         upgrade_item->drawable->refs++;
@@ -2265,7 +2323,7 @@ static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *strea
         upgrade_item->rects->num_rects = n_rects;
         region_ret_rects(&upgrade_item->drawable->tree_item.base.rgn,
                          upgrade_item->rects->rects, n_rects);
-        red_channel_client_pipe_add(channel->common.base.rcc, &upgrade_item->base);
+        red_channel_client_pipe_add(rcc, &upgrade_item->base);
     }
     red_detach_stream(worker, stream);
 }
@@ -2275,17 +2333,17 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region)
 {
     Ring *ring = &worker->streams;
     RingItem *item = ring_get_head(ring);
-    DisplayChannel *channel = worker->display_channel;
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
 
     while (item) {
         Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
         item = ring_next(ring, item);
 
-        if (channel) {
-            StreamAgent *agent = &channel->stream_agents[stream - worker->streams_buf];
+        if (dcc) {
+            StreamAgent *agent = &dcc->stream_agents[stream - worker->streams_buf];
             if (region_intersects(&agent->vis_region, region)) {
                 region_clear(&agent->vis_region);
-                push_stream_clip(channel, agent);
+                push_stream_clip(dcc, agent);
                 if (stream->current) {
                     red_detach_stream_gracefully(worker, stream);
                 }
@@ -2299,11 +2357,11 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region)
 
 static void red_streams_update_clip(RedWorker *worker, Drawable *drawable)
 {
-    DisplayChannel *channel = worker->display_channel;
     Ring *ring;
     RingItem *item;
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
 
-    if (!display_is_connected(worker)) {
+    if (!dcc) {
         return;
     }
 
@@ -2320,7 +2378,7 @@ static void red_streams_update_clip(RedWorker *worker, Drawable *drawable)
 
         item = ring_next(ring, item);
 
-        agent = &channel->stream_agents[stream - worker->streams_buf];
+        agent = &dcc->stream_agents[stream - worker->streams_buf];
 
         if (stream->current == drawable) {
             continue;
@@ -2328,7 +2386,7 @@ static void red_streams_update_clip(RedWorker *worker, Drawable *drawable)
 
         if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) {
             region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn);
-            push_stream_clip(channel, agent);
+            push_stream_clip(dcc, agent);
         }
     }
 }
@@ -2377,10 +2435,10 @@ static inline void red_handle_streams_timout(RedWorker *worker)
     }
 }
 
-static void red_display_release_stream(DisplayChannel *display, StreamAgent *agent)
+static void red_display_release_stream(RedWorker *worker, StreamAgent *agent)
 {
     ASSERT(agent->stream);
-    red_release_stream(display->common.worker, agent->stream);
+    red_release_stream(worker, agent->stream);
 }
 
 static inline Stream *red_alloc_stream(RedWorker *worker)
@@ -2394,15 +2452,15 @@ static inline Stream *red_alloc_stream(RedWorker *worker)
     return stream;
 }
 
-static int get_bit_rate(RedWorker *worker,
+static int get_bit_rate(DisplayChannelClient *dcc,
     int width, int height)
 {
     uint64_t bit_rate = width * height * BEST_BIT_RATE_PER_PIXEL;
     MainChannelClient *mcc;
     int is_low_bandwidth = 0;
 
-    if (display_is_connected(worker)) {
-        mcc = red_client_get_main(worker->display_channel->common.base.rcc->client);
+    if (dcc) {
+        mcc = red_client_get_main(dcc->common.base.client);
         is_low_bandwidth = main_channel_client_is_low_bandwidth(mcc);
     }
 
@@ -2413,9 +2471,10 @@ static int get_bit_rate(RedWorker *worker,
     return bit_rate;
 }
 
-static void red_display_create_stream(DisplayChannel *display, Stream *stream)
+static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
 {
-    StreamAgent *agent = &display->stream_agents[stream - display->common.worker->streams_buf];
+    StreamAgent *agent = &dcc->stream_agents[stream - dcc->common.worker->streams_buf];
+
     stream->refs++;
     ASSERT(region_is_empty(&agent->vis_region));
     if (stream->current) {
@@ -2426,12 +2485,15 @@ static void red_display_create_stream(DisplayChannel *display, Stream *stream)
     }
     agent->drops = 0;
     agent->fps = MAX_FPS;
-    reset_rate(display->common.worker, agent);
-    red_channel_client_pipe_add(display->common.base.rcc, &agent->create_item);
+    reset_rate(dcc, agent);
+    red_channel_client_pipe_add(&dcc->common.base, &agent->create_item);
 }
 
+/* TODO: we create the stream even if dcc is NULL, i.e. no client - or
+ * maybe we can't reach this function in that case? question: do we want to? */
 static void red_create_stream(RedWorker *worker, Drawable *drawable)
 {
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
     Stream *stream;
     SpiceRect* src_rect;
     int stream_width;
@@ -2457,48 +2519,49 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
     stream->height = src_rect->bottom - src_rect->top;
     stream->dest_area = drawable->red_drawable->bbox;
     stream->refs = 1;
-    stream->bit_rate = get_bit_rate(worker, stream_width, stream_height);
+    stream->bit_rate = get_bit_rate(dcc, stream_width, stream_height);
     SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
     stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
     drawable->stream = stream;
 
-    if (worker->display_channel) {
-        red_display_create_stream(worker->display_channel, stream);
+    if (dcc) {
+        red_display_create_stream(dcc, stream);
     }
 
     return;
 }
 
-static void red_disply_start_streams(DisplayChannel *display_channel)
+static void red_disply_start_streams(DisplayChannelClient *dcc)
 {
-    Ring *ring = &display_channel->common.worker->streams;
+    Ring *ring = &dcc->common.worker->streams;
     RingItem *item = ring;
 
     while ((item = ring_next(ring, item))) {
         Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
-        red_display_create_stream(display_channel, stream);
+        red_display_create_stream(dcc, stream);
     }
 }
 
-static void red_display_init_streams(DisplayChannel *display)
+static void red_display_client_init_streams(DisplayChannelClient *dcc)
 {
     int i;
+    RedWorker *worker = dcc->common.worker;
+    RedChannel *channel = dcc->common.base.channel;
+
     for (i = 0; i < NUM_STREAMS; i++) {
-        StreamAgent *agent = &display->stream_agents[i];
-        agent->stream = &display->common.worker->streams_buf[i];
+        StreamAgent *agent = &dcc->stream_agents[i];
+        agent->stream = &worker->streams_buf[i];
         region_init(&agent->vis_region);
-        red_channel_pipe_item_init(&display->common.base,
-                    &agent->create_item, PIPE_ITEM_TYPE_STREAM_CREATE);
-        red_channel_pipe_item_init(&display->common.base,
-                    &agent->destroy_item, PIPE_ITEM_TYPE_STREAM_DESTROY);
+        red_channel_pipe_item_init(channel, &agent->create_item, PIPE_ITEM_TYPE_STREAM_CREATE);
+        red_channel_pipe_item_init(channel, &agent->destroy_item, PIPE_ITEM_TYPE_STREAM_DESTROY);
     }
 }
 
-static void red_display_destroy_streams(DisplayChannel *display)
+static void red_display_destroy_streams(DisplayChannelClient *dcc)
 {
     int i;
     for (i = 0; i < NUM_STREAMS; i++) {
-        StreamAgent *agent = &display->stream_agents[i];
+        StreamAgent *agent = &dcc->stream_agents[i];
         region_destroy(&agent->vis_region);
     }
 }
@@ -2566,12 +2629,12 @@ static inline int red_is_next_stream_frame(RedWorker *worker, const Drawable *ca
                                       prev->stream);
 }
 
-static void reset_rate(RedWorker *worker, StreamAgent *stream_agent)
+static void reset_rate(DisplayChannelClient *dcc, StreamAgent *stream_agent)
 {
     Stream *stream = stream_agent->stream;
     int rate;
 
-    rate = get_bit_rate(worker, stream->width, stream->height);
+    rate = get_bit_rate(dcc, stream->width, stream->height);
     if (rate == stream->bit_rate) {
         return;
     }
@@ -2592,14 +2655,18 @@ static int display_channel_is_low_bandwidth(DisplayChannel *display_channel)
 
 static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
 {
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    int index;
+    StreamAgent *agent;
+
     ASSERT(stream->current);
 
-    if (!display_is_connected(worker) || !display_channel_is_low_bandwidth(worker->display_channel)) {
+    if (!dcc || !display_channel_is_low_bandwidth(worker->display_channel)) {
         return;
     }
 
-    int index = stream - worker->streams_buf;
-    StreamAgent *agent = &worker->display_channel->stream_agents[index];
+    index = stream - worker->streams_buf;
+    agent = &dcc->stream_agents[index];
 
     if (pipe_item_is_linked(&stream->current->pipe_item)) {
         ++agent->drops;
@@ -2658,10 +2725,11 @@ static inline int red_is_stream_start(Drawable *drawable)
 }
 
 // returns whether a stream was created
-static int red_stream_add_frame(RedWorker* worker, Drawable *frame_drawable,
-                                 int frames_count,
-                                 int gradual_frames_count,
-                                 int last_gradual_frame)
+static int red_stream_add_frame(RedWorker *worker,
+                                Drawable *frame_drawable,
+                                int frames_count,
+                                int gradual_frames_count,
+                                int last_gradual_frame)
 {
     red_update_copy_graduality(worker, frame_drawable);
     frame_drawable->frames_count = frames_count + 1;
@@ -2730,6 +2798,7 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
     DrawItem *other_draw_item;
     Drawable *drawable;
     Drawable *other_drawable;
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
 
     if (other->type != TREE_ITEM_TYPE_DRAWABLE) {
         return FALSE;
@@ -2750,11 +2819,11 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
         other_drawable->refs++;
         current_remove_drawable(worker, other_drawable);
         if (add_after) {
-            red_pipe_add_drawable_after(worker, drawable, other_drawable);
+            red_pipe_add_drawable_after(dcc, drawable, other_drawable);
         } else {
-            red_pipe_add_drawable(worker, drawable);
+            red_pipe_add_drawable(dcc, drawable);
         }
-        red_pipe_remove_drawable(worker, other_drawable);
+        red_pipe_remove_drawable(dcc, other_drawable);
         release_drawable(worker, other_drawable);
         return TRUE;
     }
@@ -2765,9 +2834,9 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
             other_drawable->refs++;
             current_remove_drawable(worker, other_drawable);
             if (!ring_item_is_linked(&other_drawable->pipe_item.link)) {
-                red_pipe_add_drawable(worker, drawable);
+                red_pipe_add_drawable(dcc, drawable);
             } else {
-                red_pipe_remove_drawable(worker, other_drawable);
+                red_pipe_remove_drawable(dcc, other_drawable);
             }
             release_drawable(worker, other_drawable);
             return TRUE;
@@ -2777,7 +2846,7 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
         if (is_same_geometry(worker, drawable, other_drawable)) {
             __current_add_drawable(worker, drawable, &other->siblings_link);
             remove_drawable(worker, other_drawable);
-            red_pipe_add_drawable(worker, drawable);
+            red_pipe_add_drawable(dcc, drawable);
             return TRUE;
         }
         break;
@@ -3015,6 +3084,7 @@ static inline int red_current_add_with_shadow(RedWorker *worker, Ring *ring, Dra
     // item and his shadow must initially be placed in the same container.
     // for now putting them on root.
 
+    // only primary surface streams are supported
     if (is_primary_surface(worker, item->surface_id)) {
         red_detach_streams_behind(worker, &shadow->base.rgn);
     }
@@ -3248,12 +3318,13 @@ static void free_one_drawable(RedWorker *worker, int force_glz_free)
     RingItem *ring_item = ring_get_tail(&worker->current_list);
     Drawable *drawable;
     Container *container;
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
 
     ASSERT(ring_item);
     drawable = SPICE_CONTAINEROF(ring_item, Drawable, list_link);
     if (drawable->red_glz_drawable && force_glz_free) {
         ASSERT(worker->display_channel);
-        red_display_free_glz_drawable(worker->display_channel, drawable->red_glz_drawable);
+        red_display_free_glz_drawable(dcc, drawable->red_glz_drawable);
     }
     red_draw_drawable(worker, drawable);
     container = drawable->tree_item.base.container;
@@ -3380,6 +3451,7 @@ static inline void red_process_drawable(RedWorker *worker, RedDrawable *drawable
 {
     int surface_id;
     Drawable *item = get_drawable(worker, drawable->effect, drawable, group_id);
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
 
     ASSERT(item);
 
@@ -3437,7 +3509,7 @@ static inline void red_process_drawable(RedWorker *worker, RedDrawable *drawable
         if (item->tree_item.effect != QXL_EFFECT_OPAQUE) {
             worker->transparent_count++;
         }
-        red_pipe_add_drawable(worker, item);
+        red_pipe_add_drawable(dcc, item);
 #ifdef DRAW_ALL
         red_draw_qxl_drawable(worker, item);
 #endif
@@ -3486,7 +3558,7 @@ static inline void red_process_surface(RedWorker *worker, RedSurfaceCmd *surface
            otherwise "current" will hold items that other drawables may depend on, and then
            red_current_clear will remove them from the pipe. */
         red_current_clear(worker, surface_id);
-        red_clear_surface_drawables_from_pipe(worker, surface_id, FALSE);
+        red_clear_surface_drawables_from_pipe(WORKER_TO_DCC(worker), surface_id, FALSE);
         red_destroy_surface(worker, surface_id);
         break;
     default:
@@ -4107,7 +4179,6 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac
 }
 
 #endif
-
 #endif
 
 static inline void free_cursor_item(RedWorker *worker, CursorItem *item);
@@ -4366,20 +4437,22 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
 static void red_free_some(RedWorker *worker)
 {
     int n = 0;
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    GlzSharedDictionary *glz_dict = dcc ? dcc->glz_dict : NULL;
 
-    if (worker->display_channel && worker->display_channel->glz_dict) {
+    if (glz_dict) {
         // encoding using the dictionary is prevented since the following operations might
         // change the dictionary
-        pthread_rwlock_wrlock(&worker->display_channel->glz_dict->encode_lock);
-        n = red_display_free_some_independent_glz_drawables(worker->display_channel);
+        pthread_rwlock_wrlock(&glz_dict->encode_lock);
+        n = red_display_free_some_independent_glz_drawables(dcc);
     }
 
     while (!ring_is_empty(&worker->current_list) && n++ < RED_RELEASE_BUNCH_SIZE) {
         free_one_drawable(worker, TRUE);
     }
 
-    if (worker->display_channel && worker->display_channel->glz_dict) {
-        pthread_rwlock_unlock(&worker->display_channel->glz_dict->encode_lock);
+    if (glz_dict) {
+        pthread_rwlock_unlock(&glz_dict->encode_lock);
     }
 }
 
@@ -4481,7 +4554,7 @@ typedef struct {
     uint32_t size;
 } AddBufInfo;
 
-static void marshaller_add_compressed(RedWorker *worker, SpiceMarshaller *m,
+static void marshaller_add_compressed(SpiceMarshaller *m,
                                       RedCompressBuf *comp_buf, size_t size)
 {
     size_t max = size;
@@ -4529,7 +4602,7 @@ static void fill_base(SpiceMarshaller *base_marshaller, Drawable *drawable)
     spice_marshall_DisplayBase(base_marshaller, &base);
 }
 
-static inline void fill_palette(DisplayChannel *display_channel,
+static inline void fill_palette(DisplayChannelClient *dcc,
                                 SpicePalette *palette,
                                 uint8_t *flags)
 {
@@ -4537,43 +4610,45 @@ static inline void fill_palette(DisplayChannel *display_channel,
         return;
     }
     if (palette->unique) {
-        if (red_palette_cache_find(display_channel, palette->unique)) {
+        if (red_palette_cache_find(dcc, palette->unique)) {
             *flags |= SPICE_BITMAP_FLAGS_PAL_FROM_CACHE;
             return;
         }
-        if (red_palette_cache_add(display_channel->common.base.rcc, palette->unique, 1)) {
+        if (red_palette_cache_add(dcc, palette->unique, 1)) {
             *flags |= SPICE_BITMAP_FLAGS_PAL_CACHE_ME;
         }
     }
 }
 
-static inline RedCompressBuf *red_display_alloc_compress_buf(DisplayChannel *display_channel)
+static inline RedCompressBuf *red_display_alloc_compress_buf(DisplayChannelClient *dcc)
 {
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
     RedCompressBuf *ret;
 
-    if (display_channel->send_data.free_compress_bufs) {
-        ret = display_channel->send_data.free_compress_bufs;
-        display_channel->send_data.free_compress_bufs = ret->next;
+    if (display_channel->free_compress_bufs) {
+        ret = display_channel->free_compress_bufs;
+        display_channel->free_compress_bufs = ret->next;
     } else {
         ret = spice_new(RedCompressBuf, 1);
     }
 
-    ret->next = display_channel->send_data.used_compress_bufs;
-    display_channel->send_data.used_compress_bufs = ret;
+    ret->next = dcc->send_data.used_compress_bufs;
+    dcc->send_data.used_compress_bufs = ret;
     return ret;
 }
 
-static inline void __red_display_free_compress_buf(DisplayChannel *display_channel,
+static inline void __red_display_free_compress_buf(DisplayChannel *dc,
                                                    RedCompressBuf *buf)
 {
-    buf->next = display_channel->send_data.free_compress_bufs;
-    display_channel->send_data.free_compress_bufs = buf;
+    buf->next = dc->free_compress_bufs;
+    dc->free_compress_bufs = buf;
 }
 
-static void red_display_free_compress_buf(DisplayChannel *display_channel,
+static void red_display_free_compress_buf(DisplayChannelClient *dcc,
                                           RedCompressBuf *buf)
 {
-    RedCompressBuf **curr_used = &display_channel->send_data.used_compress_bufs;
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
+    RedCompressBuf **curr_used = &dcc->send_data.used_compress_bufs;
 
     for (;;) {
         ASSERT(*curr_used);
@@ -4586,12 +4661,22 @@ static void red_display_free_compress_buf(DisplayChannel *display_channel,
     __red_display_free_compress_buf(display_channel, buf);
 }
 
-static void red_display_reset_compress_buf(DisplayChannel *display_channel)
+static void red_display_reset_compress_buf(DisplayChannelClient *dcc)
+{
+    while (dcc->send_data.used_compress_bufs) {
+        RedCompressBuf *buf = dcc->send_data.used_compress_bufs;
+        dcc->send_data.used_compress_bufs = buf->next;
+        __red_display_free_compress_buf(DCC_TO_DC(dcc), buf);
+    }
+}
+
+static void red_display_destroy_compress_bufs(DisplayChannel *display_channel)
 {
-    while (display_channel->send_data.used_compress_bufs) {
-        RedCompressBuf *buf = display_channel->send_data.used_compress_bufs;
-        display_channel->send_data.used_compress_bufs = buf->next;
-        __red_display_free_compress_buf(display_channel, buf);
+    ASSERT(!red_channel_is_connected(&display_channel->common.base));
+    while (display_channel->free_compress_bufs) {
+        RedCompressBuf *buf = display_channel->free_compress_bufs;
+        display_channel->free_compress_bufs = buf->next;
+        free(buf);
     }
 }
 
@@ -4601,7 +4686,7 @@ static void red_display_reset_compress_buf(DisplayChannel *display_channel)
 
 /* if already exists, returns it. Otherwise allocates and adds it (1) to the ring tail
    in the channel (2) to the Drawable*/
-static RedGlzDrawable *red_display_get_glz_drawable(DisplayChannel *channel, Drawable *drawable)
+static RedGlzDrawable *red_display_get_glz_drawable(DisplayChannelClient *dcc, Drawable *drawable)
 {
     RedGlzDrawable *ret;
 
@@ -4611,7 +4696,7 @@ static RedGlzDrawable *red_display_get_glz_drawable(DisplayChannel *channel, Dra
 
     ret = spice_new(RedGlzDrawable, 1);
 
-    ret->display_channel = channel;
+    ret->dcc = dcc;
     ret->red_drawable = drawable->red_drawable;
     ret->drawable = drawable;
     ret->group_id = drawable->group_id;
@@ -4620,7 +4705,7 @@ static RedGlzDrawable *red_display_get_glz_drawable(DisplayChannel *channel, Dra
     ring_init(&ret->instances);
 
     ring_item_init(&ret->link);
-    ring_add_before(&ret->link, &channel->glz_drawables);
+    ring_add_before(&ret->link, &dcc->glz_drawables);
     drawable->red_glz_drawable = ret;
 
     return ret;
@@ -4649,9 +4734,11 @@ static GlzDrawableInstanceItem *red_display_add_glz_drawable_instance(RedGlzDraw
    it is not used by Drawable).
    NOTE - 1) can be called only by the display channel that created the drawable
           2) it is assumed that the instance was already removed from the dictionary*/
-static void red_display_free_glz_drawable_instance(DisplayChannel *channel,
+static void red_display_free_glz_drawable_instance(DisplayChannelClient *dcc,
                                                    GlzDrawableInstanceItem *glz_drawable_instance)
 {
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
+    RedWorker *worker = display_channel->common.worker;
     RedGlzDrawable *glz_drawable;
 
     ASSERT(glz_drawable_instance);
@@ -4659,7 +4746,7 @@ static void red_display_free_glz_drawable_instance(DisplayChannel *channel,
 
     glz_drawable = glz_drawable_instance->red_glz_drawable;
 
-    ASSERT(glz_drawable->display_channel == channel);
+    ASSERT(glz_drawable->dcc == dcc);
     ASSERT(glz_drawable->instances_count);
 
     ring_remove(&glz_drawable_instance->glz_link);
@@ -4678,7 +4765,7 @@ static void red_display_free_glz_drawable_instance(DisplayChannel *channel,
         if (drawable) {
             drawable->red_glz_drawable = NULL;
         } else { // no reference to the qxl drawable left
-            free_red_drawable(channel->common.worker, glz_drawable->red_drawable,
+            free_red_drawable(worker, glz_drawable->red_drawable,
                               glz_drawable->group_id, glz_drawable->self_bitmap);
         }
 
@@ -4689,26 +4776,30 @@ static void red_display_free_glz_drawable_instance(DisplayChannel *channel,
     }
 }
 
-static void red_display_handle_glz_drawables_to_free(DisplayChannel* channel)
+static void red_display_handle_glz_drawables_to_free(DisplayChannelClient* dcc)
 {
     RingItem *ring_link;
-    pthread_mutex_lock(&channel->glz_drawables_inst_to_free_lock);
 
-    while ((ring_link = ring_get_head(&channel->glz_drawables_inst_to_free))) {
+    if (!dcc->glz_dict) {
+        return;
+    }
+    pthread_mutex_lock(&dcc->glz_drawables_inst_to_free_lock);
+    while ((ring_link = ring_get_head(&dcc->glz_drawables_inst_to_free))) {
         GlzDrawableInstanceItem *drawable_instance = SPICE_CONTAINEROF(ring_link,
                                                                  GlzDrawableInstanceItem,
                                                                  free_link);
-        red_display_free_glz_drawable_instance(channel, drawable_instance);
+        red_display_free_glz_drawable_instance(dcc, drawable_instance);
     }
-
-    pthread_mutex_unlock(&channel->glz_drawables_inst_to_free_lock);
+    pthread_mutex_unlock(&dcc->glz_drawables_inst_to_free_lock);
 }
 
-/* releases all the instances of the drawable from the dictionary and the display channel.
-   The release of the last instance will also release the drawable itself and the qxl drawable
-   if possible.
-   NOTE - the caller should prevent encoding using the dictionary during this operation*/
-static void red_display_free_glz_drawable(DisplayChannel *channel, RedGlzDrawable *drawable)
+/*
+ * Releases all the instances of the drawable from the dictionary and the display channel client.
+ * The release of the last instance will also release the drawable itself and the qxl drawable
+ * if possible.
+ * NOTE - the caller should prevent encoding using the dictionary during this operation
+ */
+static void red_display_free_glz_drawable(DisplayChannelClient *dcc, RedGlzDrawable *drawable)
 {
     RingItem *head_instance = ring_get_head(&drawable->instances);
     int cont = (head_instance != NULL);
@@ -4723,11 +4814,11 @@ static void red_display_free_glz_drawable(DisplayChannel *channel, RedGlzDrawabl
                                                         glz_link);
         if (!ring_item_is_linked(&instance->free_link)) {
             // the instance didn't get out from window yet
-            glz_enc_dictionary_remove_image(channel->glz_dict->dict,
+            glz_enc_dictionary_remove_image(dcc->glz_dict->dict,
                                             instance->glz_instance,
-                                            &channel->glz_data.usr);
+                                            &dcc->glz_data.usr);
         }
-        red_display_free_glz_drawable_instance(channel, instance);
+        red_display_free_glz_drawable_instance(dcc, instance);
 
         if (cont) {
             head_instance = ring_get_head(&drawable->instances);
@@ -4737,45 +4828,56 @@ static void red_display_free_glz_drawable(DisplayChannel *channel, RedGlzDrawabl
 
 /* Clear all lz drawables - enforce their removal from the global dictionary.
    NOTE - prevents encoding using the dictionary during the operation*/
-static void red_display_clear_glz_drawables(DisplayChannel *channel)
+static void red_display_client_clear_glz_drawables(DisplayChannelClient *dcc)
 {
     RingItem *ring_link;
+    GlzSharedDictionary *glz_dict = dcc ? dcc->glz_dict : NULL;
 
-    if (!channel || !channel->glz_dict) {
+    if (!glz_dict) {
         return;
     }
 
     // assure no display channel is during global lz encoding
-    pthread_rwlock_wrlock(&channel->glz_dict->encode_lock);
-
-    while ((ring_link = ring_get_head(&channel->glz_drawables))) {
+    pthread_rwlock_wrlock(&glz_dict->encode_lock);
+    while ((ring_link = ring_get_head(&dcc->glz_drawables))) {
         RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
         // no need to lock the to_free list, since we assured no other thread is encoding and
         // thus not other thread access the to_free list of the channel
-        red_display_free_glz_drawable(channel, drawable);
+        red_display_free_glz_drawable(dcc, drawable);
     }
+    pthread_rwlock_unlock(&glz_dict->encode_lock);
+}
+
+static void red_display_clear_glz_drawables(DisplayChannel *display_channel)
+{
+    DisplayChannelClient *dcc = display_channel ?
+        RCC_TO_DCC(display_channel->common.base.rcc) : NULL;
 
-    pthread_rwlock_unlock(&channel->glz_dict->encode_lock);
+    if (!dcc) {
+        return;
+    }
+    red_display_client_clear_glz_drawables(dcc);
 }
 
-/* Remove from the global lz dictionary some glz_drawables that have no reference to
-   Drawable (their qxl drawables are released too).
-   NOTE - the caller should prevent encoding using the dictionary during the operation*/
-static int red_display_free_some_independent_glz_drawables(DisplayChannel *channel)
+/*
+ * Remove from the global lz dictionary some glz_drawables that have no reference to
+ * Drawable (their qxl drawables are released too).
+ * NOTE - the caller should prevent encoding using the dictionary during the operation
+ */
+static int red_display_free_some_independent_glz_drawables(DisplayChannelClient *dcc)
 {
+    RingItem *ring_link;
     int n = 0;
 
-    if (!channel) {
+    if (!dcc) {
         return 0;
     }
-
-    RingItem *ring_link = ring_get_head(&channel->glz_drawables);
-
+    ring_link = ring_get_head(&dcc->glz_drawables);
     while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) {
         RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
-        ring_link = ring_next(&channel->glz_drawables, ring_link);
+        ring_link = ring_next(&dcc->glz_drawables, ring_link);
         if (!glz_drawable->drawable) {
-            red_display_free_glz_drawable(channel, glz_drawable);
+            red_display_free_glz_drawable(dcc, glz_drawable);
             n++;
         }
     }
@@ -4893,7 +4995,7 @@ static inline int encoder_usr_more_space(EncoderData *enc_data, uint32_t **io_pt
 {
     RedCompressBuf *buf;
 
-    if (!(buf = red_display_alloc_compress_buf(enc_data->display_channel))) {
+    if (!(buf = red_display_alloc_compress_buf(enc_data->dcc))) {
         return 0;
     }
     enc_data->bufs_tail->send_next = buf;
@@ -5003,24 +5105,22 @@ static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input)
 
     usr_data->u.compressed_data.next = usr_data->u.compressed_data.next->send_next;
     usr_data->u.compressed_data.size_left -= buf_size;
-    
     return buf_size;
-
 }
 
 static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *image)
 {
     GlzData *lz_data = (GlzData *)usr;
     GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem *)image;
-    DisplayChannel *drawable_channel = glz_drawable_instance->red_glz_drawable->display_channel;
-    DisplayChannel *this_channel = SPICE_CONTAINEROF(lz_data, DisplayChannel, glz_data);
-    if (this_channel == drawable_channel) {
-        red_display_free_glz_drawable_instance(drawable_channel, glz_drawable_instance);
+    DisplayChannelClient *drawable_cc = glz_drawable_instance->red_glz_drawable->dcc;
+    DisplayChannelClient *this_cc = SPICE_CONTAINEROF(lz_data, DisplayChannelClient, glz_data);
+    if (this_cc == drawable_cc) {
+        red_display_free_glz_drawable_instance(drawable_cc, glz_drawable_instance);
     } else {
-        pthread_mutex_lock(&drawable_channel->glz_drawables_inst_to_free_lock);
+        pthread_mutex_lock(&drawable_cc->glz_drawables_inst_to_free_lock);
         ring_add_before(&glz_drawable_instance->free_link,
-                        &drawable_channel->glz_drawables_inst_to_free);
-        pthread_mutex_unlock(&drawable_channel->glz_drawables_inst_to_free_lock);
+                        &drawable_cc->glz_drawables_inst_to_free);
+        pthread_mutex_unlock(&drawable_cc->glz_drawables_inst_to_free_lock);
     }
 }
 
@@ -5058,16 +5158,17 @@ static inline void red_init_lz(RedWorker *worker)
     }
 }
 
-static inline void red_display_init_glz_data(DisplayChannel *display)
+/* TODO: split off to DisplayChannel? avoid just copying those cb pointers */
+static inline void red_display_init_glz_data(DisplayChannelClient *dcc)
 {
-    display->glz_data.usr.error = glz_usr_error;
-    display->glz_data.usr.warn = glz_usr_warn;
-    display->glz_data.usr.info = glz_usr_warn;
-    display->glz_data.usr.malloc = glz_usr_malloc;
-    display->glz_data.usr.free = glz_usr_free;
-    display->glz_data.usr.more_space = glz_usr_more_space;
-    display->glz_data.usr.more_lines = glz_usr_more_lines;
-    display->glz_data.usr.free_image = glz_usr_free_image;
+    dcc->glz_data.usr.error = glz_usr_error;
+    dcc->glz_data.usr.warn = glz_usr_warn;
+    dcc->glz_data.usr.info = glz_usr_warn;
+    dcc->glz_data.usr.malloc = glz_usr_malloc;
+    dcc->glz_data.usr.free = glz_usr_free;
+    dcc->glz_data.usr.more_space = glz_usr_more_space;
+    dcc->glz_data.usr.more_lines = glz_usr_more_lines;
+    dcc->glz_data.usr.free_image = glz_usr_free_image;
 }
 
 static inline void red_init_jpeg(RedWorker *worker)
@@ -5242,16 +5343,17 @@ typedef struct compress_send_data_t {
 } compress_send_data_t;
 
 
-static inline int red_glz_compress_image(DisplayChannel *display_channel,
+static inline int red_glz_compress_image(DisplayChannelClient *dcc,
                                          SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
                                          compress_send_data_t* o_comp_data)
 {
-    RedWorker *worker = (RedWorker *)display_channel->common.worker;
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
+    RedWorker *worker = display_channel->common.worker;
 #ifdef COMPRESS_STAT
     stat_time_t start_time = stat_now();
 #endif
     ASSERT(BITMAP_FMT_IS_RGB[src->format]);
-    GlzData *glz_data = &display_channel->glz_data;
+    GlzData *glz_data = &dcc->glz_data;
     ZlibData *zlib_data;
     LzImageType type = MAP_BITMAP_FMT_TO_LZ_IMAGE_TYPE[src->format];
     RedGlzDrawable *glz_drawable;
@@ -5259,7 +5361,7 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
     int glz_size;
     int zlib_size;
 
-    glz_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel);
+    glz_data->data.bufs_tail = red_display_alloc_compress_buf(dcc);
     glz_data->data.bufs_head = glz_data->data.bufs_tail;
 
     if (!glz_data->data.bufs_head) {
@@ -5267,9 +5369,9 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
     }
 
     glz_data->data.bufs_head->send_next = NULL;
-    glz_data->data.display_channel = display_channel;
+    glz_data->data.dcc = dcc;
 
-    glz_drawable = red_display_get_glz_drawable(display_channel, drawable);
+    glz_drawable = red_display_get_glz_drawable(dcc, drawable);
     glz_drawable_instance = red_display_add_glz_drawable_instance(glz_drawable);
 
     glz_data->data.u.lines_data.chunks = src->data;
@@ -5278,7 +5380,7 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
     glz_data->data.u.lines_data.reverse = 0;
     glz_data->usr.more_lines = glz_usr_more_lines;
 
-    glz_size = glz_encode(display_channel->glz, type, src->x, src->y,
+    glz_size = glz_encode(dcc->glz, type, src->x, src->y,
                           (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL, 0,
                           src->stride, (uint8_t*)glz_data->data.bufs_head->buf,
                           sizeof(glz_data->data.bufs_head->buf),
@@ -5295,7 +5397,7 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
 #endif
     zlib_data = &worker->zlib_data;
 
-    zlib_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel);
+    zlib_data->data.bufs_tail = red_display_alloc_compress_buf(dcc);
     zlib_data->data.bufs_head = zlib_data->data.bufs_tail;
 
     if (!zlib_data->data.bufs_head) {
@@ -5304,7 +5406,7 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
     }
 
     zlib_data->data.bufs_head->send_next = NULL;
-    zlib_data->data.display_channel = display_channel;
+    zlib_data->data.dcc = dcc;
 
     zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head;
     zlib_data->data.u.compressed_data.size_left = glz_size;
@@ -5318,7 +5420,7 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
         while (zlib_data->data.bufs_head) {
             RedCompressBuf *buf = zlib_data->data.bufs_head;
             zlib_data->data.bufs_head = buf->send_next;
-            red_display_free_compress_buf(display_channel, buf);
+            red_display_free_compress_buf(dcc, buf);
         }
         goto glz;
     }
@@ -5342,10 +5444,11 @@ glz:
     return TRUE;
 }
 
-static inline int red_lz_compress_image(DisplayChannel *display_channel,
+static inline int red_lz_compress_image(DisplayChannelClient *dcc,
                                         SpiceImage *dest, SpiceBitmap *src,
                                         compress_send_data_t* o_comp_data, uint32_t group_id)
 {
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
     RedWorker *worker = display_channel->common.worker;
     LzData *lz_data = &worker->lz_data;
     LzContext *lz = worker->lz;
@@ -5356,7 +5459,7 @@ static inline int red_lz_compress_image(DisplayChannel *display_channel,
     stat_time_t start_time = stat_now();
 #endif
 
-    lz_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel);
+    lz_data->data.bufs_tail = red_display_alloc_compress_buf(dcc);
     lz_data->data.bufs_head = lz_data->data.bufs_tail;
 
     if (!lz_data->data.bufs_head) {
@@ -5364,13 +5467,13 @@ static inline int red_lz_compress_image(DisplayChannel *display_channel,
     }
 
     lz_data->data.bufs_head->send_next = NULL;
-    lz_data->data.display_channel = display_channel;
+    lz_data->data.dcc = dcc;
 
     if (setjmp(lz_data->data.jmp_env)) {
         while (lz_data->data.bufs_head) {
             RedCompressBuf *buf = lz_data->data.bufs_head;
             lz_data->data.bufs_head = buf->send_next;
-            red_display_free_compress_buf(display_channel, buf);
+            red_display_free_compress_buf(dcc, buf);
         }
         return FALSE;
     }
@@ -5408,7 +5511,7 @@ static inline int red_lz_compress_image(DisplayChannel *display_channel,
         o_comp_data->comp_buf = lz_data->data.bufs_head;
         o_comp_data->comp_buf_size = size;
 
-        fill_palette(display_channel, dest->u.lz_plt.palette, &(dest->u.lz_plt.flags));
+        fill_palette(dcc, dest->u.lz_plt.palette, &(dest->u.lz_plt.flags));
         o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
     }
 
@@ -5417,10 +5520,11 @@ static inline int red_lz_compress_image(DisplayChannel *display_channel,
     return TRUE;
 }
 
-static int red_jpeg_compress_image(DisplayChannel *display_channel, SpiceImage *dest,
+static int red_jpeg_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
                                    SpiceBitmap *src, compress_send_data_t* o_comp_data,
                                    uint32_t group_id)
 {
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
     RedWorker *worker = display_channel->common.worker;
     JpegData *jpeg_data = &worker->jpeg_data;
     LzData *lz_data = &worker->lz_data;
@@ -5456,7 +5560,7 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, SpiceImage *
         return FALSE;
     }
 
-    jpeg_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel);
+    jpeg_data->data.bufs_tail = red_display_alloc_compress_buf(dcc);
     jpeg_data->data.bufs_head = jpeg_data->data.bufs_tail;
 
     if (!jpeg_data->data.bufs_head) {
@@ -5465,13 +5569,13 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, SpiceImage *
     }
 
     jpeg_data->data.bufs_head->send_next = NULL;
-    jpeg_data->data.display_channel = display_channel;
+    jpeg_data->data.dcc = dcc;
 
     if (setjmp(jpeg_data->data.jmp_env)) {
         while (jpeg_data->data.bufs_head) {
             RedCompressBuf *buf = jpeg_data->data.bufs_head;
             jpeg_data->data.bufs_head = buf->send_next;
-            red_display_free_compress_buf(display_channel, buf);
+            red_display_free_compress_buf(dcc, buf);
         }
         return FALSE;
     }
@@ -5522,7 +5626,7 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, SpiceImage *
      comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled;
      lz_out_start_byte = ((uint8_t *)lz_data->data.bufs_head->buf) + comp_head_filled;
 
-     lz_data->data.display_channel = display_channel;
+     lz_data->data.dcc = dcc;
 
      lz_data->data.u.lines_data.chunks = src->data;
      lz_data->data.u.lines_data.stride = src->stride;
@@ -5558,10 +5662,11 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, SpiceImage *
     return TRUE;
 }
 
-static inline int red_quic_compress_image(DisplayChannel *display_channel, SpiceImage *dest,
+static inline int red_quic_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
                                           SpiceBitmap *src, compress_send_data_t* o_comp_data,
                                           uint32_t group_id)
 {
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
     RedWorker *worker = display_channel->common.worker;
     QuicData *quic_data = &worker->quic_data;
     QuicContext *quic = worker->quic;
@@ -5589,7 +5694,7 @@ static inline int red_quic_compress_image(DisplayChannel *display_channel, Spice
         return FALSE;
     }
 
-    quic_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel);
+    quic_data->data.bufs_tail = red_display_alloc_compress_buf(dcc);
     quic_data->data.bufs_head = quic_data->data.bufs_tail;
 
     if (!quic_data->data.bufs_head) {
@@ -5597,13 +5702,13 @@ static inline int red_quic_compress_image(DisplayChannel *display_channel, Spice
     }
 
     quic_data->data.bufs_head->send_next = NULL;
-    quic_data->data.display_channel = display_channel;
+    quic_data->data.dcc = dcc;
 
     if (setjmp(quic_data->data.jmp_env)) {
         while (quic_data->data.bufs_head) {
             RedCompressBuf *buf = quic_data->data.bufs_head;
             quic_data->data.bufs_head = buf->send_next;
-            red_display_free_compress_buf(display_channel, buf);
+            red_display_free_compress_buf(dcc, buf);
         }
         return FALSE;
     }
@@ -5646,11 +5751,12 @@ static inline int red_quic_compress_image(DisplayChannel *display_channel, Spice
 
 #define MIN_SIZE_TO_COMPRESS 54
 #define MIN_DIMENSION_TO_QUIC 3
-static inline int red_compress_image(DisplayChannel *display_channel,
+static inline int red_compress_image(DisplayChannelClient *dcc,
                                      SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
                                      int can_lossy,
                                      compress_send_data_t* o_comp_data)
 {
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
     spice_image_compression_t image_compression =
         display_channel->common.worker->image_compression;
     int quic_compress = FALSE;
@@ -5707,11 +5813,11 @@ static inline int red_compress_image(DisplayChannel *display_channel,
             (image_compression == SPICE_IMAGE_COMPRESS_AUTO_GLZ))) {
             // if we use lz for alpha, the stride can't be extra 
             if (src->format != SPICE_BITMAP_FMT_RGBA || !_stride_is_extra(src)) {
-                return red_jpeg_compress_image(display_channel, dest,
+                return red_jpeg_compress_image(dcc, dest,
                                                src, o_comp_data, drawable->group_id);
             }
         }
-        return red_quic_compress_image(display_channel, dest,
+        return red_quic_compress_image(dcc, dest,
                                        src, o_comp_data, drawable->group_id);
     } else {
         int glz;
@@ -5720,7 +5826,7 @@ static inline int red_compress_image(DisplayChannel *display_channel,
             (image_compression == SPICE_IMAGE_COMPRESS_GLZ)) {
             glz = BITMAP_FMT_IS_RGB[src->format] && (
                     (src->x * src->y) < glz_enc_dictionary_get_size(
-                        display_channel->glz_dict->dict));
+                        dcc->glz_dict->dict));
         } else if ((image_compression == SPICE_IMAGE_COMPRESS_AUTO_LZ) ||
                    (image_compression == SPICE_IMAGE_COMPRESS_LZ)) {
             glz = FALSE;
@@ -5729,20 +5835,20 @@ static inline int red_compress_image(DisplayChannel *display_channel,
         }
 
         if (glz) {
-            /* using the global dictionary only if it is not freezed */
-            pthread_rwlock_rdlock(&display_channel->glz_dict->encode_lock);
-            if (!display_channel->glz_dict->migrate_freeze) {
-                ret = red_glz_compress_image(display_channel,
+            /* using the global dictionary only if it is not frozen */
+            pthread_rwlock_rdlock(&dcc->glz_dict->encode_lock);
+            if (!dcc->glz_dict->migrate_freeze) {
+                ret = red_glz_compress_image(dcc,
                                              dest, src,
                                              drawable, o_comp_data);
             } else {
                 glz = FALSE;
             }
-            pthread_rwlock_unlock(&display_channel->glz_dict->encode_lock);
+            pthread_rwlock_unlock(&dcc->glz_dict->encode_lock);
         }
 
         if (!glz) {
-            ret = red_lz_compress_image(display_channel, dest, src, o_comp_data,
+            ret = red_lz_compress_image(dcc, dest, src, o_comp_data,
                                         drawable->group_id);
 #ifdef COMPRESS_DEBUG
             red_printf("LZ LOCAL compress");
@@ -5762,13 +5868,14 @@ static inline void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc,
                                                          int is_lossy)
 {
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
 
     if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
         ASSERT(image->descriptor.width * image->descriptor.height > 0);
         if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME)) {
-            if (pixmap_cache_add(display_channel->pixmap_cache, image->descriptor.id,
+            if (pixmap_cache_add(dcc->pixmap_cache, image->descriptor.id,
                                  image->descriptor.width * image->descriptor.height, is_lossy,
-                                 rcc)) {
+                                 dcc)) {
                 io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
                 stat_inc_counter(display_channel->add_to_cache_counter, 1);
             }
@@ -5791,11 +5898,12 @@ typedef enum {
 
 /* if the number of times fill_bits can be called per one qxl_drawable increases -
    MAX_LZ_DRAWABLE_INSTANCES must be increased as well */
-static FillBitsType fill_bits(RedChannelClient *rcc, SpiceMarshaller *m,
+static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
                               SpiceImage *simage, Drawable *drawable, int can_lossy)
 {
+    RedChannelClient *rcc = &dcc->common.base;
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
-    RedWorker *worker = display_channel->common.worker;
+    RedWorker *worker = dcc->common.worker;
     SpiceImage image;
     compress_send_data_t comp_send_data = {0};
     SpiceMarshaller *bitmap_palette_out, *lzplt_palette_out;
@@ -5809,8 +5917,8 @@ static FillBitsType fill_bits(RedChannelClient *rcc, SpiceMarshaller *m,
 
     if ((simage->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
         int lossy_cache_item;
-        if (pixmap_cache_hit(display_channel->pixmap_cache, image.descriptor.id,
-                             &lossy_cache_item, rcc)) {
+        if (pixmap_cache_hit(dcc->pixmap_cache, image.descriptor.id,
+                             &lossy_cache_item, dcc)) {
             if (can_lossy || !lossy_cache_item) {
                 if (!display_channel->enable_jpeg || lossy_cache_item) {
                     image.descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
@@ -5827,7 +5935,7 @@ static FillBitsType fill_bits(RedChannelClient *rcc, SpiceMarshaller *m,
                 stat_inc_counter(display_channel->cache_hits_counter, 1);
                 return FILL_BITS_TYPE_CACHE;
             } else {
-                pixmap_cache_set_lossy(display_channel->pixmap_cache, simage->descriptor.id,
+                pixmap_cache_set_lossy(dcc->pixmap_cache, simage->descriptor.id,
                                        FALSE);
                 image.descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME;
             }
@@ -5863,7 +5971,7 @@ static FillBitsType fill_bits(RedChannelClient *rcc, SpiceMarshaller *m,
         /* Images must be added to the cache only after they are compressed
            in order to prevent starvation in the client between pixmap_cache and
            global dictionary (in cases of multiple monitors) */
-        if (!red_compress_image(display_channel, &image, &simage->u.bitmap,
+        if (!red_compress_image(dcc, &image, &simage->u.bitmap,
                                 drawable, can_lossy, &comp_send_data)) {
             SpicePalette *palette;
 
@@ -5873,7 +5981,7 @@ static FillBitsType fill_bits(RedChannelClient *rcc, SpiceMarshaller *m,
             bitmap->flags = bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
 
             palette = bitmap->palette;
-            fill_palette(display_channel, palette, &bitmap->flags);
+            fill_palette(dcc, palette, &bitmap->flags);
             spice_marshall_Image(m, &image,
                                  &bitmap_palette_out, &lzplt_palette_out);
             ASSERT(lzplt_palette_out == NULL);
@@ -5892,7 +6000,7 @@ static FillBitsType fill_bits(RedChannelClient *rcc, SpiceMarshaller *m,
                                  &bitmap_palette_out, &lzplt_palette_out);
             ASSERT(bitmap_palette_out == NULL);
 
-            marshaller_add_compressed(worker, m, comp_send_data.comp_buf,
+            marshaller_add_compressed(m, comp_send_data.comp_buf,
                                       comp_send_data.comp_buf_size);
 
             if (lzplt_palette_out && comp_send_data.lzplt_palette) {
@@ -5923,16 +6031,17 @@ static void fill_mask(RedChannelClient *rcc, SpiceMarshaller *m,
                       SpiceImage *mask_bitmap, Drawable *drawable)
 {
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
 
     if (mask_bitmap && m) {
         if (display_channel->common.worker->image_compression != SPICE_IMAGE_COMPRESS_OFF) {
             spice_image_compression_t save_img_comp =
                 display_channel->common.worker->image_compression;
             display_channel->common.worker->image_compression = SPICE_IMAGE_COMPRESS_OFF;
-            fill_bits(rcc, m, mask_bitmap, drawable, FALSE);
+            fill_bits(dcc, m, mask_bitmap, drawable, FALSE);
             display_channel->common.worker->image_compression = save_img_comp;
         } else {
-            fill_bits(rcc, m, mask_bitmap, drawable, FALSE);
+            fill_bits(dcc, m, mask_bitmap, drawable, FALSE);
         }
     }
 }
@@ -5948,7 +6057,7 @@ static void fill_attr(SpiceMarshaller *m, SpiceLineAttr *attr, uint32_t group_id
     }
 }
 
-static void fill_cursor(CursorChannel *cursor_channel, SpiceCursor *red_cursor,
+static void fill_cursor(CursorChannelClient *ccc, SpiceCursor *red_cursor,
                         CursorItem *cursor, AddBufInfo *addbuf)
 {
     RedCursorCmd *cursor_cmd;
@@ -5963,11 +6072,11 @@ static void fill_cursor(CursorChannel *cursor_channel, SpiceCursor *red_cursor,
     *red_cursor = cursor_cmd->u.set.shape;
 
     if (red_cursor->header.unique) {
-        if (red_cursor_cache_find(cursor_channel, red_cursor->header.unique)) {
+        if (red_cursor_cache_find(ccc, red_cursor->header.unique)) {
             red_cursor->flags |= SPICE_CURSOR_FLAGS_FROM_CACHE;
             return;
         }
-        if (red_cursor_cache_add(cursor_channel->common.base.rcc, red_cursor->header.unique, 1)) {
+        if (red_cursor_cache_add(ccc, red_cursor->header.unique, 1)) {
             red_cursor->flags |= SPICE_CURSOR_FLAGS_CACHE_ME;
         }
     }
@@ -5979,24 +6088,25 @@ static void fill_cursor(CursorChannel *cursor_channel, SpiceCursor *red_cursor,
     }
 }
 
-static inline void red_display_reset_send_data(DisplayChannel *channel)
+static inline void red_display_reset_send_data(DisplayChannelClient *dcc)
 {
-    red_display_reset_compress_buf(channel);
-    channel->send_data.free_list.res->count = 0;
-    memset(channel->send_data.free_list.sync, 0, sizeof(channel->send_data.free_list.sync));
+    red_display_reset_compress_buf(dcc);
+    dcc->send_data.free_list.res->count = 0;
+    memset(dcc->send_data.free_list.sync, 0, sizeof(dcc->send_data.free_list.sync));
 }
 
 /* set area=NULL for testing the whole surface */
-static int is_surface_area_lossy(DisplayChannel *display_channel, uint32_t surface_id,
+static int is_surface_area_lossy(DisplayChannelClient *dcc, uint32_t surface_id,
                                  const SpiceRect *area, SpiceRect *out_lossy_area)
 {
     RedSurface *surface;
     QRegion *surface_lossy_region;
     QRegion lossy_region;
+    RedWorker *worker = dcc->common.worker;
 
-    validate_surface(display_channel->common.worker, surface_id);
-    surface = &display_channel->common.worker->surfaces[surface_id];
-    surface_lossy_region = &display_channel->surface_client_lossy_region[surface_id];
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
+    surface_lossy_region = &dcc->surface_client_lossy_region[surface_id];
 
     if (!area) {
         if (region_is_empty(surface_lossy_region)) {
@@ -6031,7 +6141,7 @@ static int is_surface_area_lossy(DisplayChannel *display_channel, uint32_t surfa
 static int is_bitmap_lossy(RedChannelClient *rcc, SpiceImage *image, SpiceRect *area,
                            Drawable *drawable, BitmapData *out_data)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
 
     if (image == NULL) {
         // self bitmap
@@ -6043,8 +6153,8 @@ static int is_bitmap_lossy(RedChannelClient *rcc, SpiceImage *image, SpiceRect *
         int is_hit_lossy;
 
         out_data->id = image->descriptor.id;
-        if (pixmap_cache_hit(display_channel->pixmap_cache, image->descriptor.id,
-                             &is_hit_lossy, rcc)) {
+        if (pixmap_cache_hit(dcc->pixmap_cache, image->descriptor.id,
+                             &is_hit_lossy, dcc)) {
             out_data->type = BITMAP_DATA_TYPE_CACHE;
             if (is_hit_lossy) {
                 return TRUE;
@@ -6065,7 +6175,7 @@ static int is_bitmap_lossy(RedChannelClient *rcc, SpiceImage *image, SpiceRect *
     out_data->type = BITMAP_DATA_TYPE_SURFACE;
     out_data->id = image->u.surface.surface_id;
 
-    if (is_surface_area_lossy(display_channel, out_data->id,
+    if (is_surface_area_lossy(dcc, out_data->id,
                               area, &out_data->lossy_rect))
     {
         return TRUE;
@@ -6086,7 +6196,7 @@ static int is_brush_lossy(RedChannelClient *rcc, SpiceBrush *brush,
     }
 }
 
-static void surface_lossy_region_update(RedWorker *worker, DisplayChannel *display_channel,
+static void surface_lossy_region_update(RedWorker *worker, DisplayChannelClient *dcc,
                                         Drawable *item, int has_mask, int lossy)
 {
     QRegion *surface_lossy_region;
@@ -6096,7 +6206,7 @@ static void surface_lossy_region_update(RedWorker *worker, DisplayChannel *displ
         return;
     }
 
-    surface_lossy_region = &display_channel->surface_client_lossy_region[item->surface_id];
+    surface_lossy_region = &dcc->surface_client_lossy_region[item->surface_id];
     drawable = item->red_drawable;
 
     if (drawable->clip.type == SPICE_CLIP_TYPE_RECTS ) {
@@ -6126,8 +6236,8 @@ static void surface_lossy_region_update(RedWorker *worker, DisplayChannel *displ
 
 static inline int drawable_intersects_with_areas(Drawable *drawable, int surface_ids[],
                                                  SpiceRect *surface_areas[],
-                                                 int num_surfaces) 
-{   
+                                                 int num_surfaces)
+{
     int i;
     for (i = 0; i < num_surfaces; i++) {
         if (surface_ids[i] == drawable->red_drawable->surface_id) {
@@ -6143,7 +6253,7 @@ static inline int drawable_depends_on_areas(Drawable *drawable,
                                             int surface_ids[],
                                             SpiceRect surface_areas[],
                                             int num_surfaces)
-{   
+{
     int i;
     RedDrawable *red_drawable;
     int drawable_has_shadow;
@@ -6198,7 +6308,7 @@ static inline int drawable_depends_on_areas(Drawable *drawable,
 
 
 static int pipe_rendered_drawables_intersect_with_areas(RedWorker *worker,
-                                                        DisplayChannel *display_channel,
+                                                        DisplayChannelClient *dcc,
                                                         int surface_ids[],
                                                         SpiceRect *surface_areas[],
                                                         int num_surfaces)
@@ -6207,7 +6317,7 @@ static int pipe_rendered_drawables_intersect_with_areas(RedWorker *worker,
     Ring *pipe;
 
     ASSERT(num_surfaces);
-    pipe = &display_channel->common.base.rcc->pipe;
+    pipe = &dcc->common.base.pipe;
 
     for (pipe_item = (PipeItem *)ring_get_head(pipe);
          pipe_item;
@@ -6218,7 +6328,7 @@ static int pipe_rendered_drawables_intersect_with_areas(RedWorker *worker,
         if (pipe_item->type != PIPE_ITEM_TYPE_DRAW)
             continue;
         drawable = SPICE_CONTAINEROF(pipe_item, Drawable, pipe_item);
-      
+
         if (ring_item_is_linked(&drawable->list_link))
             continue; // item hasn't been rendered
 
@@ -6231,7 +6341,7 @@ static int pipe_rendered_drawables_intersect_with_areas(RedWorker *worker,
 }
 
 static void red_pipe_replace_rendered_drawables_with_images(RedWorker *worker,
-                                                            DisplayChannel *display_channel,
+                                                            DisplayChannelClient *dcc,
                                                             int first_surface_id,
                                                             SpiceRect *first_area)
 {
@@ -6245,7 +6355,7 @@ static void red_pipe_replace_rendered_drawables_with_images(RedWorker *worker,
     resent_areas[0] = *first_area;
     num_resent = 1;
 
-    pipe = &display_channel->common.base.rcc->pipe;
+    pipe = &dcc->common.base.pipe;
 
     // going from the oldest to the newest
     for (pipe_item = (PipeItem *)ring_get_tail(pipe);
@@ -6277,23 +6387,23 @@ static void red_pipe_replace_rendered_drawables_with_images(RedWorker *worker,
         num_resent++;
 
         ASSERT(image);
-        red_pipe_remove_drawable(worker, drawable);
+        red_pipe_remove_drawable(dcc, drawable);
         pipe_item = &image->link;
     }
 }
 
 static void red_add_lossless_drawable_dependencies(RedWorker *worker,
-                                                   DisplayChannel *display_channel,
+                                                   RedChannelClient *rcc,
                                                    Drawable *item,
                                                    int deps_surfaces_ids[],
                                                    SpiceRect *deps_areas[],
                                                    int num_deps)
 {
     RedDrawable *drawable = item->red_drawable;
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     int sync_rendered = FALSE;
     int i;
 
-
     if (!ring_item_is_linked(&item->list_link)) {
         /* drawable was already rendered, we may not be able to retrieve the lossless data
            for the lossy areas */
@@ -6303,7 +6413,7 @@ static void red_add_lossless_drawable_dependencies(RedWorker *worker,
         // that were rendered, affected the areas that need to be resent
         if (!drawable_intersects_with_areas(item, deps_surfaces_ids,
                                             deps_areas, num_deps)) {
-            if (pipe_rendered_drawables_intersect_with_areas(worker, display_channel,
+            if (pipe_rendered_drawables_intersect_with_areas(worker, dcc,
                                                              deps_surfaces_ids,
                                                              deps_areas,
                                                              num_deps)) {
@@ -6315,18 +6425,19 @@ static void red_add_lossless_drawable_dependencies(RedWorker *worker,
     } else {
         sync_rendered = FALSE;
         for (i = 0; i < num_deps; i++) {
-            red_update_area_till(worker, deps_areas[i], deps_surfaces_ids[i], item);
+            red_update_area_till(worker, deps_areas[i],
+                                 deps_surfaces_ids[i], item);
         }
     }
 
     if (!sync_rendered) {
         // pushing the pipe item back to the pipe
-        red_pipe_add_drawable_to_tail(worker, item);
+        red_pipe_add_drawable_to_tail(dcc, item);
         // the surfaces areas will be sent as DRAW_COPY commands, that
         // will be executed before the current drawable
         for (i = 0; i < num_deps; i++) {
             red_add_surface_area_image(worker, deps_surfaces_ids[i], deps_areas[i],
-                                       red_pipe_get_tail(worker), FALSE);
+                                       red_pipe_get_tail(dcc), FALSE);
 
         }
     } else {
@@ -6337,18 +6448,17 @@ static void red_add_lossless_drawable_dependencies(RedWorker *worker,
         drawable_bbox[0] = &drawable->bbox;
 
         // check if the other rendered images in the pipe have updated the drawable bbox
-        if (pipe_rendered_drawables_intersect_with_areas(worker, display_channel,
+        if (pipe_rendered_drawables_intersect_with_areas(worker, dcc,
                                                          drawable_surface_id,
                                                          drawable_bbox,
                                                          1)) {
-            red_pipe_replace_rendered_drawables_with_images(worker, display_channel,
+            red_pipe_replace_rendered_drawables_with_images(worker, dcc,
                                                             drawable->surface_id,
                                                             &drawable->bbox);
         }
 
-
         red_add_surface_area_image(worker, drawable->surface_id, &drawable->bbox,
-                                   red_pipe_get_tail(worker), TRUE);
+                                   red_pipe_get_tail(dcc), TRUE);
     }
 }
 
@@ -6358,6 +6468,7 @@ static void red_marshall_qxl_draw_fill(RedWorker *worker,
                                    Drawable *item)
 {
     RedDrawable *drawable = item->red_drawable;
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *mask_bitmap_out;
     SpiceFill fill;
@@ -6371,7 +6482,7 @@ static void red_marshall_qxl_draw_fill(RedWorker *worker,
                         &mask_bitmap_out);
 
     if (brush_pat_out) {
-        fill_bits(rcc, brush_pat_out, fill.brush.u.pattern.pat, item, FALSE);
+        fill_bits(dcc, brush_pat_out, fill.brush.u.pattern.pat, item, FALSE);
     }
 
     fill_mask(rcc, mask_bitmap_out, fill.mask.bitmap, item);
@@ -6383,7 +6494,7 @@ static void red_lossy_marshall_qxl_draw_fill(RedWorker *worker,
                                          SpiceMarshaller *m,
                                          Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
 
     int dest_allowed_lossy = FALSE;
@@ -6402,7 +6513,7 @@ static void red_lossy_marshall_qxl_draw_fill(RedWorker *worker,
     brush_is_lossy = is_brush_lossy(rcc, &drawable->u.fill.brush, item,
                                     &brush_bitmap_data);
     if (!dest_allowed_lossy) {
-        dest_is_lossy = is_surface_area_lossy(display_channel, item->surface_id, &drawable->bbox,
+        dest_is_lossy = is_surface_area_lossy(dcc, item->surface_id, &drawable->bbox,
                                               &dest_lossy_area);
     }
 
@@ -6412,7 +6523,7 @@ static void red_lossy_marshall_qxl_draw_fill(RedWorker *worker,
 
         red_marshall_qxl_draw_fill(worker, rcc, m, item);
         // either the brush operation is opaque, or the dest is not lossy
-        surface_lossy_region_update(worker, display_channel, item, has_mask, FALSE);
+        surface_lossy_region_update(worker, dcc, item, has_mask, FALSE);
     } else {
         int resend_surface_ids[2];
         SpiceRect *resend_areas[2];
@@ -6430,7 +6541,7 @@ static void red_lossy_marshall_qxl_draw_fill(RedWorker *worker,
             num_resend++;
         }
 
-        red_add_lossless_drawable_dependencies(worker, display_channel, item,
+        red_add_lossless_drawable_dependencies(worker, rcc, item,
                                                resend_surface_ids, resend_areas, num_resend);
     }
 }
@@ -6440,6 +6551,7 @@ static FillBitsType red_marshall_qxl_draw_opaque(RedWorker *worker,
                                              SpiceMarshaller *base_marshaller,
                                              Drawable *item, int src_allowed_lossy)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *src_bitmap_out;
@@ -6456,11 +6568,11 @@ static FillBitsType red_marshall_qxl_draw_opaque(RedWorker *worker,
                           &brush_pat_out,
                           &mask_bitmap_out);
 
-    src_send_type = fill_bits(rcc, src_bitmap_out, opaque.src_bitmap, item,
+    src_send_type = fill_bits(dcc, src_bitmap_out, opaque.src_bitmap, item,
                               src_allowed_lossy);
 
     if (brush_pat_out) {
-        fill_bits(rcc, brush_pat_out, opaque.brush.u.pattern.pat, item, FALSE);
+        fill_bits(dcc, brush_pat_out, opaque.brush.u.pattern.pat, item, FALSE);
     }
     fill_mask(rcc, mask_bitmap_out, opaque.mask.bitmap, item);
 
@@ -6472,7 +6584,7 @@ static void red_lossy_marshall_qxl_draw_opaque(RedWorker *worker,
                                            SpiceMarshaller *m,
                                            Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
 
     int src_allowed_lossy;
@@ -6509,11 +6621,11 @@ static void red_lossy_marshall_qxl_draw_opaque(RedWorker *worker,
             src_is_lossy = FALSE;
         }
 
-        surface_lossy_region_update(worker, display_channel, item, has_mask, src_is_lossy);
+        surface_lossy_region_update(worker, dcc, item, has_mask, src_is_lossy);
     } else {
         int resend_surface_ids[2];
         SpiceRect *resend_areas[2];
-        int num_resend = 0;      
+        int num_resend = 0;
 
         if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) {
             resend_surface_ids[num_resend] = src_bitmap_data.id;
@@ -6527,7 +6639,7 @@ static void red_lossy_marshall_qxl_draw_opaque(RedWorker *worker,
             num_resend++;
         }
 
-        red_add_lossless_drawable_dependencies(worker, display_channel, item,
+        red_add_lossless_drawable_dependencies(worker, rcc, item,
                                                resend_surface_ids, resend_areas, num_resend);
     }
 }
@@ -6538,6 +6650,7 @@ static FillBitsType red_marshall_qxl_draw_copy(RedWorker *worker,
                                            Drawable *item, int src_allowed_lossy)
 {
     RedDrawable *drawable = item->red_drawable;
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     SpiceMarshaller *src_bitmap_out;
     SpiceMarshaller *mask_bitmap_out;
     SpiceCopy copy;
@@ -6551,7 +6664,7 @@ static FillBitsType red_marshall_qxl_draw_copy(RedWorker *worker,
                         &src_bitmap_out,
                         &mask_bitmap_out);
 
-    src_send_type = fill_bits(rcc, src_bitmap_out, copy.src_bitmap, item, src_allowed_lossy);
+    src_send_type = fill_bits(dcc, src_bitmap_out, copy.src_bitmap, item, src_allowed_lossy);
     fill_mask(rcc, mask_bitmap_out, copy.mask.bitmap, item);
 
     return src_send_type;
@@ -6562,7 +6675,7 @@ static void red_lossy_marshall_qxl_draw_copy(RedWorker *worker,
                                          SpiceMarshaller *base_marshaller,
                                          Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int has_mask = !!drawable->u.copy.mask.bitmap;
     int src_is_lossy;
@@ -6580,7 +6693,7 @@ static void red_lossy_marshall_qxl_draw_copy(RedWorker *worker,
         src_is_lossy = FALSE;
     }
 
-    surface_lossy_region_update(worker, display_channel, item, has_mask,
+    surface_lossy_region_update(worker, dcc, item, has_mask,
                                 src_is_lossy);
 }
 
@@ -6589,6 +6702,7 @@ static void red_marshall_qxl_draw_transparent(RedWorker *worker,
                                           SpiceMarshaller *base_marshaller,
                                           Drawable *item)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceTransparent transparent;
@@ -6599,7 +6713,7 @@ static void red_marshall_qxl_draw_transparent(RedWorker *worker,
     spice_marshall_Transparent(base_marshaller,
                                &transparent,
                                &src_bitmap_out);
-    fill_bits(rcc, src_bitmap_out, transparent.src_bitmap, item, FALSE);
+    fill_bits(dcc, src_bitmap_out, transparent.src_bitmap, item, FALSE);
 }
 
 static void red_lossy_marshall_qxl_draw_transparent(RedWorker *worker,
@@ -6607,7 +6721,6 @@ static void red_lossy_marshall_qxl_draw_transparent(RedWorker *worker,
                                                 SpiceMarshaller *base_marshaller,
                                                 Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
     BitmapData src_bitmap_data;
@@ -6625,7 +6738,7 @@ static void red_lossy_marshall_qxl_draw_transparent(RedWorker *worker,
         resend_surface_ids[0] = src_bitmap_data.id;
         resend_areas[0] = &src_bitmap_data.lossy_rect;
 
-        red_add_lossless_drawable_dependencies(worker, display_channel, item,
+        red_add_lossless_drawable_dependencies(worker, rcc, item,
                                                resend_surface_ids, resend_areas, 1);
     }
 }
@@ -6636,6 +6749,7 @@ static FillBitsType red_marshall_qxl_draw_alpha_blend(RedWorker *worker,
                                                   Drawable *item,
                                                   int src_allowed_lossy)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceAlphaBlend alpha_blend;
@@ -6647,7 +6761,7 @@ static FillBitsType red_marshall_qxl_draw_alpha_blend(RedWorker *worker,
     spice_marshall_AlphaBlend(base_marshaller,
                               &alpha_blend,
                               &src_bitmap_out);
-    src_send_type = fill_bits(rcc, src_bitmap_out, alpha_blend.src_bitmap, item, src_allowed_lossy);
+    src_send_type = fill_bits(dcc, src_bitmap_out, alpha_blend.src_bitmap, item, src_allowed_lossy);
 
     return src_send_type;
 }
@@ -6657,7 +6771,7 @@ static void red_lossy_marshall_qxl_draw_alpha_blend(RedWorker *worker,
                                                 SpiceMarshaller *base_marshaller,
                                                 Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
     BitmapData src_bitmap_data;
@@ -6675,7 +6789,7 @@ static void red_lossy_marshall_qxl_draw_alpha_blend(RedWorker *worker,
     }
 
     if (src_is_lossy) {
-        surface_lossy_region_update(worker, display_channel, item, FALSE, src_is_lossy);
+        surface_lossy_region_update(worker, dcc, item, FALSE, src_is_lossy);
     } // else, the area stays lossy/lossless as the destination
 }
 
@@ -6699,7 +6813,7 @@ static void red_lossy_marshall_qxl_copy_bits(RedWorker *worker,
                                          SpiceMarshaller *base_marshaller,
                                          Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceRect src_rect;
     int horz_offset;
@@ -6717,10 +6831,10 @@ static void red_lossy_marshall_qxl_copy_bits(RedWorker *worker,
     src_rect.right = drawable->bbox.right + horz_offset;
     src_rect.bottom = drawable->bbox.bottom + vert_offset;
 
-    src_is_lossy = is_surface_area_lossy(display_channel, item->surface_id,
+    src_is_lossy = is_surface_area_lossy(dcc, item->surface_id,
                                          &src_rect, &src_lossy_area);
 
-    surface_lossy_region_update(worker, display_channel, item, FALSE,
+    surface_lossy_region_update(worker, dcc, item, FALSE,
                                 src_is_lossy);
 }
 
@@ -6729,6 +6843,7 @@ static void red_marshall_qxl_draw_blend(RedWorker *worker,
                                     SpiceMarshaller *base_marshaller,
                                     Drawable *item)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceMarshaller *mask_bitmap_out;
@@ -6742,7 +6857,7 @@ static void red_marshall_qxl_draw_blend(RedWorker *worker,
                          &src_bitmap_out,
                          &mask_bitmap_out);
 
-    fill_bits(rcc, src_bitmap_out, blend.src_bitmap, item, FALSE);
+    fill_bits(dcc, src_bitmap_out, blend.src_bitmap, item, FALSE);
 
     fill_mask(rcc, mask_bitmap_out, blend.mask.bitmap, item);
 }
@@ -6752,7 +6867,7 @@ static void red_lossy_marshall_qxl_draw_blend(RedWorker *worker,
                                           SpiceMarshaller *base_marshaller,
                                           Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
     BitmapData src_bitmap_data;
@@ -6761,7 +6876,7 @@ static void red_lossy_marshall_qxl_draw_blend(RedWorker *worker,
 
     src_is_lossy = is_bitmap_lossy(rcc, drawable->u.blend.src_bitmap,
                                    &drawable->u.blend.src_area, item, &src_bitmap_data);
-    dest_is_lossy = is_surface_area_lossy(display_channel, drawable->surface_id,
+    dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id,
                                           &drawable->bbox, &dest_lossy_area);
 
     if (!dest_is_lossy &&
@@ -6784,7 +6899,7 @@ static void red_lossy_marshall_qxl_draw_blend(RedWorker *worker,
             num_resend++;
         }
 
-        red_add_lossless_drawable_dependencies(worker, display_channel, item,
+        red_add_lossless_drawable_dependencies(worker, rcc, item,
                                                resend_surface_ids, resend_areas, num_resend);
     }
 }
@@ -6814,13 +6929,13 @@ static void red_lossy_marshall_qxl_draw_blackness(RedWorker *worker,
                                               SpiceMarshaller *base_marshaller,
                                               Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int has_mask = !!drawable->u.blackness.mask.bitmap;
 
     red_marshall_qxl_draw_blackness(worker, rcc, base_marshaller, item);
 
-    surface_lossy_region_update(worker, display_channel, item, has_mask, FALSE);
+    surface_lossy_region_update(worker, dcc, item, has_mask, FALSE);
 }
 
 static void red_marshall_qxl_draw_whiteness(RedWorker *worker,
@@ -6848,13 +6963,13 @@ static void red_lossy_marshall_qxl_draw_whiteness(RedWorker *worker,
                                               SpiceMarshaller *base_marshaller,
                                               Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int has_mask = !!drawable->u.whiteness.mask.bitmap;
 
     red_marshall_qxl_draw_whiteness(worker, rcc, base_marshaller, item);
 
-    surface_lossy_region_update(worker, display_channel, item, has_mask, FALSE);
+    surface_lossy_region_update(worker, dcc, item, has_mask, FALSE);
 }
 
 static void red_marshall_qxl_draw_inverse(RedWorker *worker,
@@ -6890,6 +7005,7 @@ static void red_marshall_qxl_draw_rop3(RedWorker *worker,
                                    SpiceMarshaller *base_marshaller,
                                    Drawable *item)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceRop3 rop3;
     SpiceMarshaller *src_bitmap_out;
@@ -6905,10 +7021,10 @@ static void red_marshall_qxl_draw_rop3(RedWorker *worker,
                         &brush_pat_out,
                         &mask_bitmap_out);
 
-    fill_bits(rcc, src_bitmap_out, rop3.src_bitmap, item, FALSE);
+    fill_bits(dcc, src_bitmap_out, rop3.src_bitmap, item, FALSE);
 
     if (brush_pat_out) {
-        fill_bits(rcc, brush_pat_out, rop3.brush.u.pattern.pat, item, FALSE);
+        fill_bits(dcc, brush_pat_out, rop3.brush.u.pattern.pat, item, FALSE);
     }
     fill_mask(rcc, mask_bitmap_out, rop3.mask.bitmap, item);
 }
@@ -6918,7 +7034,7 @@ static void red_lossy_marshall_qxl_draw_rop3(RedWorker *worker,
                                          SpiceMarshaller *base_marshaller,
                                          Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
     BitmapData src_bitmap_data;
@@ -6931,7 +7047,7 @@ static void red_lossy_marshall_qxl_draw_rop3(RedWorker *worker,
                                    &drawable->u.rop3.src_area, item, &src_bitmap_data);
     brush_is_lossy = is_brush_lossy(rcc, &drawable->u.rop3.brush, item,
                                     &brush_bitmap_data);
-    dest_is_lossy = is_surface_area_lossy(display_channel, drawable->surface_id,
+    dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id,
                                           &drawable->bbox, &dest_lossy_area);
 
     if ((!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) &&
@@ -6939,11 +7055,11 @@ static void red_lossy_marshall_qxl_draw_rop3(RedWorker *worker,
         !dest_is_lossy) {
         int has_mask = !!drawable->u.rop3.mask.bitmap;
         red_marshall_qxl_draw_rop3(worker, rcc, base_marshaller, item);
-        surface_lossy_region_update(worker, display_channel, item, has_mask, FALSE);
+        surface_lossy_region_update(worker, dcc, item, has_mask, FALSE);
     } else {
         int resend_surface_ids[3];
         SpiceRect *resend_areas[3];
-        int num_resend = 0; 
+        int num_resend = 0;
 
         if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) {
             resend_surface_ids[num_resend] = src_bitmap_data.id;
@@ -6963,7 +7079,7 @@ static void red_lossy_marshall_qxl_draw_rop3(RedWorker *worker,
             num_resend++;
         }
 
-        red_add_lossless_drawable_dependencies(worker, display_channel, item,
+        red_add_lossless_drawable_dependencies(worker, rcc, item,
                                                resend_surface_ids, resend_areas, num_resend);
     }
 }
@@ -6973,6 +7089,7 @@ static void red_marshall_qxl_draw_stroke(RedWorker *worker,
                                      SpiceMarshaller *base_marshaller,
                                      Drawable *item)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceStroke stroke;
     SpiceMarshaller *brush_pat_out;
@@ -6988,7 +7105,7 @@ static void red_marshall_qxl_draw_stroke(RedWorker *worker,
 
     fill_attr(style_out, &stroke.attr, item->group_id);
     if (brush_pat_out) {
-        fill_bits(rcc, brush_pat_out, stroke.brush.u.pattern.pat, item, FALSE);
+        fill_bits(dcc, brush_pat_out, stroke.brush.u.pattern.pat, item, FALSE);
     }
 }
 
@@ -6997,7 +7114,7 @@ static void red_lossy_marshall_qxl_draw_stroke(RedWorker *worker,
                                            SpiceMarshaller *base_marshaller,
                                            Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int brush_is_lossy;
     BitmapData brush_bitmap_data;
@@ -7016,7 +7133,7 @@ static void red_lossy_marshall_qxl_draw_stroke(RedWorker *worker,
     if (drawable->u.stroke.brush.type != SPICE_BRUSH_TYPE_SOLID &&
         ((rop & SPICE_ROPD_OP_OR) || (rop & SPICE_ROPD_OP_AND) ||
         (rop & SPICE_ROPD_OP_XOR))) {
-        dest_is_lossy = is_surface_area_lossy(display_channel, drawable->surface_id,
+        dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id,
                                               &drawable->bbox, &dest_lossy_area);
     }
 
@@ -7042,7 +7159,7 @@ static void red_lossy_marshall_qxl_draw_stroke(RedWorker *worker,
             num_resend++;
         }
 
-        red_add_lossless_drawable_dependencies(worker, display_channel, item,
+        red_add_lossless_drawable_dependencies(worker, rcc, item,
                                                resend_surface_ids, resend_areas, num_resend);
     }
 }
@@ -7052,6 +7169,7 @@ static void red_marshall_qxl_draw_text(RedWorker *worker,
                                    SpiceMarshaller *base_marshaller,
                                    Drawable *item)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     SpiceText text;
     SpiceMarshaller *brush_pat_out;
@@ -7066,10 +7184,10 @@ static void red_marshall_qxl_draw_text(RedWorker *worker,
                         &back_brush_pat_out);
 
     if (brush_pat_out) {
-        fill_bits(rcc, brush_pat_out, text.fore_brush.u.pattern.pat, item, FALSE);
+        fill_bits(dcc, brush_pat_out, text.fore_brush.u.pattern.pat, item, FALSE);
     }
     if (back_brush_pat_out) {
-        fill_bits(rcc, back_brush_pat_out, text.back_brush.u.pattern.pat, item, FALSE);
+        fill_bits(dcc, back_brush_pat_out, text.back_brush.u.pattern.pat, item, FALSE);
     }
 }
 
@@ -7078,7 +7196,7 @@ static void red_lossy_marshall_qxl_draw_text(RedWorker *worker,
                                          SpiceMarshaller *base_marshaller,
                                          Drawable *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *drawable = item->red_drawable;
     int fg_is_lossy;
     BitmapData fg_bitmap_data;
@@ -7105,7 +7223,7 @@ static void red_lossy_marshall_qxl_draw_text(RedWorker *worker,
 
     if ((rop & SPICE_ROPD_OP_OR) || (rop & SPICE_ROPD_OP_AND) ||
         (rop & SPICE_ROPD_OP_XOR)) {
-        dest_is_lossy = is_surface_area_lossy(display_channel, drawable->surface_id,
+        dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id,
                                               &drawable->bbox, &dest_lossy_area);
     }
 
@@ -7135,7 +7253,7 @@ static void red_lossy_marshall_qxl_draw_text(RedWorker *worker,
             resend_areas[num_resend] = &dest_lossy_area;
             num_resend++;
         }
-        red_add_lossless_drawable_dependencies(worker, display_channel, item,
+        red_add_lossless_drawable_dependencies(worker, rcc, item,
                                                resend_surface_ids, resend_areas, num_resend);
     }
 }
@@ -7238,10 +7356,10 @@ static inline void red_marshall_qxl_drawable(RedWorker *worker, RedChannelClient
     }
 }
 
-static void display_channel_push_release(DisplayChannel *channel, uint8_t type, uint64_t id,
+static void display_channel_push_release(DisplayChannelClient *dcc, uint8_t type, uint64_t id,
                                          uint64_t* sync_data)
 {
-    FreeList *free_list = &channel->send_data.free_list;
+    FreeList *free_list = &dcc->send_data.free_list;
     int i;
 
     for (i = 0; i < MAX_CACHE_CLIENTS; i++) {
@@ -7266,8 +7384,8 @@ static void display_channel_push_release(DisplayChannel *channel, uint8_t type,
 static inline void display_begin_send_message(RedChannelClient *rcc,
                                               SpiceMarshaller *base_marshaller)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
-    FreeList *free_list = &display_channel->send_data.free_list;
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
+    FreeList *free_list = &dcc->send_data.free_list;
     SpiceDataHeader *header = red_channel_client_get_header(rcc);
 
     if (free_list->res->count) {
@@ -7286,7 +7404,7 @@ static inline void display_begin_send_message(RedChannelClient *rcc,
         spice_marshall_msg_display_inval_list(inval_m, free_list->res);
 
         for (i = 0; i < MAX_CACHE_CLIENTS; i++) {
-            if (i != display_channel->common.id && free_list->sync[i] != 0) {
+            if (i != dcc->common.id && free_list->sync[i] != 0) {
                 free_list->wait.header.wait_list[sync_count].channel_type = SPICE_CHANNEL_DISPLAY;
                 free_list->wait.header.wait_list[sync_count].channel_id = i;
                 free_list->wait.header.wait_list[sync_count++].message_serial = free_list->sync[i];
@@ -7316,29 +7434,6 @@ static inline void display_begin_send_message(RedChannelClient *rcc,
     red_channel_client_begin_send_message(rcc);
 }
 
-static inline RedChannel *red_ref_channel(RedChannel *channel)
-{
-    CommonChannel *common;
-
-    if (!channel) {
-        return NULL;
-    }
-    common = SPICE_CONTAINEROF(channel, CommonChannel, base);
-    ++common->listener.refs;
-    return channel;
-}
-
-static inline void red_unref_channel(RedChannel *channel)
-{
-    CommonChannel *common;
-
-    ASSERT(channel);
-    common = SPICE_CONTAINEROF(channel, CommonChannel, base);
-    if (!--common->listener.refs) {
-        red_channel_destroy(channel);
-    }
-}
-
 static inline uint8_t *red_get_image_line(RedWorker *worker, SpiceChunks *chunks, size_t *offset,
                                           int *chunk_nr, int stride)
 {
@@ -7405,10 +7500,11 @@ static int encode_frame (RedWorker *worker, const SpiceRect *src,
 static inline int red_marshall_stream_data(RedChannelClient *rcc,
                   SpiceMarshaller *base_marshaller, Drawable *drawable)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     Stream *stream = drawable->stream;
     SpiceImage *image;
-    RedWorker* worker;
+    RedWorker *worker = dcc->common.worker;
     int n;
 
     ASSERT(stream);
@@ -7421,7 +7517,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
         return FALSE;
     }
 
-    StreamAgent *agent = &display_channel->stream_agents[stream - worker->streams_buf];
+    StreamAgent *agent = &dcc->stream_agents[stream - worker->streams_buf];
     uint64_t time_now = red_now();
     size_t outbuf_size;
     if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) {
@@ -7429,9 +7525,9 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
         return TRUE;
     }
 
-    outbuf_size = display_channel->send_data.stream_outbuf_size;
+    outbuf_size = dcc->send_data.stream_outbuf_size;
     if (!mjpeg_encoder_start_frame(stream->mjpeg_encoder, image->u.bitmap.format,
-                                   &display_channel->send_data.stream_outbuf,
+                                   &dcc->send_data.stream_outbuf,
                                    &outbuf_size)) {
         return FALSE;
     }
@@ -7440,7 +7536,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
         return FALSE;
     }
     n = mjpeg_encoder_end_frame(stream->mjpeg_encoder);
-    display_channel->send_data.stream_outbuf_size = outbuf_size;
+    dcc->send_data.stream_outbuf_size = outbuf_size;
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
 
@@ -7451,7 +7547,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
     stream_data.data_size = n;
     spice_marshall_msg_display_stream_data(base_marshaller, &stream_data);
     spice_marshaller_add_ref(base_marshaller,
-                             display_channel->send_data.stream_outbuf, n);
+                             dcc->send_data.stream_outbuf, n);
     agent->last_send_time = time_now;
     return TRUE;
 }
@@ -7503,30 +7599,30 @@ static void display_channel_marshall_migrate(RedChannelClient *rcc,
 static void display_channel_marshall_migrate_data(RedChannelClient *rcc,
                                                   SpiceMarshaller *base_marshaller)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     DisplayChannelMigrateData display_data;
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, NULL);
 
-    ASSERT(display_channel->pixmap_cache);
+    ASSERT(dcc->pixmap_cache);
     display_data.magic = DISPLAY_MIGRATE_DATA_MAGIC;
     ASSERT(MAX_CACHE_CLIENTS == 4); //MIGRATE_DATA_VERSION dependent
     display_data.version = DISPLAY_MIGRATE_DATA_VERSION;
 
     display_data.message_serial = red_channel_client_get_message_serial(rcc);
 
-    display_data.pixmap_cache_freezer = pixmap_cache_freeze(display_channel->pixmap_cache);
-    display_data.pixmap_cache_id = display_channel->pixmap_cache->id;
-    display_data.pixmap_cache_size = display_channel->pixmap_cache->size;
-    memcpy(display_data.pixmap_cache_clients, display_channel->pixmap_cache->sync,
+    display_data.pixmap_cache_freezer = pixmap_cache_freeze(dcc->pixmap_cache);
+    display_data.pixmap_cache_id = dcc->pixmap_cache->id;
+    display_data.pixmap_cache_size = dcc->pixmap_cache->size;
+    memcpy(display_data.pixmap_cache_clients, dcc->pixmap_cache->sync,
            sizeof(display_data.pixmap_cache_clients));
 
-    ASSERT(display_channel->glz_dict);
-    red_freeze_glz(display_channel);
-    display_data.glz_dict_id = display_channel->glz_dict->id;
-    glz_enc_dictionary_get_restore_data(display_channel->glz_dict->dict,
+    ASSERT(dcc->glz_dict);
+    red_freeze_glz(dcc);
+    display_data.glz_dict_id = dcc->glz_dict->id;
+    glz_enc_dictionary_get_restore_data(dcc->glz_dict->dict,
                                         &display_data.glz_dict_restore_data,
-                                        &display_channel->glz_data.usr);
+                                        &dcc->glz_data.usr);
 
     spice_marshaller_add_ref(base_marshaller,
                              (uint8_t *)&display_data, sizeof(display_data));
@@ -7535,12 +7631,12 @@ static void display_channel_marshall_migrate_data(RedChannelClient *rcc,
 static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc,
                                                  SpiceMarshaller *base_marshaller)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     SpiceMsgWaitForChannels wait;
     PixmapCache *pixmap_cache;
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_WAIT_FOR_CHANNELS, NULL);
-    pixmap_cache = display_channel->pixmap_cache;
+    pixmap_cache = dcc->pixmap_cache;
 
     pthread_mutex_lock(&pixmap_cache->lock);
 
@@ -7548,8 +7644,8 @@ static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc,
     wait.wait_list[0].channel_type = SPICE_CHANNEL_DISPLAY;
     wait.wait_list[0].channel_id = pixmap_cache->generation_initiator.client;
     wait.wait_list[0].message_serial = pixmap_cache->generation_initiator.message;
-    display_channel->pixmap_cache_generation = pixmap_cache->generation;
-    display_channel->pending_pixmaps_sync = FALSE;
+    dcc->pixmap_cache_generation = pixmap_cache->generation;
+    dcc->pending_pixmaps_sync = FALSE;
 
     pthread_mutex_unlock(&pixmap_cache->lock);
 
@@ -7559,11 +7655,11 @@ static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc,
 static void display_channel_marshall_reset_cache(RedChannelClient *rcc,
                                                  SpiceMarshaller *base_marshaller)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     SpiceMsgWaitForChannels wait;
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS, NULL);
-    pixmap_cache_reset(display_channel->pixmap_cache, rcc, &wait);
+    pixmap_cache_reset(dcc->pixmap_cache, dcc, &wait);
 
     spice_marshall_msg_display_inval_all_pixmaps(base_marshaller,
                                                  &wait);
@@ -7571,7 +7667,8 @@ static void display_channel_marshall_reset_cache(RedChannelClient *rcc,
 
 static void red_marshall_image( RedChannelClient *rcc, SpiceMarshaller *m, ImageItem *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
     SpiceImage red_image;
     RedWorker *worker;
     SpiceBitmap bitmap;
@@ -7645,7 +7742,7 @@ static void red_marshall_image( RedChannelClient *rcc, SpiceMarshaller *m, Image
                                                           &bitmap,
                                                           worker->mem_slots.internal_groupslot_id);
                 if (grad_level == BITMAP_GRADUAL_HIGH) {
-                    // if we use lz for alpha, the stride can't be extra 
+                    // if we use lz for alpha, the stride can't be extra
                     lossy_comp = display_channel->enable_jpeg && item->can_lossy;
                 } else {
                     lz_comp = TRUE;
@@ -7657,27 +7754,27 @@ static void red_marshall_image( RedChannelClient *rcc, SpiceMarshaller *m, Image
     }
 
     if (lossy_comp) {
-        comp_succeeded = red_jpeg_compress_image(display_channel, &red_image,
+        comp_succeeded = red_jpeg_compress_image(dcc, &red_image,
                                                  &bitmap, &comp_send_data,
                                                  worker->mem_slots.internal_groupslot_id);
     } else {
         if (!lz_comp) {
-            comp_succeeded = red_quic_compress_image(display_channel, &red_image, &bitmap,
+            comp_succeeded = red_quic_compress_image(dcc, &red_image, &bitmap,
                                                      &comp_send_data,
                                                      worker->mem_slots.internal_groupslot_id);
         } else {
-            comp_succeeded = red_lz_compress_image(display_channel, &red_image, &bitmap,
+            comp_succeeded = red_lz_compress_image(dcc, &red_image, &bitmap,
                                                    &comp_send_data,
                                                    worker->mem_slots.internal_groupslot_id);
         }
     }
 
-    surface_lossy_region = &display_channel->surface_client_lossy_region[item->surface_id];
+    surface_lossy_region = &dcc->surface_client_lossy_region[item->surface_id];
     if (comp_succeeded) {
         spice_marshall_Image(src_bitmap_out, &red_image,
                              &bitmap_palette_out, &lzplt_palette_out);
 
-        marshaller_add_compressed(worker, src_bitmap_out,
+        marshaller_add_compressed(src_bitmap_out,
                                   comp_send_data.comp_buf, comp_send_data.comp_buf_size);
 
         if (lzplt_palette_out && comp_send_data.lzplt_palette) {
@@ -7705,6 +7802,7 @@ static void red_marshall_image( RedChannelClient *rcc, SpiceMarshaller *m, Image
 static void red_display_marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller *m,
                                          UpgradeItem *item)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     RedDrawable *red_drawable;
     SpiceMsgDisplayDrawCopy copy;
     SpiceMarshaller *src_bitmap_out, *mask_bitmap_out;
@@ -7727,13 +7825,13 @@ static void red_display_marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller
     spice_marshall_msg_display_draw_copy(m, &copy,
                                          &src_bitmap_out, &mask_bitmap_out);
 
-    fill_bits(rcc, src_bitmap_out, copy.data.src_bitmap, item->drawable, FALSE);
+    fill_bits(dcc, src_bitmap_out, copy.data.src_bitmap, item->drawable, FALSE);
 }
 
 static void red_display_marshall_stream_start(RedChannelClient *rcc,
                      SpiceMarshaller *base_marshaller, StreamAgent *agent)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     Stream *stream = agent->stream;
 
     agent->last_send_time = 0;
@@ -7744,7 +7842,7 @@ static void red_display_marshall_stream_start(RedChannelClient *rcc,
     SpiceClipRects clip_rects;
 
     stream_create.surface_id = 0;
-    stream_create.id = agent - display_channel->stream_agents;
+    stream_create.id = agent - dcc->stream_agents;
     stream_create.flags = stream->top_down ? SPICE_STREAM_FLAGS_TOP_DOWN : 0;
     stream_create.codec_type = SPICE_VIDEO_CODEC_TYPE_MJPEG;
 
@@ -7770,7 +7868,7 @@ static void red_display_marshall_stream_clip(RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
                                          StreamClipItem *item)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     StreamAgent *agent = item->stream_agent;
     Stream *stream = agent->stream;
 
@@ -7779,7 +7877,7 @@ static void red_display_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 = agent - display_channel->stream_agents;
+    stream_clip.id = agent - dcc->stream_agents;
     stream_clip.clip.type = item->clip_type;
     stream_clip.clip.rects = item->rects;
 
@@ -7789,11 +7887,11 @@ static void red_display_marshall_stream_clip(RedChannelClient *rcc,
 static void red_display_marshall_stream_end(RedChannelClient *rcc,
                    SpiceMarshaller *base_marshaller, StreamAgent* agent)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     SpiceMsgDisplayStreamDestroy destroy;
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL);
-    destroy.id = agent - display_channel->stream_agents;
+    destroy.id = agent - dcc->stream_agents;
 
     spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy);
 }
@@ -7808,6 +7906,7 @@ static void red_cursor_marshall_inval(RedChannelClient *rcc,
 static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
 {
     CursorChannel *cursor_channel;
+    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
     RedWorker *worker;
     SpiceMsgCursorInit msg;
     AddBufInfo info;
@@ -7822,7 +7921,7 @@ static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *bas
     msg.trail_length = worker->cursor_trail_length;
     msg.trail_frequency = worker->cursor_trail_frequency;
 
-    fill_cursor(cursor_channel, &msg.cursor, worker->cursor, &info);
+    fill_cursor(ccc, &msg.cursor, worker->cursor, &info);
     spice_marshall_msg_cursor_init(base_marshaller, &msg);
     add_buf_from_info(base_marshaller, &info);
 }
@@ -7842,6 +7941,7 @@ static void red_marshall_cursor(RedChannelClient *rcc,
                    SpiceMarshaller *m, CursorItem *cursor)
 {
     CursorChannel *cursor_channel = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base);
+    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
     RedCursorCmd *cmd;
     RedWorker *worker;
 
@@ -7868,7 +7968,7 @@ static void red_marshall_cursor(RedChannelClient *rcc,
             cursor_set.position = cmd->u.set.position;
             cursor_set.visible = worker->cursor_visible;
 
-            fill_cursor(cursor_channel, &cursor_set.cursor, cursor, &info);
+            fill_cursor(ccc, &cursor_set.cursor, cursor, &info);
             spice_marshall_msg_cursor_set(m, &cursor_set);
             add_buf_from_info(m, &info);
             break;
@@ -7895,10 +7995,9 @@ static void red_marshall_cursor(RedChannelClient *rcc,
 static void red_marshall_surface_create(RedChannelClient *rcc,
     SpiceMarshaller *base_marshaller, SpiceMsgSurfaceCreate *surface_create)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
 
-    ASSERT(display_channel);
-    region_init(&display_channel->surface_client_lossy_region[surface_create->surface_id]);
+    region_init(&dcc->surface_client_lossy_region[surface_create->surface_id]);
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE, NULL);
 
     spice_marshall_msg_display_surface_create(base_marshaller, surface_create);
@@ -7907,12 +8006,10 @@ static void red_marshall_surface_create(RedChannelClient *rcc,
 static void red_marshall_surface_destroy(RedChannelClient *rcc,
        SpiceMarshaller *base_marshaller, uint32_t surface_id)
 {
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     SpiceMsgSurfaceDestroy surface_destroy;
 
-    ASSERT(display_channel);
-
-    region_destroy(&display_channel->surface_client_lossy_region[surface_id]);
+    region_destroy(&dcc->surface_client_lossy_region[surface_id]);
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_DESTROY, NULL);
 
     surface_destroy.surface_id = surface_id;
@@ -7923,9 +8020,9 @@ static void red_marshall_surface_destroy(RedChannelClient *rcc,
 static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
 {
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-    DisplayChannel *display_channel = (DisplayChannel *)red_ref_channel(rcc->channel);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
 
-    red_display_reset_send_data(display_channel);
+    red_display_reset_send_data(dcc);
     switch (pipe_item->type) {
     case PIPE_ITEM_TYPE_DRAW: {
         Drawable *drawable = SPICE_CONTAINEROF(pipe_item, Drawable, pipe_item);
@@ -7973,7 +8070,7 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
         display_channel_marshall_reset_cache(rcc, m);
         break;
     case PIPE_ITEM_TYPE_INVAL_PALLET_CACHE:
-        red_reset_palette_cache(display_channel);
+        red_reset_palette_cache(dcc);
         red_marshall_verb(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
         break;
     case PIPE_ITEM_TYPE_CREATE_SURFACE: {
@@ -7992,21 +8089,19 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
         red_error("invalid pipe item type");
     }
 
-    display_channel_release_item_before_push(display_channel, pipe_item);
+    display_channel_client_release_item_before_push(dcc, pipe_item);
 
     // a message is pending
     if (red_channel_client_send_message_pending(rcc)) {
         display_begin_send_message(rcc, m);
     }
-    red_unref_channel(&display_channel->common.base);
 }
 
 static void cursor_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
 {
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-    CursorChannel *cursor_channel = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base);
+    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
 
-    red_ref_channel(rcc->channel);
     switch (pipe_item->type) {
     case PIPE_ITEM_TYPE_CURSOR:
         red_marshall_cursor(rcc, m, (CursorItem *)pipe_item);
@@ -8022,20 +8117,19 @@ static void cursor_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
         cursor_channel_marshall_migrate(rcc, m);
         break;
     case PIPE_ITEM_TYPE_CURSOR_INIT:
-        red_reset_cursor_cache(cursor_channel);
+        red_reset_cursor_cache(rcc);
         red_marshall_cursor_init(rcc, m);
         break;
     case PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE:
-        red_reset_cursor_cache(cursor_channel);
+        red_reset_cursor_cache(rcc);
         red_marshall_verb(rcc, SPICE_MSG_CURSOR_INVAL_ALL);
         break;
     default:
         red_error("invalid pipe item type");
     }
 
-    cursor_channel_release_item_before_push(cursor_channel, pipe_item);
+    cursor_channel_client_release_item_before_push(ccc, pipe_item);
     red_channel_client_begin_send_message(rcc);
-    red_unref_channel(rcc->channel);
 }
 
 static inline void red_push(RedWorker *worker)
@@ -8109,7 +8203,7 @@ void red_show_tree(RedWorker *worker)
     show_tree_data.container = NULL;
     for (x = 0; x < NUM_SURFACES; ++x) {
         if (worker->surfaces[x].context.canvas) {
-            current_tree_for_each(worker, &worker->surfaces[x].current, __show_tree_call,
+            current_tree_for_each(&worker->surfaces[x].current, __show_tree_call,
                                   &show_tree_data);
         }
     }
@@ -8119,13 +8213,13 @@ void red_show_tree(RedWorker *worker)
 static void red_disconnect_channel(RedChannel *channel)
 {
     red_channel_disconnect(channel);
-    red_unref_channel(channel);
 }
 
-static void red_disconnect_display(RedChannelClient *rcc)
+static void display_channel_client_disconnect(RedChannelClient *rcc)
 {
     // TODO: MC: right now we assume single channel
     DisplayChannel *display_channel;
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     CommonChannel *common;
     RedWorker *worker;
 
@@ -8139,31 +8233,36 @@ static void red_disconnect_display(RedChannelClient *rcc)
 #ifdef COMPRESS_STAT
     print_compress_stats(display_channel);
 #endif
-    worker->display_channel = NULL;
-    red_release_pixmap_cache(display_channel);
-    red_release_glz(display_channel);
-    red_reset_palette_cache(display_channel);
-    free(display_channel->send_data.stream_outbuf);
-    red_display_reset_compress_buf(display_channel);
-    while (display_channel->send_data.free_compress_bufs) {
-        RedCompressBuf *buf = display_channel->send_data.free_compress_bufs;
-        display_channel->send_data.free_compress_bufs = buf->next;
-        free(buf);
+    red_release_pixmap_cache(dcc);
+    red_release_glz(dcc);
+    red_reset_palette_cache(dcc);
+    free(dcc->send_data.stream_outbuf);
+    red_display_reset_compress_buf(dcc);
+    free(dcc->send_data.free_list.res);
+    red_display_destroy_streams(dcc);
+    red_channel_client_pipe_clear(rcc); // do this before deleting surfaces
+    red_channel_client_disconnect(rcc);
+
+    // this was the last channel client
+    if (!red_channel_is_connected(rcc->channel)) {
+        red_display_destroy_compress_bufs(display_channel);
     }
-    free(display_channel->send_data.free_list.res);
-    red_display_destroy_streams(display_channel);
-    red_disconnect_channel(rcc->channel);
 }
 
 void red_disconnect_all_display_TODO_remove_me(RedChannel *channel)
 {
+    DisplayChannel *display_channel;
+    RedWorker *worker;
     // TODO: we need to record the client that actually causes the timeout. So
     // we need to check the locations of the various pipe heads when counting,
     // and disconnect only those/that.
     if (!channel) {
         return;
     }
-    red_channel_apply_clients(channel, red_disconnect_display);
+    display_channel = SPICE_CONTAINEROF(channel, DisplayChannel, common.base);
+    worker = display_channel->common.worker;
+    red_channel_apply_clients(channel, display_channel_client_disconnect);
+    worker->display_channel = NULL;
 }
 
 static void red_migrate_display(RedWorker *worker)
@@ -8283,41 +8382,32 @@ static SurfaceCreateItem *get_surface_create_item(
     return create;
 }
 
-static inline void __red_create_surface_item(RedWorker *worker, int surface_id, uint32_t flags)
+static inline void red_create_surface_item(DisplayChannelClient *dcc, int surface_id)
 {
     RedSurface *surface;
     SurfaceCreateItem *create;
+    RedWorker *worker = dcc ? dcc->common.worker : NULL;
+    uint32_t flags = is_primary_surface(worker, surface_id) ? SPICE_SURFACE_FLAGS_PRIMARY : 0;
 
-    if (!display_is_connected(worker)) {
+    /* don't send redundant create surface commands to client */
+    if (!dcc || dcc->surface_client_created[surface_id]) {
         return;
     }
-
     surface = &worker->surfaces[surface_id];
-
-    create = get_surface_create_item(&worker->display_channel->common.base,
+    create = get_surface_create_item(dcc->common.base.channel,
             surface_id, surface->context.width, surface->context.height,
                                      surface->context.format, flags);
-
-    worker->display_channel->surface_client_created[surface_id] = TRUE;
-
-    red_channel_client_pipe_add(worker->display_channel->common.base.rcc, &create->pipe_item);
-}
-
-static inline void red_create_surface_item(RedWorker *worker, int surface_id)
-{
-    if (is_primary_surface(worker, surface_id)) {
-        __red_create_surface_item(worker, surface_id, SPICE_SURFACE_FLAGS_PRIMARY);
-    } else {
-        __red_create_surface_item(worker, surface_id, 0);
-    }
+    dcc->surface_client_created[surface_id] = TRUE;
+    red_channel_client_pipe_add(&dcc->common.base, &create->pipe_item);
 }
 
 static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, uint32_t width,
                                       uint32_t height, int32_t stride, uint32_t format,
                                       void *line_0, int data_is_valid, int send_client)
 {
-    uint32_t i;
     RedSurface *surface = &worker->surfaces[surface_id];
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    uint32_t i;
     if (stride >= 0) {
         PANIC("Untested path stride >= 0");
     }
@@ -8348,7 +8438,7 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
         }
 
         if (send_client) {
-            red_create_surface_item(worker, surface_id);
+            red_create_surface_item(dcc, surface_id);
             if (data_is_valid) {
                 red_add_surface_image(worker, surface_id);
             }
@@ -8363,7 +8453,7 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
         if (surface->context.canvas) { //no need canvas check
             worker->renderer = worker->renderers[i];
             if (send_client) {
-                red_create_surface_item(worker, surface_id);
+                red_create_surface_item(dcc, surface_id);
                 if (data_is_valid) {
                     red_add_surface_image(worker, surface_id);
                 }
@@ -8375,7 +8465,8 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
     PANIC("unable to create drawing canvas");
 }
 
-static void red_wait_outgoing_item(RedChannel *channel);
+static void red_wait_outgoing_item(RedChannelClient *rcc);
+static void red_wait_outgoing_items(RedChannel *channel);
 
 static inline void flush_display_commands(RedWorker *worker)
 {
@@ -8404,7 +8495,6 @@ static inline void flush_display_commands(RedWorker *worker)
                 break;
             }
             RedChannel *channel = (RedChannel *)worker->display_channel;
-            red_ref_channel(channel);
             red_channel_receive(channel);
             red_channel_send(channel);
             // TODO: MC: the whole timeout will break since it takes lowest timeout, should
@@ -8416,7 +8506,6 @@ static inline void flush_display_commands(RedWorker *worker)
                 sleep_count++;
                 usleep(DISPLAY_CLIENT_RETRY_INTERVAL);
             }
-            red_unref_channel(channel);
         }
     }
 }
@@ -8448,7 +8537,6 @@ static inline void flush_cursor_commands(RedWorker *worker)
                 break;
             }
             RedChannel *channel = (RedChannel *)worker->cursor_channel;
-            red_ref_channel(channel);
             red_channel_receive(channel);
             red_channel_send(channel);
             if (red_now() >= end_time) {
@@ -8458,7 +8546,6 @@ static inline void flush_cursor_commands(RedWorker *worker)
                 sleep_count++;
                 usleep(DISPLAY_CLIENT_RETRY_INTERVAL);
             }
-            red_unref_channel(channel);
         }
     }
 }
@@ -8469,42 +8556,41 @@ static inline void flush_all_qxl_commands(RedWorker *worker)
     flush_cursor_commands(worker);
 }
 
-static void push_new_primary_surface(RedWorker *worker)
+static void push_new_primary_surface(DisplayChannelClient *dcc)
 {
-    DisplayChannel *display_channel;
+    RedWorker *worker = DCC_TO_WORKER(dcc);
+    RedChannelClient *rcc = &dcc->common.base;
 
-    if ((display_channel = worker->display_channel)) {
-        red_channel_client_pipe_add_type(display_channel->common.base.rcc,
-                                         PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
-        if (!display_channel->common.base.migrate) {
-            red_create_surface_item(worker, 0);
-        }
-        red_channel_push(&worker->display_channel->common.base);
+    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+    if (!worker->display_channel->common.base.migrate) {
+        red_create_surface_item(dcc, 0);
     }
+    red_channel_client_push(rcc);
 }
 
-static int display_channel_wait_for_init(DisplayChannel *display_channel)
+/* TODO: this function is evil^Wsynchronous, fix */
+static int display_channel_client_wait_for_init(DisplayChannelClient *dcc)
 {
-    display_channel->expect_init = TRUE;
+    dcc->expect_init = TRUE;
     uint64_t end_time = red_now() + DISPLAY_CLIENT_TIMEOUT;
     for (;;) {
-        red_channel_receive((RedChannel *)display_channel);
-        if (!red_channel_is_connected(&display_channel->common.base)) {
+        red_channel_client_receive(&dcc->common.base);
+        if (!red_channel_client_is_connected(&dcc->common.base)) {
             break;
         }
-        if (display_channel->pixmap_cache && display_channel->glz_dict) {
-            display_channel->pixmap_cache_generation = display_channel->pixmap_cache->generation;
-            display_channel->glz = glz_encoder_create(display_channel->common.id,
-                                                      display_channel->glz_dict->dict,
-                                                      &display_channel->glz_data.usr);
-            if (!display_channel->glz) {
+        if (dcc->pixmap_cache && dcc->glz_dict) {
+            dcc->pixmap_cache_generation = dcc->pixmap_cache->generation;
+            /* TODO: move common.id? if it's used for a per client structure.. */
+            red_printf("creating encoder with id == %d", dcc->common.id);
+            dcc->glz = glz_encoder_create(dcc->common.id, dcc->glz_dict->dict, &dcc->glz_data.usr);
+            if (!dcc->glz) {
                 PANIC("create global lz failed");
             }
             return TRUE;
         }
         if (red_now() > end_time) {
             red_printf("timeout");
-            red_disconnect_all_display_TODO_remove_me((RedChannel *)display_channel);
+            display_channel_client_disconnect(&dcc->common.base);
             break;
         }
         usleep(DISPLAY_CLIENT_RETRY_INTERVAL);
@@ -8512,10 +8598,11 @@ static int display_channel_wait_for_init(DisplayChannel *display_channel)
     return FALSE;
 }
 
-static void on_new_display_channel(RedWorker *worker)
+static void on_new_display_channel_client(DisplayChannelClient *dcc)
 {
-    DisplayChannel *display_channel = worker->display_channel;
-    ASSERT(display_channel);
+    DisplayChannel *display_channel = DCC_TO_DC(dcc);
+    RedWorker *worker = display_channel->common.worker;
+    RedChannelClient *rcc = &dcc->common.base;
 
     red_channel_push_set_ack(&display_channel->common.base);
 
@@ -8524,17 +8611,17 @@ static void on_new_display_channel(RedWorker *worker)
         return;
     }
 
-    if (!display_channel_wait_for_init(display_channel)) {
+    if (!display_channel_client_wait_for_init(dcc)) {
         return;
     }
-    red_channel_ack_zero_messages_window(&display_channel->common.base);
+    red_channel_client_ack_zero_messages_window(&dcc->common.base);
     if (worker->surfaces[0].context.canvas) {
         red_current_flush(worker, 0);
-        push_new_primary_surface(worker);
+        push_new_primary_surface(dcc);
         red_add_surface_image(worker, 0);
         if (red_channel_is_connected(&display_channel->common.base)) {
-            red_pipe_add_verb(display_channel->common.base.rcc, SPICE_MSG_DISPLAY_MARK);
-            red_disply_start_streams(display_channel);
+            red_pipe_add_verb(rcc, SPICE_MSG_DISPLAY_MARK);
+            red_disply_start_streams(dcc);
         }
     }
 }
@@ -8556,11 +8643,11 @@ static GlzSharedDictionary *_red_find_glz_dictionary(uint8_t dict_id)
     return ret;
 }
 
-static GlzSharedDictionary *_red_create_glz_dictionary(DisplayChannel *display,
-                                                       uint8_t id,
+static GlzSharedDictionary *_red_create_glz_dictionary(uint8_t id,
                                                        GlzEncDictContext *opaque_dict)
 {
     GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
+
     shared_dict->dict = opaque_dict;
     shared_dict->id = id;
     shared_dict->refs = 1;
@@ -8570,12 +8657,12 @@ static GlzSharedDictionary *_red_create_glz_dictionary(DisplayChannel *display,
     return shared_dict;
 }
 
-static GlzSharedDictionary *red_create_glz_dictionary(DisplayChannel *display,
+static GlzSharedDictionary *red_create_glz_dictionary(DisplayChannelClient *dcc,
                                                       uint8_t id, int window_size)
 {
     GlzEncDictContext *glz_dict = glz_enc_dictionary_create(window_size,
                                                             MAX_LZ_ENCODERS,
-                                                            &display->glz_data.usr);
+                                                            &dcc->glz_data.usr);
 #ifdef COMPRESS_DEBUG
     red_printf("Lz Window %d Size=%d", id, window_size);
 #endif
@@ -8583,23 +8670,23 @@ static GlzSharedDictionary *red_create_glz_dictionary(DisplayChannel *display,
         PANIC("failed creating lz dictionary");
         return NULL;
     }
-    return _red_create_glz_dictionary(display, id, glz_dict);
+    return _red_create_glz_dictionary(id, glz_dict);
 }
 
-static GlzSharedDictionary *red_create_restored_glz_dictionary(DisplayChannel *display,
+static GlzSharedDictionary *red_create_restored_glz_dictionary(DisplayChannelClient *dcc,
                                                                uint8_t id,
                                                                GlzEncDictRestoreData *restore_data)
 {
     GlzEncDictContext *glz_dict = glz_enc_dictionary_restore(restore_data,
-                                                             &display->glz_data.usr);
+                                                             &dcc->glz_data.usr);
     if (!glz_dict) {
         PANIC("failed creating lz dictionary");
         return NULL;
     }
-    return _red_create_glz_dictionary(display, id, glz_dict);
+    return _red_create_glz_dictionary(id, glz_dict);
 }
 
-static GlzSharedDictionary *red_get_glz_dictionary(DisplayChannel *display,
+static GlzSharedDictionary *red_get_glz_dictionary(DisplayChannelClient *dcc,
                                                    uint8_t id, int window_size)
 {
     GlzSharedDictionary *shared_dict = NULL;
@@ -8609,7 +8696,7 @@ static GlzSharedDictionary *red_get_glz_dictionary(DisplayChannel *display,
     shared_dict = _red_find_glz_dictionary(id);
 
     if (!shared_dict) {
-        shared_dict = red_create_glz_dictionary(display, id, window_size);
+        shared_dict = red_create_glz_dictionary(dcc, id, window_size);
         ring_add(&glz_dictionary_list, &shared_dict->base);
     } else {
         shared_dict->refs++;
@@ -8618,7 +8705,7 @@ static GlzSharedDictionary *red_get_glz_dictionary(DisplayChannel *display,
     return shared_dict;
 }
 
-static GlzSharedDictionary *red_restore_glz_dictionary(DisplayChannel *display,
+static GlzSharedDictionary *red_restore_glz_dictionary(DisplayChannelClient *dcc,
                                                        uint8_t id,
                                                        GlzEncDictRestoreData *restore_data)
 {
@@ -8629,7 +8716,7 @@ static GlzSharedDictionary *red_restore_glz_dictionary(DisplayChannel *display,
     shared_dict = _red_find_glz_dictionary(id);
 
     if (!shared_dict) {
-        shared_dict = red_create_restored_glz_dictionary(display, id, restore_data);
+        shared_dict = red_create_restored_glz_dictionary(dcc, id, restore_data);
         ring_add(&glz_dictionary_list, &shared_dict->base);
     } else {
         shared_dict->refs++;
@@ -8638,30 +8725,30 @@ static GlzSharedDictionary *red_restore_glz_dictionary(DisplayChannel *display,
     return shared_dict;
 }
 
-static void red_freeze_glz(DisplayChannel *channel)
+static void red_freeze_glz(DisplayChannelClient *dcc)
 {
-    pthread_rwlock_wrlock(&channel->glz_dict->encode_lock);
-    if (!channel->glz_dict->migrate_freeze) {
-        channel->glz_dict->migrate_freeze = TRUE;
+    pthread_rwlock_wrlock(&dcc->glz_dict->encode_lock);
+    if (!dcc->glz_dict->migrate_freeze) {
+        dcc->glz_dict->migrate_freeze = TRUE;
     }
-    pthread_rwlock_unlock(&channel->glz_dict->encode_lock);
+    pthread_rwlock_unlock(&dcc->glz_dict->encode_lock);
 }
 
 /* destroy encoder, and dictionary if no one uses it*/
-static void red_release_glz(DisplayChannel *channel)
+static void red_release_glz(DisplayChannelClient *dcc)
 {
     GlzSharedDictionary *shared_dict;
 
-    red_display_clear_glz_drawables(channel);
+    red_display_client_clear_glz_drawables(dcc);
 
-    glz_encoder_destroy(channel->glz);
-    channel->glz = NULL;
+    glz_encoder_destroy(dcc->glz);
+    dcc->glz = NULL;
 
-    if (!(shared_dict = channel->glz_dict)) {
+    if (!(shared_dict = dcc->glz_dict)) {
         return;
     }
 
-    channel->glz_dict = NULL;
+    dcc->glz_dict = NULL;
     pthread_mutex_lock(&glz_dictionary_list_lock);
     if (--shared_dict->refs) {
         pthread_mutex_unlock(&glz_dictionary_list_lock);
@@ -8669,7 +8756,7 @@ static void red_release_glz(DisplayChannel *channel)
     }
     ring_remove(&shared_dict->base);
     pthread_mutex_unlock(&glz_dictionary_list_lock);
-    glz_enc_dictionary_destroy(shared_dict->dict, &channel->glz_data.usr);
+    glz_enc_dictionary_destroy(shared_dict->dict, &dcc->glz_data.usr);
     free(shared_dict);
 }
 
@@ -8708,13 +8795,13 @@ static PixmapCache *red_get_pixmap_cache(uint8_t id, int64_t size)
     return cache;
 }
 
-static void red_release_pixmap_cache(DisplayChannel *channel)
+static void red_release_pixmap_cache(DisplayChannelClient *dcc)
 {
     PixmapCache *cache;
-    if (!(cache = channel->pixmap_cache)) {
+    if (!(cache = dcc->pixmap_cache)) {
         return;
     }
-    channel->pixmap_cache = NULL;
+    dcc->pixmap_cache = NULL;
     pthread_mutex_lock(&cache_lock);
     if (--cache->refs) {
         pthread_mutex_unlock(&cache_lock);
@@ -8726,52 +8813,54 @@ static void red_release_pixmap_cache(DisplayChannel *channel)
     free(cache);
 }
 
-static int display_channel_init_cache(DisplayChannel *channel, SpiceMsgcDisplayInit *init_info)
+static int display_channel_init_cache(DisplayChannelClient *dcc, SpiceMsgcDisplayInit *init_info)
 {
-    ASSERT(!channel->pixmap_cache);
-    return !!(channel->pixmap_cache = red_get_pixmap_cache(init_info->pixmap_cache_id,
-                                                           init_info->pixmap_cache_size));
+    ASSERT(!dcc->pixmap_cache);
+    return !!(dcc->pixmap_cache = red_get_pixmap_cache(init_info->pixmap_cache_id,
+                                                       init_info->pixmap_cache_size));
 }
 
-static int display_channel_init_glz_dictionary(DisplayChannel *channel, SpiceMsgcDisplayInit *init_info)
+static int display_channel_init_glz_dictionary(DisplayChannelClient *dcc,
+                                               SpiceMsgcDisplayInit *init_info)
 {
-    ASSERT(!channel->glz_dict);
-    ring_init(&channel->glz_drawables);
-    ring_init(&channel->glz_drawables_inst_to_free);
-    pthread_mutex_init(&channel->glz_drawables_inst_to_free_lock, NULL);
-    return !!(channel->glz_dict = red_get_glz_dictionary(channel,
-                                                         init_info->glz_dictionary_id,
-                                                         init_info->glz_dictionary_window_size));
+    ASSERT(!dcc->glz_dict);
+    ring_init(&dcc->glz_drawables);
+    ring_init(&dcc->glz_drawables_inst_to_free);
+    pthread_mutex_init(&dcc->glz_drawables_inst_to_free_lock, NULL);
+    return !!(dcc->glz_dict = red_get_glz_dictionary(dcc,
+                                                     init_info->glz_dictionary_id,
+                                                     init_info->glz_dictionary_window_size));
 }
 
-static int display_channel_init(DisplayChannel *channel, SpiceMsgcDisplayInit *init_info)
+static int display_channel_init(DisplayChannelClient *dcc, SpiceMsgcDisplayInit *init_info)
 {
-    return (display_channel_init_cache(channel, init_info) &&
-            display_channel_init_glz_dictionary(channel, init_info));
+    return (display_channel_init_cache(dcc, init_info) &&
+            display_channel_init_glz_dictionary(dcc, init_info));
 }
 
-static int display_channel_handle_migrate_glz_dictionary(DisplayChannel *channel,
+static int display_channel_handle_migrate_glz_dictionary(DisplayChannelClient *dcc,
                                                          DisplayChannelMigrateData *migrate_info)
 {
-    ASSERT(!channel->glz_dict);
-    ring_init(&channel->glz_drawables);
-    ring_init(&channel->glz_drawables_inst_to_free);
-    pthread_mutex_init(&channel->glz_drawables_inst_to_free_lock, NULL);
-    return !!(channel->glz_dict = red_restore_glz_dictionary(channel,
+    ASSERT(!dcc->glz_dict);
+    ring_init(&dcc->glz_drawables);
+    ring_init(&dcc->glz_drawables_inst_to_free);
+    pthread_mutex_init(&dcc->glz_drawables_inst_to_free_lock, NULL);
+    return !!(dcc->glz_dict = red_restore_glz_dictionary(dcc,
                                                          migrate_info->glz_dict_id,
                                                          &migrate_info->glz_dict_restore_data));
 }
 
 static int display_channel_handle_migrate_mark(RedChannelClient *rcc)
 {
-    DisplayChannel *channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    RedChannel *channel = &display_channel->common.base;
 
-    if (!channel->expect_migrate_mark) {
+    if (!display_channel->expect_migrate_mark) {
         red_printf("unexpected");
         return FALSE;
     }
-    channel->expect_migrate_mark = FALSE;
-    red_channel_client_pipe_add_type(channel->common.base.rcc, PIPE_ITEM_TYPE_MIGRATE_DATA);
+    display_channel->expect_migrate_mark = FALSE;
+    red_channel_pipes_add_type(channel, PIPE_ITEM_TYPE_MIGRATE_DATA);
     return TRUE;
 }
 
@@ -8795,8 +8884,10 @@ static uint64_t display_channel_handle_migrate_data_get_serial(
 static uint64_t display_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *message)
 {
     DisplayChannelMigrateData *migrate_data;
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
+    RedChannel *channel = &display_channel->common.base;
     int i;
-    DisplayChannel *channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
 
     if (size < sizeof(*migrate_data)) {
         red_printf("bad message size");
@@ -8808,52 +8899,54 @@ static uint64_t display_channel_handle_migrate_data(RedChannelClient *rcc, uint3
         red_printf("invalid content");
         return FALSE;
     }
-    if (!channel->expect_migrate_data) {
+    if (!display_channel->expect_migrate_data) {
         red_printf("unexpected");
         return FALSE;
     }
-    channel->expect_migrate_data = FALSE;
-    if (!(channel->pixmap_cache = red_get_pixmap_cache(migrate_data->pixmap_cache_id, -1))) {
+    display_channel->expect_migrate_data = FALSE;
+    if (!(dcc->pixmap_cache = red_get_pixmap_cache(migrate_data->pixmap_cache_id, -1))) {
         return FALSE;
     }
-    pthread_mutex_lock(&channel->pixmap_cache->lock);
+    pthread_mutex_lock(&dcc->pixmap_cache->lock);
     for (i = 0; i < MAX_CACHE_CLIENTS; i++) {
-        channel->pixmap_cache->sync[i] = MAX(channel->pixmap_cache->sync[i],
-                                             migrate_data->pixmap_cache_clients[i]);
+        dcc->pixmap_cache->sync[i] = MAX(dcc->pixmap_cache->sync[i],
+                                         migrate_data->pixmap_cache_clients[i]);
     }
-    pthread_mutex_unlock(&channel->pixmap_cache->lock);
+    pthread_mutex_unlock(&dcc->pixmap_cache->lock);
 
     if (migrate_data->pixmap_cache_freezer) {
-        channel->pixmap_cache->size = migrate_data->pixmap_cache_size;
-        red_channel_client_pipe_add_type(channel->common.base.rcc, PIPE_ITEM_TYPE_PIXMAP_RESET);
+        dcc->pixmap_cache->size = migrate_data->pixmap_cache_size;
+        // TODO - should this be red_channel_client_pipe_add_type?
+        red_channel_pipes_add_type(channel,
+                                   PIPE_ITEM_TYPE_PIXMAP_RESET);
     }
 
-    if (display_channel_handle_migrate_glz_dictionary(channel, migrate_data)) {
-        channel->glz = glz_encoder_create(channel->common.id,
-                                          channel->glz_dict->dict, &channel->glz_data.usr);
-        if (!channel->glz) {
+    if (display_channel_handle_migrate_glz_dictionary(dcc, migrate_data)) {
+        dcc->glz = glz_encoder_create(dcc->common.id,
+                                      dcc->glz_dict->dict, &dcc->glz_data.usr);
+        if (!dcc->glz) {
             PANIC("create global lz failed");
         }
     } else {
         PANIC("restoring global lz dictionary failed");
     }
 
-    red_channel_client_pipe_add_type(channel->common.base.rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
-    red_channel_ack_zero_messages_window(&channel->common.base);
+    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+    red_channel_client_ack_zero_messages_window(rcc);
     return TRUE;
 }
 
 static int display_channel_handle_message(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
 {
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
     switch (type) {
     case SPICE_MSGC_DISPLAY_INIT:
-        if (!((DisplayChannel *)rcc->channel)->expect_init) {
+        if (!dcc->expect_init) {
             red_printf("unexpected SPICE_MSGC_DISPLAY_INIT");
             return FALSE;
         }
-        ((DisplayChannel *)rcc->channel)->expect_init = FALSE;
-        return display_channel_init((DisplayChannel *)rcc->channel,
-                                    (SpiceMsgcDisplayInit *)message);
+        dcc->expect_init = FALSE;
+        return display_channel_init(dcc, (SpiceMsgcDisplayInit *)message);
     default:
         return red_channel_client_handle_message(rcc, size, type, message);
     }
@@ -8885,11 +8978,12 @@ static int common_channel_config_socket(RedChannelClient *rcc)
     return TRUE;
 }
 
-static void free_common_channel_from_listener(EventListener *ctx)
+static void free_common_cc_from_listener(EventListener *ctx)
 {
-    CommonChannel* common = SPICE_CONTAINEROF(ctx, CommonChannel, listener);
+    CommonChannelClient* common_cc = SPICE_CONTAINEROF(ctx, CommonChannelClient, listener);
 
-    free(common);
+    red_printf("");
+    free(common_cc);
 }
 
 static void worker_watch_update_mask(SpiceWatch *watch, int event_mask)
@@ -8911,8 +9005,74 @@ SpiceCoreInterface worker_core = {
     .watch_remove = worker_watch_remove,
 };
 
-static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_id,
-                                 RedClient *client, RedsStream *stream, int migrate,
+static CommonChannelClient *common_channel_client_create(int size,
+    CommonChannel *common, RedClient *client, RedsStream *stream)
+{
+    MainChannelClient *mcc = red_client_get_main(client);
+    RedChannelClient *rcc =
+        red_channel_client_create(size, &common->base, client, stream);
+    CommonChannelClient *common_cc = (CommonChannelClient*)rcc;
+    common_cc->worker = common->worker;
+
+    // TODO: move wide/narrow ack setting to red_channel.
+    red_channel_client_ack_set_client_window(rcc,
+        main_channel_client_is_low_bandwidth(mcc) ?
+        WIDE_CLIENT_ACK_WINDOW : NARROW_CLIENT_ACK_WINDOW);
+    return common_cc;
+}
+
+
+DisplayChannelClient *display_channel_client_create(CommonChannel *common,
+                             RedClient *client, RedsStream *stream)
+{
+    DisplayChannelClient *dcc =
+        (DisplayChannelClient*)common_channel_client_create(
+            sizeof(DisplayChannelClient), common, client, stream);
+
+    if (!dcc) {
+        return NULL;
+    }
+    ring_init(&dcc->palette_cache_lru);
+    dcc->palette_cache_available = CLIENT_PALETTE_CACHE_SIZE;
+    return dcc;
+}
+
+CursorChannelClient *cursor_channel_create_rcc(CommonChannel *common,
+                             RedClient *client, RedsStream *stream)
+{
+    CursorChannelClient *ccc =
+        (CursorChannelClient*)common_channel_client_create(
+            sizeof(CursorChannelClient), common, client, stream);
+
+    if (!ccc) {
+        return NULL;
+    }
+    ring_init(&ccc->cursor_cache_lru);
+    ccc->cursor_cache_available = CLIENT_CURSOR_CACHE_SIZE;
+    return ccc;
+}
+
+static int listen_to_new_client_channel(CommonChannel *common,
+    CommonChannelClient *common_cc, RedsStream *stream)
+{
+    struct epoll_event event;
+
+    common_cc->listener.refs = 1;
+    common_cc->listener.action = common->listener_action;
+    common_cc->listener.free = free_common_cc_from_listener;
+    ASSERT(common->base.clients_num);
+    common_cc->id = common->worker->id;
+    red_printf("NEW ID = %d", common_cc->id);
+    event.events = EPOLLIN | EPOLLOUT | EPOLLET;
+    event.data.ptr = &common_cc->listener;
+    if (epoll_ctl(common->worker->epoll, EPOLL_CTL_ADD, stream->socket, &event) == -1) {
+        red_printf("epoll_ctl failed, %s", strerror(errno));
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_id, int migrate,
                                  event_listener_action_proc handler,
                                  channel_disconnect_proc disconnect,
                                  channel_send_pipe_item_proc send_item,
@@ -8925,10 +9085,8 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
                                  channel_handle_migrate_data_proc handle_migrate_data,
                                  channel_handle_migrate_data_get_serial_proc handle_migrate_data_get_serial)
 {
-    struct epoll_event event;
-    RedChannel *channel;
+    RedChannel *channel = NULL;
     CommonChannel *common;
-    MainChannelClient *mcc = red_client_get_main(client);
 
     channel = red_channel_create_parser(size, &worker_core, migrate,
                                         TRUE /* handle_acks */,
@@ -8950,45 +9108,26 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
     if (!channel) {
         goto error;
     }
-    red_channel_client_create(sizeof(RedChannelClient), channel, client, stream);
-    common->id = worker->id;
-    common->listener.refs = 1;
-    common->listener.action = handler;
-    common->listener.free = free_common_channel_from_listener;
     common->worker = worker;
-    // TODO: Should this be distinctive for the Display/Cursor channels? doesn't
-    // make sense, does it?
-    red_channel_ack_set_client_window(channel,
-        main_channel_client_is_low_bandwidth(mcc) ?
-        WIDE_CLIENT_ACK_WINDOW : NARROW_CLIENT_ACK_WINDOW);
-
-    event.events = EPOLLIN | EPOLLOUT | EPOLLET;
-    event.data.ptr = &common->listener;
-    if (epoll_ctl(worker->epoll, EPOLL_CTL_ADD, stream->socket, &event) == -1) {
-        red_printf("epoll_ctl failed, %s", strerror(errno));
-        goto error;
-    }
-
+    common->listener_action = handler;
     return channel;
 
 error:
     free(channel);
-    reds_stream_free(stream);
-
     return NULL;
 }
 
 static void handle_channel_events(EventListener *in_listener, uint32_t events)
 {
-    CommonChannel *common = SPICE_CONTAINEROF(in_listener, CommonChannel, listener);
-    RedChannel *channel = &common->base;
+    CommonChannelClient *common_cc = SPICE_CONTAINEROF(in_listener, CommonChannelClient, listener);
+    RedChannelClient *rcc = &common_cc->base;
 
-    if ((events & EPOLLIN)) {
-        red_channel_receive(channel);
+    if ((events & EPOLLIN) && red_channel_client_is_connected(rcc)) {
+        red_channel_client_receive(rcc);
     }
 
-    if (red_channel_any_blocked(channel)) {
-        red_channel_send(channel);
+    if (rcc->send_data.blocked && red_channel_client_is_connected(rcc)) {
+        red_channel_client_push(rcc);
     }
 }
 
@@ -9014,9 +9153,10 @@ static void display_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item
     }
 }
 
-static void display_channel_release_item_after_push(DisplayChannel *display_channel, PipeItem *item)
+static void display_channel_client_release_item_after_push(DisplayChannelClient *dcc,
+                                                           PipeItem *item)
 {
-    RedWorker *worker = display_channel->common.worker;
+    RedWorker *worker = dcc->common.worker;
 
     switch (item->type) {
     case PIPE_ITEM_TYPE_DRAW:
@@ -9024,7 +9164,7 @@ static void display_channel_release_item_after_push(DisplayChannel *display_chan
         release_drawable(worker, SPICE_CONTAINEROF(item, Drawable, pipe_item));
         break;
     case PIPE_ITEM_TYPE_STREAM_CLIP:
-        red_display_release_stream_clip(display_channel, (StreamClipItem *)item);
+        red_display_release_stream_clip(worker, (StreamClipItem *)item);
         break;
     case PIPE_ITEM_TYPE_UPGRADE:
         release_upgrade_item(worker, (UpgradeItem *)item);
@@ -9040,9 +9180,10 @@ static void display_channel_release_item_after_push(DisplayChannel *display_chan
     }
 }
 
-static void display_channel_release_item_before_push(DisplayChannel *display_channel, PipeItem *item)
+static void display_channel_client_release_item_before_push(DisplayChannelClient *dcc,
+                                                            PipeItem *item)
 {
-    RedWorker *worker = display_channel->common.worker;
+    RedWorker *worker = dcc->common.worker;
 
     switch (item->type) {
     case PIPE_ITEM_TYPE_DRAW:
@@ -9050,15 +9191,15 @@ static void display_channel_release_item_before_push(DisplayChannel *display_cha
         break;
     case PIPE_ITEM_TYPE_STREAM_CREATE: {
         StreamAgent *agent = SPICE_CONTAINEROF(item, StreamAgent, create_item);
-        red_display_release_stream(display_channel, agent);
+        red_display_release_stream(worker, agent);
         break;
     }
     case PIPE_ITEM_TYPE_STREAM_CLIP:
-        red_display_release_stream_clip(display_channel, (StreamClipItem *)item);
+        red_display_release_stream_clip(worker, (StreamClipItem *)item);
         break;
     case PIPE_ITEM_TYPE_STREAM_DESTROY: {
         StreamAgent *agent = SPICE_CONTAINEROF(item, StreamAgent, destroy_item);
-        red_display_release_stream(display_channel, agent);
+        red_display_release_stream(worker, agent);
         break;
     }
     case PIPE_ITEM_TYPE_UPGRADE:
@@ -9095,15 +9236,14 @@ static void display_channel_release_item_before_push(DisplayChannel *display_cha
 
 static void display_channel_release_item(RedChannelClient *rcc, PipeItem *item, int item_pushed)
 {
-    RedChannel *channel = rcc->channel;
-    CommonChannel *common = SPICE_CONTAINEROF(channel, CommonChannel, base);
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
 
     ASSERT(item);
     if (item_pushed) {
-        display_channel_release_item_after_push((DisplayChannel *)common, item);
+        display_channel_client_release_item_after_push(dcc, item);
     } else {
         red_printf_once("not pushed (%d)", item->type);
-        display_channel_release_item_before_push((DisplayChannel *)common, item);
+        display_channel_client_release_item_before_push(dcc, item);
     }
 }
 
@@ -9151,19 +9291,19 @@ static void dispatch_cursor_channel_client_disconnect(RedChannelClient *rcc)
     red_dispatcher_disconnect_cursor_client(dispatcher, rcc);
 }
 
-static void handle_new_display_channel(RedWorker *worker, RedClient *client, RedsStream *stream,
-                                       int migrate)
+static void ensure_display_channel_created(RedWorker *worker, int migrate)
 {
     DisplayChannel *display_channel;
-    size_t stream_buf_size;
-    int is_low_bandwidth = main_channel_client_is_low_bandwidth(red_client_get_main(client));
 
-    red_disconnect_all_display_TODO_remove_me((RedChannel *)worker->display_channel);
+    if (worker->display_channel) {
+        return;
+    }
 
-    if (!(display_channel = (DisplayChannel *)__new_channel(
+    red_printf("create display channel");
+    if (!(worker->display_channel = (DisplayChannel *)__new_channel(
             worker, sizeof(*display_channel),
-            SPICE_CHANNEL_DISPLAY, client, stream,
-            migrate, handle_channel_events,
+            SPICE_CHANNEL_DISPLAY, migrate,
+            handle_channel_events,
             dispatch_display_channel_client_disconnect,
             display_channel_send_item,
             display_channel_hold_pipe_item,
@@ -9173,9 +9313,11 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
             display_channel_on_outgoing_error,
             display_channel_handle_migrate_mark,
             display_channel_handle_migrate_data,
-            display_channel_handle_migrate_data_get_serial))) {
+            display_channel_handle_migrate_data_get_serial
+            ))) {
         return;
     }
+    display_channel = worker->display_channel;
 #ifdef RED_STATISTICS
     display_channel->stat = stat_add_node(worker->stat, "display_channel", TRUE);
     display_channel->common.base.out_bytes_counter = stat_add_counter(display_channel->stat,
@@ -9187,21 +9329,44 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
     display_channel->non_cache_counter = stat_add_counter(display_channel->stat,
                                                           "non_cache", TRUE);
 #endif
-    ring_init(&display_channel->palette_cache_lru);
-    display_channel->palette_cache_available = CLIENT_PALETTE_CACHE_SIZE;
-    red_display_init_streams(display_channel);
+    stat_compress_init(&display_channel->lz_stat, lz_stat_name);
+    stat_compress_init(&display_channel->glz_stat, glz_stat_name);
+    stat_compress_init(&display_channel->quic_stat, quic_stat_name);
+    stat_compress_init(&display_channel->jpeg_stat, jpeg_stat_name);
+    stat_compress_init(&display_channel->zlib_glz_stat, zlib_stat_name);
+    stat_compress_init(&display_channel->jpeg_alpha_stat, jpeg_alpha_stat_name);
+}
 
-    stream_buf_size = 32*1024;
-    display_channel->send_data.stream_outbuf = spice_malloc(stream_buf_size);
-    display_channel->send_data.stream_outbuf_size = stream_buf_size;
-    red_display_init_glz_data(display_channel);
-    worker->display_channel = display_channel;
 
+static void handle_new_display_channel(RedWorker *worker, RedClient *client, RedsStream *stream,
+                                       int migrate)
+{
+    DisplayChannel *display_channel;
+    DisplayChannelClient *dcc;
+    size_t stream_buf_size;
+    int is_low_bandwidth = main_channel_client_is_low_bandwidth(red_client_get_main(client));
+
+    red_disconnect_all_display_TODO_remove_me((RedChannel *)worker->display_channel);
+    ensure_display_channel_created(worker, migrate);
+    if (!worker->display_channel) {
+        return;
+    }
+    display_channel = worker->display_channel;
+    red_printf("add display channel client");
+    dcc = display_channel_client_create(&display_channel->common, client, stream);
+    if (!dcc) {
+        return;
+    }
 
-    display_channel->send_data.free_list.res =
+    stream_buf_size = 32*1024;
+    dcc->send_data.stream_outbuf = spice_malloc(stream_buf_size);
+    dcc->send_data.stream_outbuf_size = stream_buf_size;
+    red_display_init_glz_data(dcc);
+
+    dcc->send_data.free_list.res =
         spice_malloc(sizeof(SpiceResourceList) +
                      DISPLAY_FREE_LIST_DEFAULT_SIZE * sizeof(SpiceResourceID));
-    display_channel->send_data.free_list.res_size = DISPLAY_FREE_LIST_DEFAULT_SIZE;
+    dcc->send_data.free_list.res_size = DISPLAY_FREE_LIST_DEFAULT_SIZE;
 
     if (worker->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) {
         display_channel->enable_jpeg = is_low_bandwidth;
@@ -9224,17 +9389,15 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
 
     // todo: tune level according to bandwidth
     display_channel->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL;
+    if (!listen_to_new_client_channel(&display_channel->common, &dcc->common, stream)) {
+        goto error;
+    }
+    red_display_client_init_streams(dcc);
+    on_new_display_channel_client(dcc);
+    return;
 
-    red_ref_channel((RedChannel*)display_channel);
-    on_new_display_channel(worker);
-    red_unref_channel((RedChannel*)display_channel);
-
-    stat_compress_init(&display_channel->lz_stat, lz_stat_name);
-    stat_compress_init(&display_channel->glz_stat, glz_stat_name);
-    stat_compress_init(&display_channel->quic_stat, quic_stat_name);
-    stat_compress_init(&display_channel->jpeg_stat, jpeg_stat_name);
-    stat_compress_init(&display_channel->zlib_glz_stat, zlib_stat_name);
-    stat_compress_init(&display_channel->jpeg_alpha_stat, jpeg_alpha_stat_name);
+error:
+    red_channel_client_destroy(&dcc->common.base);
 }
 
 static void red_disconnect_cursor(RedChannel *channel)
@@ -9247,7 +9410,7 @@ static void red_disconnect_cursor(RedChannel *channel)
     common = SPICE_CONTAINEROF(channel, CommonChannel, base);
     ASSERT(channel == (RedChannel *)common->worker->cursor_channel);
     common->worker->cursor_channel = NULL;
-    red_reset_cursor_cache((CursorChannel *)channel);
+    red_channel_apply_clients(channel, red_reset_cursor_cache);
     red_disconnect_channel(channel);
 }
 
@@ -9261,15 +9424,17 @@ static void red_migrate_cursor(RedWorker *worker)
     }
 }
 
-static void on_new_cursor_channel(RedWorker *worker)
+static void on_new_cursor_channel(RedWorker *worker, RedChannelClient *rcc)
 {
     CursorChannel *channel = worker->cursor_channel;
 
     ASSERT(channel);
-    red_channel_ack_zero_messages_window(&channel->common.base);
-    red_channel_push_set_ack(&channel->common.base);
+    red_channel_client_ack_zero_messages_window(rcc);
+    red_channel_client_push_set_ack(rcc);
+    // TODO: why do we check for context.canvas? defer this to after display cc is connected
+    // and test it's canvas? this is just a test to see if there is an active renderer?
     if (worker->surfaces[0].context.canvas && !channel->common.base.migrate) {
-        red_channel_client_pipe_add_type(worker->cursor_channel->common.base.rcc, PIPE_ITEM_TYPE_CURSOR_INIT);
+        red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_CURSOR_INIT);
     }
 }
 
@@ -9279,7 +9444,8 @@ static void cursor_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
     ((CursorItem *)item)->refs++;
 }
 
-static void cursor_channel_release_item_before_push(CursorChannel *cursor_channel, PipeItem *item)
+static void cursor_channel_client_release_item_before_push(CursorChannelClient *ccc,
+                                                           PipeItem *item)
 {
     switch (item->type) {
     case PIPE_ITEM_TYPE_CURSOR:
@@ -9296,11 +9462,12 @@ static void cursor_channel_release_item_before_push(CursorChannel *cursor_channe
     }
 }
 
-static void cursor_channel_release_item_after_push(CursorChannel *cursor_channel, PipeItem *item)
+static void cursor_channel_client_release_item_after_push(CursorChannelClient *ccc,
+                                                          PipeItem *item)
 {
     switch (item->type) {
         case PIPE_ITEM_TYPE_CURSOR:
-            red_release_cursor(cursor_channel->common.worker, SPICE_CONTAINEROF(item, CursorItem, pipe_data));
+            red_release_cursor(ccc->common.worker, SPICE_CONTAINEROF(item, CursorItem, pipe_data));
             break;
         default:
             PANIC("invalid item type");
@@ -9309,48 +9476,64 @@ static void cursor_channel_release_item_after_push(CursorChannel *cursor_channel
 
 static void cursor_channel_release_item(RedChannelClient *rcc, PipeItem *item, int item_pushed)
 {
-    CommonChannel *common = SPICE_CONTAINEROF(rcc->channel, CommonChannel, base);
+    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
+
     ASSERT(item);
 
     if (item_pushed) {
-        cursor_channel_release_item_after_push((CursorChannel *)common, item);
+        cursor_channel_client_release_item_after_push(ccc, item);
     } else {
         red_printf_once("not pushed (%d)", item->type);
-        cursor_channel_release_item_before_push((CursorChannel *)common, item);
+        cursor_channel_client_release_item_before_push(ccc, item);
+    }
+}
+
+static void ensure_cursor_channel_created(RedWorker *worker, int migrate)
+{
+    if (worker->cursor_channel != NULL) {
+        return;
     }
+    red_printf("create cursor channel");
+    worker->cursor_channel = (CursorChannel *)__new_channel(
+        worker, sizeof(*worker->cursor_channel),
+        SPICE_CHANNEL_CURSOR, migrate,
+        handle_channel_events,
+        dispatch_cursor_channel_client_disconnect,
+        cursor_channel_send_item,
+        cursor_channel_hold_pipe_item,
+        cursor_channel_release_item,
+        red_channel_client_handle_message,
+        cursor_channel_on_incoming_error,
+        cursor_channel_on_outgoing_error,
+        NULL,
+        NULL,
+        NULL);
 }
 
 static void red_connect_cursor(RedWorker *worker, RedClient *client, RedsStream *stream,
                                int migrate)
 {
     CursorChannel *channel;
+    CursorChannelClient *ccc;
 
     red_disconnect_cursor((RedChannel *)worker->cursor_channel);
-
-    if (!(channel = (CursorChannel *)__new_channel(
-            worker, sizeof(*channel),
-            SPICE_CHANNEL_CURSOR, client, stream, migrate,
-            handle_channel_events,
-            dispatch_cursor_channel_client_disconnect,
-            cursor_channel_send_item,
-            cursor_channel_hold_pipe_item,
-            cursor_channel_release_item,
-            red_channel_client_handle_message,
-            cursor_channel_on_incoming_error,
-            cursor_channel_on_outgoing_error,
-            NULL,
-            NULL,
-            NULL))) {
+    ensure_cursor_channel_created(worker, migrate);
+    if (worker->cursor_channel == NULL) {
+        red_printf("failed to create cursor channel");
+        return;
+    }
+    channel = worker->cursor_channel;
+    red_printf("add cursor channel client");
+    ccc = cursor_channel_create_rcc(&channel->common, client, stream);
+    if (!ccc) {
         return;
     }
 #ifdef RED_STATISTICS
     channel->stat = stat_add_node(worker->stat, "cursor_channel", TRUE);
     channel->common.base.out_bytes_counter = stat_add_counter(channel->stat, "out_bytes", TRUE);
 #endif
-    ring_init(&channel->cursor_cache_lru);
-    channel->cursor_cache_available = CLIENT_CURSOR_CACHE_SIZE;
-    worker->cursor_channel = channel;
-    on_new_cursor_channel(worker);
+    listen_to_new_client_channel(&channel->common, &ccc->common, stream);
+    on_new_cursor_channel(worker, &ccc->common.base);
 }
 
 typedef struct __attribute__ ((__packed__)) CursorData {
@@ -9362,16 +9545,47 @@ typedef struct __attribute__ ((__packed__)) CursorData {
     SpiceCursor _cursor;
 } CursorData;
 
-static void red_wait_outgoing_item(RedChannel *channel)
+static void red_wait_outgoing_item(RedChannelClient *rcc)
 {
     uint64_t end_time;
     int blocked;
 
-    if (!channel || !red_channel_all_blocked(channel)) {
+    if (!red_channel_client_blocked(rcc)) {
         return;
     }
-    red_ref_channel(channel);
+    end_time = red_now() + DETACH_TIMEOUT;
+    red_printf("blocked");
+
+    do {
+        usleep(DETACH_SLEEP_DURATION);
+        red_channel_client_receive(rcc);
+        red_channel_client_send(rcc);
+    } while ((blocked = red_channel_client_blocked(rcc)) && red_now() < end_time);
 
+    if (blocked) {
+        red_printf("timeout");
+        // TODO - shutting down the socket but we still need to trigger
+        // disconnection. Right now we wait for main channel to error for that.
+        red_channel_client_shutdown(rcc);
+    }
+}
+
+static void rcc_shutdown_if_blocked(RedChannelClient *rcc)
+{
+    if (red_channel_client_blocked(rcc)) {
+        red_channel_client_shutdown(rcc);
+    }
+}
+
+static void red_wait_outgoing_items(RedChannel *channel)
+{
+    uint64_t end_time;
+    int blocked;
+
+    if (!red_channel_any_blocked(channel)) {
+        return;
+    }
+ 
     end_time = red_now() + DETACH_TIMEOUT;
     red_printf("blocked");
 
@@ -9379,59 +9593,52 @@ static void red_wait_outgoing_item(RedChannel *channel)
         usleep(DETACH_SLEEP_DURATION);
         red_channel_receive(channel);
         red_channel_send(channel);
-    } while ((blocked = red_channel_all_blocked(channel)) && red_now() < end_time);
+    } while ((blocked = red_channel_any_blocked(channel)) && red_now() < end_time);
 
     if (blocked) {
         red_printf("timeout");
-        // TODO - not MC friendly
-        red_channel_apply_clients(channel, channel->disconnect);
+        red_channel_apply_clients(channel, rcc_shutdown_if_blocked);
     }
-    red_unref_channel(channel);
 }
 
-static void red_wait_pipe_item_sent(RedChannel *channel, PipeItem *item)
+/* TODO: more evil sync stuff. anything with the word wait in it's name. */
+static void red_wait_pipe_item_sent(RedChannelClient *rcc, PipeItem *item)
 {
-    CommonChannel *common;
+    RedChannel *channel = rcc->channel;
     uint64_t end_time;
     int item_in_pipe;
 
-    if (!channel) {
+    if (!red_channel_client_blocked(rcc)) {
         return;
     }
 
     red_printf("");
-    common = SPICE_CONTAINEROF(channel, CommonChannel, base);
-    red_ref_channel(channel);
-    channel->hold_item(channel->rcc, item);
+    channel->hold_item(rcc, item);
 
     end_time = red_now() + CHANNEL_PUSH_TIMEOUT;
 
-    if (red_channel_all_blocked(channel)) {
-        red_channel_receive(channel);
-        red_channel_send(channel);
+    if (red_channel_client_blocked(rcc)) {
+        red_channel_client_receive(rcc);
+        red_channel_client_send(rcc);
     }
-    // todo: different push for each channel
-    red_push(common->worker);
+    red_channel_client_push(rcc);
 
     while((item_in_pipe = ring_item_is_linked(&item->link)) && (red_now() < end_time)) {
         usleep(CHANNEL_PUSH_SLEEP_DURATION);
-        red_channel_receive(channel);
-        red_channel_send(channel);
-        red_push(common->worker);
+        red_channel_client_receive(rcc);
+        red_channel_client_send(rcc);
+        red_channel_client_push(rcc);
     }
 
     if (item_in_pipe) {
         red_printf("timeout");
-        // TODO - not MC friendly
-        red_channel_apply_clients(channel, channel->disconnect);
+        red_channel_client_disconnect(rcc);
     } else {
-        if (red_channel_item_being_sent(channel, item)) {
-            red_wait_outgoing_item(channel);
+        if (red_channel_client_item_being_sent(rcc, item)) {
+            red_wait_outgoing_item(rcc);
         }
     }
-
-    channel->release_item(channel->rcc, item, FALSE);
-    red_unref_channel(channel);
+    channel->release_item(rcc, item, FALSE);
 }
 
 static void surface_dirty_region_to_rects(RedSurface *surface,
@@ -9549,6 +9756,9 @@ static inline void handle_dev_del_memslot(RedWorker *worker)
 
 static inline void destroy_surface_wait(RedWorker *worker, int surface_id)
 {
+    DisplayChannelClient *dcc = WORKER_TO_DCC(worker);
+    RedChannelClient *rcc = &dcc->common.base;
+
     if (!worker->surfaces[surface_id].context.canvas) {
         return;
     }
@@ -9558,12 +9768,12 @@ static inline void destroy_surface_wait(RedWorker *worker, int surface_id)
        otherwise "current" will hold items that other drawables may depend on, and then
        red_current_clear will remove them from the pipe. */
     red_current_clear(worker, surface_id);
-    red_clear_surface_drawables_from_pipe(worker, surface_id, TRUE);
+    red_clear_surface_drawables_from_pipe(dcc, surface_id, TRUE);
     // in case that the pipe didn't contain any item that is dependent on the surface, but
     // there is one during sending.
-    red_wait_outgoing_item((RedChannel *)worker->display_channel);
-    if (worker->display_channel) {
-        ASSERT(red_channel_no_item_being_sent(&worker->display_channel->common.base));
+    red_wait_outgoing_item(rcc);
+    if (dcc) {
+        ASSERT(red_channel_client_no_item_being_sent(rcc));
     }
 }
 
@@ -9593,17 +9803,19 @@ static inline void red_cursor_reset(RedWorker *worker)
     worker->cursor_position.x = worker->cursor_position.y = 0;
     worker->cursor_trail_length = worker->cursor_trail_frequency = 0;
 
-    if (worker->cursor_channel) {
+    if (cursor_is_connected(worker)) {
         red_channel_pipes_add_type(&worker->cursor_channel->common.base, PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
         if (!worker->cursor_channel->common.base.migrate) {
             red_pipes_add_verb(&worker->cursor_channel->common.base, SPICE_MSG_CURSOR_RESET);
         }
-        red_wait_outgoing_item((RedChannel *)worker->cursor_channel);
+        red_wait_outgoing_items(&worker->cursor_channel->common.base);
         ASSERT(red_channel_no_item_being_sent(&worker->cursor_channel->common.base));
     }
 }
 
 /* called upon device reset */
+
+/* TODO: split me*/
 static inline void handle_dev_destroy_surfaces(RedWorker *worker)
 {
     int i;
@@ -9621,9 +9833,11 @@ static inline void handle_dev_destroy_surfaces(RedWorker *worker)
     }
     ASSERT(ring_is_empty(&worker->streams));
 
-    if (worker->display_channel) {
-        red_channel_client_pipe_add_type(worker->display_channel->common.base.rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
-        red_pipe_add_verb(worker->display_channel->common.base.rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL);
+    if (display_is_connected(worker)) {
+        red_channel_pipes_add_type(&worker->display_channel->common.base,
+                                   PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+        red_pipes_add_verb(&worker->display_channel->common.base,
+                           SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL);
     }
 
     red_display_clear_glz_drawables(worker->display_channel);
@@ -9700,18 +9914,18 @@ static void handle_dev_flush_surfaces(RedWorker *worker)
 {
     flush_all_qxl_commands(worker);
     flush_all_surfaces(worker);
-    red_wait_outgoing_item((RedChannel *)worker->display_channel);
-    red_wait_outgoing_item((RedChannel *)worker->cursor_channel);
+    red_wait_outgoing_items(&worker->display_channel->common.base);
+    red_wait_outgoing_items(&worker->cursor_channel->common.base);
 }
 
 static void handle_dev_stop(RedWorker *worker)
 {
     ASSERT(worker->running);
     worker->running = FALSE;
-    red_display_clear_glz_drawables(worker->display_channel);
+    red_display_client_clear_glz_drawables(WORKER_TO_DCC(worker));
     flush_all_surfaces(worker);
-    red_wait_outgoing_item((RedChannel *)worker->display_channel);
-    red_wait_outgoing_item((RedChannel *)worker->cursor_channel);
+    red_wait_outgoing_items(&worker->display_channel->common.base);
+    red_wait_outgoing_items(&worker->cursor_channel->common.base);
 }
 
 static void handle_dev_start(RedWorker *worker)
@@ -9833,7 +10047,7 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
 
         red_printf("disconnect display client");
         receive_data(worker->channel, &rcc, sizeof(RedChannelClient *));
-        red_disconnect_display(rcc);
+        display_channel_client_disconnect(rcc);
         message = RED_WORKER_MESSAGE_READY;
         write_message(worker->channel, &message);
         break;
@@ -10073,6 +10287,13 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     write_message(worker->channel, &message);
 }
 
+static void red_display_cc_free_glz_drawables(RedChannelClient *rcc)
+{
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
+
+    red_display_handle_glz_drawables_to_free(dcc);
+}
+
 void *red_worker_main(void *arg)
 {
     RedWorker worker;
@@ -10103,11 +10324,12 @@ void *red_worker_main(void *arg)
         num_events = epoll_wait(worker.epoll, events, MAX_EPOLL_SOURCES, worker.epoll_timeout);
         red_handle_streams_timout(&worker);
 
-        if (worker.display_channel && worker.display_channel->glz_dict) {
+        if (worker.display_channel) {
             /* during migration, in the dest, the display channel can be initialized
                while the global lz data not since migrate data msg hasn't been
                received yet */
-            red_display_handle_glz_drawables_to_free(worker.display_channel);
+            red_channel_apply_clients(&worker.display_channel->common.base,
+                red_display_cc_free_glz_drawables);
         }
 
         worker.epoll_timeout = INF_EPOLL_WAIT;
commit 406751888ecade931eab99dd4c4e3a76de7252c8
Author: Alon Levy <alevy at redhat.com>
Date:   Thu May 5 12:49:35 2011 +0300

    server/red_worker: cleanup

diff --git a/server/red_worker.c b/server/red_worker.c
index 0ad7f75..22abe8d 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -933,7 +933,8 @@ static void red_display_release_stream_clip(DisplayChannel* channel, StreamClipI
 static int red_display_free_some_independent_glz_drawables(DisplayChannel *channel);
 static void red_display_free_glz_drawable(DisplayChannel *channel, RedGlzDrawable *drawable);
 static void reset_rate(RedWorker *worker, StreamAgent *stream_agent);
-static BitmapGradualType _get_bitmap_graduality_level(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_id);
+static BitmapGradualType _get_bitmap_graduality_level(RedWorker *worker, SpiceBitmap *bitmap,
+                                                      uint32_t group_id);
 static inline int _stride_is_extra(SpiceBitmap *bitmap);
 static void red_disconnect_cursor(RedChannel *channel);
 static void red_wait_pipe_item_sent(RedChannel *channel, PipeItem *item);
@@ -1214,7 +1215,8 @@ static inline void red_pipe_add_drawable_to_tail(RedWorker *worker, Drawable *dr
     }
     red_handle_drawable_surfaces_client_synced(worker, drawable);
     drawable->refs++;
-    red_channel_client_pipe_add_tail(worker->display_channel->common.base.rcc, &drawable->pipe_item);
+    red_channel_client_pipe_add_tail(worker->display_channel->common.base.rcc,
+                                     &drawable->pipe_item);
 }
 
 static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *drawable,
@@ -1230,7 +1232,8 @@ static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *draw
     }
     red_handle_drawable_surfaces_client_synced(worker, drawable);
     drawable->refs++;
-    red_channel_client_pipe_add_after(worker->display_channel->common.base.rcc, &drawable->pipe_item, &pos_after->pipe_item);
+    red_channel_client_pipe_add_after(worker->display_channel->common.base.rcc,
+                                      &drawable->pipe_item, &pos_after->pipe_item);
 }
 
 static inline PipeItem *red_pipe_get_tail(RedWorker *worker)
@@ -2260,7 +2263,8 @@ static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *strea
         n_rects = pixman_region32_n_rects(&upgrade_item->drawable->tree_item.base.rgn);
         upgrade_item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects));
         upgrade_item->rects->num_rects = n_rects;
-        region_ret_rects(&upgrade_item->drawable->tree_item.base.rgn, upgrade_item->rects->rects, n_rects);
+        region_ret_rects(&upgrade_item->drawable->tree_item.base.rgn,
+                         upgrade_item->rects->rects, n_rects);
         red_channel_client_pipe_add(channel->common.base.rcc, &upgrade_item->base);
     }
     red_detach_stream(worker, stream);
@@ -2641,7 +2645,8 @@ static inline void red_update_copy_graduality(RedWorker* worker, Drawable *drawa
         (bitmap->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
         drawable->copy_bitmap_graduality = BITMAP_GRADUAL_NOT_AVAIL;
     } else  {
-        drawable->copy_bitmap_graduality = _get_bitmap_graduality_level(worker, bitmap, drawable->group_id);
+        drawable->copy_bitmap_graduality =
+            _get_bitmap_graduality_level(worker, bitmap,drawable->group_id);
     }
 }
 
@@ -3197,8 +3202,10 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
 
     bpp = SPICE_SURFACE_FMT_DEPTH(surface->context.format) / 8;
 
-    width = drawable->red_drawable->self_bitmap_area.right - drawable->red_drawable->self_bitmap_area.left;
-    height = drawable->red_drawable->self_bitmap_area.bottom - drawable->red_drawable->self_bitmap_area.top;
+    width = drawable->red_drawable->self_bitmap_area.right
+            - drawable->red_drawable->self_bitmap_area.left;
+    height = drawable->red_drawable->self_bitmap_area.bottom
+            - drawable->red_drawable->self_bitmap_area.top;
     dest_stride = SPICE_ALIGN(width * bpp, 4);
 
     image = spice_new0(SpiceImage, 1);
@@ -3643,7 +3650,8 @@ static void image_cache_eaging(ImageCache *cache)
 #endif
 }
 
-static void localize_bitmap(RedWorker *worker, SpiceImage **image_ptr, SpiceImage *image_store, Drawable *drawable)
+static void localize_bitmap(RedWorker *worker, SpiceImage **image_ptr, SpiceImage *image_store,
+                            Drawable *drawable)
 {
     SpiceImage *image = *image_ptr;
 
@@ -4006,7 +4014,7 @@ static void red_update_area_till(RedWorker *worker, const SpiceRect *area, int s
     region_init(&rgn);
     region_add(&rgn, area);
 
-    // find the first older drawable that intersects with the area 
+    // find the first older drawable that intersects with the area
     do {
         now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link);
         if (region_intersects(&rgn, &now->tree_item.base.rgn)) {
@@ -4016,7 +4024,7 @@ static void red_update_area_till(RedWorker *worker, const SpiceRect *area, int s
     } while ((ring_item = ring_next(ring, ring_item)));
 
     region_destroy(&rgn);
-    
+
     if (!surface_last) {
         return;
     }
@@ -4343,8 +4351,9 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
             red_error("bad command type");
         }
         n++;
-        if ((worker->display_channel && red_channel_all_blocked(&worker->display_channel->common.base)) ||
-            red_now() - start > 10 * 1000 * 1000) {
+        if ((worker->display_channel &&
+             red_channel_all_blocked(&worker->display_channel->common.base))
+            || red_now() - start > 10 * 1000 * 1000) {
             worker->epoll_timeout = 0;
             return n;
         }
@@ -5131,7 +5140,8 @@ typedef uint16_t rgb16_pixel_t;
 #define GRADUAL_MEDIUM_SCORE_TH 0.002
 
 // assumes that stride doesn't overflow
-static BitmapGradualType _get_bitmap_graduality_level(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_id)
+static BitmapGradualType _get_bitmap_graduality_level(RedWorker *worker, SpiceBitmap *bitmap,
+                                                      uint32_t group_id)
 {
     double score = 0.0;
     int num_samples = 0;
@@ -5938,7 +5948,8 @@ static void fill_attr(SpiceMarshaller *m, SpiceLineAttr *attr, uint32_t group_id
     }
 }
 
-static void fill_cursor(CursorChannel *cursor_channel, SpiceCursor *red_cursor, CursorItem *cursor, AddBufInfo *addbuf)
+static void fill_cursor(CursorChannel *cursor_channel, SpiceCursor *red_cursor,
+                        CursorItem *cursor, AddBufInfo *addbuf)
 {
     RedCursorCmd *cursor_cmd;
     addbuf->data = NULL;
@@ -7239,7 +7250,8 @@ static void display_channel_push_release(DisplayChannel *channel, uint8_t type,
 
     if (free_list->res->count == free_list->res_size) {
         SpiceResourceList *new_list;
-        new_list = spice_malloc(sizeof(*new_list) + free_list->res_size * sizeof(SpiceResourceID) * 2);
+        new_list = spice_malloc(sizeof(*new_list) +
+                                free_list->res_size * sizeof(SpiceResourceID) * 2);
         new_list->count = free_list->res->count;
         memcpy(new_list->resources, free_list->res->resources,
                new_list->count * sizeof(SpiceResourceID));
@@ -7251,7 +7263,8 @@ static void display_channel_push_release(DisplayChannel *channel, uint8_t type,
     free_list->res->resources[free_list->res->count++].id = id;
 }
 
-static inline void display_begin_send_message(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
+static inline void display_begin_send_message(RedChannelClient *rcc,
+                                              SpiceMarshaller *base_marshaller)
 {
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     FreeList *free_list = &display_channel->send_data.free_list;
@@ -7268,8 +7281,8 @@ static inline void display_begin_send_message(RedChannelClient *rcc, SpiceMarsha
 
         /* type + size + submessage */
         spice_marshaller_add_uint16(inval_m, SPICE_MSG_DISPLAY_INVAL_LIST);
-        spice_marshaller_add_uint32(inval_m,
-                                    sizeof(*free_list->res) + free_list->res->count * sizeof(free_list->res->resources[0]));
+        spice_marshaller_add_uint32(inval_m, sizeof(*free_list->res) +
+                        free_list->res->count * sizeof(free_list->res->resources[0]));
         spice_marshall_msg_display_inval_list(inval_m, free_list->res);
 
         for (i = 0; i < MAX_CACHE_CLIENTS; i++) {
@@ -7286,8 +7299,8 @@ static inline void display_begin_send_message(RedChannelClient *rcc, SpiceMarsha
 
             /* type + size + submessage */
             spice_marshaller_add_uint16(wait_m, SPICE_MSG_WAIT_FOR_CHANNELS);
-            spice_marshaller_add_uint32(wait_m,
-                                        sizeof(free_list->wait.header) + sync_count * sizeof(free_list->wait.buf[0]));
+            spice_marshaller_add_uint32(wait_m, sizeof(free_list->wait.header) +
+                                                sync_count * sizeof(free_list->wait.buf[0]));
             spice_marshall_msg_wait_for_channels(wait_m, &free_list->wait.header);
             sub_list_len++;
         }
@@ -7487,7 +7500,8 @@ static void display_channel_marshall_migrate(RedChannelClient *rcc,
     display_channel->expect_migrate_mark = TRUE;
 }
 
-static void display_channel_marshall_migrate_data(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
+static void display_channel_marshall_migrate_data(RedChannelClient *rcc,
+                                                  SpiceMarshaller *base_marshaller)
 {
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     DisplayChannelMigrateData display_data;
@@ -7518,7 +7532,8 @@ static void display_channel_marshall_migrate_data(RedChannelClient *rcc, SpiceMa
                              (uint8_t *)&display_data, sizeof(display_data));
 }
 
-static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
+static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc,
+                                                 SpiceMarshaller *base_marshaller)
 {
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     SpiceMsgWaitForChannels wait;
@@ -7541,7 +7556,8 @@ static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc, SpiceMar
     spice_marshall_msg_wait_for_channels(base_marshaller, &wait);
 }
 
-static void display_channel_marshall_reset_cache(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
+static void display_channel_marshall_reset_cache(RedChannelClient *rcc,
+                                                 SpiceMarshaller *base_marshaller)
 {
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     SpiceMsgWaitForChannels wait;
@@ -7686,7 +7702,8 @@ static void red_marshall_image( RedChannelClient *rcc, SpiceMarshaller *m, Image
     spice_chunks_destroy(chunks);
 }
 
-static void red_display_marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller *m, UpgradeItem *item)
+static void red_display_marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller *m,
+                                         UpgradeItem *item)
 {
     RedDrawable *red_drawable;
     SpiceMsgDisplayDrawCopy copy;
@@ -7810,7 +7827,8 @@ static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *bas
     add_buf_from_info(base_marshaller, &info);
 }
 
-static void cursor_channel_marshall_migrate(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
+static void cursor_channel_marshall_migrate(RedChannelClient *rcc,
+                                            SpiceMarshaller *base_marshaller)
 {
     SpiceMsgMigrate migrate;
 
@@ -8456,7 +8474,8 @@ static void push_new_primary_surface(RedWorker *worker)
     DisplayChannel *display_channel;
 
     if ((display_channel = worker->display_channel)) {
-        red_channel_client_pipe_add_type(display_channel->common.base.rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+        red_channel_client_pipe_add_type(display_channel->common.base.rcc,
+                                         PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
         if (!display_channel->common.base.migrate) {
             red_create_surface_item(worker, 0);
         }
@@ -8739,8 +8758,8 @@ static int display_channel_handle_migrate_glz_dictionary(DisplayChannel *channel
     ring_init(&channel->glz_drawables_inst_to_free);
     pthread_mutex_init(&channel->glz_drawables_inst_to_free_lock, NULL);
     return !!(channel->glz_dict = red_restore_glz_dictionary(channel,
-                                                             migrate_info->glz_dict_id,
-                                                             &migrate_info->glz_dict_restore_data));
+                                                         migrate_info->glz_dict_id,
+                                                         &migrate_info->glz_dict_restore_data));
 }
 
 static int display_channel_handle_migrate_mark(RedChannelClient *rcc)
@@ -8833,7 +8852,8 @@ static int display_channel_handle_message(RedChannelClient *rcc, uint32_t size,
             return FALSE;
         }
         ((DisplayChannel *)rcc->channel)->expect_init = FALSE;
-        return display_channel_init((DisplayChannel *)rcc->channel, (SpiceMsgcDisplayInit *)message);
+        return display_channel_init((DisplayChannel *)rcc->channel,
+                                    (SpiceMsgcDisplayInit *)message);
     default:
         return red_channel_client_handle_message(rcc, size, type, message);
     }
@@ -10275,4 +10295,3 @@ static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_i
 }
 
 #endif
-
commit eded227f4c95667bee72ca994a107493eefa7f20
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Apr 11 23:58:52 2011 +0300

    server/smartcard: support multiple clients
    
    each client supplying a smartcard channel gets it's own smartcard. If
    there are not enough smartcards provided by the server (read: qemu)
    then it will be as though there are none.
    
    currently disabled - later patches that enable smartcard don't make
    this channel available to any but the first client.

diff --git a/server/smartcard.c b/server/smartcard.c
index 76ae606..2a3332c 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -21,10 +21,10 @@
 
 #include <arpa/inet.h>
 
+#include <vscard_common.h>
 #include "server/char_device.h"
 #include "server/red_channel.h"
 #include "server/smartcard.h"
-#include "vscard_common.h"
 
 #define SMARTCARD_MAX_READERS 10
 
@@ -136,21 +136,26 @@ void smartcard_char_device_on_message_from_device(
     if (state->reader_id == VSCARD_UNDEFINED_READER_ID && vheader->type != VSC_Init) {
         red_printf("error: reader_id not assigned for message of type %d", vheader->type);
     }
-    sent_header = spice_memdup(vheader, sizeof(*vheader) + vheader->length);
-    /* We patch the reader_id, since the device only knows about itself, and
-     * we know about the sum of readers. */
-    sent_header->reader_id = state->reader_id;
     if (state->rcc) {
+        sent_header = spice_memdup(vheader, sizeof(*vheader) + vheader->length);
+        /* We patch the reader_id, since the device only knows about itself, and
+         * we know about the sum of readers. */
+        sent_header->reader_id = state->reader_id;
         smartcard_on_message_from_device(state->rcc, sent_header);
     }
 }
 
-static void smartcard_readers_detach_all(SmartCardChannel *smartcard_channel)
+static void smartcard_readers_detach_all(RedChannelClient *rcc)
 {
     int i;
+    SmartCardDeviceState *st;
+    // TODO - can track rcc->{sin}
 
     for (i = 0 ; i < g_smartcard_readers.num; ++i) {
-        smartcard_char_device_detach(g_smartcard_readers.sin[i]);
+        st = SPICE_CONTAINEROF(g_smartcard_readers.sin[i]->st, SmartCardDeviceState, base);
+        if (!rcc || st->rcc == rcc) {
+            smartcard_char_device_detach(g_smartcard_readers.sin[i]);
+        }
     }
 }
 
@@ -261,7 +266,7 @@ static void smartcard_char_device_detach(SpiceCharDeviceInstance *char_device)
     smartcard_channel_write_to_reader(&vheader);
 }
 
-static int smartcard_channel_config_socket(RedChannelClient *rcc)
+static int smartcard_channel_client_config_socket(RedChannelClient *rcc)
 {
     return TRUE;
 }
@@ -323,7 +328,8 @@ static void smartcard_channel_send_item(RedChannelClient *rcc, PipeItem *item)
 }
 
 
-static void smartcard_channel_release_pipe_item(RedChannelClient *rcc, PipeItem *item, int item_pushed)
+static void smartcard_channel_release_pipe_item(RedChannelClient *rcc,
+                                      PipeItem *item, int item_pushed)
 {
     if (item->type == PIPE_ITEM_TYPE_MSG) {
         free(((MsgItem*)item)->vheader);
@@ -331,16 +337,16 @@ static void smartcard_channel_release_pipe_item(RedChannelClient *rcc, PipeItem
     free(item);
 }
 
-static void smartcard_channel_disconnect(RedChannelClient *rcc)
+static void smartcard_channel_client_disconnect(RedChannelClient *rcc)
 {
-    smartcard_readers_detach_all((SmartCardChannel*)(rcc->channel));
+    smartcard_readers_detach_all(rcc);
     red_channel_client_destroy(rcc);
 }
 
 /* this is called from both device input and client input. since the device is
  * a usb device, the context is still the main thread (kvm_main_loop, timers)
  * so no mutex is required. */
-static void smartcard_channel_pipe_add_push(RedChannelClient *rcc, PipeItem *item)
+static void smartcard_channel_client_pipe_add_push(RedChannelClient *rcc, PipeItem *item)
 {
     red_channel_client_pipe_add_push(rcc, item);
 }
@@ -349,21 +355,25 @@ static void smartcard_push_error(RedChannelClient *rcc, uint32_t reader_id, VSCE
 {
     ErrorItem *error_item = spice_new0(ErrorItem, 1);
 
+    red_channel_pipe_item_init(rcc->channel, &error_item->base,
+                               PIPE_ITEM_TYPE_ERROR);
+
     error_item->base.type = PIPE_ITEM_TYPE_ERROR;
     error_item->vheader.reader_id = reader_id;
     error_item->vheader.type = VSC_Error;
     error_item->vheader.length = sizeof(error_item->error);
     error_item->error.code = error;
-    smartcard_channel_pipe_add_push(rcc, &error_item->base);
+    smartcard_channel_client_pipe_add_push(rcc, &error_item->base);
 }
 
 static void smartcard_push_vscmsg(RedChannelClient *rcc, VSCMsgHeader *vheader)
 {
     MsgItem *msg_item = spice_new0(MsgItem, 1);
 
-    msg_item->base.type = PIPE_ITEM_TYPE_MSG;
+    red_channel_pipe_item_init(rcc->channel, &msg_item->base,
+                               PIPE_ITEM_TYPE_MSG);
     msg_item->vheader = vheader;
-    smartcard_channel_pipe_add_push(rcc, &msg_item->base);
+    smartcard_channel_client_pipe_add_push(rcc, &msg_item->base);
 }
 
 void smartcard_on_message_from_device(RedChannelClient *rcc, VSCMsgHeader* vheader)
@@ -477,21 +487,20 @@ static void smartcard_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *it
 {
 }
 
-static void smartcard_link(Channel *channel, RedClient *client, RedsStream *stream,
-                        int migration, int num_common_caps,
-                        uint32_t *common_caps, int num_caps,
-                        uint32_t *caps)
+static void smartcard_link(Channel *channel, RedClient *client,
+                        RedsStream *stream, int migration,
+                        int num_common_caps, uint32_t *common_caps,
+                        int num_caps, uint32_t *caps)
 {
-    if (channel->data) {
-        red_channel_destroy((RedChannel*)channel->data);
-        channel->data = NULL;
-    }
+    RedChannelClient *rcc;
+
     if (!channel->data) {
-        channel->data = red_channel_create(sizeof(SmartCardChannel),
-                                        core,
-                                        migration, FALSE /* handle_acks */,
-                                        smartcard_channel_config_socket,
-                                        smartcard_channel_disconnect,
+        channel->data =
+            red_channel_create(sizeof(SmartCardChannel),
+                                        core, migration,
+                                        FALSE /* handle_acks */,
+                                        smartcard_channel_client_config_socket,
+                                        smartcard_channel_client_disconnect,
                                         smartcard_channel_handle_message,
                                         smartcard_channel_alloc_msg_rcv_buf,
                                         smartcard_channel_release_msg_rcv_buf,
@@ -501,13 +510,16 @@ static void smartcard_link(Channel *channel, RedClient *client, RedsStream *stre
                                         NULL,
                                         NULL,
                                         NULL);
+        if (channel->data) {
+            red_channel_init_outgoing_messages_window((RedChannel*)channel->data);
+        } else {
+            red_printf("ERROR: smartcard channel creation failed");
+            return;
+        }
     }
-    if (!channel->data) {
-        red_printf("ERROR: smartcard channel creation failed");
-        return;
-    }
-    red_channel_client_create(sizeof(RedChannelClient), channel->data, client, stream);
-    red_channel_init_outgoing_messages_window((RedChannel*)channel->data);
+    rcc = red_channel_client_create(sizeof(RedChannelClient),
+                                    (RedChannel*)channel->data, client, stream);
+    red_channel_client_ack_zero_messages_window(rcc);
 }
 
 static void smartcard_shutdown(Channel *channel)
commit 9aa62c4baa50321c123b8809fb5bbee00734777d
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Apr 11 23:51:14 2011 +0300

    server/red_tunnel_worker: trivial multi client support
    
    s/TunnelChannel/TunnelChannelClient/
    
    That's about it.  this is probably the wrong way to do it. Not tested
    at all. What do we want, a separate interface per client? same interface
    for all clients? probably the later. This doesn't do that. Not tested,
    so probably doesn't even work.
    
    changes red_channel_pipe_item_is_linked to red_channel_client_pipe_item_is_linked,
    since tunnel channel is the only user, must be done in patch to not break compilation.

diff --git a/server/red_channel.c b/server/red_channel.c
index f084dbe..eab1384 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -763,11 +763,6 @@ int red_channel_client_pipe_item_is_linked(RedChannelClient *rcc,
     return ring_item_is_linked(&item->link);
 }
 
-int red_channel_pipe_item_is_linked(RedChannel *channel, PipeItem *item)
-{
-    return channel->rcc && red_channel_client_pipe_item_is_linked(channel->rcc, item);
-}
-
 void red_channel_client_pipe_add_tail_no_push(RedChannelClient *rcc,
                                               PipeItem *item)
 {
diff --git a/server/red_channel.h b/server/red_channel.h
index f11565b..617ccfb 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -283,7 +283,7 @@ void red_channel_pipes_new_add_tail(RedChannel *channel, new_pipe_item_t creator
 void red_channel_client_pipe_add_push(RedChannelClient *rcc, PipeItem *item);
 void red_channel_client_pipe_add(RedChannelClient *rcc, PipeItem *item);
 void red_channel_client_pipe_add_after(RedChannelClient *rcc, PipeItem *item, PipeItem *pos);
-int red_channel_pipe_item_is_linked(RedChannel *channel, PipeItem *item);
+int red_channel_client_pipe_item_is_linked(RedChannelClient *rcc, PipeItem *item);
 void red_channel_pipe_item_remove(RedChannel *channel, PipeItem *item);
 void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc, PipeItem *item);
 void red_channel_client_pipe_add_tail(RedChannelClient *rcc, PipeItem *item);
diff --git a/server/red_tunnel_worker.c b/server/red_tunnel_worker.c
index 27686b5..38e654e 100644
--- a/server/red_tunnel_worker.c
+++ b/server/red_tunnel_worker.c
@@ -343,7 +343,7 @@ static const ServiceCallback SERVICES_CALLBACKS[3][2] = {
 /****************************************************
 *   Migration data
 ****************************************************/
-typedef struct TunnelChannel TunnelChannel;
+typedef struct TunnelChannelClient TunnelChannelClient;
 
 #define TUNNEL_MIGRATE_DATA_MAGIC (*(uint32_t *)"TMDA")
 #define TUNNEL_MIGRATE_DATA_VERSION 1
@@ -471,7 +471,7 @@ typedef struct TunnelMigrateItem {
     TunnelMigrateSocketItem sockets_data[MAX_SOCKETS_NUM];
 } TunnelMigrateItem;
 
-static inline void tunnel_channel_activate_migrated_sockets(TunnelChannel *channel);
+static inline void tunnel_channel_activate_migrated_sockets(TunnelChannelClient *channel);
 
 /*******************************************************************************************/
 
@@ -485,8 +485,8 @@ static inline void tunnel_channel_activate_migrated_sockets(TunnelChannel *chann
 /* should be checked after each subroutine that may cause error or after calls to slirp routines */
 #define CHECK_TUNNEL_ERROR(channel) (channel->tunnel_error)
 
-struct TunnelChannel {
-    RedChannel base;
+struct TunnelChannelClient {
+    RedChannelClient base;
     TunnelWorker *worker;
     int mig_inprogress;
     int expect_migrate_mark;
@@ -538,7 +538,7 @@ typedef struct TunnelPrintService {
 
 struct TunnelWorker {
     Channel channel_interface; // for reds
-    TunnelChannel *channel;
+    TunnelChannelClient *channel;
 
     SpiceCoreInterface *core_interface;
     SpiceNetWireInstance *sin;
@@ -602,9 +602,11 @@ static void arm_timer(SlirpUsrNetworkInterface *usr_interface, UserTimer *timer,
 
 
 /* reds interface */
-static void handle_tunnel_channel_link(Channel *channel, RedClient *client, RedsStream *stream,
-                                       int migration, int num_common_caps, uint32_t *common_caps,
-                                       int num_caps, uint32_t *caps);
+static void handle_tunnel_channel_link(Channel *channel, RedClient *client,
+                                       RedsStream *stream, int migration,
+                                       int num_common_caps,
+                                       uint32_t *common_caps, int num_caps,
+                                       uint32_t *caps);
 static void handle_tunnel_channel_shutdown(struct Channel *channel);
 static void handle_tunnel_channel_migrate(struct Channel *channel);
 
@@ -615,7 +617,7 @@ static void tunnel_shutdown(TunnelWorker *worker)
     red_printf("");
     /* shutdown input from channel */
     if (worker->channel) {
-        red_channel_shutdown(&worker->channel->base);
+        red_channel_shutdown(worker->channel->base.channel);
     }
 
     /* shutdown socket pipe items */
@@ -724,9 +726,9 @@ static inline RedSocketRawRcvBuf *__tunnel_worker_alloc_socket_rcv_buf(TunnelWor
     return ret;
 }
 
-static inline void __process_rcv_buf_tokens(TunnelChannel *channel, RedSocket *sckt)
+static inline void __process_rcv_buf_tokens(TunnelChannelClient *channel, RedSocket *sckt)
 {
-    if ((sckt->client_status != CLIENT_SCKT_STATUS_OPEN) || red_channel_pipe_item_is_linked(
+    if ((sckt->client_status != CLIENT_SCKT_STATUS_OPEN) || red_channel_client_pipe_item_is_linked(
             &channel->base, &sckt->out_data.token_pipe_item) || channel->mig_inprogress) {
         return;
     }
@@ -734,7 +736,7 @@ static inline void __process_rcv_buf_tokens(TunnelChannel *channel, RedSocket *s
     if ((sckt->in_data.num_tokens >= SOCKET_TOKENS_TO_SEND) ||
         (!sckt->in_data.client_total_num_tokens && !sckt->in_data.ready_chunks_queue.head)) {
         sckt->out_data.token_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_TOKEN;
-        red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.token_pipe_item);
+        red_channel_client_pipe_add(&channel->base, &sckt->out_data.token_pipe_item);
     }
 }
 
@@ -971,7 +973,7 @@ SPICE_GNUC_VISIBLE void spice_server_net_wire_recv_packet(SpiceNetWireInstance *
     TunnelWorker *worker = sin->st->worker;
     ASSERT(worker);
 
-    if (worker->channel && worker->channel->base.migrate) {
+    if (worker->channel && worker->channel->base.channel->migrate) {
         return; // during migration and the tunnel state hasn't been restored yet.
     }
 
@@ -1094,7 +1096,7 @@ static inline TunnelService *__tunnel_worker_add_service(TunnelWorker *worker, u
 #endif
     if (!virt_ip) {
         new_service->pipe_item.type = PIPE_ITEM_TYPE_SERVICE_IP_MAP;
-        red_channel_client_pipe_add(worker->channel->base.rcc, &new_service->pipe_item);
+        red_channel_client_pipe_add(&worker->channel->base, &new_service->pipe_item);
     }
 
     return new_service;
@@ -1152,7 +1154,7 @@ static TunnelPrintService *tunnel_worker_add_print_service(TunnelWorker *worker,
     return service;
 }
 
-static int tunnel_channel_handle_service_add(TunnelChannel *channel,
+static int tunnel_channel_handle_service_add(TunnelChannelClient *channel,
                                              SpiceMsgcTunnelAddGenericService *service_msg)
 {
     TunnelService *out_service = NULL;
@@ -1291,23 +1293,23 @@ static RedSocket *tunnel_worker_create_socket(TunnelWorker *worker, uint16_t loc
 static void tunnel_worker_free_socket(TunnelWorker *worker, RedSocket *sckt)
 {
     if (worker->channel) {
-        if (red_channel_pipe_item_is_linked(&worker->channel->base,
+        if (red_channel_client_pipe_item_is_linked(&worker->channel->base,
                                             &sckt->out_data.data_pipe_item)) {
-            red_channel_pipe_item_remove(&worker->channel->base,
+            red_channel_client_pipe_remove_and_release(&worker->channel->base,
                                          &sckt->out_data.data_pipe_item);
             return;
         }
 
-        if (red_channel_pipe_item_is_linked(&worker->channel->base,
+        if (red_channel_client_pipe_item_is_linked(&worker->channel->base,
                                             &sckt->out_data.status_pipe_item)) {
-            red_channel_pipe_item_remove(&worker->channel->base,
+            red_channel_client_pipe_remove_and_release(&worker->channel->base,
                                          &sckt->out_data.status_pipe_item);
             return;
         }
 
-        if (red_channel_pipe_item_is_linked(&worker->channel->base,
+        if (red_channel_client_pipe_item_is_linked(&worker->channel->base,
                                             &sckt->out_data.token_pipe_item)) {
-            red_channel_pipe_item_remove(&worker->channel->base,
+            red_channel_client_pipe_remove_and_release(&worker->channel->base,
                                          &sckt->out_data.token_pipe_item);
             return;
         }
@@ -1343,39 +1345,41 @@ static inline RedSocket *tunnel_worker_find_socket(TunnelWorker *worker,
     return NULL;
 }
 
-static inline void __tunnel_socket_add_fin_to_pipe(TunnelChannel *channel, RedSocket *sckt)
+static inline void __tunnel_socket_add_fin_to_pipe(TunnelChannelClient *channel, RedSocket *sckt)
 {
-    ASSERT(!red_channel_pipe_item_is_linked(&channel->base, &sckt->out_data.status_pipe_item));
+    ASSERT(!red_channel_client_pipe_item_is_linked(&channel->base, &sckt->out_data.status_pipe_item));
     sckt->out_data.status_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_FIN;
-    red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.status_pipe_item);
+    red_channel_client_pipe_add(&channel->base, &sckt->out_data.status_pipe_item);
 }
 
-static inline void __tunnel_socket_add_close_to_pipe(TunnelChannel *channel, RedSocket *sckt)
+static inline void __tunnel_socket_add_close_to_pipe(TunnelChannelClient *channel, RedSocket *sckt)
 {
     ASSERT(!channel->mig_inprogress);
 
-    if (red_channel_pipe_item_is_linked(&channel->base, &sckt->out_data.status_pipe_item)) {
+    if (red_channel_client_pipe_item_is_linked(&channel->base, &sckt->out_data.status_pipe_item)) {
         ASSERT(sckt->out_data.status_pipe_item.type == PIPE_ITEM_TYPE_SOCKET_FIN);
         // close is stronger than FIN
-        red_channel_pipe_item_remove(&channel->base, &sckt->out_data.status_pipe_item);
+        red_channel_client_pipe_remove_and_release(&channel->base,
+                                        &sckt->out_data.status_pipe_item);
     }
     sckt->pushed_close = TRUE;
     sckt->out_data.status_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_CLOSE;
-    red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.status_pipe_item);
+    red_channel_client_pipe_add(&channel->base, &sckt->out_data.status_pipe_item);
 }
 
-static inline void __tunnel_socket_add_close_ack_to_pipe(TunnelChannel *channel, RedSocket *sckt)
+static inline void __tunnel_socket_add_close_ack_to_pipe(TunnelChannelClient *channel, RedSocket *sckt)
 {
     ASSERT(!channel->mig_inprogress);
 
-    if (red_channel_pipe_item_is_linked(&channel->base, &sckt->out_data.status_pipe_item)) {
+    if (red_channel_client_pipe_item_is_linked(&channel->base, &sckt->out_data.status_pipe_item)) {
         ASSERT(sckt->out_data.status_pipe_item.type == PIPE_ITEM_TYPE_SOCKET_FIN);
         // close is stronger than FIN
-        red_channel_pipe_item_remove(&channel->base, &sckt->out_data.status_pipe_item);
+        red_channel_client_pipe_remove_and_release(&channel->base,
+                                    &sckt->out_data.status_pipe_item);
     }
 
     sckt->out_data.status_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_CLOSED_ACK;
-    red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.status_pipe_item);
+    red_channel_client_pipe_add(&channel->base, &sckt->out_data.status_pipe_item);
 }
 
 /*
@@ -1383,14 +1387,14 @@ static inline void __tunnel_socket_add_close_ack_to_pipe(TunnelChannel *channel,
     If possible, notify slirp to recv data (which will return 0)
     When close ack is received from client, we notify slirp (maybe again) if needed.
 */
-static void tunnel_socket_force_close(TunnelChannel *channel, RedSocket *sckt)
+static void tunnel_socket_force_close(TunnelChannelClient *channel, RedSocket *sckt)
 {
-    if (red_channel_pipe_item_is_linked(&channel->base, &sckt->out_data.token_pipe_item)) {
-        red_channel_pipe_item_remove(&channel->base, &sckt->out_data.token_pipe_item);
+    if (red_channel_client_pipe_item_is_linked(&channel->base, &sckt->out_data.token_pipe_item)) {
+        red_channel_client_pipe_remove_and_release(&channel->base, &sckt->out_data.token_pipe_item);
     }
 
-    if (red_channel_pipe_item_is_linked(&channel->base, &sckt->out_data.data_pipe_item)) {
-        red_channel_pipe_item_remove(&channel->base, &sckt->out_data.data_pipe_item);
+    if (red_channel_client_pipe_item_is_linked(&channel->base, &sckt->out_data.data_pipe_item)) {
+        red_channel_client_pipe_remove_and_release(&channel->base, &sckt->out_data.data_pipe_item);
     }
 
 
@@ -1412,13 +1416,13 @@ static void tunnel_socket_force_close(TunnelChannel *channel, RedSocket *sckt)
     }
 }
 
-static int tunnel_channel_handle_socket_connect_ack(TunnelChannel *channel, RedSocket *sckt,
+static int tunnel_channel_handle_socket_connect_ack(TunnelChannelClient *channel, RedSocket *sckt,
                                                     uint32_t tokens)
 {
 #ifdef DEBUG_NETWORK
     red_printf("TUNNEL_DBG");
 #endif
-    if (channel->mig_inprogress || channel->base.migrate) {
+    if (channel->mig_inprogress || channel->base.channel->migrate) {
         sckt->mig_client_status_msg = SPICE_MSGC_TUNNEL_SOCKET_OPEN_ACK;
         sckt->mig_open_ack_tokens = tokens;
         return TRUE;
@@ -1446,12 +1450,12 @@ static int tunnel_channel_handle_socket_connect_ack(TunnelChannel *channel, RedS
     return (!CHECK_TUNNEL_ERROR(channel));
 }
 
-static int tunnel_channel_handle_socket_connect_nack(TunnelChannel *channel, RedSocket *sckt)
+static int tunnel_channel_handle_socket_connect_nack(TunnelChannelClient *channel, RedSocket *sckt)
 {
 #ifdef DEBUG_NETWORK
     PRINT_SCKT(sckt);
 #endif
-    if (channel->mig_inprogress || channel->base.migrate) {
+    if (channel->mig_inprogress || channel->base.channel->migrate) {
         sckt->mig_client_status_msg = SPICE_MSGC_TUNNEL_SOCKET_OPEN_NACK;
         return TRUE;
     }
@@ -1471,12 +1475,12 @@ static int tunnel_channel_handle_socket_connect_nack(TunnelChannel *channel, Red
     return (!CHECK_TUNNEL_ERROR(channel));
 }
 
-static int tunnel_channel_handle_socket_fin(TunnelChannel *channel, RedSocket *sckt)
+static int tunnel_channel_handle_socket_fin(TunnelChannelClient *channel, RedSocket *sckt)
 {
 #ifdef DEBUG_NETWORK
     PRINT_SCKT(sckt);
 #endif
-    if (channel->mig_inprogress || channel->base.migrate) {
+    if (channel->mig_inprogress || channel->base.channel->migrate) {
         sckt->mig_client_status_msg = SPICE_MSGC_TUNNEL_SOCKET_FIN;
         return TRUE;
     }
@@ -1506,7 +1510,7 @@ static int tunnel_channel_handle_socket_fin(TunnelChannel *channel, RedSocket *s
     return (!CHECK_TUNNEL_ERROR(channel));
 }
 
-static int tunnel_channel_handle_socket_closed(TunnelChannel *channel, RedSocket *sckt)
+static int tunnel_channel_handle_socket_closed(TunnelChannelClient *channel, RedSocket *sckt)
 {
     int prev_client_status = sckt->client_status;
 
@@ -1514,7 +1518,7 @@ static int tunnel_channel_handle_socket_closed(TunnelChannel *channel, RedSocket
     PRINT_SCKT(sckt);
 #endif
 
-    if (channel->mig_inprogress || channel->base.migrate) {
+    if (channel->mig_inprogress || channel->base.channel->migrate) {
         sckt->mig_client_status_msg = SPICE_MSGC_TUNNEL_SOCKET_CLOSED;
         return TRUE;
     }
@@ -1555,12 +1559,12 @@ static int tunnel_channel_handle_socket_closed(TunnelChannel *channel, RedSocket
     return (!CHECK_TUNNEL_ERROR(channel));
 }
 
-static int tunnel_channel_handle_socket_closed_ack(TunnelChannel *channel, RedSocket *sckt)
+static int tunnel_channel_handle_socket_closed_ack(TunnelChannelClient *channel, RedSocket *sckt)
 {
 #ifdef DEBUG_NETWORK
     PRINT_SCKT(sckt);
 #endif
-    if (channel->mig_inprogress || channel->base.migrate) {
+    if (channel->mig_inprogress || channel->base.channel->migrate) {
         sckt->mig_client_status_msg = SPICE_MSGC_TUNNEL_SOCKET_CLOSED_ACK;
         return TRUE;
     }
@@ -1582,7 +1586,7 @@ static int tunnel_channel_handle_socket_closed_ack(TunnelChannel *channel, RedSo
     return (!CHECK_TUNNEL_ERROR(channel));
 }
 
-static int tunnel_channel_handle_socket_receive_data(TunnelChannel *channel, RedSocket *sckt,
+static int tunnel_channel_handle_socket_receive_data(TunnelChannelClient *channel, RedSocket *sckt,
                                                      RedSocketRawRcvBuf *recv_data, int buf_size)
 {
     if ((sckt->client_status == CLIENT_SCKT_STATUS_SHUTDOWN_SEND) ||
@@ -1598,7 +1602,7 @@ static int tunnel_channel_handle_socket_receive_data(TunnelChannel *channel, Red
         __tunnel_worker_free_socket_rcv_buf(sckt->worker, recv_data);
         return (!CHECK_TUNNEL_ERROR(channel));
     } else if ((sckt->in_data.num_buffers == MAX_SOCKET_IN_BUFFERS) &&
-               !channel->mig_inprogress && !channel->base.migrate) {
+               !channel->mig_inprogress && !channel->base.channel->migrate) {
         red_printf("socket in buffers overflow, socket will be closed"
                    " (local_port=%d, service_id=%d)",
                    ntohs(sckt->local_port), sckt->far_service->id);
@@ -1630,16 +1634,16 @@ static inline int __client_socket_can_receive(RedSocket *sckt)
             !sckt->worker->channel->mig_inprogress);
 }
 
-static int tunnel_channel_handle_socket_token(TunnelChannel *channel, RedSocket *sckt,
+static int tunnel_channel_handle_socket_token(TunnelChannelClient *channel, RedSocket *sckt,
                                               SpiceMsgcTunnelSocketTokens *message)
 {
     sckt->out_data.num_tokens += message->num_tokens;
 
     if (__client_socket_can_receive(sckt) && sckt->out_data.ready_chunks_queue.head &&
-        !red_channel_pipe_item_is_linked(&channel->base, &sckt->out_data.data_pipe_item)) {
+        !red_channel_client_pipe_item_is_linked(&channel->base, &sckt->out_data.data_pipe_item)) {
         // data is pending to be sent
         sckt->out_data.data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-        red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.data_pipe_item);
+        red_channel_client_pipe_add(&channel->base, &sckt->out_data.data_pipe_item);
     }
 
     return TRUE;
@@ -1648,7 +1652,7 @@ static int tunnel_channel_handle_socket_token(TunnelChannel *channel, RedSocket
 static uint8_t *tunnel_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
                                                  SpiceDataHeader *msg_header)
 {
-    TunnelChannel *tunnel_channel = (TunnelChannel *)rcc->channel;
+    TunnelChannelClient *tunnel_channel = (TunnelChannelClient *)rcc->channel;
 
     if (msg_header->type == SPICE_MSGC_TUNNEL_SOCKET_DATA) {
         return (__tunnel_worker_alloc_socket_rcv_buf(tunnel_channel->worker)->buf);
@@ -1664,7 +1668,7 @@ static uint8_t *tunnel_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
 static void tunnel_channel_release_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header,
                                                uint8_t *msg)
 {
-    TunnelChannel *tunnel_channel = (TunnelChannel *)rcc->channel;
+    TunnelChannelClient *tunnel_channel = (TunnelChannelClient *)rcc->channel;
 
     if (msg_header->type == SPICE_MSGC_TUNNEL_SOCKET_DATA) {
         ASSERT(!(SPICE_CONTAINEROF(msg, RedSocketRawRcvBuf, buf)->base.usr_opaque));
@@ -1673,7 +1677,7 @@ static void tunnel_channel_release_msg_rcv_buf(RedChannelClient *rcc, SpiceDataH
     }
 }
 
-static void __tunnel_channel_fill_service_migrate_item(TunnelChannel *channel,
+static void __tunnel_channel_fill_service_migrate_item(TunnelChannelClient *channel,
                                                        TunnelService *service,
                                                        TunnelMigrateServiceItem *migrate_item)
 {
@@ -1695,7 +1699,7 @@ static void __tunnel_channel_fill_service_migrate_item(TunnelChannel *channel,
     memcpy(general_data->virt_ip, &service->virt_ip.s_addr, 4);
 }
 
-static void __tunnel_channel_fill_socket_migrate_item(TunnelChannel *channel, RedSocket *sckt,
+static void __tunnel_channel_fill_socket_migrate_item(TunnelChannelClient *channel, RedSocket *sckt,
                                                       TunnelMigrateSocketItem *migrate_item)
 {
     TunnelMigrateSocket *mig_sckt = &migrate_item->mig_socket;
@@ -1747,9 +1751,9 @@ static void __tunnel_channel_fill_socket_migrate_item(TunnelChannel *channel, Re
 }
 
 static void release_migrate_item(TunnelMigrateItem *item);
-static int tunnel_channel_handle_migrate_mark(RedChannelClient *rcc)
+static int tunnel_channel_handle_migrate_mark(RedChannelClient *base)
 {
-    TunnelChannel *channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    TunnelChannelClient *channel = SPICE_CONTAINEROF(base->channel, TunnelChannelClient, base);
     TunnelMigrateItem *migrate_item = NULL;
     TunnelService *service;
     TunnelMigrateServiceItem *mig_service;
@@ -1809,7 +1813,7 @@ static int tunnel_channel_handle_migrate_mark(RedChannelClient *rcc)
         }
     }
 
-    red_channel_client_pipe_add(channel->base.rcc, &migrate_item->base);
+    red_channel_client_pipe_add(&channel->base, &migrate_item->base);
 
     return TRUE;
 error:
@@ -1939,7 +1943,7 @@ static void __restore_process_queue(RedSocket *sckt, TunneledBufferProcessQueue
     }
 }
 
-static void tunnel_channel_restore_migrated_service(TunnelChannel *channel,
+static void tunnel_channel_restore_migrated_service(TunnelChannelClient *channel,
                                                     TunnelMigrateService *mig_service,
                                                     uint8_t *data_buf)
 {
@@ -1974,7 +1978,7 @@ static void tunnel_channel_restore_migrated_service(TunnelChannel *channel,
     }
 }
 
-static void tunnel_channel_restore_migrated_socket(TunnelChannel *channel,
+static void tunnel_channel_restore_migrated_socket(TunnelChannelClient *channel,
                                                    TunnelMigrateSocket *mig_socket,
                                                    uint8_t *data_buf)
 {
@@ -2060,7 +2064,7 @@ static void tunnel_channel_restore_migrated_socket(TunnelChannel *channel,
     }
 }
 
-static void tunnel_channel_restore_socket_state(TunnelChannel *channel, RedSocket *sckt)
+static void tunnel_channel_restore_socket_state(TunnelChannelClient *channel, RedSocket *sckt)
 {
     int ret = TRUE;
     red_printf("");
@@ -2110,10 +2114,10 @@ static void tunnel_channel_restore_socket_state(TunnelChannel *channel, RedSocke
 
     // handling data transfer
     if (__client_socket_can_receive(sckt) && sckt->out_data.ready_chunks_queue.head) {
-        if (!red_channel_pipe_item_is_linked(
+        if (!red_channel_client_pipe_item_is_linked(
                 &channel->base, &sckt->out_data.data_pipe_item)) {
             sckt->out_data.data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-            red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.data_pipe_item);
+            red_channel_client_pipe_add(&channel->base, &sckt->out_data.data_pipe_item);
         }
     }
 
@@ -2139,7 +2143,7 @@ static void tunnel_channel_restore_socket_state(TunnelChannel *channel, RedSocke
     __process_rcv_buf_tokens(channel, sckt);
 }
 
-static inline void tunnel_channel_activate_migrated_sockets(TunnelChannel *channel)
+static inline void tunnel_channel_activate_migrated_sockets(TunnelChannelClient *channel)
 {
     // if we are overgoing migration again, no need to restore the state, we will wait
     // for the next host.
@@ -2162,7 +2166,7 @@ static inline void tunnel_channel_activate_migrated_sockets(TunnelChannel *chann
     }
 }
 
-static uint64_t tunnel_channel_handle_migrate_data_get_serial(RedChannelClient *rcc,
+static uint64_t tunnel_channel_handle_migrate_data_get_serial(RedChannelClient *base,
                                               uint32_t size, void *msg)
 {
     TunnelMigrateData *migrate_data = msg;
@@ -2175,10 +2179,10 @@ static uint64_t tunnel_channel_handle_migrate_data_get_serial(RedChannelClient *
     return migrate_data->message_serial;
 }
 
-static uint64_t tunnel_channel_handle_migrate_data(RedChannelClient *rcc,
+static uint64_t tunnel_channel_handle_migrate_data(RedChannelClient *base,
                                               uint32_t size, void *msg)
 {
-    TunnelChannel *channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    TunnelChannelClient *channel = SPICE_CONTAINEROF(base->channel, TunnelChannelClient, base);
     TunnelMigrateSocketList *sockets_list;
     TunnelMigrateServicesList *services_list;
     TunnelMigrateData *migrate_data = msg;
@@ -2229,8 +2233,8 @@ static uint64_t tunnel_channel_handle_migrate_data(RedChannelClient *rcc,
     }
 
     // activate channel
-    channel->base.migrate = FALSE;
-    red_channel_init_outgoing_messages_window(&channel->base);
+    channel->base.channel->migrate = FALSE;
+    red_channel_init_outgoing_messages_window(channel->base.channel);
 
     tunnel_channel_activate_migrated_sockets(channel);
 
@@ -2247,7 +2251,7 @@ error:
 //  msg was allocated by tunnel_channel_alloc_msg_rcv_buf
 static int tunnel_channel_handle_message(RedChannelClient *rcc, SpiceDataHeader *header, uint8_t *msg)
 {
-    TunnelChannel *tunnel_channel = (TunnelChannel *)rcc->channel;
+    TunnelChannelClient *tunnel_channel = (TunnelChannelClient *)rcc->channel;
     RedSocket *sckt = NULL;
     // retrieve the sckt
     switch (header->type) {
@@ -2351,10 +2355,10 @@ static int tunnel_channel_handle_message(RedChannelClient *rcc, SpiceDataHeader
 
 static void tunnel_channel_marshall_migrate(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel;
+    TunnelChannelClient *tunnel_channel;
 
     ASSERT(rcc);
-    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     tunnel_channel->send_data.u.migrate.flags =
         SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER;
     tunnel_channel->expect_migrate_mark = TRUE;
@@ -2364,7 +2368,7 @@ static void tunnel_channel_marshall_migrate(RedChannelClient *rcc, SpiceMarshall
         sizeof(SpiceMsgMigrate));
 }
 
-static int __tunnel_channel_marshall_process_bufs_migrate_data(TunnelChannel *channel,
+static int __tunnel_channel_marshall_process_bufs_migrate_data(TunnelChannelClient *channel,
                                     SpiceMarshaller *m, TunneledBufferProcessQueue *queue)
 {
     int buf_offset = queue->head_offset;
@@ -2381,7 +2385,7 @@ static int __tunnel_channel_marshall_process_bufs_migrate_data(TunnelChannel *ch
     return size;
 }
 
-static int __tunnel_channel_marshall_ready_bufs_migrate_data(TunnelChannel *channel,
+static int __tunnel_channel_marshall_ready_bufs_migrate_data(TunnelChannelClient *channel,
                                     SpiceMarshaller *m, ReadyTunneledChunkQueue *queue)
 {
     int offset = queue->offset;
@@ -2398,7 +2402,7 @@ static int __tunnel_channel_marshall_ready_bufs_migrate_data(TunnelChannel *chan
 }
 
 // returns the size to send
-static int __tunnel_channel_marshall_service_migrate_data(TunnelChannel *channel,
+static int __tunnel_channel_marshall_service_migrate_data(TunnelChannelClient *channel,
                                                       SpiceMarshaller *m,
                                                       TunnelMigrateServiceItem *item,
                                                       int offset)
@@ -2433,7 +2437,7 @@ static int __tunnel_channel_marshall_service_migrate_data(TunnelChannel *channel
 }
 
 // returns the size to send
-static int __tunnel_channel_marshall_socket_migrate_data(TunnelChannel *channel,
+static int __tunnel_channel_marshall_socket_migrate_data(TunnelChannelClient *channel,
                                 SpiceMarshaller *m, TunnelMigrateSocketItem *item, int offset)
 {
     RedSocket *sckt = item->socket;
@@ -2501,14 +2505,14 @@ static int __tunnel_channel_marshall_socket_migrate_data(TunnelChannel *channel,
 static void tunnel_channel_marshall_migrate_data(RedChannelClient *rcc,
                                         SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel;
+    TunnelChannelClient *tunnel_channel;
     TunnelMigrateData *migrate_data;
     TunnelMigrateItem *migrate_item = (TunnelMigrateItem *)item;
     int i;
 
     uint32_t data_buf_offset = 0; // current location in data[0] field
     ASSERT(rcc);
-    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     migrate_data = &tunnel_channel->send_data.u.migrate_data;
 
     migrate_data->magic = TUNNEL_MIGRATE_DATA_MAGIC;
@@ -2549,10 +2553,10 @@ static void tunnel_channel_marshall_migrate_data(RedChannelClient *rcc,
 
 static void tunnel_channel_marshall_init(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *channel;
+    TunnelChannelClient *channel;
 
     ASSERT(rcc);
-    channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     channel->send_data.u.init.max_socket_data_size = MAX_SOCKET_DATA_SIZE;
     channel->send_data.u.init.max_num_of_sockets = MAX_SOCKETS_NUM;
 
@@ -2562,10 +2566,10 @@ static void tunnel_channel_marshall_init(RedChannelClient *rcc, SpiceMarshaller
 
 static void tunnel_channel_marshall_service_ip_map(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel;
+    TunnelChannelClient *tunnel_channel;
     TunnelService *service = SPICE_CONTAINEROF(item, TunnelService, pipe_item);
 
-    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     tunnel_channel->send_data.u.service_ip.service_id = service->id;
     tunnel_channel->send_data.u.service_ip.virtual_ip.type = SPICE_TUNNEL_IP_TYPE_IPv4;
 
@@ -2577,11 +2581,11 @@ static void tunnel_channel_marshall_service_ip_map(RedChannelClient *rcc, SpiceM
 
 static void tunnel_channel_marshall_socket_open(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel;
+    TunnelChannelClient *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, status_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
-    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     tunnel_channel->send_data.u.socket_open.connection_id = sckt->connection_id;
     tunnel_channel->send_data.u.socket_open.service_id = sckt->far_service->id;
     tunnel_channel->send_data.u.socket_open.tokens = SOCKET_WINDOW_SIZE;
@@ -2598,13 +2602,13 @@ static void tunnel_channel_marshall_socket_open(RedChannelClient *rcc, SpiceMars
 
 static void tunnel_channel_marshall_socket_fin(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel;
+    TunnelChannelClient *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, status_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
     ASSERT(!sckt->out_data.ready_chunks_queue.head);
 
-    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     if (sckt->out_data.process_queue->head) {
         red_printf("socket sent FIN but there are still buffers in outgoing process queue"
                    "(local_port=%d, service_id=%d)",
@@ -2623,11 +2627,11 @@ static void tunnel_channel_marshall_socket_fin(RedChannelClient *rcc, SpiceMarsh
 
 static void tunnel_channel_marshall_socket_close(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel;
+    TunnelChannelClient *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, status_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
-    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     // can happen when it is a forced close
     if (sckt->out_data.ready_chunks_queue.head) {
         red_printf("socket closed but there are still buffers in outgoing ready queue"
@@ -2654,11 +2658,11 @@ static void tunnel_channel_marshall_socket_close(RedChannelClient *rcc, SpiceMar
 
 static void tunnel_channel_marshall_socket_closed_ack(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel;
+    TunnelChannelClient *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, status_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
-    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     tunnel_channel->send_data.u.socket_close_ack.connection_id = sckt->connection_id;
 
     // pipe item is null because we free the sckt.
@@ -2678,14 +2682,14 @@ static void tunnel_channel_marshall_socket_closed_ack(RedChannelClient *rcc, Spi
 
 static void tunnel_channel_marshall_socket_token(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel;
+    TunnelChannelClient *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, token_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
     /* notice that the num of tokens sent can be > SOCKET_TOKENS_TO_SEND, since
        the sending is performed after the pipe item was pushed */
 
-    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     tunnel_channel->send_data.u.socket_token.connection_id = sckt->connection_id;
 
     if (sckt->in_data.num_tokens > 0) {
@@ -2705,8 +2709,8 @@ static void tunnel_channel_marshall_socket_token(RedChannelClient *rcc, SpiceMar
 
 static void tunnel_channel_marshall_socket_out_data(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel;
-    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    TunnelChannelClient *tunnel_channel;
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannelClient, base);
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, data_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
     ReadyTunneledChunk *chunk;
@@ -2792,10 +2796,10 @@ static void tunnel_worker_release_socket_out_data(TunnelWorker *worker, PipeItem
             if (sckt_out_data->ready_chunks_queue.head) {
                 // the pipe item may already be linked, if for example the send was
                 // blocked and before it finished and called release, tunnel_socket_send was called
-                if (!red_channel_pipe_item_is_linked(
+                if (!red_channel_client_pipe_item_is_linked(
                         &worker->channel->base, &sckt_out_data->data_pipe_item)) {
                     sckt_out_data->data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-                    red_channel_client_pipe_add(worker->channel->base.rcc, &sckt_out_data->data_pipe_item);
+                    red_channel_client_pipe_add(&worker->channel->base, &sckt_out_data->data_pipe_item);
                 }
             } else if ((sckt->slirp_status == SLIRP_SCKT_STATUS_SHUTDOWN_SEND) ||
                        (sckt->slirp_status == SLIRP_SCKT_STATUS_WAIT_CLOSE)) {
@@ -2876,7 +2880,8 @@ static void tunnel_channel_release_pipe_item(RedChannelClient *rcc, PipeItem *it
         break;
     case PIPE_ITEM_TYPE_SOCKET_DATA:
         if (item_pushed) {
-            tunnel_worker_release_socket_out_data(((TunnelChannel *)rcc->channel)->worker, item);
+            tunnel_worker_release_socket_out_data(
+                SPICE_CONTAINEROF(rcc, TunnelChannelClient, base)->worker, item);
         }
         break;
     case PIPE_ITEM_TYPE_MIGRATE:
@@ -2959,7 +2964,7 @@ static int tunnel_socket_connect(SlirpUsrNetworkInterface *usr_interface,
 #endif
     *o_usr_s = sckt;
     sckt->out_data.status_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_OPEN;
-    red_channel_client_pipe_add(worker->channel->base.rcc, &sckt->out_data.status_pipe_item);
+    red_channel_client_pipe_add(&worker->channel->base, &sckt->out_data.status_pipe_item);
 
     errno = EINPROGRESS;
     return -1;
@@ -3045,10 +3050,10 @@ static int tunnel_socket_send(SlirpUsrNetworkInterface *usr_interface, UserSocke
         sckt->out_data.data_size += size_to_send;
 
         if (sckt->out_data.ready_chunks_queue.head &&
-            !red_channel_pipe_item_is_linked(&worker->channel->base,
+            !red_channel_client_pipe_item_is_linked(&worker->channel->base,
                                              &sckt->out_data.data_pipe_item)) {
             sckt->out_data.data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-            red_channel_client_pipe_add(worker->channel->base.rcc, &sckt->out_data.data_pipe_item);
+            red_channel_client_pipe_add(&worker->channel->base, &sckt->out_data.data_pipe_item);
         }
     }
 
@@ -3395,7 +3400,7 @@ static void tunnel_worker_disconnect_slirp(TunnelWorker *worker)
    since it closes all its sockets and slirp is not aware of it */
 static void tunnel_channel_disconnect(RedChannel *channel)
 {
-    TunnelChannel *tunnel_channel = (TunnelChannel *)channel;
+    TunnelChannelClient *tunnel_channel = (TunnelChannelClient *)channel;
     TunnelWorker *worker;
     if (!channel) {
         return;
@@ -3418,15 +3423,15 @@ static void tunnel_channel_disconnect_client(RedChannelClient *rcc)
 
 /* interface for reds */
 
-static void on_new_tunnel_channel(TunnelChannel *channel)
+static void on_new_tunnel_channel(TunnelChannelClient *tcc)
 {
-    red_channel_push_set_ack(&channel->base);
+    red_channel_client_push_set_ack(&tcc->base);
 
-    if (channel->base.migrate) {
-        channel->expect_migrate_data = TRUE;
+    if (tcc->base.channel->migrate) {
+        tcc->expect_migrate_data = TRUE;
     } else {
-        red_channel_init_outgoing_messages_window(&channel->base);
-        red_channel_client_pipe_add_type(channel->base.rcc, PIPE_ITEM_TYPE_TUNNEL_INIT);
+        red_channel_init_outgoing_messages_window(tcc->base.channel);
+        red_channel_client_pipe_add_type(&tcc->base, PIPE_ITEM_TYPE_TUNNEL_INIT);
     }
 }
 
@@ -3434,18 +3439,22 @@ static void tunnel_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
 }
 
-static void handle_tunnel_channel_link(Channel *channel, RedClient *client, RedsStream *stream,
-                                       int migration, int num_common_caps, uint32_t *common_caps,
-                                       int num_caps, uint32_t *caps)
+static void handle_tunnel_channel_link(Channel *channel, RedClient *client,
+                                       RedsStream *stream, int migration,
+                                       int num_common_caps,
+                                       uint32_t *common_caps, int num_caps,
+                                       uint32_t *caps)
 {
-    TunnelChannel *tunnel_channel;
+    TunnelChannelClient *tcc;
     TunnelWorker *worker = (TunnelWorker *)channel->data;
+    RedChannel *tunnel_channel;
+
     if (worker->channel) {
-        tunnel_channel_disconnect(&worker->channel->base);
+        tunnel_channel_disconnect(worker->channel->base.channel);
     }
 
-    tunnel_channel =
-        (TunnelChannel *)red_channel_create(sizeof(*tunnel_channel), worker->core_interface,
+    tunnel_channel = red_channel_create(sizeof(RedChannel),
+                                            worker->core_interface,
                                             migration, TRUE,
                                             tunnel_channel_config_socket,
                                             tunnel_channel_disconnect_client,
@@ -3462,18 +3471,20 @@ static void handle_tunnel_channel_link(Channel *channel, RedClient *client, Reds
     if (!tunnel_channel) {
         return;
     }
-    red_channel_client_create(sizeof(RedChannelClient), &tunnel_channel->base, client, stream);
+    tcc = (TunnelChannelClient*)red_channel_client_create(
+                    sizeof(TunnelChannelClient),
+                    tunnel_channel, client, stream);
 
-    tunnel_channel->worker = worker;
-    tunnel_channel->worker->channel = tunnel_channel;
+    tcc->worker = worker;
+    tcc->worker->channel = tcc;
     net_slirp_set_net_interface(&worker->tunnel_interface.base);
 
-    on_new_tunnel_channel(tunnel_channel);
+    on_new_tunnel_channel(tcc);
 }
 
 static void handle_tunnel_channel_shutdown(struct Channel *channel)
 {
-    tunnel_channel_disconnect(&((TunnelWorker *)channel->data)->channel->base);
+    tunnel_channel_disconnect(((TunnelWorker *)channel->data)->channel->base.channel);
 }
 
 static void handle_tunnel_channel_migrate(struct Channel *channel)
@@ -3481,9 +3492,9 @@ static void handle_tunnel_channel_migrate(struct Channel *channel)
 #ifdef DEBUG_NETWORK
     red_printf("TUNNEL_DBG: MIGRATE STARTED");
 #endif
-    TunnelChannel *tunnel_channel = ((TunnelWorker *)channel->data)->channel;
+    TunnelChannelClient *tunnel_channel = ((TunnelWorker *)channel->data)->channel;
     tunnel_channel->mig_inprogress = TRUE;
     net_slirp_freeze();
-    red_channel_client_pipe_add_type(tunnel_channel->base.rcc, PIPE_ITEM_TYPE_MIGRATE);
+    red_channel_client_pipe_add_type(&tunnel_channel->base, PIPE_ITEM_TYPE_MIGRATE);
 }
 
commit 82524cec130abd1c5c1e040adee856c44f349fcc
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Apr 11 22:51:54 2011 +0300

    server/inputs_channel: support multiple clients
    
    from server events are broadcast - leds change. The rest is client
    to server, so it is just passed on.

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index 0fd4bd6..b8d7801 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -59,11 +59,15 @@ struct SpiceTabletState {
     int dummy;
 };
 
+typedef struct InputsChannelClient {
+    RedChannelClient base;
+    uint32_t motion_count;
+} InputsChannelClient;
+
 typedef struct InputsChannel {
     RedChannel base;
     uint8_t recv_buf[RECEIVE_BUF_SIZE];
     VDAgentMouseState mouse_state;
-    uint32_t motion_count;
 } InputsChannel;
 
 enum {
@@ -221,15 +225,15 @@ static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
     return sif->get_leds(sin);
 }
 
-static KeyModifiersPipeItem *inputs_key_modifiers_item_new(
-    InputsChannel *inputs_channel, uint8_t modifiers)
+static PipeItem *inputs_key_modifiers_item_new(
+    RedChannelClient *rcc, void *data, int num)
 {
     KeyModifiersPipeItem *item = spice_malloc(sizeof(KeyModifiersPipeItem));
 
-    red_channel_pipe_item_init(&inputs_channel->base, &item->base,
+    red_channel_pipe_item_init(rcc->channel, &item->base,
                                PIPE_ITEM_KEY_MODIFIERS);
-    item->modifiers = modifiers;
-    return item;
+    item->modifiers = *(uint8_t *)data;
+    return &item->base;
 }
 
 static void inputs_channel_release_pipe_item(RedChannelClient *rcc,
@@ -277,6 +281,7 @@ static void inputs_channel_send_item(RedChannelClient *rcc, PipeItem *base)
 static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
 {
     InputsChannel *inputs_channel = (InputsChannel *)rcc->channel;
+    InputsChannelClient *icc = (InputsChannelClient *)rcc;
     uint8_t *buf = (uint8_t *)message;
 
     ASSERT(g_inputs_channel == inputs_channel);
@@ -300,7 +305,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui
     case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
         SpiceMsgcMouseMotion *mouse_motion = (SpiceMsgcMouseMotion *)buf;
 
-        if (++inputs_channel->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
+        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
             red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
         }
         if (mouse && reds_get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
@@ -315,7 +320,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui
     case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
         SpiceMsgcMousePosition *pos = (SpiceMsgcMousePosition *)buf;
 
-        if (++inputs_channel->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
+        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
             red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
         }
         if (reds_get_mouse_mode() != SPICE_MOUSE_MODE_CLIENT) {
@@ -457,10 +462,9 @@ static void inputs_shutdown(Channel *channel)
 static void inputs_migrate(Channel *channel)
 {
     InputsChannel *inputs_channel = channel->data;
-    RedChannelClient *rcc = inputs_channel->base.rcc;
 
     ASSERT(g_inputs_channel == (InputsChannel *)channel->data);
-    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MIGRATE);
+    red_channel_pipes_add_type(&inputs_channel->base, PIPE_ITEM_MIGRATE);
 }
 
 static void inputs_pipe_add_init(RedChannelClient *rcc)
@@ -497,17 +501,17 @@ static void inputs_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
 }
 
-static void inputs_link(Channel *channel, RedClient *client, RedsStream *stream, int migration,
-                        int num_common_caps, uint32_t *common_caps, int num_caps,
-                        uint32_t *caps)
+static void inputs_link(Channel *channel, RedClient *client,
+                        RedsStream *stream, int migration,
+                        int num_common_caps, uint32_t *common_caps,
+                        int num_caps, uint32_t *caps)
 {
-    RedChannelClient *rcc;
+    InputsChannelClient *icc;
 
     ASSERT(channel->data == g_inputs_channel);
-
     if (channel->data == NULL) {
-        red_printf("input channel create");
-        g_inputs_channel = (InputsChannel*)red_channel_create_parser(
+        red_printf("inputs channel create");
+        channel->data = g_inputs_channel = (InputsChannel*)red_channel_create_parser(
             sizeof(InputsChannel), core, migration, FALSE /* handle_acks */
             ,inputs_channel_config_socket
             ,inputs_channel_disconnect
@@ -523,25 +527,22 @@ static void inputs_link(Channel *channel, RedClient *client, RedsStream *stream,
             ,NULL
             ,NULL
             ,NULL);
+        ASSERT(channel->data);
     }
-    channel->data = g_inputs_channel;
-    ASSERT(g_inputs_channel);
-    red_printf("input channel client create");
-    rcc = red_channel_client_create(sizeof(RedChannelClient), &g_inputs_channel->base,
-                                    client, stream);
-    ASSERT(rcc);
-    inputs_pipe_add_init(rcc);
+    red_printf("inputs channel client create");
+    icc = (InputsChannelClient*)red_channel_client_create(sizeof(InputsChannelClient),
+                              channel->data, client, stream);
+    icc->motion_count = 0;
+    inputs_pipe_add_init(&icc->base);
 }
 
 static void inputs_push_keyboard_modifiers(uint8_t modifiers)
 {
-    KeyModifiersPipeItem *item;
-
     if (!g_inputs_channel || !red_channel_is_connected(&g_inputs_channel->base)) {
         return;
     }
-    item = inputs_key_modifiers_item_new(g_inputs_channel, modifiers);
-    red_channel_client_pipe_add_push(g_inputs_channel->base.rcc, &item->base);
+    red_channel_pipes_new_add_push(&g_inputs_channel->base,
+        inputs_key_modifiers_item_new, (void*)&modifiers);
 }
 
 void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds)
commit 9cece23ba3e49e6eec9e380daa95d6ee0133c91c
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Apr 11 22:29:58 2011 +0300

    server/main_channel: support multiple clients
    
    The main channel deals with connecting new clients, announcing mouse mode
    changes, and the agent channel. The implementation is currently done without
    any changes to the protocol, so everything has to be either broadcast or
    to a specific client.
    
     channels list - specific client
     mouse mode - broadcast
     agent - broadcast
     notify - broadcast (should have two modes, and use the appropriate)
    
    Notable TODOs:
     * migration testing
     * agent tokens are wrongly sent (or did I fix that? check)

diff --git a/server/main_channel.c b/server/main_channel.c
index b2f12cd..1c80105 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -64,6 +64,11 @@ struct RedsOutItem {
     PipeItem base;
 };
 
+typedef struct RefsPipeItem {
+    PipeItem base;
+    int *refs;
+} RefsPipeItem;
+
 typedef struct PingPipeItem {
     PipeItem base;
     int size;
@@ -80,8 +85,13 @@ typedef struct TokensPipeItem {
     int tokens;
 } TokensPipeItem;
 
+typedef struct AgentDataPipeItemRefs {
+    int refs;
+} AgentDataPipeItemRefs;
+
 typedef struct AgentDataPipeItem {
     PipeItem base;
+    AgentDataPipeItemRefs *refs;
     uint8_t* data;
     size_t len;
     spice_marshaller_item_free_func free_data;
@@ -155,25 +165,27 @@ static void main_disconnect(MainChannel *main_chan)
     red_channel_destroy(&main_chan->base);
 }
 
+#define MAIN_FOREACH(_link, _main, _mcc) \
+    if ((_main) && ((_mcc) = \
+        SPICE_CONTAINEROF((_main)->base.rcc, MainChannelClient, base)))
+
 RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
 {
     MainChannelClient *mcc;
 
-    if (!main_chan || !main_chan->base.rcc) {
-        return NULL;
-    }
-    mcc = SPICE_CONTAINEROF(main_chan->base.rcc, MainChannelClient, base);
-    if (mcc->connection_id == connection_id) {
-        return mcc->base.client;
+    MAIN_FOREACH(link, main_chan, mcc) {
+        if (mcc->connection_id == connection_id) {
+            return mcc->base.client;
+        }
     }
     return NULL;
 }
 
-static int main_channel_client_push_ping(MainChannelClient *rcc, int size);
+static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
 
-void main_channel_start_net_test(MainChannelClient *mcc)
+void main_channel_client_start_net_test(MainChannelClient *mcc)
 {
-    if (!mcc) {
+    if (!mcc || mcc->net_test_id) {
         return;
     }
     if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
@@ -184,59 +196,73 @@ void main_channel_start_net_test(MainChannelClient *mcc)
     }
 }
 
-static MouseModePipeItem *main_mouse_mode_item_new(RedChannelClient *rcc,
-    int current_mode, int is_client_mouse_allowed)
+typedef struct MainMouseModeItemInfo {
+    int current_mode;
+    int is_client_mouse_allowed;
+} MainMouseModeItemInfo;
+
+static PipeItem *main_mouse_mode_item_new(RedChannelClient *rcc, void *data, int num)
 {
     MouseModePipeItem *item = spice_malloc(sizeof(MouseModePipeItem));
+    MainMouseModeItemInfo *info = data;
 
     red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_MOUSE_MODE);
-    item->current_mode = current_mode;
-    item->is_client_mouse_allowed = is_client_mouse_allowed;
-    return item;
+    item->current_mode = info->current_mode;
+    item->is_client_mouse_allowed = info->is_client_mouse_allowed;
+    return &item->base;
 }
 
-static PingPipeItem *main_ping_item_new(RedChannelClient *rcc, int size)
+static PipeItem *main_ping_item_new(MainChannelClient *mcc, int size)
 {
     PingPipeItem *item = spice_malloc(sizeof(PingPipeItem));
 
-    red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_PING);
+    red_channel_pipe_item_init(mcc->base.channel, &item->base, SPICE_MSG_PING);
     item->size = size;
-    return item;
+    return &item->base;
 }
 
-static TokensPipeItem *main_tokens_item_new(RedChannelClient *rcc, int tokens)
+static PipeItem *main_tokens_item_new(RedChannelClient *rcc, void *data, int num)
 {
     TokensPipeItem *item = spice_malloc(sizeof(TokensPipeItem));
 
     red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_AGENT_TOKEN);
-    item->tokens = tokens;
-    return item;
+    item->tokens = (uint64_t)data;
+    return &item->base;
 }
 
-static AgentDataPipeItem *main_agent_data_item_new(RedChannelClient *rcc,
-           uint8_t* data, size_t len,
-           spice_marshaller_item_free_func free_data, void *opaque)
+typedef struct MainAgentDataItemInfo {
+    uint8_t* data;
+    size_t len;
+    spice_marshaller_item_free_func free_data;
+    void *opaque;
+    AgentDataPipeItemRefs *refs;
+} MainAgentDataItemInfo;
+
+static PipeItem *main_agent_data_item_new(RedChannelClient *rcc, void *data, int num)
 {
+    MainAgentDataItemInfo *info = data;
     AgentDataPipeItem *item = spice_malloc(sizeof(AgentDataPipeItem));
 
-    red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_MAIN_AGENT_DATA);
-    item->data = data;
-    item->len = len;
-    item->free_data = free_data;
-    item->opaque = opaque;
-    return item;
+    red_channel_pipe_item_init(rcc->channel, &item->base,
+                               SPICE_MSG_MAIN_AGENT_DATA);
+    item->refs = info->refs;
+    item->data = info->data;
+    item->len = info->len;
+    item->free_data = info->free_data;
+    item->opaque = info->opaque;
+    return &item->base;
 }
 
-static InitPipeItem *main_init_item_new(RedChannelClient *rcc,
+static PipeItem *main_init_item_new(MainChannelClient *mcc,
     int connection_id, int display_channels_hint, int current_mouse_mode,
     int is_client_mouse_allowed, int multi_media_time,
     int ram_hint)
 {
     InitPipeItem *item = spice_malloc(sizeof(InitPipeItem));
 
-    red_channel_pipe_item_init(rcc->channel, &item->base,
+    red_channel_pipe_item_init(mcc->base.channel, &item->base,
                                SPICE_MSG_MAIN_INIT);
     item->connection_id = connection_id;
     item->display_channels_hint = display_channels_hint;
@@ -244,54 +270,68 @@ static InitPipeItem *main_init_item_new(RedChannelClient *rcc,
     item->is_client_mouse_allowed = is_client_mouse_allowed;
     item->multi_media_time = multi_media_time;
     item->ram_hint = ram_hint;
-    return item;
+    return &item->base;
 }
 
-static NotifyPipeItem *main_notify_item_new(RedChannelClient *rcc,
-                                        uint8_t *mess, const int mess_len)
+typedef struct NotifyPipeInfo {
+    uint8_t *mess;
+    int mess_len;
+} NotifyPipeInfo;
+
+static PipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num)
 {
     NotifyPipeItem *item = spice_malloc(sizeof(NotifyPipeItem));
+    NotifyPipeInfo *info = data;
 
     red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_NOTIFY);
-    item->mess = mess;
-    item->mess_len = mess_len;
-    return item;
+    item->mess = info->mess;
+    item->mess_len = info->mess_len;
+    return &item->base;
 }
 
-static MigrateBeginPipeItem *main_migrate_begin_item_new(
-    RedChannelClient *rcc, int port, int sport,
-    char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len,
-    uint8_t *cert_pub_key)
+typedef struct MigrateBeginItemInfo {
+    int port;
+    int sport;
+    char *host;
+    uint16_t cert_pub_key_type;
+    uint32_t cert_pub_key_len;
+    uint8_t *cert_pub_key;
+} MigrateBeginItemInfo;
+
+// TODO: MC: migration is not tested at all with multiclient.
+static PipeItem *main_migrate_begin_item_new(
+    RedChannelClient *rcc, void *data, int num)
 {
     MigrateBeginPipeItem *item = spice_malloc(sizeof(MigrateBeginPipeItem));
+    MigrateBeginItemInfo *info = data;
 
     red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_MIGRATE_BEGIN);
-    item->port = port;
-    item->sport = sport;
-    item->host = host;
-    item->cert_pub_key_type = cert_pub_key_type;
-    item->cert_pub_key_len = cert_pub_key_len;
-    item->cert_pub_key = cert_pub_key;
-    return item;
+    item->port = info->port;
+    item->sport = info->sport;
+    item->host = info->host;
+    item->cert_pub_key_type = info->cert_pub_key_type;
+    item->cert_pub_key_len = info->cert_pub_key_len;
+    item->cert_pub_key = info->cert_pub_key;
+    return &item->base;
 }
 
-static MultiMediaTimePipeItem *main_multi_media_time_item_new(
-    RedChannelClient *rcc, int time)
+static PipeItem *main_multi_media_time_item_new(
+    RedChannelClient *rcc, void *data, int num)
 {
-    MultiMediaTimePipeItem *item;
+    MultiMediaTimePipeItem *item, *info = data;
 
     item = spice_malloc(sizeof(MultiMediaTimePipeItem));
     red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_MULTI_MEDIA_TIME);
-    item->time = time;
-    return item;
+    item->time = info->time;
+    return &item->base;
 }
 
-static void main_channel_push_channels(RedChannelClient *rcc)
+static void main_channel_push_channels(MainChannelClient *mcc)
 {
-    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_CHANNELS_LIST);
+    red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_CHANNELS_LIST);
 }
 
 static void main_channel_marshall_channels(SpiceMarshaller *m)
@@ -307,20 +347,14 @@ static void main_channel_marshall_channels(SpiceMarshaller *m)
 
 int main_channel_client_push_ping(MainChannelClient *mcc, int size)
 {
-    PingPipeItem *item;
+    PipeItem *item;
 
-    item = main_ping_item_new(&mcc->base, size);
-    red_channel_client_pipe_add_push(&mcc->base, &item->base);
-    return TRUE;
-}
-
-int main_channel_push_ping(MainChannel *main_chan, int size)
-{
-    if (main_chan->base.rcc == NULL) {
+    if (mcc == NULL) {
         return FALSE;
     }
-    return main_channel_client_push_ping(
-        SPICE_CONTAINEROF(main_chan->base.rcc, MainChannelClient, base), size);
+    item = main_ping_item_new(mcc, size);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+    return TRUE;
 }
 
 static void main_channel_marshall_ping(SpiceMarshaller *m, int size, int ping_id)
@@ -340,26 +374,16 @@ static void main_channel_marshall_ping(SpiceMarshaller *m, int size, int ping_id
     }
 }
 
-static void main_channel_client_push_mouse_mode(RedChannelClient *rcc, int current_mode,
-                                         int is_client_mouse_allowed);
-
 void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode,
                                   int is_client_mouse_allowed)
 {
-    if (main_chan && main_chan->base.rcc != NULL) {
-        main_channel_client_push_mouse_mode(main_chan->base.rcc, current_mode,
-                                            is_client_mouse_allowed);
-    }
-}
-
-static void main_channel_client_push_mouse_mode(RedChannelClient *rcc, int current_mode,
-                                         int is_client_mouse_allowed)
-{
-    MouseModePipeItem *item;
+    MainMouseModeItemInfo info = {
+        .current_mode=current_mode,
+        .is_client_mouse_allowed=is_client_mouse_allowed,
+    };
 
-    item = main_mouse_mode_item_new(rcc, current_mode,
-                                    is_client_mouse_allowed);
-    red_channel_client_pipe_add_push(rcc, &item->base);
+    red_channel_pipes_new_add_push(&main_chan->base,
+        main_mouse_mode_item_new, &info);
 }
 
 static void main_channel_marshall_mouse_mode(SpiceMarshaller *m, int current_mode, int is_client_mouse_allowed)
@@ -375,19 +399,12 @@ static void main_channel_marshall_mouse_mode(SpiceMarshaller *m, int current_mod
 
 void main_channel_push_agent_connected(MainChannel *main_chan)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
-
-    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_AGENT_CONNECTED);
+    red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_AGENT_CONNECTED);
 }
 
 void main_channel_push_agent_disconnected(MainChannel *main_chan)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
-
-    if (!rcc) {
-        return;
-    }
-    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_AGENT_DISCONNECTED);
+    red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_AGENT_DISCONNECTED);
 }
 
 static void main_channel_marshall_agent_disconnected(SpiceMarshaller *m)
@@ -398,16 +415,11 @@ static void main_channel_marshall_agent_disconnected(SpiceMarshaller *m)
     spice_marshall_msg_main_agent_disconnected(m, &disconnect);
 }
 
+// TODO: make this targeted (requires change to agent token accounting)
 void main_channel_push_tokens(MainChannel *main_chan, uint32_t num_tokens)
 {
-    TokensPipeItem *item;
-    RedChannelClient *rcc = main_chan->base.rcc;
-
-    if (!rcc) {
-        return;
-    }
-    item = main_tokens_item_new(rcc, num_tokens);
-    red_channel_client_pipe_add_push(rcc, &item->base);
+    red_channel_pipes_new_add_push(&main_chan->base,
+        main_tokens_item_new, (void*)(uint64_t)num_tokens);
 }
 
 static void main_channel_marshall_tokens(SpiceMarshaller *m, uint32_t num_tokens)
@@ -421,28 +433,28 @@ static void main_channel_marshall_tokens(SpiceMarshaller *m, uint32_t num_tokens
 void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t len,
            spice_marshaller_item_free_func free_data, void *opaque)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
-    AgentDataPipeItem *item;
+    MainAgentDataItemInfo info = {
+        .data = data,
+        .len = len,
+        .free_data = free_data,
+        .opaque = opaque,
+        .refs = spice_malloc(sizeof(AgentDataPipeItemRefs)),
+    };
 
-    item = main_agent_data_item_new(rcc, data, len, free_data, opaque);
-    red_channel_client_pipe_add_push(rcc, &item->base);
+    info.refs->refs = main_chan->base.clients_num;
+    red_channel_pipes_new_add_push(&main_chan->base,
+        main_agent_data_item_new, &info);
 }
 
 static void main_channel_marshall_agent_data(SpiceMarshaller *m,
                                   AgentDataPipeItem *item)
 {
-    spice_marshaller_add_ref_full(m,
-        item->data, item->len, item->free_data, item->opaque);
+    spice_marshaller_add_ref(m, item->data, item->len);
 }
 
 static void main_channel_push_migrate_data_item(MainChannel *main_chan)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
-
-    if (!rcc) {
-        return;
-    }
-    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MIGRATE_DATA);
+    red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MIGRATE_DATA);
 }
 
 static void main_channel_marshall_migrate_data_item(SpiceMarshaller *m, int serial, int ping_id)
@@ -454,8 +466,7 @@ static void main_channel_marshall_migrate_data_item(SpiceMarshaller *m, int seri
     data->ping_id = ping_id;
 }
 
-static uint64_t main_channel_handle_migrate_data_get_serial(
-    RedChannelClient *rcc,
+static uint64_t main_channel_handle_migrate_data_get_serial(RedChannelClient *base,
     uint32_t size, void *message)
 {
     MainMigrateData *data = message;
@@ -467,10 +478,10 @@ static uint64_t main_channel_handle_migrate_data_get_serial(
     return data->serial;
 }
 
-static uint64_t main_channel_handle_migrate_data(RedChannelClient *rcc,
+static uint64_t main_channel_handle_migrate_data(RedChannelClient *base,
     uint32_t size, void *message)
 {
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+    MainChannelClient *mcc = SPICE_CONTAINEROF(base, MainChannelClient, base);
     MainMigrateData *data = message;
 
     if (size < sizeof(*data)) {
@@ -487,18 +498,18 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id,
     int is_client_mouse_allowed, int multi_media_time,
     int ram_hint)
 {
-    InitPipeItem *item;
+    PipeItem *item;
 
-    item = main_init_item_new(&mcc->base,
+    item = main_init_item_new(mcc,
              connection_id, display_channels_hint, current_mouse_mode,
              is_client_mouse_allowed, multi_media_time, ram_hint);
-    red_channel_client_pipe_add_push(&mcc->base, &item->base);
+    red_channel_client_pipe_add_push(&mcc->base, item);
 }
 
 static void main_channel_marshall_init(SpiceMarshaller *m,
                                        InitPipeItem *item)
 {
-    SpiceMsgMainInit init;
+    SpiceMsgMainInit init; // TODO - remove this copy, make InitPipeItem reuse SpiceMsgMainInit
 
     init.session_id = item->connection_id;
     init.display_channels_hint = item->display_channels_hint;
@@ -514,13 +525,16 @@ static void main_channel_marshall_init(SpiceMarshaller *m,
     spice_marshall_msg_main_init(m, &init);
 }
 
+// TODO - some notifications are new client only (like "keyboard is insecure" on startup)
 void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
-    NotifyPipeItem *item;
+    NotifyPipeInfo info = {
+        .mess = mess,
+        .mess_len = mess_len,
+    };
 
-    item = main_notify_item_new(rcc, mess, mess_len);
-    red_channel_client_pipe_add_push(rcc, &item->base);
+    red_channel_pipes_new_add_push(&main_chan->base,
+        main_notify_item_new, &info);
 }
 
 static uint64_t get_time_stamp(void)
@@ -547,12 +561,17 @@ void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport
     char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len,
     uint8_t *cert_pub_key)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
-    MigrateBeginPipeItem *item;
+    MigrateBeginItemInfo info = {
+        .port =port,
+        .sport = sport,
+        .host = host,
+        .cert_pub_key_type = cert_pub_key_type,
+        .cert_pub_key_len = cert_pub_key_len,
+        .cert_pub_key = cert_pub_key,
+    };
 
-    item = main_migrate_begin_item_new(rcc, port,
-        sport, host, cert_pub_key_type, cert_pub_key_len, cert_pub_key);
-    red_channel_client_pipe_add_push(rcc, &item->base);
+    red_channel_pipes_new_add_push(&main_chan->base,
+        main_migrate_begin_item_new, &info);
 }
 
 static void main_channel_marshall_migrate_begin(SpiceMarshaller *m,
@@ -572,12 +591,7 @@ static void main_channel_marshall_migrate_begin(SpiceMarshaller *m,
 
 void main_channel_push_migrate(MainChannel *main_chan)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
-
-    if (!rcc) {
-        return;
-    }
-    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MIGRATE);
+    red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MIGRATE);
 }
 
 static void main_channel_marshall_migrate(SpiceMarshaller *m)
@@ -590,38 +604,37 @@ static void main_channel_marshall_migrate(SpiceMarshaller *m)
 
 void main_channel_push_migrate_cancel(MainChannel *main_chan)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
-
-    if (!rcc) {
-        return;
-    }
-    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+    red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
 }
 
 void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
-    MultiMediaTimePipeItem *item;
+    MultiMediaTimePipeItem info = {
+        .time = time,
+    };
 
-    item =main_multi_media_time_item_new(rcc, time);
-    red_channel_client_pipe_add_push(rcc, &item->base);
+    red_channel_pipes_new_add_push(&main_chan->base,
+        main_multi_media_time_item_new, &info);
 }
 
-static PipeItem *main_migrate_switch_item_new(MainChannel *main_chan)
+static PipeItem *main_migrate_switch_item_new(RedChannelClient *rcc,
+                                              void *data, int num)
 {
-    PipeItem *item = spice_malloc(sizeof(*item));
+    RefsPipeItem *item = spice_malloc(sizeof(*item));
 
-    red_channel_pipe_item_init(&main_chan->base, item,
+    item->refs = data;
+    red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
-    return item;
+    return &item->base;
 }
 
 void main_channel_push_migrate_switch(MainChannel *main_chan)
 {
-    RedChannelClient *rcc = main_chan->base.rcc;
+    int *refs = spice_malloc0(sizeof(int));
 
-    red_channel_client_pipe_add_push(rcc,
-        main_migrate_switch_item_new(main_chan));
+    *refs = main_chan->base.clients_num;
+    red_channel_pipes_new_add_push(&main_chan->base,
+        main_migrate_switch_item_new, (void*)refs);
 }
 
 static void main_channel_marshall_migrate_switch(SpiceMarshaller *m)
@@ -632,8 +645,6 @@ static void main_channel_marshall_migrate_switch(SpiceMarshaller *m)
 
     reds_fill_mig_switch(&migrate);
     spice_marshall_msg_main_migrate_switch_host(m, &migrate);
-
-    reds_mig_release();
 }
 
 static void main_channel_marshall_multi_media_time(SpiceMarshaller *m,
@@ -712,6 +723,27 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
 static void main_channel_release_pipe_item(RedChannelClient *rcc,
     PipeItem *base, int item_pushed)
 {
+    switch (base->type) {
+        case SPICE_MSG_MAIN_AGENT_DATA: {
+            AgentDataPipeItem *data = (AgentDataPipeItem*)base;
+            if (!--data->refs->refs) {
+                red_printf("SPICE_MSG_MAIN_AGENT_DATA %p %p, %d", data, data->refs, data->refs->refs);
+                free(data->refs);
+                data->free_data(data->data, data->opaque);
+            }
+            break;
+        }
+        case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST: {
+            RefsPipeItem *data = (RefsPipeItem*)base;
+            if (!--*(data->refs)) {
+                free(data->refs);
+                reds_mig_release();
+            }
+            break;
+        }
+        default:
+            break;
+    }
     free(base);
 }
 
@@ -729,13 +761,13 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
         reds_on_main_agent_start();
         break;
     case SPICE_MSGC_MAIN_AGENT_DATA: {
-        reds_on_main_agent_data(message, size);
+        reds_on_main_agent_data(mcc, message, size);
         break;
     }
     case SPICE_MSGC_MAIN_AGENT_TOKEN:
         break;
     case SPICE_MSGC_MAIN_ATTACH_CHANNELS:
-        main_channel_push_channels(rcc);
+        main_channel_push_channels(mcc);
         break;
     case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
         red_printf("connected");
@@ -776,7 +808,8 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
                                "bandwidth", mcc->latency, roundtrip);
                     break;
                 }
-                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000 / (roundtrip - mcc->latency);
+                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000
+                                        / (roundtrip - mcc->latency);
                 red_printf("net test: latency %f ms, bitrate %lu bps (%f Mbps)%s",
                            (double)mcc->latency / 1000,
                            mcc->bitrate_per_sec,
@@ -811,6 +844,7 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
 
 static void main_channel_on_error(RedChannelClient *rcc)
 {
+    red_printf("");
     reds_client_disconnect(rcc->client);
 }
 
@@ -875,6 +909,11 @@ static void ping_timer_cb(void *opaque)
 }
 #endif /* RED_STATISTICS */
 
+uint32_t main_channel_client_get_link_id(MainChannelClient *mcc)
+{
+    return mcc->connection_id;
+}
+
 MainChannelClient *main_channel_client_create(MainChannel *main_chan,
     RedClient *client, RedsStream *stream, uint32_t connection_id)
 {
@@ -919,6 +958,9 @@ MainChannelClient *main_channel_link(Channel *channel, RedClient *client,
             ,main_channel_handle_migrate_data_get_serial);
         ASSERT(channel->data);
     }
+    // TODO - migration - I removed it from channel creation, now put it
+    // into usage somewhere (not an issue until we return migration to it's
+    // former glory)
     red_printf("add main channel client");
     mcc = main_channel_client_create(channel->data, client, stream, connection_id);
     return mcc;
diff --git a/server/main_channel.h b/server/main_channel.h
index fd7e38b..0dd3797 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <spice/vd_agent.h>
 #include "common/marshaller.h"
+#include "reds.h"
 #include "red_channel.h"
 
 /* This is a temporary measure for reds/main split - should not be in a header,
@@ -48,19 +49,18 @@ typedef struct MainChannel MainChannel;
 
 Channel *main_channel_init(void);
 RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
-/* This is a 'clone' from the reds.h Channel.link callback */
+/* This is a 'clone' from the reds.h Channel.link callback to allow passing link_id */
 MainChannelClient *main_channel_link(struct Channel *, RedClient *client,
      RedsStream *stream, uint32_t link_id, int migration, int num_common_caps,
      uint32_t *common_caps, int num_caps, uint32_t *caps);
 void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
-int main_channel_push_ping(MainChannel *main_chan, int size);
 void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
 void main_channel_push_agent_connected(MainChannel *main_chan);
 void main_channel_push_agent_disconnected(MainChannel *main_chan);
 void main_channel_push_tokens(MainChannel *main_chan, uint32_t num_tokens);
 void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t len,
            spice_marshaller_item_free_func free_data, void *opaque);
-void main_channel_start_net_test(MainChannelClient *mcc);
+void main_channel_client_start_net_test(MainChannelClient *mcc);
 // TODO: huge. Consider making a reds_* interface for these functions
 // and calling from main.
 void main_channel_push_init(MainChannelClient *mcc, int connection_id, int display_channels_hint,
@@ -76,6 +76,7 @@ void main_channel_push_migrate_cancel(MainChannel *main_chan);
 void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
 int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
 int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
+uint32_t main_channel_client_get_link_id(MainChannelClient *mcc);
 
 int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
 uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
diff --git a/server/reds.c b/server/reds.c
index 7cc380b..e078594 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -986,8 +986,10 @@ void reds_on_main_agent_start(void)
     reds->agent_state.write_filter.discard_all = FALSE;
 }
 
-void reds_on_main_agent_data(void *message, size_t size)
+void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size)
 {
+    // TODO - use mcc (and start tracking agent data per channel. probably just move the whole
+    // tokens accounting to mainchannel.
     RingItem *ring_item;
     VDAgentExtBuf *buf;
     int res;
@@ -1558,7 +1560,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
             reds_get_mm_time() - MM_TIME_DELTA,
             red_dispatcher_qxl_ram_size());
 
-        main_channel_start_net_test(mcc);
+        main_channel_client_start_net_test(mcc);
         /* Now that we have a client, forward any pending agent data */
         while (read_from_vdi_port());
     }
diff --git a/server/reds.h b/server/reds.h
index 990ea85..6930fe6 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -152,9 +152,9 @@ void reds_update_stat_value(uint32_t value);
 
 // callbacks from main channel messages
 void reds_on_main_agent_start(void);
-void reds_on_main_agent_data(void *message, size_t size);
-void reds_on_main_migrate_connected(void);
-void reds_on_main_migrate_connect_error(void);
+void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size);
+void reds_on_main_migrate_connected();
+void reds_on_main_migrate_connect_error();
 void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end);
 void reds_on_main_mouse_mode_request(void *message, size_t size);
 
commit 0f0bdb190b8586ef7ac20fcefffe641e44eb15b9
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Apr 11 21:44:16 2011 +0300

    server/red_channel: introduce pipes functions
    
    Introduce functions to add (via producer method) the same item to multiple
    pipes, all for the same channel.
    
    Note: Right now there is only a single channel, but the next patches will do the
    per-channel breakdown to channel and channel_client before actually introducing
    a ring in RedChannel, this makes it easier to make smaller changes - the
    channel->rcc link will exist until removed in the ring introducing patch.

diff --git a/server/red_channel.c b/server/red_channel.c
index a6fee99..f084dbe 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -906,17 +906,17 @@ int red_channel_all_clients_serials_are_zero(RedChannel *channel)
     return (!channel->rcc || channel->rcc->send_data.serial == 0);
 }
 
-void red_channel_apply_clients(RedChannel *channel, channel_client_visitor v)
+void red_channel_apply_clients(RedChannel *channel, channel_client_callback cb)
 {
     if (channel->rcc) {
-        v(channel->rcc);
+        cb(channel->rcc);
     }
 }
 
-void red_channel_apply_clients_data(RedChannel *channel, channel_client_visitor_data v, void *data)
+void red_channel_apply_clients_data(RedChannel *channel, channel_client_callback_data cb, void *data)
 {
     if (channel->rcc) {
-        v(channel->rcc, data);
+        cb(channel->rcc, data);
     }
 }
 
@@ -1071,3 +1071,54 @@ MainChannelClient *red_client_get_main(RedClient *client) {
 void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
     client->mcc = mcc;
 }
+
+/*
+ * Functions to push the same item to multiple pipes.
+ */
+
+/*
+ * TODO: after convinced of correctness, add paths for single client
+ * that avoid the whole loop. perhaps even have a function pointer table
+ * later.
+ * TODO - inline? macro? right now this is the simplest from code amount
+ */
+
+typedef void (*rcc_item_t)(RedChannelClient *rcc, PipeItem *item);
+typedef int (*rcc_item_cond_t)(RedChannelClient *rcc, PipeItem *item);
+
+static void red_channel_pipes_create_batch(RedChannel *channel,
+                                new_pipe_item_t creator, void *data,
+                                rcc_item_t callback)
+{
+    RedChannelClient *rcc;
+    PipeItem *item;
+    int num = 0;
+
+    if (!(rcc = channel->rcc)) {
+        return;
+    }
+    item = (*creator)(rcc, data, num++);
+    if (callback) {
+        (*callback)(rcc, item);
+    }
+}
+
+void red_channel_pipes_new_add_push(RedChannel *channel,
+                              new_pipe_item_t creator, void *data)
+{
+    red_channel_pipes_create_batch(channel, creator, data,
+                                     red_channel_client_pipe_add);
+    red_channel_push(channel);
+}
+
+void red_channel_pipes_new_add(RedChannel *channel, new_pipe_item_t creator, void *data)
+{
+    red_channel_pipes_create_batch(channel, creator, data,
+                                     red_channel_client_pipe_add);
+}
+
+void red_channel_pipes_new_add_tail(RedChannel *channel, new_pipe_item_t creator, void *data)
+{
+    red_channel_pipes_create_batch(channel, creator, data,
+                                     red_channel_client_pipe_add_tail_no_push);
+}
diff --git a/server/red_channel.h b/server/red_channel.h
index 09a613a..f11565b 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -270,6 +270,16 @@ void red_channel_client_set_message_serial(RedChannelClient *channel, uint64_t);
 void red_channel_client_begin_send_message(RedChannelClient *rcc);
 
 void red_channel_pipe_item_init(RedChannel *channel, PipeItem *item, int type);
+
+// TODO: add back the channel_pipe_add functionality - by adding reference counting
+// to the PipeItem.
+
+// helper to push a new item to all channels
+typedef PipeItem *(*new_pipe_item_t)(RedChannelClient *rcc, void *data, int num);
+void red_channel_pipes_new_add_push(RedChannel *channel, new_pipe_item_t creator, void *data);
+void red_channel_pipes_new_add(RedChannel *channel, new_pipe_item_t creator, void *data);
+void red_channel_pipes_new_add_tail(RedChannel *channel, new_pipe_item_t creator, void *data);
+
 void red_channel_client_pipe_add_push(RedChannelClient *rcc, PipeItem *item);
 void red_channel_client_pipe_add(RedChannelClient *rcc, PipeItem *item);
 void red_channel_client_pipe_add_after(RedChannelClient *rcc, PipeItem *item, PipeItem *pos);
@@ -358,10 +368,10 @@ RedClient *red_channel_client_get_client(RedChannelClient *rcc);
 SpiceDataHeader *red_channel_client_get_header(RedChannelClient *rcc);
 
 /* apply given function to all connected clients */
-typedef void (*channel_client_visitor)(RedChannelClient *rcc);
-typedef void (*channel_client_visitor_data)(RedChannelClient *rcc, void *data);
-void red_channel_apply_clients(RedChannel *channel, channel_client_visitor v);
-void red_channel_apply_clients_data(RedChannel *channel, channel_client_visitor_data v, void *data);
+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);
 
 struct RedClient {
     RingItem link;
commit 03cf66383ca9393e6521b96289a53dfc9d7cab60
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Apr 11 21:42:05 2011 +0300

    server/red_channel: ignore error if already shutdown
    
    on red_channel_peer_on_incoming_error, if we are already shutdown, do not
    call the channel's error handler. Since the channel has been shutdown, we
    assume this is a second or later error, and handling has already occured.

diff --git a/server/red_channel.c b/server/red_channel.c
index 418cf50..a6fee99 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -220,6 +220,9 @@ void red_channel_client_default_peer_on_error(RedChannelClient *rcc)
 
 static void red_channel_peer_on_incoming_error(RedChannelClient *rcc)
 {
+    if (rcc->stream->shutdown) {
+        return; // assume error has already been handled which caused the shutdown.
+    }
     rcc->channel->on_incoming_error(rcc);
 }
 
commit 4e304ff40f89c8619d462b584ee6ad7d6e10ce93
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Apr 11 20:58:01 2011 +0300

    server/main_channel: move connection_id from reds
    
    Expose additional api to find a client given a connection_id. The connection_id
    is first set when the first channel connects, which is the main channel.
    It could also be kept in the RedClient instead, not sure.
    
    TODO:
     multiple todo's added for multiclient handling. I don't remember why
     I wrote them exactly, and besides if I did any migration tests. So: TODO.

diff --git a/server/main_channel.c b/server/main_channel.c
index d9f6943..b2f12cd 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -121,6 +121,7 @@ typedef struct MultiMediaTimePipeItem {
 
 struct MainChannelClient {
     RedChannelClient base;
+    uint32_t connection_id;
     uint32_t ping_id;
     uint32_t net_test_id;
     int net_test_stage;
@@ -154,6 +155,20 @@ static void main_disconnect(MainChannel *main_chan)
     red_channel_destroy(&main_chan->base);
 }
 
+RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
+{
+    MainChannelClient *mcc;
+
+    if (!main_chan || !main_chan->base.rcc) {
+        return NULL;
+    }
+    mcc = SPICE_CONTAINEROF(main_chan->base.rcc, MainChannelClient, base);
+    if (mcc->connection_id == connection_id) {
+        return mcc->base.client;
+    }
+    return NULL;
+}
+
 static int main_channel_client_push_ping(MainChannelClient *rcc, int size);
 
 void main_channel_start_net_test(MainChannelClient *mcc)
@@ -861,11 +876,12 @@ static void ping_timer_cb(void *opaque)
 #endif /* RED_STATISTICS */
 
 MainChannelClient *main_channel_client_create(MainChannel *main_chan,
-    RedClient *client, RedsStream *stream)
+    RedClient *client, RedsStream *stream, uint32_t connection_id)
 {
     MainChannelClient *mcc = (MainChannelClient*)red_channel_client_create(
         sizeof(MainChannelClient), &main_chan->base, client, stream);
 
+    mcc->connection_id = connection_id;
     mcc->bitrate_per_sec = ~0;
 #ifdef RED_STATISTICS
     if (!(mcc->ping_timer = core->timer_add(ping_timer_cb, NULL))) {
@@ -877,7 +893,7 @@ MainChannelClient *main_channel_client_create(MainChannel *main_chan,
 }
 
 MainChannelClient *main_channel_link(Channel *channel, RedClient *client,
-                        RedsStream *stream, int migration,
+                        RedsStream *stream, uint32_t connection_id, int migration,
                         int num_common_caps, uint32_t *common_caps, int num_caps,
                         uint32_t *caps)
 {
@@ -904,7 +920,7 @@ MainChannelClient *main_channel_link(Channel *channel, RedClient *client,
         ASSERT(channel->data);
     }
     red_printf("add main channel client");
-    mcc = main_channel_client_create(channel->data, client, stream);
+    mcc = main_channel_client_create(channel->data, client, stream, connection_id);
     return mcc;
 }
 
diff --git a/server/main_channel.h b/server/main_channel.h
index 651334d..fd7e38b 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -47,10 +47,11 @@ struct MainMigrateData {
 typedef struct MainChannel MainChannel;
 
 Channel *main_channel_init(void);
+RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
 /* This is a 'clone' from the reds.h Channel.link callback */
 MainChannelClient *main_channel_link(struct Channel *, RedClient *client,
-                 RedsStream *stream, int migration, int num_common_caps,
-                 uint32_t *common_caps, int num_caps, uint32_t *caps);
+     RedsStream *stream, uint32_t link_id, int migration, int num_common_caps,
+     uint32_t *common_caps, int num_caps, uint32_t *caps);
 void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
 int main_channel_push_ping(MainChannel *main_chan, int size);
 void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
diff --git a/server/reds.c b/server/reds.c
index 7dd4c50..7cc380b 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -199,7 +199,6 @@ typedef struct RedsState {
     int pending_mouse_event;
     Ring clients;
     int num_clients;
-    uint32_t link_id;
     Channel *main_channel_factory;
     MainChannel *main_channel;
 
@@ -592,7 +591,6 @@ void reds_client_disconnect(RedClient *client)
 
     red_printf("");
     client->disconnecting = TRUE;
-    reds->link_id = 0;
 
     /* Reset write filter to start with clean state on client reconnect */
     agent_msg_filter_init(&reds->agent_state.write_filter, agent_copypaste,
@@ -1483,6 +1481,12 @@ static void reds_send_link_result(RedLinkInfo *link, uint32_t error)
     sync_write(link->stream, &error, sizeof(error));
 }
 
+int reds_expects_link_id()
+{
+    red_printf("TODO: keep a list of connection_id's from migration, compare to them");
+    return 1;
+}
+
 // TODO: now that main is a separate channel this should
 // actually be joined with reds_handle_other_links, become reds_handle_link
 static void reds_handle_main_link(RedLinkInfo *link)
@@ -1505,7 +1509,8 @@ static void reds_handle_main_link(RedLinkInfo *link)
         memcpy(&(reds->taTicket), &taTicket, sizeof(reds->taTicket));
         reds->mig_target = FALSE;
     } else {
-        if (link_mess->connection_id != reds->link_id) {
+        // migration - check if this is one of the expected connection_id's
+        if (!reds_expects_link_id(link_mess->connection_id)) {
             reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
             reds_link_free(link);
             return;
@@ -1515,7 +1520,6 @@ static void reds_handle_main_link(RedLinkInfo *link)
         reds->mig_target = TRUE;
     }
 
-    reds->link_id = connection_id;
     reds->mig_inprogress = FALSE;
     reds->mig_wait_connect = FALSE;
     reds->mig_wait_disconnect = FALSE;
@@ -1534,7 +1538,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
     ring_add(&reds->clients, &client->link);
     reds->num_clients++;
     mcc = main_channel_link(reds->main_channel_factory, client,
-                  stream, reds->mig_target, link_mess->num_common_caps,
+                  stream, connection_id, reds->mig_target, link_mess->num_common_caps,
                   link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
                   link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
     reds->main_channel = (MainChannel*)reds->main_channel_factory->data;
@@ -1602,22 +1606,21 @@ static void reds_handle_other_links(RedLinkInfo *link)
     uint32_t *caps;
 
     link_mess = link->link_mess;
-    if (reds->num_clients == 1) {
-        client = SPICE_CONTAINEROF(ring_get_head(&reds->clients), RedClient, link);
+    if (reds->main_channel) {
+        client = main_channel_get_client_by_link_id(reds->main_channel,
+            link_mess->connection_id);
     }
 
+    // TODO: MC: broke migration (at least for the dont-drop-connection kind).
+    // On migration we should get a connection_id to expect (must be a security measure)
+    // where do we store it? on reds, but should be a list (MC).
     if (!client) {
         reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
         reds_link_free(link);
         return;
     }
 
-    if (!reds->link_id || reds->link_id != link_mess->connection_id) {
-        reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
-        reds_link_free(link);
-        return;
-    }
-
+    // TODO: MC: be less lenient. Tally connections from same connection_id (by same client).
     if (!(channel = reds_find_channel(link_mess->channel_type,
                                       link_mess->channel_id))) {
         reds_send_link_result(link, SPICE_LINK_ERR_CHANNEL_NOT_AVAILABLE);
@@ -1626,7 +1629,7 @@ static void reds_handle_other_links(RedLinkInfo *link)
     }
 
     reds_send_link_result(link, SPICE_LINK_ERR_OK);
-    reds_show_new_channel(link, reds->link_id);
+    reds_show_new_channel(link, link_mess->connection_id);
     if (link_mess->channel_type == SPICE_CHANNEL_INPUTS && !link->stream->ssl) {
         char *mess = "keyboard channel is insecure";
         const int mess_len = strlen(mess);
@@ -2982,7 +2985,7 @@ struct RedsMigSpice {
 };
 
 typedef struct RedsMigSpiceMessage {
-    uint32_t link_id;
+    uint32_t connection_id;
 } RedsMigSpiceMessage;
 
 typedef struct RedsMigCertPubKeyInfo {
commit d65b08e12d337379e9c62112565ced8d1839c10a
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Apr 11 18:54:51 2011 +0300

    server/main_channel: move ping here from reds.
    
    cleanup only. Note that the ping function is half used since the opt parameter
    stopped being called with anything but NULL, should be returned at some point,
    specifically when we drop the 250kbyte ping on start and do a continuous check
    for latency and bandwidth.
    
    See:
     81945d897 - server: add new vd interface QTerm2Interface, Yaniv Kamay
      introducing the usage of ping with a non NULL opt
     3f7ea8e7a - zap qterm interfaces, Gerd Hoffman
      removing it

diff --git a/server/main_channel.c b/server/main_channel.c
index fa2bec2..d9f6943 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -55,6 +55,8 @@
 #define NET_TEST_WARMUP_BYTES 0
 #define NET_TEST_BYTES (1024 * 250)
 
+#define PING_INTERVAL (1000 * 10)
+
 static uint8_t zero_page[ZERO_BUF_SIZE] = {0};
 
 typedef struct RedsOutItem RedsOutItem;
@@ -124,6 +126,10 @@ struct MainChannelClient {
     int net_test_stage;
     uint64_t latency;
     uint64_t bitrate_per_sec;
+#ifdef RED_STATISTICS
+    SpiceTimer *ping_timer;
+    int ping_interval;
+#endif
 };
 
 struct MainChannel {
@@ -148,16 +154,16 @@ static void main_disconnect(MainChannel *main_chan)
     red_channel_destroy(&main_chan->base);
 }
 
-static int main_channel_client_push_ping(RedChannelClient *rcc, int size);
+static int main_channel_client_push_ping(MainChannelClient *rcc, int size);
 
 void main_channel_start_net_test(MainChannelClient *mcc)
 {
     if (!mcc) {
         return;
     }
-    if (main_channel_client_push_ping(&mcc->base, NET_TEST_WARMUP_BYTES)
-        && main_channel_client_push_ping(&mcc->base, 0)
-        && main_channel_client_push_ping(&mcc->base, NET_TEST_BYTES)) {
+    if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
+        && main_channel_client_push_ping(mcc, 0)
+        && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
         mcc->net_test_id = mcc->ping_id - 2;
         mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
     }
@@ -284,12 +290,12 @@ static void main_channel_marshall_channels(SpiceMarshaller *m)
     free(channels_info);
 }
 
-int main_channel_client_push_ping(RedChannelClient *rcc, int size)
+int main_channel_client_push_ping(MainChannelClient *mcc, int size)
 {
     PingPipeItem *item;
 
-    item = main_ping_item_new(rcc, size);
-    red_channel_client_pipe_add_push(rcc, &item->base);
+    item = main_ping_item_new(&mcc->base, size);
+    red_channel_client_pipe_add_push(&mcc->base, &item->base);
     return TRUE;
 }
 
@@ -298,7 +304,8 @@ int main_channel_push_ping(MainChannel *main_chan, int size)
     if (main_chan->base.rcc == NULL) {
         return FALSE;
     }
-    return main_channel_client_push_ping(main_chan->base.rcc, size);
+    return main_channel_client_push_ping(
+        SPICE_CONTAINEROF(main_chan->base.rcc, MainChannelClient, base), size);
 }
 
 static void main_channel_marshall_ping(SpiceMarshaller *m, int size, int ping_id)
@@ -820,6 +827,39 @@ static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
     return TRUE;
 }
 
+#ifdef RED_STATISTICS
+static void do_ping_client(MainChannelClient *mcc,
+    const char *opt, int has_interval, int interval)
+{
+    red_printf("");
+    if (!opt) {
+        main_channel_client_push_ping(mcc, 0);
+    } else if (!strcmp(opt, "on")) {
+        if (has_interval && interval > 0) {
+            mcc->ping_interval = interval * 1000;
+        }
+        core->timer_start(mcc->ping_timer, mcc->ping_interval);
+    } else if (!strcmp(opt, "off")) {
+        core->timer_cancel(mcc->ping_timer);
+    } else {
+        return;
+    }
+}
+
+static void ping_timer_cb(void *opaque)
+{
+    MainChannelClient *mcc = opaque;
+
+    if (!red_channel_client_is_connected(&mcc->base)) {
+        red_printf("not connected to peer, ping off");
+        core->timer_cancel(mcc->ping_timer);
+        return;
+    }
+    do_ping_client(mcc, NULL, 0, 0);
+    core->timer_start(mcc->ping_timer, mcc->ping_interval);
+}
+#endif /* RED_STATISTICS */
+
 MainChannelClient *main_channel_client_create(MainChannel *main_chan,
     RedClient *client, RedsStream *stream)
 {
@@ -827,6 +867,12 @@ MainChannelClient *main_channel_client_create(MainChannel *main_chan,
         sizeof(MainChannelClient), &main_chan->base, client, stream);
 
     mcc->bitrate_per_sec = ~0;
+#ifdef RED_STATISTICS
+    if (!(mcc->ping_timer = core->timer_add(ping_timer_cb, NULL))) {
+        red_error("ping timer create failed");
+    }
+    mcc->ping_interval = PING_INTERVAL;
+#endif
     return mcc;
 }
 
diff --git a/server/red_channel.h b/server/red_channel.h
index 60dcf69..09a613a 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -237,7 +237,9 @@ RedChannel *red_channel_create_parser(int size,
                                channel_handle_migrate_data_get_serial_proc handle_migrate_data_get_serial);
 RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
                                             RedsStream *stream);
+
 int red_channel_is_connected(RedChannel *channel);
+int red_channel_client_is_connected(RedChannelClient *rcc);
 
 void red_channel_client_destroy(RedChannelClient *rcc);
 void red_channel_destroy(RedChannel *channel);
diff --git a/server/reds.c b/server/reds.c
index a10dd3e..7dd4c50 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -110,7 +110,6 @@ int agent_copypaste = TRUE;
 static void openssl_init();
 
 #define MIGRATE_TIMEOUT (1000 * 10) /* 10sec */
-#define PING_INTERVAL (1000 * 10)
 #define MM_TIMER_GRANULARITY_MS (1000 / 30)
 #define MM_TIME_DELTA 400 /*ms*/
 #define VDI_PORT_WRITE_RETRY_TIMEOUT 100 /*ms*/
@@ -228,8 +227,6 @@ typedef struct RedsState {
     SpiceStat *stat;
     pthread_mutex_t stat_lock;
     RedsStatValue roundtrip_stat;
-    SpiceTimer *ping_timer;
-    int ping_interval;
 #endif
     int peer_minor_version;
 } RedsState;
@@ -634,42 +631,6 @@ static void reds_mig_disconnect()
     }
 }
 
-#ifdef RED_STATISTICS
-
-static void do_ping_client(const char *opt, int has_interval, int interval)
-{
-    if (!reds_main_channel_connected()) {
-        red_printf("not connected to peer");
-        return;
-    }
-
-    if (!opt) {
-        main_channel_push_ping(reds->main_channel, 0);
-    } else if (!strcmp(opt, "on")) {
-        if (has_interval && interval > 0) {
-            reds->ping_interval = interval * 1000;
-        }
-        core->timer_start(reds->ping_timer, reds->ping_interval);
-    } else if (!strcmp(opt, "off")) {
-        core->timer_cancel(reds->ping_timer);
-    } else {
-        return;
-    }
-}
-
-static void ping_timer_cb()
-{
-    if (!reds_main_channel_connected()) {
-        red_printf("not connected to peer, ping off");
-        core->timer_cancel(reds->ping_timer);
-        return;
-    }
-    do_ping_client(NULL, 0, 0);
-    core->timer_start(reds->ping_timer, reds->ping_interval);
-}
-
-#endif
-
 int reds_get_mouse_mode(void)
 {
     return reds->mouse_mode;
@@ -3540,10 +3501,6 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
     if (pthread_mutex_init(&reds->stat_lock, NULL)) {
         red_error("mutex init failed");
     }
-    if (!(reds->ping_timer = core->timer_add(ping_timer_cb, NULL))) {
-        red_error("ping timer create failed");
-    }
-    reds->ping_interval = PING_INTERVAL;
 #endif
 
     if (!(reds->mm_timer = core->timer_add(mm_timer_proc, NULL))) {
commit 5bcdf37997a2edfeb70999d16da3a49d15af122e
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 10 21:11:20 2011 +0300

    server/main_channel: move latency and bitrate to channel client
    
    They were globals before. This introduces api for other channels
    to query the low bandwidth status. The queries themselves are still done
    from the wrong context (channel and not channel client) but that's because
    the decoupling of channel and channel client will be done in the following
    patches.
    
    Note that snd_worker.c got two copied function declarations that belong to
    main_channel.h but can't be easily dragged into snd_worker.c since it still
    uses it's own RedChannel struct.

diff --git a/server/main_channel.c b/server/main_channel.c
index 978cdb3..fa2bec2 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -122,6 +122,8 @@ struct MainChannelClient {
     uint32_t ping_id;
     uint32_t net_test_id;
     int net_test_stage;
+    uint64_t latency;
+    uint64_t bitrate_per_sec;
 };
 
 struct MainChannel {
@@ -136,9 +138,6 @@ enum NetTestStage {
     NET_TEST_STAGE_RATE,
 };
 
-static uint64_t latency = 0;
-uint64_t bitrate_per_sec = ~0;
-
 static void main_channel_client_disconnect(RedChannelClient *rcc)
 {
     red_channel_client_disconnect(rcc);
@@ -147,9 +146,6 @@ static void main_channel_client_disconnect(RedChannelClient *rcc)
 static void main_disconnect(MainChannel *main_chan)
 {
     red_channel_destroy(&main_chan->base);
-
-    latency = 0;
-    bitrate_per_sec = ~0;
 }
 
 static int main_channel_client_push_ping(RedChannelClient *rcc, int size);
@@ -747,23 +743,23 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
             case NET_TEST_STAGE_LATENCY:
                 mcc->net_test_id++;
                 mcc->net_test_stage = NET_TEST_STAGE_RATE;
-                latency = roundtrip;
+                mcc->latency = roundtrip;
                 break;
             case NET_TEST_STAGE_RATE:
                 mcc->net_test_id = 0;
-                if (roundtrip <= latency) {
+                if (roundtrip <= mcc->latency) {
                     // probably high load on client or server result with incorrect values
-                    latency = 0;
+                    mcc->latency = 0;
                     red_printf("net test: invalid values, latency %lu roundtrip %lu. assuming high"
-                               "bandwidth", latency, roundtrip);
+                               "bandwidth", mcc->latency, roundtrip);
                     break;
                 }
-                bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000 / (roundtrip - latency);
+                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000 / (roundtrip - mcc->latency);
                 red_printf("net test: latency %f ms, bitrate %lu bps (%f Mbps)%s",
-                           (double)latency / 1000,
-                           bitrate_per_sec,
-                           (double)bitrate_per_sec / 1024 / 1024,
-                           IS_LOW_BANDWIDTH() ? " LOW BANDWIDTH" : "");
+                           (double)mcc->latency / 1000,
+                           mcc->bitrate_per_sec,
+                           (double)mcc->bitrate_per_sec / 1024 / 1024,
+                           main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
                 mcc->net_test_stage = NET_TEST_STAGE_INVALID;
                 break;
             default:
@@ -824,18 +820,27 @@ static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
     return TRUE;
 }
 
+MainChannelClient *main_channel_client_create(MainChannel *main_chan,
+    RedClient *client, RedsStream *stream)
+{
+    MainChannelClient *mcc = (MainChannelClient*)red_channel_client_create(
+        sizeof(MainChannelClient), &main_chan->base, client, stream);
+
+    mcc->bitrate_per_sec = ~0;
+    return mcc;
+}
+
 MainChannelClient *main_channel_link(Channel *channel, RedClient *client,
                         RedsStream *stream, int migration,
                         int num_common_caps, uint32_t *common_caps, int num_caps,
                         uint32_t *caps)
 {
-    MainChannel *main_chan;
     MainChannelClient *mcc;
 
     if (channel->data == NULL) {
         red_printf("create main channel");
         channel->data = red_channel_create_parser(
-            sizeof(*main_chan), core, migration, FALSE /* handle_acks */
+            sizeof(MainChannel), core, migration, FALSE /* handle_acks */
             ,main_channel_config_socket
             ,main_channel_client_disconnect
             ,spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL)
@@ -852,10 +857,8 @@ MainChannelClient *main_channel_link(Channel *channel, RedClient *client,
             ,main_channel_handle_migrate_data_get_serial);
         ASSERT(channel->data);
     }
-    main_chan = (MainChannel*)channel->data;
     red_printf("add main channel client");
-    mcc = (MainChannelClient*)
-            red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, client, stream);
+    mcc = main_channel_client_create(channel->data, client, stream);
     return mcc;
 }
 
@@ -878,6 +881,17 @@ void main_channel_close(MainChannel *main_chan)
     }
 }
 
+int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
+{
+    // TODO: configurable?
+    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
+}
+
+uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
+{
+    return mcc->bitrate_per_sec;
+}
+
 static void main_channel_shutdown(Channel *channel)
 {
     MainChannel *main_chan = channel->data;
diff --git a/server/main_channel.h b/server/main_channel.h
index ed17d53..651334d 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -76,6 +76,9 @@ void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
 int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
 int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
 
+int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
+uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
+
 // 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
diff --git a/server/red_channel.c b/server/red_channel.c
index 4b108dd..418cf50 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -955,6 +955,11 @@ RedsStream *red_channel_client_get_stream(RedChannelClient *rcc)
     return rcc->stream;
 }
 
+RedClient *red_channel_client_get_client(RedChannelClient *rcc)
+{
+    return rcc->client;
+}
+
 SpiceDataHeader *red_channel_client_get_header(RedChannelClient *rcc)
 {
     return rcc->send_data.header;
diff --git a/server/red_channel.h b/server/red_channel.h
index 874fdf0..60dcf69 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -345,6 +345,7 @@ void red_channel_client_disconnect(RedChannelClient *rcc);
 /* Note: the valid times to call red_channel_get_marshaller are just during send_item callback. */
 SpiceMarshaller *red_channel_client_get_marshaller(RedChannelClient *rcc);
 RedsStream *red_channel_client_get_stream(RedChannelClient *rcc);
+RedClient *red_channel_client_get_client(RedChannelClient *rcc);
 
 /* this is a convenience function for sending messages, sometimes (migration only?)
  * the serial from the header needs to be available for sending. Note that the header
diff --git a/server/red_worker.c b/server/red_worker.c
index afde6d7..0ad7f75 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -62,6 +62,7 @@
 #include "zlib_encoder.h"
 #include "red_channel.h"
 #include "red_dispatcher.h"
+#include "main_channel.h"
 
 //#define COMPRESS_STAT
 //#define DUMP_BITMAP
@@ -100,7 +101,7 @@
 
 //best bit rate per pixel base on 13000000 bps for frame size 720x576 pixels and 25 fps
 #define BEST_BIT_RATE_PER_PIXEL 38
-#define WARST_BIT_RATE_PER_PIXEL 4
+#define WORST_BIT_RATE_PER_PIXEL 4
 
 #define RED_COMPRESS_BUF_SIZE (1024 * 64)
 
@@ -931,7 +932,7 @@ static void display_channel_push_release(DisplayChannel *channel, uint8_t type,
 static void red_display_release_stream_clip(DisplayChannel* channel, StreamClipItem *item);
 static int red_display_free_some_independent_glz_drawables(DisplayChannel *channel);
 static void red_display_free_glz_drawable(DisplayChannel *channel, RedGlzDrawable *drawable);
-static void reset_rate(StreamAgent *stream_agent);
+static void reset_rate(RedWorker *worker, StreamAgent *stream_agent);
 static BitmapGradualType _get_bitmap_graduality_level(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_id);
 static inline int _stride_is_extra(SpiceBitmap *bitmap);
 static void red_disconnect_cursor(RedChannel *channel);
@@ -2389,12 +2390,21 @@ static inline Stream *red_alloc_stream(RedWorker *worker)
     return stream;
 }
 
-static int get_bit_rate(int width, int height)
+static int get_bit_rate(RedWorker *worker,
+    int width, int height)
 {
     uint64_t bit_rate = width * height * BEST_BIT_RATE_PER_PIXEL;
-    if (IS_LOW_BANDWIDTH()) {
-        bit_rate = MIN(bitrate_per_sec * 70 / 100, bit_rate);
-        bit_rate = MAX(bit_rate, width * height * WARST_BIT_RATE_PER_PIXEL);
+    MainChannelClient *mcc;
+    int is_low_bandwidth = 0;
+
+    if (display_is_connected(worker)) {
+        mcc = red_client_get_main(worker->display_channel->common.base.rcc->client);
+        is_low_bandwidth = main_channel_client_is_low_bandwidth(mcc);
+    }
+
+    if (is_low_bandwidth) {
+        bit_rate = MIN(main_channel_client_get_bitrate_per_sec(mcc) * 70 / 100, bit_rate);
+        bit_rate = MAX(bit_rate, width * height * WORST_BIT_RATE_PER_PIXEL);
     }
     return bit_rate;
 }
@@ -2412,7 +2422,7 @@ static void red_display_create_stream(DisplayChannel *display, Stream *stream)
     }
     agent->drops = 0;
     agent->fps = MAX_FPS;
-    reset_rate(agent);
+    reset_rate(display->common.worker, agent);
     red_channel_client_pipe_add(display->common.base.rcc, &agent->create_item);
 }
 
@@ -2443,7 +2453,7 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
     stream->height = src_rect->bottom - src_rect->top;
     stream->dest_area = drawable->red_drawable->bbox;
     stream->refs = 1;
-    stream->bit_rate = get_bit_rate(stream_width, stream_height);
+    stream->bit_rate = get_bit_rate(worker, stream_width, stream_height);
     SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
     stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
     drawable->stream = stream;
@@ -2552,12 +2562,12 @@ static inline int red_is_next_stream_frame(RedWorker *worker, const Drawable *ca
                                       prev->stream);
 }
 
-static void reset_rate(StreamAgent *stream_agent)
+static void reset_rate(RedWorker *worker, StreamAgent *stream_agent)
 {
     Stream *stream = stream_agent->stream;
     int rate;
 
-    rate = get_bit_rate(stream->width, stream->height);
+    rate = get_bit_rate(worker, stream->width, stream->height);
     if (rate == stream->bit_rate) {
         return;
     }
@@ -2565,11 +2575,22 @@ static void reset_rate(StreamAgent *stream_agent)
     /* MJpeg has no rate limiting anyway, so do nothing */
 }
 
+static int display_channel_is_low_bandwidth(DisplayChannel *display_channel)
+{
+    if (display_channel->common.base.rcc) {
+        if (main_channel_client_is_low_bandwidth(
+                red_client_get_main(display_channel->common.base.rcc->client))) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
 static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
 {
     ASSERT(stream->current);
 
-    if (!display_is_connected(worker) || !IS_LOW_BANDWIDTH()) {
+    if (!display_is_connected(worker) || !display_channel_is_low_bandwidth(worker->display_channel)) {
         return;
     }
 
@@ -8820,9 +8841,11 @@ static int display_channel_handle_message(RedChannelClient *rcc, uint32_t size,
 
 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);
     int flags;
     int delay_val;
-    RedsStream *stream = red_channel_client_get_stream(rcc);
 
     if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
         red_printf("accept failed, %s", strerror(errno));
@@ -8835,7 +8858,7 @@ static int common_channel_config_socket(RedChannelClient *rcc)
     }
 
     // TODO - this should be dynamic, not one time at channel creation
-    delay_val = IS_LOW_BANDWIDTH() ? 0 : 1;
+    delay_val = main_channel_client_is_low_bandwidth(mcc) ? 0 : 1;
     if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
         red_printf("setsockopt failed, %s", strerror(errno));
     }
@@ -8885,6 +8908,7 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
     struct epoll_event event;
     RedChannel *channel;
     CommonChannel *common;
+    MainChannelClient *mcc = red_client_get_main(client);
 
     channel = red_channel_create_parser(size, &worker_core, migrate,
                                         TRUE /* handle_acks */,
@@ -8915,7 +8939,8 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
     // TODO: Should this be distinctive for the Display/Cursor channels? doesn't
     // make sense, does it?
     red_channel_ack_set_client_window(channel,
-        IS_LOW_BANDWIDTH() ? WIDE_CLIENT_ACK_WINDOW : NARROW_CLIENT_ACK_WINDOW);
+        main_channel_client_is_low_bandwidth(mcc) ?
+        WIDE_CLIENT_ACK_WINDOW : NARROW_CLIENT_ACK_WINDOW);
 
     event.events = EPOLLIN | EPOLLOUT | EPOLLET;
     event.data.ptr = &common->listener;
@@ -9111,6 +9136,7 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
 {
     DisplayChannel *display_channel;
     size_t stream_buf_size;
+    int is_low_bandwidth = main_channel_client_is_low_bandwidth(red_client_get_main(client));
 
     red_disconnect_all_display_TODO_remove_me((RedChannel *)worker->display_channel);
 
@@ -9158,7 +9184,7 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
     display_channel->send_data.free_list.res_size = DISPLAY_FREE_LIST_DEFAULT_SIZE;
 
     if (worker->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) {
-        display_channel->enable_jpeg = IS_LOW_BANDWIDTH();
+        display_channel->enable_jpeg = is_low_bandwidth;
     } else {
         display_channel->enable_jpeg = (worker->jpeg_state == SPICE_WAN_COMPRESSION_ALWAYS);
     }
@@ -9167,7 +9193,7 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
     display_channel->jpeg_quality = 85;
 
     if (worker->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) {
-        display_channel->enable_zlib_glz_wrap = IS_LOW_BANDWIDTH();
+        display_channel->enable_zlib_glz_wrap = is_low_bandwidth;
     } else {
         display_channel->enable_zlib_glz_wrap = (worker->zlib_glz_state == 
                                                  SPICE_WAN_COMPRESSION_ALWAYS);
diff --git a/server/reds.h b/server/reds.h
index 8773715..990ea85 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -135,9 +135,6 @@ int reds_has_vdagent(void); // used by inputs channel
 void reds_handle_agent_mouse_event(const VDAgentMouseState *mouse_state); // used by inputs_channel
 
 extern struct SpiceCoreInterface *core;
-extern uint64_t bitrate_per_sec;
-
-#define IS_LOW_BANDWIDTH() (bitrate_per_sec < 10 * 1024 * 1024)
 
 // Temporary measures to make splitting reds.c to inputs_channel.c easier
 void reds_client_disconnect(RedClient *client);
diff --git a/server/snd_worker.c b/server/snd_worker.c
index 67f7cf9..332612f 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -35,6 +35,12 @@
 #include "generated_marshallers.h"
 #include "demarshallers.h"
 
+/* main_channel.h inclusion drags red_channel.h which has conflicting types.
+ * until the channels here are defined in terms of red_channel.h we have some
+ * duplicate declarations */
+MainChannelClient *red_client_get_main(RedClient *client);
+int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
+
 #define MAX_SEND_VEC 100
 
 #define RECIVE_BUF_SIZE (16 * 1024 * 2)
@@ -855,6 +861,7 @@ static void snd_record_send(void* data)
 }
 
 static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_id,
+                                 RedClient *client,
                                  RedsStream *stream,
                                  int migrate, send_messages_proc send_messages,
                                  handle_message_proc handle_message,
@@ -867,6 +874,7 @@ static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_i
     int flags;
     int priority;
     int tos;
+    MainChannelClient *mcc = red_client_get_main(client);
 
     if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
         red_printf("accept failed, %s", strerror(errno));
@@ -884,7 +892,7 @@ static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_i
         red_printf("setsockopt failed, %s", strerror(errno));
     }
 
-    delay_val = IS_LOW_BANDWIDTH() ? 0 : 1;
+    delay_val = main_channel_client_is_low_bandwidth(mcc) ? 0 : 1;
     if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
         red_printf("setsockopt failed, %s", strerror(errno));
     }
@@ -1116,6 +1124,7 @@ static void snd_set_playback_peer(Channel *channel, RedClient *client, RedsStrea
     if (!(playback_channel = (PlaybackChannel *)__new_channel(worker,
                                                               sizeof(*playback_channel),
                                                               SPICE_CHANNEL_PLAYBACK,
+                                                              client,
                                                               stream,
                                                               migration,
                                                               snd_playback_send,
@@ -1315,6 +1324,7 @@ static void snd_set_record_peer(Channel *channel, RedClient *client, RedsStream
     if (!(record_channel = (RecordChannel *)__new_channel(worker,
                                                           sizeof(*record_channel),
                                                           SPICE_CHANNEL_RECORD,
+                                                          client,
                                                           stream,
                                                           migration,
                                                           snd_record_send,
commit 614df171931ebc746652537215a5ce83bdf8a458
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 24 10:32:10 2011 +0300

    server/reds: add concept of secondary channels
    
    Secondary channels are those that don't support multiple clients. The
    support added in this patch just doesn't let the second or more connected
    client receive the unsupported channels in the channels list sent by the
    server to the client. This doesn't handle the situation where:
    
    client A connects (gets all channels)
    client B connects (gets supported multiple client channels)
    client A disconnects (Suboptimal 1: B doesn't get new channels at this point)
    client C connects (Suboptimal 2: C doesn't get the full list of channels, but
    the partial one)
    
    Specifically the channels that only support a single client are:
     sound (both playback and record channels)
     smartcard
     tunnel

diff --git a/server/reds.c b/server/reds.c
index c4a05d5..a10dd3e 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -982,18 +982,40 @@ int reds_num_of_channels()
     return reds ? reds->num_of_channels : 0;
 }
 
+static int secondary_channels[] = {
+    SPICE_CHANNEL_MAIN, SPICE_CHANNEL_DISPLAY, SPICE_CHANNEL_CURSOR, SPICE_CHANNEL_INPUTS};
+
+static int channel_is_secondary(Channel *channel)
+{
+    int i;
+    for (i = 0 ; i < sizeof(secondary_channels)/sizeof(secondary_channels[0]); ++i) {
+        if (channel->type == secondary_channels[i]) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
 void reds_fill_channels(SpiceMsgChannels *channels_info)
 {
     Channel *channel;
     int i;
+    int used_channels = 0;
 
     channels_info->num_of_channels = reds->num_of_channels;
     channel = reds->channels;
-    for (i = 0; i < reds->num_of_channels; i++) {
+    for (i = 0; i < reds->num_of_channels; i++, channel = channel->next) {
         ASSERT(channel);
-        channels_info->channels[i].type = channel->type;
-        channels_info->channels[i].id = channel->id;
-        channel = channel->next;
+        if (reds->num_clients > 1 && !channel_is_secondary(channel)) {
+            continue;
+        }
+        channels_info->channels[used_channels].type = channel->type;
+        channels_info->channels[used_channels].id = channel->id;
+        used_channels++;
+    }
+    channels_info->num_of_channels = used_channels;
+    if (used_channels != reds->num_of_channels) {
+        red_printf("sent %d out of %d", used_channels, reds->num_of_channels);
     }
 }
 
commit 448ed75bd6c8db7ca48cab8aa1256a262e87fcc0
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Apr 11 12:44:00 2011 +0300

    server: Add RedClient
    
    That means RedClient tracks a ring of channels. Right now there will be only
    a single client because of the disconnection mechanism - whenever a new
    client comes we disconnect all existing clients. But this patch adds already
    a ring of clients to reds.c (stored in RedServer).
    
    There is a known problem handling many connections and disconnections at the
    same time, trigerrable easily by the following script:
    
    export NEW_DISPLAY=:3.0
    
    Xephyr $NEW_DISPLAY -noreset &
    for ((i = 0 ; i < 5; ++i)); do
        for ((j = 0 ; j < 10; ++j)); do
            DISPLAY=$NEW_DISPLAY c_win7x86_qxl_tests &
        done
        sleep 2;
    done
    
    I fixed a few of the problems resulting from this in the same patch. This
    required already introducing a few other changes:
     * make sure all removal of channels happens in the main thread, for that
     two additional dispatcher calls are added to remove a specific channel
     client (RED_WORKER_MESSAGE_CURSOR_DISCONNECT_CLIENT and
     RED_WORKER_MESSAGE_DISPLAY_DISCONNECT_CLIENT).
     * change some asserts in input channel.
     * make main channel disconnect not recursive
     * introduce disconnect call back to red_channel_create_parser
    
    The remaining abort is from a double free in the main channel, still can't
    find it (doesn't happen when running under valgrind - probably due to the
    slowness resulting from that), but is easy to see when running under gdb.

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index e350689..0fd4bd6 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -429,10 +429,16 @@ static void inputs_relase_keys(void)
     kbd_push_scan(keyboard, 0x38 | 0x80); //LALT
 }
 
-static void inputs_channel_on_error(RedChannelClient *rcc)
+static void inputs_channel_disconnect(RedChannelClient *rcc)
 {
     inputs_relase_keys();
-    red_channel_client_destroy(rcc);
+    red_channel_client_disconnect(rcc);
+}
+
+static void inputs_channel_on_error(RedChannelClient *rcc)
+{
+    red_printf("");
+    inputs_channel_disconnect(rcc);
 }
 
 static void inputs_shutdown(Channel *channel)
@@ -491,36 +497,39 @@ static void inputs_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
 }
 
-static void inputs_link(Channel *channel, RedsStream *stream, int migration,
+static void inputs_link(Channel *channel, RedClient *client, RedsStream *stream, int migration,
                         int num_common_caps, uint32_t *common_caps, int num_caps,
                         uint32_t *caps)
 {
-    InputsChannel *inputs_channel;
     RedChannelClient *rcc;
 
-    ASSERT(channel->data == NULL);
-
-    red_printf("input channel create");
-    g_inputs_channel = inputs_channel = (InputsChannel*)red_channel_create_parser(
-        sizeof(*inputs_channel), core, migration, FALSE /* handle_acks */
-        ,inputs_channel_config_socket
-        ,spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL)
-        ,inputs_channel_handle_parsed
-        ,inputs_channel_alloc_msg_rcv_buf
-        ,inputs_channel_release_msg_rcv_buf
-        ,inputs_channel_hold_pipe_item
-        ,inputs_channel_send_item
-        ,inputs_channel_release_pipe_item
-        ,inputs_channel_on_error
-        ,inputs_channel_on_error
-        ,NULL
-        ,NULL
-        ,NULL);
-    ASSERT(inputs_channel);
+    ASSERT(channel->data == g_inputs_channel);
+
+    if (channel->data == NULL) {
+        red_printf("input channel create");
+        g_inputs_channel = (InputsChannel*)red_channel_create_parser(
+            sizeof(InputsChannel), core, migration, FALSE /* handle_acks */
+            ,inputs_channel_config_socket
+            ,inputs_channel_disconnect
+            ,spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL)
+            ,inputs_channel_handle_parsed
+            ,inputs_channel_alloc_msg_rcv_buf
+            ,inputs_channel_release_msg_rcv_buf
+            ,inputs_channel_hold_pipe_item
+            ,inputs_channel_send_item
+            ,inputs_channel_release_pipe_item
+            ,inputs_channel_on_error
+            ,inputs_channel_on_error
+            ,NULL
+            ,NULL
+            ,NULL);
+    }
+    channel->data = g_inputs_channel;
+    ASSERT(g_inputs_channel);
     red_printf("input channel client create");
-    rcc = red_channel_client_create(sizeof(RedChannelClient), &g_inputs_channel->base, stream);
+    rcc = red_channel_client_create(sizeof(RedChannelClient), &g_inputs_channel->base,
+                                    client, stream);
     ASSERT(rcc);
-    channel->data = inputs_channel;
     inputs_pipe_add_init(rcc);
 }
 
diff --git a/server/main_channel.c b/server/main_channel.c
index a7f53e4..978cdb3 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -139,6 +139,11 @@ enum NetTestStage {
 static uint64_t latency = 0;
 uint64_t bitrate_per_sec = ~0;
 
+static void main_channel_client_disconnect(RedChannelClient *rcc)
+{
+    red_channel_client_disconnect(rcc);
+}
+
 static void main_disconnect(MainChannel *main_chan)
 {
     red_channel_destroy(&main_chan->base);
@@ -788,7 +793,7 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
 
 static void main_channel_on_error(RedChannelClient *rcc)
 {
-    reds_disconnect();
+    reds_client_disconnect(rcc->client);
 }
 
 static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header)
@@ -819,19 +824,20 @@ static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
     return TRUE;
 }
 
-MainChannelClient *main_channel_link(Channel *channel, RedsStream *stream, int migration,
+MainChannelClient *main_channel_link(Channel *channel, RedClient *client,
+                        RedsStream *stream, int migration,
                         int num_common_caps, uint32_t *common_caps, int num_caps,
                         uint32_t *caps)
 {
     MainChannel *main_chan;
     MainChannelClient *mcc;
 
-    ASSERT(channel->data == NULL);
     if (channel->data == NULL) {
         red_printf("create main channel");
         channel->data = red_channel_create_parser(
             sizeof(*main_chan), core, migration, FALSE /* handle_acks */
             ,main_channel_config_socket
+            ,main_channel_client_disconnect
             ,spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL)
             ,main_channel_handle_parsed
             ,main_channel_alloc_msg_rcv_buf
@@ -849,7 +855,7 @@ MainChannelClient *main_channel_link(Channel *channel, RedsStream *stream, int m
     main_chan = (MainChannel*)channel->data;
     red_printf("add main channel client");
     mcc = (MainChannelClient*)
-            red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, stream);
+            red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, client, stream);
     return mcc;
 }
 
diff --git a/server/main_channel.h b/server/main_channel.h
index a82e07c..ed17d53 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -48,7 +48,7 @@ typedef struct MainChannel MainChannel;
 
 Channel *main_channel_init(void);
 /* This is a 'clone' from the reds.h Channel.link callback */
-MainChannelClient *main_channel_link(struct Channel *,
+MainChannelClient *main_channel_link(struct Channel *, RedClient *client,
                  RedsStream *stream, int migration, int num_common_caps,
                  uint32_t *common_caps, int num_caps, uint32_t *caps);
 void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
diff --git a/server/red_channel.c b/server/red_channel.c
index f795953..4b108dd 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -34,6 +34,7 @@
 #include "generated_marshallers.h"
 
 static void red_channel_client_event(int fd, int event, void *data);
+static void red_client_add_channel(RedClient *client, RedChannelClient *rcc);
 
 /* return the number of bytes read. -1 in case of error */
 static int red_peer_receive(RedsStream *stream, uint8_t *buf, uint32_t size)
@@ -356,11 +357,13 @@ static void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc)
 {
     ASSERT(rcc && !channel->rcc);
 	channel->rcc = rcc;
+    channel->clients_num++;
 }
 
 RedChannelClient *red_channel_client_create(
     int size,
     RedChannel *channel,
+    RedClient  *client,
     RedsStream *stream)
 {
     RedChannelClient *rcc;
@@ -369,6 +372,7 @@ RedChannelClient *red_channel_client_create(
     rcc = spice_malloc0(size);
     rcc->stream = stream;
     rcc->channel = channel;
+    rcc->client = client;
     rcc->ack_data.messages_window = ~0;  // blocks send message (maybe use send_data.blocked +
                                              // block flags)
     rcc->ack_data.client_generation = ~0;
@@ -392,8 +396,9 @@ RedChannelClient *red_channel_client_create(
     stream->watch = channel->core->watch_add(stream->socket,
                                            SPICE_WATCH_EVENT_READ,
                                            red_channel_client_event, rcc);
-    rcc->id = 0;
+    rcc->id = channel->clients_num;
     red_channel_add_client(channel, rcc);
+    red_client_add_channel(client, rcc);
     return rcc;
 error:
     free(rcc);
@@ -453,10 +458,6 @@ RedChannel *red_channel_create(int size,
     return channel;
 }
 
-static void do_nothing_disconnect(RedChannelClient *rcc)
-{
-}
-
 static int do_nothing_handle_message(RedChannelClient *rcc, SpiceDataHeader *header, uint8_t *msg)
 {
     return TRUE;
@@ -466,6 +467,7 @@ RedChannel *red_channel_create_parser(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
                                channel_configure_socket_proc config_socket,
+                               channel_disconnect_proc disconnect,
                                spice_parse_channel_func_t parser,
                                channel_handle_parsed_proc handle_parsed,
                                channel_alloc_msg_recv_buf_proc alloc_recv_buf,
@@ -480,7 +482,7 @@ RedChannel *red_channel_create_parser(int size,
                                channel_handle_migrate_data_get_serial_proc handle_migrate_data_get_serial)
 {
     RedChannel *channel = red_channel_create(size,
-        core, migrate, handle_acks, config_socket, do_nothing_disconnect,
+        core, migrate, handle_acks, config_socket, disconnect,
         do_nothing_handle_message, alloc_recv_buf, release_recv_buf, hold_item,
         send_item, release_item, handle_migrate_flush_mark, handle_migrate_data,
         handle_migrate_data_get_serial);
@@ -516,7 +518,7 @@ void red_channel_destroy(RedChannel *channel)
     free(channel);
 }
 
-static void red_channel_client_shutdown(RedChannelClient *rcc)
+void red_channel_client_shutdown(RedChannelClient *rcc)
 {
     if (rcc->stream && !rcc->stream->shutdown) {
         rcc->channel->core->watch_remove(rcc->stream->watch);
@@ -864,6 +866,15 @@ void red_channel_ack_set_client_window(RedChannel* channel, int client_window)
     }
 }
 
+static void red_channel_client_remove(RedChannelClient *rcc)
+{
+    ring_remove(&rcc->client_link);
+    rcc->client->channels_num--;
+    ASSERT(rcc->channel->rcc == rcc);
+    rcc->channel->rcc = NULL;
+    rcc->channel->clients_num--;
+}
+
 void red_channel_client_disconnect(RedChannelClient *rcc)
 {
     red_printf("%p (channel %p)", rcc, rcc->channel);
@@ -876,7 +887,7 @@ void red_channel_client_disconnect(RedChannelClient *rcc)
     rcc->send_data.item = NULL;
     rcc->send_data.blocked = FALSE;
     rcc->send_data.size = 0;
-    rcc->channel->rcc = NULL;
+    red_channel_client_remove(rcc);
 }
 
 void red_channel_disconnect(RedChannel *channel)
@@ -984,3 +995,71 @@ void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc,
     red_channel_client_pipe_remove(rcc, item);
     red_channel_client_release_item(rcc, item, FALSE);
 }
+
+/*
+ * RedClient implementation - kept in red_channel.c because they are
+ * pretty tied together.
+ */
+
+RedClient *red_client_new()
+{
+    RedClient *client;
+
+    client = spice_malloc0(sizeof(RedClient));
+    ring_init(&client->channels);
+    return client;
+}
+
+void red_client_shutdown(RedClient *client)
+{
+    RingItem *link, *next;
+
+    red_printf("#channels %d", client->channels_num);
+    RING_FOREACH_SAFE(link, next, &client->channels) {
+        red_channel_client_shutdown(SPICE_CONTAINEROF(link, RedChannelClient, client_link));
+    }
+}
+
+void red_client_destroy(RedClient *client)
+{
+    RingItem *link, *next;
+    RedChannelClient *rcc;
+
+    red_printf("destroy client with #channels %d", client->channels_num);
+    RING_FOREACH_SAFE(link, next, &client->channels) {
+        // some channels may be in other threads, so disconnection
+        // is not synchronous.
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, client_link);
+        rcc->channel->disconnect(rcc); // this may call another thread. it also frees. (eventually - doesn't have to be in sync)
+    }
+    free(client);
+}
+
+void red_client_disconnect(RedClient *client)
+{
+    RingItem *link, *next;
+    RedChannelClient *rcc;
+
+    red_printf("#channels %d", client->channels_num);
+    RING_FOREACH_SAFE(link, next, &client->channels) {
+        // some channels may be in other threads, so disconnection
+        // is not synchronous.
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, client_link);
+        rcc->channel->disconnect(rcc);
+    }
+}
+
+static void red_client_add_channel(RedClient *client, RedChannelClient *rcc)
+{
+    ASSERT(rcc && client);
+    ring_add(&client->channels, &rcc->client_link);
+    client->channels_num++;
+}
+
+MainChannelClient *red_client_get_main(RedClient *client) {
+    return client->mcc;
+}
+
+void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
+    client->mcc = mcc;
+}
diff --git a/server/red_channel.h b/server/red_channel.h
index 2bd3054..874fdf0 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -140,7 +140,9 @@ typedef uint64_t (*channel_handle_migrate_data_get_serial_proc)(RedChannelClient
 
 struct RedChannelClient {
     RingItem channel_link;
+    RingItem client_link;
     RedChannel *channel;
+    RedClient  *client;
     RedsStream *stream;
     struct {
         uint32_t generation;
@@ -172,6 +174,7 @@ struct RedChannel {
     int handle_acks;
 
     RedChannelClient *rcc;
+    uint32_t clients_num;
 
     OutgoingHandlerInterface outgoing_cb;
     IncomingHandlerInterface incoming_cb;
@@ -219,6 +222,7 @@ RedChannel *red_channel_create_parser(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
                                channel_configure_socket_proc config_socket,
+                               channel_disconnect_proc disconnect,
                                spice_parse_channel_func_t parser,
                                channel_handle_parsed_proc handle_parsed,
                                channel_alloc_msg_recv_buf_proc alloc_recv_buf,
@@ -231,13 +235,19 @@ RedChannel *red_channel_create_parser(int size,
                                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);
-RedChannelClient *red_channel_client_create(int size, RedChannel *channel,
+RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
                                             RedsStream *stream);
 int red_channel_is_connected(RedChannel *channel);
 
 void red_channel_client_destroy(RedChannelClient *rcc);
 void red_channel_destroy(RedChannel *channel);
 
+/* shutdown is the only safe thing to do out of the client/channel
+ * thread. It will not touch the rings, just shutdown the socket.
+ * It should be followed by some way to gurantee a disconnection. */
+void red_channel_client_shutdown(RedChannelClient *rcc);
+void red_channel_shutdown(RedChannel *channel);
+
 /* should be called when a new channel is ready to send messages */
 void red_channel_init_outgoing_messages_window(RedChannel *channel);
 
@@ -350,4 +360,17 @@ typedef void (*channel_client_visitor_data)(RedChannelClient *rcc, void *data);
 void red_channel_apply_clients(RedChannel *channel, channel_client_visitor v);
 void red_channel_apply_clients_data(RedChannel *channel, channel_client_visitor_data v, void *data);
 
+struct RedClient {
+    RingItem link;
+    Ring channels;
+    int channels_num;
+    int disconnecting;
+    MainChannelClient *mcc;
+};
+
+RedClient *red_client_new();
+void red_client_destroy(RedClient *client);
+void red_client_set_main(RedClient *client, MainChannelClient *mcc);
+MainChannelClient *red_client_get_main(RedClient *client);
+
 #endif
diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
index 8f4a8a8..8cbdec9 100644
--- a/server/red_dispatcher.c
+++ b/server/red_dispatcher.c
@@ -76,7 +76,8 @@ extern spice_wan_compression_t zlib_glz_state;
 
 static RedDispatcher *dispatchers = NULL;
 
-static void red_dispatcher_set_peer(Channel *channel, RedsStream *stream, int migration,
+static void red_dispatcher_set_peer(Channel *channel, RedClient *client,
+                                    RedsStream *stream, int migration,
                                     int num_common_caps, uint32_t *common_caps, int num_caps,
                                     uint32_t *caps)
 {
@@ -86,6 +87,7 @@ static void red_dispatcher_set_peer(Channel *channel, RedsStream *stream, int mi
     dispatcher = (RedDispatcher *)channel->data;
     RedWorkerMessage message = RED_WORKER_MESSAGE_DISPLAY_CONNECT;
     write_message(dispatcher->channel, &message);
+    send_data(dispatcher->channel, &client, sizeof(RedClient *));
     send_data(dispatcher->channel, &stream, sizeof(RedsStream *));
     send_data(dispatcher->channel, &migration, sizeof(int));
 }
@@ -106,7 +108,7 @@ static void red_dispatcher_migrate(Channel *channel)
     write_message(dispatcher->channel, &message);
 }
 
-static void red_dispatcher_set_cursor_peer(Channel *channel, RedsStream *stream,
+static void red_dispatcher_set_cursor_peer(Channel *channel, RedClient *client, RedsStream *stream,
                                            int migration, int num_common_caps,
                                            uint32_t *common_caps, int num_caps,
                                            uint32_t *caps)
@@ -115,6 +117,7 @@ static void red_dispatcher_set_cursor_peer(Channel *channel, RedsStream *stream,
     red_printf("");
     RedWorkerMessage message = RED_WORKER_MESSAGE_CURSOR_CONNECT;
     write_message(dispatcher->channel, &message);
+    send_data(dispatcher->channel, &client, sizeof(RedClient *));
     send_data(dispatcher->channel, &stream, sizeof(RedsStream *));
     send_data(dispatcher->channel, &migration, sizeof(int));
 }
@@ -588,6 +591,36 @@ static void qxl_worker_loadvm_commands(QXLWorker *qxl_worker,
     red_dispatcher_loadvm_commands((RedDispatcher*)qxl_worker, ext, count);
 }
 
+static void red_dispatcher_send_disconnect(RedDispatcher *dispatcher,
+                    struct RedChannelClient *rcc, RedWorkerMessage message)
+{
+    write_message(dispatcher->channel, &message);
+    send_data(dispatcher->channel, &rcc, sizeof(struct RedChannelClient *));
+}
+
+void red_dispatcher_disconnect_display_client(RedDispatcher *dispatcher,
+                                      struct RedChannelClient *rcc)
+{
+    RedWorkerMessage message = RED_WORKER_MESSAGE_STOP;
+
+    red_dispatcher_send_disconnect(dispatcher, rcc,
+            RED_WORKER_MESSAGE_DISPLAY_DISCONNECT_CLIENT);
+    read_message(dispatcher->channel, &message);
+    ASSERT(message == RED_WORKER_MESSAGE_READY);
+}
+
+void red_dispatcher_disconnect_cursor_client(RedDispatcher *dispatcher,
+                                      struct RedChannelClient *rcc)
+{
+    RedWorkerMessage message = RED_WORKER_MESSAGE_STOP;
+
+    red_dispatcher_send_disconnect(dispatcher, rcc,
+            RED_WORKER_MESSAGE_CURSOR_DISCONNECT_CLIENT);
+    read_message(dispatcher->channel, &message);
+    ASSERT(message == RED_WORKER_MESSAGE_READY);
+}
+
+
 void red_dispatcher_set_mm_time(uint32_t mm_time)
 {
     RedDispatcher *now = dispatchers;
diff --git a/server/red_dispatcher.h b/server/red_dispatcher.h
index fa347f1..07a95ae 100644
--- a/server/red_dispatcher.h
+++ b/server/red_dispatcher.h
@@ -18,6 +18,8 @@
 #ifndef _H_RED_DISPATCHER
 #define _H_RED_DISPATCHER
 
+struct RedChannelClient;
+
 struct RedDispatcher *red_dispatcher_init(QXLInstance *qxl);
 
 void red_dispatcher_set_mm_time(uint32_t);
@@ -30,5 +32,9 @@ uint32_t red_dispatcher_qxl_ram_size(void);
 int red_dispatcher_qxl_count(void);
 void red_dispatcher_async_complete(struct RedDispatcher*, uint64_t);
 
-#endif
+void red_dispatcher_disconnect_display_client(struct RedDispatcher *dispatcher,
+                                      struct RedChannelClient *rcc);
+void red_dispatcher_disconnect_cursor_client(struct RedDispatcher *dispatcher,
+                                      struct RedChannelClient *rcc);
 
+#endif
diff --git a/server/red_tunnel_worker.c b/server/red_tunnel_worker.c
index f10ab48..27686b5 100644
--- a/server/red_tunnel_worker.c
+++ b/server/red_tunnel_worker.c
@@ -602,9 +602,9 @@ static void arm_timer(SlirpUsrNetworkInterface *usr_interface, UserTimer *timer,
 
 
 /* reds interface */
-static void handle_tunnel_channel_link(Channel *channel, RedsStream *stream, int migration,
-                                       int num_common_caps, uint32_t *common_caps, int num_caps,
-                                       uint32_t *caps);
+static void handle_tunnel_channel_link(Channel *channel, RedClient *client, RedsStream *stream,
+                                       int migration, int num_common_caps, uint32_t *common_caps,
+                                       int num_caps, uint32_t *caps);
 static void handle_tunnel_channel_shutdown(struct Channel *channel);
 static void handle_tunnel_channel_migrate(struct Channel *channel);
 
@@ -3434,9 +3434,9 @@ static void tunnel_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
 }
 
-static void handle_tunnel_channel_link(Channel *channel, RedsStream *stream, int migration,
-                                       int num_common_caps, uint32_t *common_caps, int num_caps,
-                                       uint32_t *caps)
+static void handle_tunnel_channel_link(Channel *channel, RedClient *client, RedsStream *stream,
+                                       int migration, int num_common_caps, uint32_t *common_caps,
+                                       int num_caps, uint32_t *caps)
 {
     TunnelChannel *tunnel_channel;
     TunnelWorker *worker = (TunnelWorker *)channel->data;
@@ -3462,7 +3462,7 @@ static void handle_tunnel_channel_link(Channel *channel, RedsStream *stream, int
     if (!tunnel_channel) {
         return;
     }
-    red_channel_client_create(sizeof(RedChannelClient), &tunnel_channel->base, stream);
+    red_channel_client_create(sizeof(RedChannelClient), &tunnel_channel->base, client, stream);
 
     tunnel_channel->worker = worker;
     tunnel_channel->worker->channel = tunnel_channel;
diff --git a/server/red_worker.c b/server/red_worker.c
index 6ff84f2..afde6d7 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -8848,6 +8848,7 @@ static void free_common_channel_from_listener(EventListener *ctx)
 
     free(common);
 }
+
 static void worker_watch_update_mask(SpiceWatch *watch, int event_mask)
 {
 }
@@ -8868,13 +8869,15 @@ SpiceCoreInterface worker_core = {
 };
 
 static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_id,
-                                 RedsStream *stream, int migrate,
+                                 RedClient *client, RedsStream *stream, int migrate,
                                  event_listener_action_proc handler,
                                  channel_disconnect_proc disconnect,
                                  channel_send_pipe_item_proc send_item,
                                  channel_hold_pipe_item_proc hold_item,
                                  channel_release_pipe_item_proc release_item,
                                  channel_handle_parsed_proc handle_parsed,
+                                 channel_on_incoming_error_proc on_incoming_error,
+                                 channel_on_outgoing_error_proc on_outgoing_error,
                                  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)
@@ -8886,6 +8889,7 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
     channel = red_channel_create_parser(size, &worker_core, migrate,
                                         TRUE /* handle_acks */,
                                         common_channel_config_socket,
+                                        disconnect,
                                         spice_get_client_channel_parser(channel_id, NULL),
                                         handle_parsed,
                                         common_alloc_recv_buf,
@@ -8893,8 +8897,8 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
                                         hold_item,
                                         send_item,
                                         release_item,
-                                        red_channel_client_default_peer_on_error,
-                                        red_channel_client_default_peer_on_error,
+                                        on_incoming_error,
+                                        on_outgoing_error,
                                         handle_migrate_flush_mark,
                                         handle_migrate_data,
                                         handle_migrate_data_get_serial);
@@ -8902,7 +8906,7 @@ static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_i
     if (!channel) {
         goto error;
     }
-    red_channel_client_create(sizeof(RedChannelClient), channel, stream);
+    red_channel_client_create(sizeof(RedChannelClient), channel, client, stream);
     common->id = worker->id;
     common->listener.refs = 1;
     common->listener.action = handler;
@@ -9058,25 +9062,72 @@ static void display_channel_release_item(RedChannelClient *rcc, PipeItem *item,
     }
 }
 
-static void handle_new_display_channel(RedWorker *worker, RedsStream *stream, int migrate)
+static void display_channel_on_incoming_error(RedChannelClient *rcc)
+{
+    red_printf("");
+    red_channel_client_shutdown(rcc);
+}
+
+static void display_channel_on_outgoing_error(RedChannelClient *rcc)
+{
+    red_printf("");
+    red_channel_client_shutdown(rcc);
+}
+
+static void cursor_channel_on_incoming_error(RedChannelClient *rcc)
+{
+    red_printf("");
+    red_channel_client_shutdown(rcc);
+}
+
+static void cursor_channel_on_outgoing_error(RedChannelClient *rcc)
+{
+    red_printf("");
+    red_channel_client_shutdown(rcc);
+}
+
+// call this from dispatcher thread context
+static void dispatch_display_channel_client_disconnect(RedChannelClient *rcc)
+{
+    RedWorker *worker = ((DisplayChannel*)rcc->channel)->common.worker;
+    struct RedDispatcher *dispatcher = worker->qxl->st->dispatcher;
+
+    red_printf("");
+    red_dispatcher_disconnect_display_client(dispatcher, rcc);
+}
+
+// call this from dispatcher thread context
+static void dispatch_cursor_channel_client_disconnect(RedChannelClient *rcc)
+{
+    RedWorker *worker = ((CursorChannel*)rcc->channel)->common.worker;
+    struct RedDispatcher *dispatcher = worker->qxl->st->dispatcher;
+
+    red_printf("");
+    red_dispatcher_disconnect_cursor_client(dispatcher, rcc);
+}
+
+static void handle_new_display_channel(RedWorker *worker, RedClient *client, RedsStream *stream,
+                                       int migrate)
 {
     DisplayChannel *display_channel;
     size_t stream_buf_size;
 
     red_disconnect_all_display_TODO_remove_me((RedChannel *)worker->display_channel);
 
-    if (!(display_channel = (DisplayChannel *)__new_channel(worker, sizeof(*display_channel),
-                                                            SPICE_CHANNEL_DISPLAY, stream,
-                                                            migrate, handle_channel_events,
-                                                            red_disconnect_display,
-                                                            display_channel_send_item,
-                                                            display_channel_hold_pipe_item,
-                                                            display_channel_release_item,
-                                                            display_channel_handle_message,
-                                                            display_channel_handle_migrate_mark,
-                                                            display_channel_handle_migrate_data,
-                                                            display_channel_handle_migrate_data_get_serial
-                                                            ))) {
+    if (!(display_channel = (DisplayChannel *)__new_channel(
+            worker, sizeof(*display_channel),
+            SPICE_CHANNEL_DISPLAY, client, stream,
+            migrate, handle_channel_events,
+            dispatch_display_channel_client_disconnect,
+            display_channel_send_item,
+            display_channel_hold_pipe_item,
+            display_channel_release_item,
+            display_channel_handle_message,
+            display_channel_on_incoming_error,
+            display_channel_on_outgoing_error,
+            display_channel_handle_migrate_mark,
+            display_channel_handle_migrate_data,
+            display_channel_handle_migrate_data_get_serial))) {
         return;
     }
 #ifdef RED_STATISTICS
@@ -9140,11 +9191,6 @@ static void handle_new_display_channel(RedWorker *worker, RedsStream *stream, in
     stat_compress_init(&display_channel->jpeg_alpha_stat, jpeg_alpha_stat_name);
 }
 
-static void red_disconnect_cursor_client(RedChannelClient *rcc)
-{
-    red_disconnect_cursor(rcc->channel);
-}
-
 static void red_disconnect_cursor(RedChannel *channel)
 {
     CommonChannel *common;
@@ -9228,23 +9274,27 @@ static void cursor_channel_release_item(RedChannelClient *rcc, PipeItem *item, i
     }
 }
 
-static void red_connect_cursor(RedWorker *worker, RedsStream *stream, int migrate)
+static void red_connect_cursor(RedWorker *worker, RedClient *client, RedsStream *stream,
+                               int migrate)
 {
     CursorChannel *channel;
 
     red_disconnect_cursor((RedChannel *)worker->cursor_channel);
 
-    if (!(channel = (CursorChannel *)__new_channel(worker, sizeof(*channel),
-                                                   SPICE_CHANNEL_CURSOR, stream, migrate,
-                                                   handle_channel_events,
-                                                   red_disconnect_cursor_client,
-                                                   cursor_channel_send_item,
-                                                   cursor_channel_hold_pipe_item,
-                                                   cursor_channel_release_item,
-                                                   red_channel_client_handle_message,
-                                                   NULL,
-                                                   NULL,
-                                                   NULL))) {
+    if (!(channel = (CursorChannel *)__new_channel(
+            worker, sizeof(*channel),
+            SPICE_CHANNEL_CURSOR, client, stream, migrate,
+            handle_channel_events,
+            dispatch_cursor_channel_client_disconnect,
+            cursor_channel_send_item,
+            cursor_channel_hold_pipe_item,
+            cursor_channel_release_item,
+            red_channel_client_handle_message,
+            cursor_channel_on_incoming_error,
+            cursor_channel_on_outgoing_error,
+            NULL,
+            NULL,
+            NULL))) {
         return;
     }
 #ifdef RED_STATISTICS
@@ -9722,12 +9772,24 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
         break;
     case RED_WORKER_MESSAGE_DISPLAY_CONNECT: {
         RedsStream *stream;
+        RedClient *client;
         int migrate;
         red_printf("connect");
 
+        receive_data(worker->channel, &client, sizeof(RedClient *));
         receive_data(worker->channel, &stream, sizeof(RedsStream *));
         receive_data(worker->channel, &migrate, sizeof(int));
-        handle_new_display_channel(worker, stream, migrate);
+        handle_new_display_channel(worker, client, stream, migrate);
+        break;
+    }
+    case RED_WORKER_MESSAGE_DISPLAY_DISCONNECT_CLIENT: {
+        RedChannelClient *rcc;
+
+        red_printf("disconnect display client");
+        receive_data(worker->channel, &rcc, sizeof(RedChannelClient *));
+        red_disconnect_display(rcc);
+        message = RED_WORKER_MESSAGE_READY;
+        write_message(worker->channel, &message);
         break;
     }
     case RED_WORKER_MESSAGE_DISPLAY_DISCONNECT:
@@ -9749,12 +9811,24 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
         break;
     case RED_WORKER_MESSAGE_CURSOR_CONNECT: {
         RedsStream *stream;
+        RedClient *client;
         int migrate;
 
         red_printf("cursor connect");
+        receive_data(worker->channel, &client, sizeof(RedClient *));
         receive_data(worker->channel, &stream, sizeof(RedsStream *));
         receive_data(worker->channel, &migrate, sizeof(int));
-        red_connect_cursor(worker, stream, migrate);
+        red_connect_cursor(worker, client, stream, migrate);
+        break;
+    }
+    case RED_WORKER_MESSAGE_CURSOR_DISCONNECT_CLIENT: {
+        RedChannelClient *rcc;
+
+        red_printf("disconnect cursor client");
+        receive_data(worker->channel, &rcc, sizeof(RedChannelClient *));
+        red_disconnect_cursor(rcc->channel); /* TODO - assumes a single client */
+        message = RED_WORKER_MESSAGE_READY;
+        write_message(worker->channel, &message);
         break;
     }
     case RED_WORKER_MESSAGE_CURSOR_DISCONNECT:
diff --git a/server/red_worker.h b/server/red_worker.h
index 80cd8c0..6fbe061 100644
--- a/server/red_worker.h
+++ b/server/red_worker.h
@@ -53,11 +53,13 @@ enum {
     RED_WORKER_MESSAGE_READY,
     RED_WORKER_MESSAGE_DISPLAY_CONNECT,
     RED_WORKER_MESSAGE_DISPLAY_DISCONNECT,
+    RED_WORKER_MESSAGE_DISPLAY_DISCONNECT_CLIENT,
     RED_WORKER_MESSAGE_DISPLAY_MIGRATE,
     RED_WORKER_MESSAGE_START,
     RED_WORKER_MESSAGE_STOP,
     RED_WORKER_MESSAGE_CURSOR_CONNECT,
     RED_WORKER_MESSAGE_CURSOR_DISCONNECT,
+    RED_WORKER_MESSAGE_CURSOR_DISCONNECT_CLIENT,
     RED_WORKER_MESSAGE_CURSOR_MIGRATE,
     RED_WORKER_MESSAGE_SET_COMPRESSION,
     RED_WORKER_MESSAGE_SET_STREAMING_VIDEO,
diff --git a/server/reds.c b/server/reds.c
index 65e32d1..c4a05d5 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -198,6 +198,8 @@ typedef struct RedsState {
     int disconnecting;
     VDIPortState agent_state;
     int pending_mouse_event;
+    Ring clients;
+    int num_clients;
     uint32_t link_id;
     Channel *main_channel_factory;
     MainChannel *main_channel;
@@ -535,15 +537,6 @@ static Channel *reds_find_channel(uint32_t type, uint32_t id)
     return channel;
 }
 
-static void reds_shatdown_channels()
-{
-    Channel *channel = reds->channels;
-    while (channel) {
-        channel->shutdown(channel);
-        channel = channel->next;
-    }
-}
-
 static void reds_mig_cleanup()
 {
     if (reds->mig_inprogress) {
@@ -594,14 +587,14 @@ static int reds_main_channel_connected(void)
     return !!reds->main_channel;
 }
 
-void reds_disconnect()
+void reds_client_disconnect(RedClient *client)
 {
-    if (!reds_main_channel_connected() || reds->disconnecting) {
+    if (!reds_main_channel_connected() || client->disconnecting) {
         return;
     }
 
     red_printf("");
-    reds->disconnecting = TRUE;
+    client->disconnecting = TRUE;
     reds->link_id = 0;
 
     /* Reset write filter to start with clean state on client reconnect */
@@ -612,14 +605,26 @@ void reds_disconnect()
     reds->agent_state.read_filter.result = AGENT_MSG_FILTER_DISCARD;
     reds->agent_state.read_filter.discard_all = TRUE;
 
-    reds_shatdown_channels();
-    reds->main_channel_factory->shutdown(reds->main_channel_factory);
-    reds->main_channel_factory->data = NULL;
-    reds->main_channel = NULL;
+    ring_remove(&client->link);
+    reds->num_clients--;
+    red_client_destroy(client);
+
     reds_mig_cleanup();
     reds->disconnecting = FALSE;
 }
 
+// TODO: go over all usage of reds_disconnect, most/some of it should be converted to
+// reds_client_disconnect
+static void reds_disconnect(void)
+{
+    RingItem *link, *next;
+
+    red_printf("");
+    RING_FOREACH_SAFE(link, next, &reds->clients) {
+        reds_client_disconnect(SPICE_CONTAINEROF(link, RedClient, link));
+    }
+}
+
 static void reds_mig_disconnect()
 {
     if (reds_main_channel_connected()) {
@@ -1342,6 +1347,7 @@ void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end)
 static int sync_write(RedsStream *stream, const void *in_buf, size_t n)
 {
     const uint8_t *buf = (uint8_t *)in_buf;
+
     while (n) {
         int now = reds_stream_write(stream, buf, n);
         if (now <= 0) {
@@ -1441,7 +1447,6 @@ static int reds_send_link_ack(RedLinkInfo *link)
     BIO_get_mem_ptr(bio, &bmBuf);
     memcpy(ack.pub_key, bmBuf->data, sizeof(ack.pub_key));
 
-
     if (!sync_write(link->stream, &header, sizeof(header)))
         goto end;
     if (!sync_write(link->stream, &ack, sizeof(ack)))
@@ -1499,6 +1504,7 @@ static void reds_send_link_result(RedLinkInfo *link, uint32_t error)
 // actually be joined with reds_handle_other_links, become reds_handle_link
 static void reds_handle_main_link(RedLinkInfo *link)
 {
+    RedClient *client;
     RedsStream *stream;
     SpiceLinkMess *link_mess;
     uint32_t *caps;
@@ -1541,13 +1547,17 @@ static void reds_handle_main_link(RedLinkInfo *link)
     if (!reds->main_channel_factory) {
         reds->main_channel_factory = main_channel_init();
     }
-    mcc = main_channel_link(reds->main_channel_factory,
+    client = red_client_new();
+    ring_add(&reds->clients, &client->link);
+    reds->num_clients++;
+    mcc = main_channel_link(reds->main_channel_factory, client,
                   stream, reds->mig_target, link_mess->num_common_caps,
                   link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
                   link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
     reds->main_channel = (MainChannel*)reds->main_channel_factory->data;
     ASSERT(reds->main_channel);
     free(link_mess);
+    red_client_set_main(client, mcc);
 
     if (vdagent) {
         reds->agent_state.read_filter.discard_all = FALSE;
@@ -1603,11 +1613,21 @@ static void openssl_init(RedLinkInfo *link)
 static void reds_handle_other_links(RedLinkInfo *link)
 {
     Channel *channel;
+    RedClient *client = NULL;
     RedsStream *stream;
     SpiceLinkMess *link_mess;
     uint32_t *caps;
 
     link_mess = link->link_mess;
+    if (reds->num_clients == 1) {
+        client = SPICE_CONTAINEROF(ring_get_head(&reds->clients), RedClient, link);
+    }
+
+    if (!client) {
+        reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
+        reds_link_free(link);
+        return;
+    }
 
     if (!reds->link_id || reds->link_id != link_mess->connection_id) {
         reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
@@ -1635,7 +1655,7 @@ static void reds_handle_other_links(RedLinkInfo *link)
     link->link_mess = NULL;
     reds_link_free(link);
     caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
-    channel->link(channel, stream, reds->mig_target, link_mess->num_common_caps,
+    channel->link(channel, client, stream, reds->mig_target, link_mess->num_common_caps,
                   link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
                   link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
     free(link_mess);
@@ -3463,6 +3483,8 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
     reds->listen_socket = -1;
     reds->secure_listen_socket = -1;
     init_vd_agent_resources();
+    ring_init(&reds->clients);
+    reds->num_clients = 0;
 
     if (!(reds->mig_timer = core->timer_add(migrate_timout, NULL))) {
         red_error("migration timer create failed");
diff --git a/server/reds.h b/server/reds.h
index 4ab7f63..8773715 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -35,6 +35,7 @@
 #define SPICE_GNUC_VISIBLE __attribute__ ((visibility ("default")))
 
 typedef struct RedsStream RedsStream;
+typedef struct RedClient RedClient;
 typedef struct MainChannelClient MainChannelClient;
 
 #if HAVE_SASL
@@ -95,7 +96,8 @@ typedef struct Channel {
     uint32_t *common_caps;
     int num_caps;
     uint32_t *caps;
-    void (*link)(struct Channel *, RedsStream *stream, int migration, int num_common_caps,
+    void (*link)(struct Channel *, RedClient *client, RedsStream *stream,
+                 int migration, int num_common_caps,
                  uint32_t *common_caps, int num_caps, uint32_t *caps);
     void (*shutdown)(struct Channel *);
     void (*migrate)(struct Channel *);
@@ -138,7 +140,7 @@ extern uint64_t bitrate_per_sec;
 #define IS_LOW_BANDWIDTH() (bitrate_per_sec < 10 * 1024 * 1024)
 
 // Temporary measures to make splitting reds.c to inputs_channel.c easier
-void reds_disconnect(void);
+void reds_client_disconnect(RedClient *client);
 
 // Temporary (?) for splitting main channel
 typedef struct MainMigrateData MainMigrateData;
diff --git a/server/smartcard.c b/server/smartcard.c
index eda73fe..76ae606 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -477,7 +477,7 @@ static void smartcard_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *it
 {
 }
 
-static void smartcard_link(Channel *channel, RedsStream *stream,
+static void smartcard_link(Channel *channel, RedClient *client, RedsStream *stream,
                         int migration, int num_common_caps,
                         uint32_t *common_caps, int num_caps,
                         uint32_t *caps)
@@ -506,7 +506,7 @@ static void smartcard_link(Channel *channel, RedsStream *stream,
         red_printf("ERROR: smartcard channel creation failed");
         return;
     }
-    red_channel_client_create(sizeof(RedChannelClient), channel->data, stream);
+    red_channel_client_create(sizeof(RedChannelClient), channel->data, client, stream);
     red_channel_init_outgoing_messages_window((RedChannel*)channel->data);
 }
 
diff --git a/server/snd_worker.c b/server/snd_worker.c
index eab2fdf..67f7cf9 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -1088,9 +1088,9 @@ static void snd_playback_cleanup(SndChannel *channel)
     celt051_mode_destroy(playback_channel->celt_mode);
 }
 
-static void snd_set_playback_peer(Channel *channel, RedsStream *stream, int migration,
-                                  int num_common_caps, uint32_t *common_caps, int num_caps,
-                                  uint32_t *caps)
+static void snd_set_playback_peer(Channel *channel, RedClient *client, RedsStream *stream,
+                                  int migration, int num_common_caps, uint32_t *common_caps,
+                                  int num_caps, uint32_t *caps)
 {
     SndWorker *worker = (SndWorker *)channel;
     SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
@@ -1287,9 +1287,9 @@ static void snd_record_cleanup(SndChannel *channel)
     celt051_mode_destroy(record_channel->celt_mode);
 }
 
-static void snd_set_record_peer(Channel *channel, RedsStream *stream, int migration,
-                                int num_common_caps, uint32_t *common_caps, int num_caps,
-                                uint32_t *caps)
+static void snd_set_record_peer(Channel *channel, RedClient *client, RedsStream *stream,
+                                int migration, int num_common_caps, uint32_t *common_caps,
+                                int num_caps, uint32_t *caps)
 {
     SndWorker *worker = (SndWorker *)channel;
     SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
diff --git a/server/usbredir.c b/server/usbredir.c
index 8806702..1aa89b2 100644
--- a/server/usbredir.c
+++ b/server/usbredir.c
@@ -177,7 +177,7 @@ static void usbredir_red_channel_release_pipe_item(RedChannelClient *rcc,
     free(item);
 }
 
-static void usbredir_link(Channel *channel, RedsStream *stream, int migration,
+static void usbredir_link(Channel *channel, RedClient *client, RedsStream *stream, int migration,
     int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps)
 {
     UsbRedirState *state;
@@ -216,7 +216,8 @@ static void usbredir_link(Channel *channel, RedsStream *stream, int migration,
     if (!state->red_channel) {
         return;
     }
-    state->rcc = red_channel_client_create(sizeof(RedChannelClient), state->red_channel, stream);
+    state->rcc = red_channel_client_create(sizeof(RedChannelClient), state->red_channel,
+                                           client, stream);
     if (!state->rcc) {
         red_printf("failed to create usbredir channel client\n");
         return;
commit 22084c4703282699a34dfb72f3c6318159ddcedf
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 10 21:51:58 2011 +0300

    server/main_channel+reds: make main_channel_init return MainChannelClient
    
    This makes it easier to introduce RedClient in the next patch.

diff --git a/server/main_channel.c b/server/main_channel.c
index 9e65930..a7f53e4 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -149,16 +149,14 @@ static void main_disconnect(MainChannel *main_chan)
 
 static int main_channel_client_push_ping(RedChannelClient *rcc, int size);
 
-void main_channel_start_net_test(RedChannelClient *rcc)
+void main_channel_start_net_test(MainChannelClient *mcc)
 {
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
-
-    if (!rcc) {
+    if (!mcc) {
         return;
     }
-    if (main_channel_client_push_ping(rcc, NET_TEST_WARMUP_BYTES)
-        && main_channel_client_push_ping(rcc, 0)
-        && main_channel_client_push_ping(rcc, NET_TEST_BYTES)) {
+    if (main_channel_client_push_ping(&mcc->base, NET_TEST_WARMUP_BYTES)
+        && main_channel_client_push_ping(&mcc->base, 0)
+        && main_channel_client_push_ping(&mcc->base, NET_TEST_BYTES)) {
         mcc->net_test_id = mcc->ping_id - 2;
         mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
     }
@@ -461,17 +459,17 @@ static uint64_t main_channel_handle_migrate_data(RedChannelClient *rcc,
     return TRUE;
 }
 
-void main_channel_push_init(RedChannelClient *rcc, int connection_id,
+void main_channel_push_init(MainChannelClient *mcc, int connection_id,
     int display_channels_hint, int current_mouse_mode,
     int is_client_mouse_allowed, int multi_media_time,
     int ram_hint)
 {
     InitPipeItem *item;
 
-    item = main_init_item_new(rcc,
+    item = main_init_item_new(&mcc->base,
              connection_id, display_channels_hint, current_mouse_mode,
              is_client_mouse_allowed, multi_media_time, ram_hint);
-    red_channel_client_pipe_add_push(rcc, &item->base);
+    red_channel_client_pipe_add_push(&mcc->base, &item->base);
 }
 
 static void main_channel_marshall_init(SpiceMarshaller *m,
@@ -821,12 +819,12 @@ static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
     return TRUE;
 }
 
-RedChannelClient *main_channel_link(Channel *channel, RedsStream *stream, int migration,
+MainChannelClient *main_channel_link(Channel *channel, RedsStream *stream, int migration,
                         int num_common_caps, uint32_t *common_caps, int num_caps,
                         uint32_t *caps)
 {
     MainChannel *main_chan;
-    RedChannelClient *rcc;
+    MainChannelClient *mcc;
 
     ASSERT(channel->data == NULL);
     if (channel->data == NULL) {
@@ -850,8 +848,9 @@ RedChannelClient *main_channel_link(Channel *channel, RedsStream *stream, int mi
     }
     main_chan = (MainChannel*)channel->data;
     red_printf("add main channel client");
-    rcc = red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, stream);
-    return rcc;
+    mcc = (MainChannelClient*)
+            red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, stream);
+    return mcc;
 }
 
 int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
diff --git a/server/main_channel.h b/server/main_channel.h
index 31da720..a82e07c 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -48,7 +48,7 @@ typedef struct MainChannel MainChannel;
 
 Channel *main_channel_init(void);
 /* This is a 'clone' from the reds.h Channel.link callback */
-RedChannelClient *main_channel_link(struct Channel *,
+MainChannelClient *main_channel_link(struct Channel *,
                  RedsStream *stream, int migration, int num_common_caps,
                  uint32_t *common_caps, int num_caps, uint32_t *caps);
 void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
@@ -59,10 +59,10 @@ void main_channel_push_agent_disconnected(MainChannel *main_chan);
 void main_channel_push_tokens(MainChannel *main_chan, uint32_t num_tokens);
 void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t len,
            spice_marshaller_item_free_func free_data, void *opaque);
-void main_channel_start_net_test(RedChannelClient *rcc);
+void main_channel_start_net_test(MainChannelClient *mcc);
 // TODO: huge. Consider making a reds_* interface for these functions
 // and calling from main.
-void main_channel_push_init(RedChannelClient *rcc, int connection_id, int display_channels_hint,
+void main_channel_push_init(MainChannelClient *mcc, int connection_id, int display_channels_hint,
     int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
     int ram_hint);
 void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
diff --git a/server/reds.c b/server/reds.c
index 4ffc19a..65e32d1 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1503,7 +1503,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
     SpiceLinkMess *link_mess;
     uint32_t *caps;
     uint32_t connection_id;
-    RedChannelClient *rcc;
+    MainChannelClient *mcc;
 
     red_printf("");
     link_mess = link->link_mess;
@@ -1541,12 +1541,13 @@ static void reds_handle_main_link(RedLinkInfo *link)
     if (!reds->main_channel_factory) {
         reds->main_channel_factory = main_channel_init();
     }
-    rcc = main_channel_link(reds->main_channel_factory,
+    mcc = main_channel_link(reds->main_channel_factory,
                   stream, reds->mig_target, link_mess->num_common_caps,
                   link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
                   link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
+    reds->main_channel = (MainChannel*)reds->main_channel_factory->data;
+    ASSERT(reds->main_channel);
     free(link_mess);
-    reds->main_channel = (MainChannel*)rcc->channel;
 
     if (vdagent) {
         reds->agent_state.read_filter.discard_all = FALSE;
@@ -1555,12 +1556,12 @@ static void reds_handle_main_link(RedLinkInfo *link)
 
     if (!reds->mig_target) {
         reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
-        main_channel_push_init(rcc, connection_id, red_dispatcher_count(),
+        main_channel_push_init(mcc, connection_id, red_dispatcher_count(),
             reds->mouse_mode, reds->is_client_mouse_allowed,
             reds_get_mm_time() - MM_TIME_DELTA,
             red_dispatcher_qxl_ram_size());
 
-        main_channel_start_net_test(rcc);
+        main_channel_start_net_test(mcc);
         /* Now that we have a client, forward any pending agent data */
         while (read_from_vdi_port());
     }
commit 2550e825ecf2e63b142a0f6c97b9bd6e3869feb2
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 10 20:17:39 2011 +0300

    server/red_worker: release PIPE_ITEM_TYPE_VERB in display channel
    
    We send a SPICE_MSG_DISPLAY_MARK verb.

diff --git a/server/red_worker.c b/server/red_worker.c
index 699820c..6ff84f2 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -8983,6 +8983,9 @@ static void display_channel_release_item_after_push(DisplayChannel *display_chan
     case PIPE_ITEM_TYPE_IMAGE:
         release_image_item((ImageItem *)item);
         break;
+    case PIPE_ITEM_TYPE_VERB:
+        free(item);
+        break;
     default:
         PANIC("invalid item type");
     }
commit 9ae19999d252a1e3021222ffc60122ac3ca4279e
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 10 20:03:11 2011 +0300

    server/red_client: clear pipe on disconnect

diff --git a/server/red_channel.c b/server/red_channel.c
index 172dedd..f795953 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -871,7 +871,7 @@ void red_channel_client_disconnect(RedChannelClient *rcc)
     if (rcc->send_data.item) {
         rcc->channel->release_item(rcc, rcc->send_data.item, FALSE);
     }
-    // TODO: clear our references from the pipe
+    red_channel_client_pipe_clear(rcc);
     reds_stream_free(rcc->stream);
     rcc->send_data.item = NULL;
     rcc->send_data.blocked = FALSE;
commit cd2c87da3eda0df76be2f8e5863b374ad5a8d314
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 10 20:02:50 2011 +0300

    server/red_channel: workaround for fast client disconnect bug (TODO - real fix)

diff --git a/server/red_channel.c b/server/red_channel.c
index 529bd4e..172dedd 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -581,6 +581,11 @@ void red_channel_client_push(RedChannelClient *rcc)
         red_channel_client_send(rcc);
     }
 
+    if (rcc->send_data.item && !rcc->send_data.blocked) {
+        rcc->send_data.blocked = TRUE;
+        red_printf("ERROR: an item waiting to be sent and not blocked");
+    }
+
     while ((pipe_item = red_channel_client_pipe_item_get(rcc))) {
         red_channel_client_send_item(rcc, pipe_item);
     }
commit 6be2c77dd9a902bb4910f6a39fa3cd58b1651d3f
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Apr 10 18:18:11 2011 +0300

    server/main_channel: use MainChannel in sig
    
    use MainChannel* instead of Channel* for a many functions in main_channel.h
    (affects main_channel.c and reds.c).
    
    some one liner fixes are hidden in here too.

diff --git a/server/main_channel.c b/server/main_channel.c
index 435aace..9e65930 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -117,13 +117,17 @@ typedef struct MultiMediaTimePipeItem {
     int time;
 } MultiMediaTimePipeItem;
 
-typedef struct MainChannel {
-    RedChannel base;
-    uint8_t recv_buf[RECEIVE_BUF_SIZE];
+struct MainChannelClient {
+    RedChannelClient base;
     uint32_t ping_id;
     uint32_t net_test_id;
     int net_test_stage;
-} MainChannel;
+};
+
+struct MainChannel {
+    RedChannel base;
+    uint8_t recv_buf[RECEIVE_BUF_SIZE];
+};
 
 enum NetTestStage {
     NET_TEST_STAGE_INVALID,
@@ -137,9 +141,6 @@ uint64_t bitrate_per_sec = ~0;
 
 static void main_disconnect(MainChannel *main_chan)
 {
-    main_chan->ping_id = 0;
-    main_chan->net_test_id = 0;
-    main_chan->net_test_stage = NET_TEST_STAGE_INVALID;
     red_channel_destroy(&main_chan->base);
 
     latency = 0;
@@ -148,21 +149,18 @@ static void main_disconnect(MainChannel *main_chan)
 
 static int main_channel_client_push_ping(RedChannelClient *rcc, int size);
 
-void main_channel_start_net_test(Channel *channel)
+void main_channel_start_net_test(RedChannelClient *rcc)
 {
-    MainChannel *main_chan = channel->data;
-    RedChannelClient *rcc;
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
 
-    if (!main_chan || !main_chan->base.rcc || main_chan->net_test_id) {
+    if (!rcc) {
         return;
     }
-    rcc = main_chan->base.rcc;
-
     if (main_channel_client_push_ping(rcc, NET_TEST_WARMUP_BYTES)
         && main_channel_client_push_ping(rcc, 0)
         && main_channel_client_push_ping(rcc, NET_TEST_BYTES)) {
-        main_chan->net_test_id = main_chan->ping_id - 2;
-        main_chan->net_test_stage = NET_TEST_STAGE_WARMUP;
+        mcc->net_test_id = mcc->ping_id - 2;
+        mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
     }
 }
 
@@ -296,10 +294,8 @@ int main_channel_client_push_ping(RedChannelClient *rcc, int size)
     return TRUE;
 }
 
-int main_channel_push_ping(Channel *channel, int size)
+int main_channel_push_ping(MainChannel *main_chan, int size)
 {
-    MainChannel *main_chan = channel->data;
-
     if (main_chan->base.rcc == NULL) {
         return FALSE;
     }
@@ -326,11 +322,9 @@ static void main_channel_marshall_ping(SpiceMarshaller *m, int size, int ping_id
 static void main_channel_client_push_mouse_mode(RedChannelClient *rcc, int current_mode,
                                          int is_client_mouse_allowed);
 
-void main_channel_push_mouse_mode(Channel *channel, int current_mode,
+void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode,
                                   int is_client_mouse_allowed)
 {
-    MainChannel *main_chan = channel->data;
-
     if (main_chan && main_chan->base.rcc != NULL) {
         main_channel_client_push_mouse_mode(main_chan->base.rcc, current_mode,
                                             is_client_mouse_allowed);
@@ -358,16 +352,15 @@ static void main_channel_marshall_mouse_mode(SpiceMarshaller *m, int current_mod
     spice_marshall_msg_main_mouse_mode(m, &mouse_mode);
 }
 
-void main_channel_push_agent_connected(Channel *channel)
+void main_channel_push_agent_connected(MainChannel *main_chan)
 {
-    RedChannelClient *rcc = ((MainChannel*)channel->data)->base.rcc;
+    RedChannelClient *rcc = main_chan->base.rcc;
 
     red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_AGENT_CONNECTED);
 }
 
-void main_channel_push_agent_disconnected(Channel *channel)
+void main_channel_push_agent_disconnected(MainChannel *main_chan)
 {
-    MainChannel *main_chan = channel->data;
     RedChannelClient *rcc = main_chan->base.rcc;
 
     if (!rcc) {
@@ -384,10 +377,9 @@ static void main_channel_marshall_agent_disconnected(SpiceMarshaller *m)
     spice_marshall_msg_main_agent_disconnected(m, &disconnect);
 }
 
-void main_channel_push_tokens(Channel *channel, uint32_t num_tokens)
+void main_channel_push_tokens(MainChannel *main_chan, uint32_t num_tokens)
 {
     TokensPipeItem *item;
-    MainChannel *main_chan = channel->data;
     RedChannelClient *rcc = main_chan->base.rcc;
 
     if (!rcc) {
@@ -405,10 +397,9 @@ static void main_channel_marshall_tokens(SpiceMarshaller *m, uint32_t num_tokens
     spice_marshall_msg_main_agent_token(m, &tokens);
 }
 
-void main_channel_push_agent_data(Channel *channel, uint8_t* data, size_t len,
+void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t len,
            spice_marshaller_item_free_func free_data, void *opaque)
 {
-    MainChannel *main_chan = channel->data;
     RedChannelClient *rcc = main_chan->base.rcc;
     AgentDataPipeItem *item;
 
@@ -458,26 +449,24 @@ static uint64_t main_channel_handle_migrate_data_get_serial(
 static uint64_t main_channel_handle_migrate_data(RedChannelClient *rcc,
     uint32_t size, void *message)
 {
-    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
     MainMigrateData *data = message;
 
     if (size < sizeof(*data)) {
         red_printf("bad message size");
         return FALSE;
     }
-    main_chan->ping_id = data->ping_id;
+    mcc->ping_id = data->ping_id;
     reds_on_main_receive_migrate_data(data, ((uint8_t*)message) + size);
     return TRUE;
 }
 
-void main_channel_push_init(Channel *channel, int connection_id,
+void main_channel_push_init(RedChannelClient *rcc, int connection_id,
     int display_channels_hint, int current_mouse_mode,
     int is_client_mouse_allowed, int multi_media_time,
     int ram_hint)
 {
     InitPipeItem *item;
-    MainChannel *main_chan = channel->data;
-    RedChannelClient *rcc = main_chan->base.rcc;
 
     item = main_init_item_new(rcc,
              connection_id, display_channels_hint, current_mouse_mode,
@@ -504,9 +493,8 @@ static void main_channel_marshall_init(SpiceMarshaller *m,
     spice_marshall_msg_main_init(m, &init);
 }
 
-void main_channel_push_notify(Channel *channel, uint8_t *mess, const int mess_len)
+void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len)
 {
-    MainChannel *main_chan = channel->data;
     RedChannelClient *rcc = main_chan->base.rcc;
     NotifyPipeItem *item;
 
@@ -534,11 +522,10 @@ static void main_channel_marshall_notify(SpiceMarshaller *m, NotifyPipeItem *ite
     spice_marshaller_add(m, item->mess, item->mess_len + 1);
 }
 
-void main_channel_push_migrate_begin(Channel *channel, int port, int sport,
+void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport,
     char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len,
     uint8_t *cert_pub_key)
 {
-    MainChannel *main_chan = channel->data;
     RedChannelClient *rcc = main_chan->base.rcc;
     MigrateBeginPipeItem *item;
 
@@ -562,9 +549,8 @@ static void main_channel_marshall_migrate_begin(SpiceMarshaller *m,
     spice_marshall_msg_main_migrate_begin(m, &migrate);
 }
 
-void main_channel_push_migrate(Channel *channel)
+void main_channel_push_migrate(MainChannel *main_chan)
 {
-    MainChannel *main_chan = channel->data;
     RedChannelClient *rcc = main_chan->base.rcc;
 
     if (!rcc) {
@@ -581,9 +567,8 @@ static void main_channel_marshall_migrate(SpiceMarshaller *m)
     spice_marshall_msg_migrate(m, &migrate);
 }
 
-void main_channel_push_migrate_cancel(Channel *channel)
+void main_channel_push_migrate_cancel(MainChannel *main_chan)
 {
-    MainChannel *main_chan = channel->data;
     RedChannelClient *rcc = main_chan->base.rcc;
 
     if (!rcc) {
@@ -592,9 +577,8 @@ void main_channel_push_migrate_cancel(Channel *channel)
     red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_MIGRATE_CANCEL);
 }
 
-void main_channel_push_multi_media_time(Channel *channel, int time)
+void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
 {
-    MainChannel *main_chan = channel->data;
     RedChannelClient *rcc = main_chan->base.rcc;
     MultiMediaTimePipeItem *item;
 
@@ -611,9 +595,8 @@ static PipeItem *main_migrate_switch_item_new(MainChannel *main_chan)
     return item;
 }
 
-void main_channel_push_migrate_switch(Channel *channel)
+void main_channel_push_migrate_switch(MainChannel *main_chan)
 {
-    MainChannel *main_chan = channel->data;
     RedChannelClient *rcc = main_chan->base.rcc;
 
     red_channel_client_pipe_add_push(rcc,
@@ -643,7 +626,7 @@ static void main_channel_marshall_multi_media_time(SpiceMarshaller *m,
 
 static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
 {
-    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
 
     red_channel_client_init_send_data(rcc, base->type, base);
@@ -653,7 +636,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
             break;
         case SPICE_MSG_PING:
             main_channel_marshall_ping(m,
-                SPICE_CONTAINEROF(base, PingPipeItem, base)->size, ++main_chan->ping_id);
+                SPICE_CONTAINEROF(base, PingPipeItem, base)->size, ++(mcc->ping_id));
             break;
         case SPICE_MSG_MAIN_MOUSE_MODE:
             {
@@ -677,7 +660,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
         case SPICE_MSG_MIGRATE_DATA:
             main_channel_marshall_migrate_data_item(m,
                 red_channel_client_get_message_serial(rcc),
-                main_chan->ping_id);
+                mcc->ping_id);
             break;
         case SPICE_MSG_MAIN_INIT:
             main_channel_marshall_init(m,
@@ -714,6 +697,7 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
 static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
 {
     MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
 
     switch (type) {
     case SPICE_MSGC_MAIN_AGENT_START:
@@ -751,19 +735,19 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
         clock_gettime(CLOCK_MONOTONIC, &ts);
         roundtrip = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL - ping->timestamp;
 
-        if (ping->id == main_chan->net_test_id) {
-            switch (main_chan->net_test_stage) {
+        if (ping->id == mcc->net_test_id) {
+            switch (mcc->net_test_stage) {
             case NET_TEST_STAGE_WARMUP:
-                main_chan->net_test_id++;
-                main_chan->net_test_stage = NET_TEST_STAGE_LATENCY;
+                mcc->net_test_id++;
+                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
                 break;
             case NET_TEST_STAGE_LATENCY:
-                main_chan->net_test_id++;
-                main_chan->net_test_stage = NET_TEST_STAGE_RATE;
+                mcc->net_test_id++;
+                mcc->net_test_stage = NET_TEST_STAGE_RATE;
                 latency = roundtrip;
                 break;
             case NET_TEST_STAGE_RATE:
-                main_chan->net_test_id = 0;
+                mcc->net_test_id = 0;
                 if (roundtrip <= latency) {
                     // probably high load on client or server result with incorrect values
                     latency = 0;
@@ -777,13 +761,13 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
                            bitrate_per_sec,
                            (double)bitrate_per_sec / 1024 / 1024,
                            IS_LOW_BANDWIDTH() ? " LOW BANDWIDTH" : "");
-                main_chan->net_test_stage = NET_TEST_STAGE_INVALID;
+                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
                 break;
             default:
                 red_printf("invalid net test stage, ping id %d test id %d stage %d",
                            ping->id,
-                           main_chan->net_test_id,
-                           main_chan->net_test_stage);
+                           mcc->net_test_id,
+                           mcc->net_test_stage);
             }
             break;
         }
@@ -837,13 +821,14 @@ static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
     return TRUE;
 }
 
-static void main_channel_link(Channel *channel, RedsStream *stream, int migration,
+RedChannelClient *main_channel_link(Channel *channel, RedsStream *stream, int migration,
                         int num_common_caps, uint32_t *common_caps, int num_caps,
                         uint32_t *caps)
 {
     MainChannel *main_chan;
-    ASSERT(channel->data == NULL);
+    RedChannelClient *rcc;
 
+    ASSERT(channel->data == NULL);
     if (channel->data == NULL) {
         red_printf("create main channel");
         channel->data = red_channel_create_parser(
@@ -865,26 +850,22 @@ static void main_channel_link(Channel *channel, RedsStream *stream, int migratio
     }
     main_chan = (MainChannel*)channel->data;
     red_printf("add main channel client");
-    red_channel_client_create(sizeof(RedChannelClient), channel->data, stream);
+    rcc = red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, stream);
+    return rcc;
 }
 
-int main_channel_getsockname(Channel *channel, struct sockaddr *sa, socklen_t *salen)
+int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
 {
-    MainChannel *main_chan = channel->data;
-
     return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
 }
 
-int main_channel_getpeername(Channel *channel, struct sockaddr *sa, socklen_t *salen)
+int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
 {
-    MainChannel *main_chan = channel->data;
-
     return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
 }
 
-void main_channel_close(Channel *channel)
+void main_channel_close(MainChannel *main_chan)
 {
-    MainChannel *main_chan = channel->data;
     int socketfd;
 
     if (main_chan && (socketfd = red_channel_get_first_socket(&main_chan->base)) != -1) {
@@ -911,9 +892,8 @@ Channel* main_channel_init(void)
 
     channel = spice_new0(Channel, 1);
     channel->type = SPICE_CHANNEL_MAIN;
-    channel->link = main_channel_link;
+    channel->link = NULL; /* the main channel client is created by reds.c explicitly */
     channel->shutdown = main_channel_shutdown;
     channel->migrate = main_channel_migrate;
     return channel;
 }
-
diff --git a/server/main_channel.h b/server/main_channel.h
index 2776f01..31da720 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <spice/vd_agent.h>
 #include "common/marshaller.h"
+#include "red_channel.h"
 
 /* This is a temporary measure for reds/main split - should not be in a header,
  * but private (although only reds.c includes main_channel.h) */
@@ -43,31 +44,37 @@ struct MainMigrateData {
     uint32_t write_queue_size;
 };
 
+typedef struct MainChannel MainChannel;
+
 Channel *main_channel_init(void);
-void main_channel_close(Channel *channel); // not destroy, just socket close
-int main_channel_push_ping(Channel *channel, int size);
-void main_channel_push_mouse_mode(Channel *channel, int current_mode, int is_client_mouse_allowed);
-void main_channel_push_agent_connected(Channel *channel);
-void main_channel_push_agent_disconnected(Channel *channel);
-void main_channel_push_tokens(Channel *channel, uint32_t num_tokens);
-void main_channel_push_agent_data(Channel *channel, uint8_t* data, size_t len,
+/* This is a 'clone' from the reds.h Channel.link callback */
+RedChannelClient *main_channel_link(struct Channel *,
+                 RedsStream *stream, int migration, int num_common_caps,
+                 uint32_t *common_caps, int num_caps, uint32_t *caps);
+void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
+int main_channel_push_ping(MainChannel *main_chan, int size);
+void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
+void main_channel_push_agent_connected(MainChannel *main_chan);
+void main_channel_push_agent_disconnected(MainChannel *main_chan);
+void main_channel_push_tokens(MainChannel *main_chan, uint32_t num_tokens);
+void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t len,
            spice_marshaller_item_free_func free_data, void *opaque);
-void main_channel_start_net_test(Channel *channel);
+void main_channel_start_net_test(RedChannelClient *rcc);
 // TODO: huge. Consider making a reds_* interface for these functions
 // and calling from main.
-void main_channel_push_init(Channel *channel, int connection_id, int display_channels_hint,
+void main_channel_push_init(RedChannelClient *rcc, int connection_id, int display_channels_hint,
     int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
     int ram_hint);
-void main_channel_push_notify(Channel *channel, uint8_t *mess, const int mess_len);
+void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
 // TODO: consider exporting RedsMigSpice from reds.c
-void main_channel_push_migrate_begin(Channel *channel, int port, int sport, char *host,
+void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport, char *host,
     uint16_t cert_pub_key_type, uint32_t cert_pub_key_len, uint8_t *cert_pub_key);
-void main_channel_push_migrate(Channel *channel);
-void main_channel_push_migrate_switch(Channel *channel);
-void main_channel_push_migrate_cancel(Channel *channel);
-void main_channel_push_multi_media_time(Channel *channel, int time);
-int main_channel_getsockname(Channel *channel, struct sockaddr *sa, socklen_t *salen);
-int main_channel_getpeername(Channel *channel, struct sockaddr *sa, socklen_t *salen);
+void main_channel_push_migrate(MainChannel *main_chan);
+void main_channel_push_migrate_switch(MainChannel *main_chan);
+void main_channel_push_migrate_cancel(MainChannel *main_chan);
+void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
+int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
+int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
 
 // 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.
@@ -75,4 +82,3 @@ int main_channel_getpeername(Channel *channel, struct sockaddr *sa, socklen_t *s
 #define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
 
 #endif
-
diff --git a/server/red_channel.c b/server/red_channel.c
index 1927f47..529bd4e 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -354,7 +354,7 @@ static void red_channel_client_pipe_remove(RedChannelClient *rcc, PipeItem *item
 
 static void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc)
 {
-    ASSERT(rcc);
+    ASSERT(rcc && !channel->rcc);
 	channel->rcc = rcc;
 }
 
@@ -363,7 +363,7 @@ RedChannelClient *red_channel_client_create(
     RedChannel *channel,
     RedsStream *stream)
 {
-    RedChannelClient *rcc = NULL;
+    RedChannelClient *rcc;
 
     ASSERT(stream && channel && size >= sizeof(RedChannelClient));
     rcc = spice_malloc0(size);
diff --git a/server/reds.c b/server/reds.c
index 4490675..4ffc19a 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -199,7 +199,8 @@ typedef struct RedsState {
     VDIPortState agent_state;
     int pending_mouse_event;
     uint32_t link_id;
-    Channel *main_channel;
+    Channel *main_channel_factory;
+    MainChannel *main_channel;
 
     int mig_wait_connect;
     int mig_wait_disconnect;
@@ -612,7 +613,8 @@ void reds_disconnect()
     reds->agent_state.read_filter.discard_all = TRUE;
 
     reds_shatdown_channels();
-    reds->main_channel->shutdown(reds->main_channel);
+    reds->main_channel_factory->shutdown(reds->main_channel_factory);
+    reds->main_channel_factory->data = NULL;
     reds->main_channel = NULL;
     reds_mig_cleanup();
     reds->disconnecting = FALSE;
@@ -1494,13 +1496,14 @@ static void reds_send_link_result(RedLinkInfo *link, uint32_t error)
 }
 
 // TODO: now that main is a separate channel this should
-// actually be joined with reds_handle_other_links, ebcome reds_handle_link
+// actually be joined with reds_handle_other_links, become reds_handle_link
 static void reds_handle_main_link(RedLinkInfo *link)
 {
     RedsStream *stream;
     SpiceLinkMess *link_mess;
     uint32_t *caps;
     uint32_t connection_id;
+    RedChannelClient *rcc;
 
     red_printf("");
     link_mess = link->link_mess;
@@ -1535,11 +1538,15 @@ static void reds_handle_main_link(RedLinkInfo *link)
     link->link_mess = NULL;
     reds_link_free(link);
     caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
-    reds->main_channel = main_channel_init();
-    reds->main_channel->link(reds->main_channel, stream, reds->mig_target, link_mess->num_common_caps,
+    if (!reds->main_channel_factory) {
+        reds->main_channel_factory = main_channel_init();
+    }
+    rcc = main_channel_link(reds->main_channel_factory,
+                  stream, reds->mig_target, link_mess->num_common_caps,
                   link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
                   link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
     free(link_mess);
+    reds->main_channel = (MainChannel*)rcc->channel;
 
     if (vdagent) {
         reds->agent_state.read_filter.discard_all = FALSE;
@@ -1548,12 +1555,12 @@ static void reds_handle_main_link(RedLinkInfo *link)
 
     if (!reds->mig_target) {
         reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
-        main_channel_push_init(reds->main_channel, connection_id, red_dispatcher_count(),
+        main_channel_push_init(rcc, connection_id, red_dispatcher_count(),
             reds->mouse_mode, reds->is_client_mouse_allowed,
             reds_get_mm_time() - MM_TIME_DELTA,
             red_dispatcher_qxl_ram_size());
 
-        main_channel_start_net_test(reds->main_channel);
+        main_channel_start_net_test(rcc);
         /* Now that we have a client, forward any pending agent data */
         while (read_from_vdi_port());
     }
diff --git a/server/reds.h b/server/reds.h
index e5ba1c1..4ab7f63 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -35,6 +35,7 @@
 #define SPICE_GNUC_VISIBLE __attribute__ ((visibility ("default")))
 
 typedef struct RedsStream RedsStream;
+typedef struct MainChannelClient MainChannelClient;
 
 #if HAVE_SASL
 typedef struct RedsSASL {
commit 09ae4700d27b7c1cca64c2ce4c90f0c6cdf81ccf
Author: Alon Levy <alevy at redhat.com>
Date:   Sat Apr 2 12:54:52 2011 +0300

    server: move pipe from RedChannel to RedChannelClient
    
    Another cleanup patch, no change to behavior (still one client, and it
    disconnects previous client if any).
    
    The implementation for multiple client is straightforward: the pipe
    remains per (channel,client) pair, so it needs to move from the RedChannel
    that to RedChannelClient. Implementation using a single pipe with multiple
    consumers (to reflect different latencies) doesn't fit well with pipe rewriting
    that is used by the display channel. Additionally this approach is much simpler
    to verify. Lastly it doesn't add considerable overhead (but see the display
    channel changes in a later patch for a real place to rethink).
    
    This patch is just technical, changing signatures to reflect the first
    argument (oop style) so red_channel becomes red_channel_client. Some places
    may seem odd but they should be fixed with later comits where the channels
    grow to support multiple clients.
    
    Sound (playback/record) channels are the only ones not touched - this is
    consistent with previous patches, since they have been left out of the
    RedChannel refactoring.  That is left as future work. (note that they don't use
    a pipe, which was the reason for not refactoring).

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index 3a4a0d0..e350689 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -221,14 +221,6 @@ static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
     return sif->get_leds(sin);
 }
 
-static InputsPipeItem *inputs_pipe_item_new(InputsChannel *inputs_channel, int type)
-{
-    InputsPipeItem *item = spice_malloc(sizeof(InputsPipeItem));
-
-    red_channel_pipe_item_init(&inputs_channel->base, &item->base, type);
-    return item;
-}
-
 static KeyModifiersPipeItem *inputs_key_modifiers_item_new(
     InputsChannel *inputs_channel, uint8_t modifiers)
 {
@@ -240,15 +232,6 @@ static KeyModifiersPipeItem *inputs_key_modifiers_item_new(
     return item;
 }
 
-// Right now every PipeItem we add is an InputsPipeItem, later maybe make it simpler
-// for type only PipeItems
-static void inputs_pipe_add_type(InputsChannel *channel, int type)
-{
-    InputsPipeItem* pipe_item = inputs_pipe_item_new(channel, type);
-
-    red_channel_pipe_add_push(&channel->base, &pipe_item->base);
-}
-
 static void inputs_channel_release_pipe_item(RedChannelClient *rcc,
     PipeItem *base, int item_pushed)
 {
@@ -318,7 +301,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui
         SpiceMsgcMouseMotion *mouse_motion = (SpiceMsgcMouseMotion *)buf;
 
         if (++inputs_channel->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
-            inputs_pipe_add_type(inputs_channel, PIPE_ITEM_MOUSE_MOTION_ACK);
+            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
         }
         if (mouse && reds_get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
             SpiceMouseInterface *sif;
@@ -333,7 +316,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui
         SpiceMsgcMousePosition *pos = (SpiceMsgcMousePosition *)buf;
 
         if (++inputs_channel->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
-            inputs_pipe_add_type(inputs_channel, PIPE_ITEM_MOUSE_MOTION_ACK);
+            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
         }
         if (reds_get_mouse_mode() != SPICE_MOUSE_MODE_CLIENT) {
             break;
@@ -468,21 +451,20 @@ static void inputs_shutdown(Channel *channel)
 static void inputs_migrate(Channel *channel)
 {
     InputsChannel *inputs_channel = channel->data;
-    InputsPipeItem *item;
+    RedChannelClient *rcc = inputs_channel->base.rcc;
 
     ASSERT(g_inputs_channel == (InputsChannel *)channel->data);
-    item = inputs_pipe_item_new(inputs_channel, PIPE_ITEM_MIGRATE);
-    red_channel_pipe_add_push(&inputs_channel->base, &item->base);
+    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MIGRATE);
 }
 
-static void inputs_pipe_add_init(InputsChannel *inputs_channel)
+static void inputs_pipe_add_init(RedChannelClient *rcc)
 {
     InputsInitPipeItem *item = spice_malloc(sizeof(InputsInitPipeItem));
 
-    red_channel_pipe_item_init(&inputs_channel->base, &item->base,
+    red_channel_pipe_item_init(rcc->channel, &item->base,
                                PIPE_ITEM_INPUTS_INIT);
     item->modifiers = kbd_get_leds(keyboard);
-    red_channel_pipe_add_push(&inputs_channel->base, &item->base);
+    red_channel_client_pipe_add_push(rcc, &item->base);
 }
 
 static int inputs_channel_config_socket(RedChannelClient *rcc)
@@ -539,7 +521,7 @@ static void inputs_link(Channel *channel, RedsStream *stream, int migration,
     rcc = red_channel_client_create(sizeof(RedChannelClient), &g_inputs_channel->base, stream);
     ASSERT(rcc);
     channel->data = inputs_channel;
-    inputs_pipe_add_init(inputs_channel);
+    inputs_pipe_add_init(rcc);
 }
 
 static void inputs_push_keyboard_modifiers(uint8_t modifiers)
@@ -550,7 +532,7 @@ static void inputs_push_keyboard_modifiers(uint8_t modifiers)
         return;
     }
     item = inputs_key_modifiers_item_new(g_inputs_channel, modifiers);
-    red_channel_pipe_add_push(&g_inputs_channel->base, &item->base);
+    red_channel_client_pipe_add_push(g_inputs_channel->base.rcc, &item->base);
 }
 
 void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds)
diff --git a/server/main_channel.c b/server/main_channel.c
index 0718f88..435aace 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -146,68 +146,64 @@ static void main_disconnect(MainChannel *main_chan)
     bitrate_per_sec = ~0;
 }
 
+static int main_channel_client_push_ping(RedChannelClient *rcc, int size);
+
 void main_channel_start_net_test(Channel *channel)
 {
     MainChannel *main_chan = channel->data;
+    RedChannelClient *rcc;
 
-    if (!main_chan || main_chan->net_test_id) {
+    if (!main_chan || !main_chan->base.rcc || main_chan->net_test_id) {
         return;
     }
+    rcc = main_chan->base.rcc;
 
-    if (main_channel_push_ping(channel, NET_TEST_WARMUP_BYTES)
-        && main_channel_push_ping(channel, 0)
-        && main_channel_push_ping(channel, NET_TEST_BYTES)) {
+    if (main_channel_client_push_ping(rcc, NET_TEST_WARMUP_BYTES)
+        && main_channel_client_push_ping(rcc, 0)
+        && main_channel_client_push_ping(rcc, NET_TEST_BYTES)) {
         main_chan->net_test_id = main_chan->ping_id - 2;
         main_chan->net_test_stage = NET_TEST_STAGE_WARMUP;
     }
 }
 
-static RedsOutItem *main_pipe_item_new(MainChannel *main_chan, int type)
-{
-    RedsOutItem *item = spice_malloc(sizeof(RedsOutItem));
-
-    red_channel_pipe_item_init(&main_chan->base, &item->base, type);
-    return item;
-}
-
-static MouseModePipeItem *main_mouse_mode_item_new(MainChannel *main_chan,
+static MouseModePipeItem *main_mouse_mode_item_new(RedChannelClient *rcc,
     int current_mode, int is_client_mouse_allowed)
 {
     MouseModePipeItem *item = spice_malloc(sizeof(MouseModePipeItem));
 
-    red_channel_pipe_item_init(&main_chan->base, &item->base,
+    red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_MOUSE_MODE);
     item->current_mode = current_mode;
     item->is_client_mouse_allowed = is_client_mouse_allowed;
     return item;
 }
 
-static PingPipeItem *main_ping_item_new(MainChannel *channel, int size)
+static PingPipeItem *main_ping_item_new(RedChannelClient *rcc, int size)
 {
     PingPipeItem *item = spice_malloc(sizeof(PingPipeItem));
 
-    red_channel_pipe_item_init(&channel->base, &item->base, SPICE_MSG_PING);
+    red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_PING);
     item->size = size;
     return item;
 }
 
-static TokensPipeItem *main_tokens_item_new(MainChannel *main_chan, int tokens)
+static TokensPipeItem *main_tokens_item_new(RedChannelClient *rcc, int tokens)
 {
     TokensPipeItem *item = spice_malloc(sizeof(TokensPipeItem));
 
-    red_channel_pipe_item_init(&main_chan->base, &item->base,
+    red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_AGENT_TOKEN);
     item->tokens = tokens;
     return item;
 }
 
-static AgentDataPipeItem *main_agent_data_item_new(MainChannel *channel,
+static AgentDataPipeItem *main_agent_data_item_new(RedChannelClient *rcc,
            uint8_t* data, size_t len,
            spice_marshaller_item_free_func free_data, void *opaque)
 {
     AgentDataPipeItem *item = spice_malloc(sizeof(AgentDataPipeItem));
 
-    red_channel_pipe_item_init(&channel->base, &item->base, SPICE_MSG_MAIN_AGENT_DATA);
+    red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_MAIN_AGENT_DATA);
     item->data = data;
     item->len = len;
     item->free_data = free_data;
@@ -215,14 +211,14 @@ static AgentDataPipeItem *main_agent_data_item_new(MainChannel *channel,
     return item;
 }
 
-static InitPipeItem *main_init_item_new(MainChannel *main_chan,
+static InitPipeItem *main_init_item_new(RedChannelClient *rcc,
     int connection_id, int display_channels_hint, int current_mouse_mode,
     int is_client_mouse_allowed, int multi_media_time,
     int ram_hint)
 {
     InitPipeItem *item = spice_malloc(sizeof(InitPipeItem));
 
-    red_channel_pipe_item_init(&main_chan->base, &item->base,
+    red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_INIT);
     item->connection_id = connection_id;
     item->display_channels_hint = display_channels_hint;
@@ -233,12 +229,12 @@ static InitPipeItem *main_init_item_new(MainChannel *main_chan,
     return item;
 }
 
-static NotifyPipeItem *main_notify_item_new(MainChannel *main_chan,
+static NotifyPipeItem *main_notify_item_new(RedChannelClient *rcc,
                                         uint8_t *mess, const int mess_len)
 {
     NotifyPipeItem *item = spice_malloc(sizeof(NotifyPipeItem));
 
-    red_channel_pipe_item_init(&main_chan->base, &item->base,
+    red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_NOTIFY);
     item->mess = mess;
     item->mess_len = mess_len;
@@ -246,13 +242,13 @@ static NotifyPipeItem *main_notify_item_new(MainChannel *main_chan,
 }
 
 static MigrateBeginPipeItem *main_migrate_begin_item_new(
-    MainChannel *main_chan, int port, int sport,
+    RedChannelClient *rcc, int port, int sport,
     char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len,
     uint8_t *cert_pub_key)
 {
     MigrateBeginPipeItem *item = spice_malloc(sizeof(MigrateBeginPipeItem));
 
-    red_channel_pipe_item_init(&main_chan->base, &item->base,
+    red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_MIGRATE_BEGIN);
     item->port = port;
     item->sport = sport;
@@ -264,23 +260,20 @@ static MigrateBeginPipeItem *main_migrate_begin_item_new(
 }
 
 static MultiMediaTimePipeItem *main_multi_media_time_item_new(
-    MainChannel *main_chan, int time)
+    RedChannelClient *rcc, int time)
 {
     MultiMediaTimePipeItem *item;
 
     item = spice_malloc(sizeof(MultiMediaTimePipeItem));
-    red_channel_pipe_item_init(&main_chan->base, &item->base,
+    red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_MULTI_MEDIA_TIME);
     item->time = time;
     return item;
 }
 
-static void main_channel_push_channels(MainChannel *main_chan)
+static void main_channel_push_channels(RedChannelClient *rcc)
 {
-    RedsOutItem *item;
-
-    item = main_pipe_item_new(main_chan, SPICE_MSG_MAIN_CHANNELS_LIST);
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_CHANNELS_LIST);
 }
 
 static void main_channel_marshall_channels(SpiceMarshaller *m)
@@ -294,17 +287,23 @@ static void main_channel_marshall_channels(SpiceMarshaller *m)
     free(channels_info);
 }
 
+int main_channel_client_push_ping(RedChannelClient *rcc, int size)
+{
+    PingPipeItem *item;
+
+    item = main_ping_item_new(rcc, size);
+    red_channel_client_pipe_add_push(rcc, &item->base);
+    return TRUE;
+}
+
 int main_channel_push_ping(Channel *channel, int size)
 {
     MainChannel *main_chan = channel->data;
-    PingPipeItem *item;
-    
-    if (main_chan == NULL) {
+
+    if (main_chan->base.rcc == NULL) {
         return FALSE;
     }
-    item = main_ping_item_new(main_chan, size);
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
-    return TRUE;
+    return main_channel_client_push_ping(main_chan->base.rcc, size);
 }
 
 static void main_channel_marshall_ping(SpiceMarshaller *m, int size, int ping_id)
@@ -324,15 +323,28 @@ static void main_channel_marshall_ping(SpiceMarshaller *m, int size, int ping_id
     }
 }
 
+static void main_channel_client_push_mouse_mode(RedChannelClient *rcc, int current_mode,
+                                         int is_client_mouse_allowed);
+
 void main_channel_push_mouse_mode(Channel *channel, int current_mode,
                                   int is_client_mouse_allowed)
 {
     MainChannel *main_chan = channel->data;
+
+    if (main_chan && main_chan->base.rcc != NULL) {
+        main_channel_client_push_mouse_mode(main_chan->base.rcc, current_mode,
+                                            is_client_mouse_allowed);
+    }
+}
+
+static void main_channel_client_push_mouse_mode(RedChannelClient *rcc, int current_mode,
+                                         int is_client_mouse_allowed)
+{
     MouseModePipeItem *item;
 
-    item = main_mouse_mode_item_new(main_chan, current_mode,
+    item = main_mouse_mode_item_new(rcc, current_mode,
                                     is_client_mouse_allowed);
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    red_channel_client_pipe_add_push(rcc, &item->base);
 }
 
 static void main_channel_marshall_mouse_mode(SpiceMarshaller *m, int current_mode, int is_client_mouse_allowed)
@@ -348,20 +360,20 @@ static void main_channel_marshall_mouse_mode(SpiceMarshaller *m, int current_mod
 
 void main_channel_push_agent_connected(Channel *channel)
 {
-    RedsOutItem *item;
-    MainChannel *main_chan = channel->data;
+    RedChannelClient *rcc = ((MainChannel*)channel->data)->base.rcc;
 
-    item = main_pipe_item_new(main_chan, SPICE_MSG_MAIN_AGENT_CONNECTED);
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_AGENT_CONNECTED);
 }
 
 void main_channel_push_agent_disconnected(Channel *channel)
 {
-    RedsOutItem *item;
     MainChannel *main_chan = channel->data;
+    RedChannelClient *rcc = main_chan->base.rcc;
 
-    item = main_pipe_item_new(main_chan, SPICE_MSG_MAIN_AGENT_DISCONNECTED);
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    if (!rcc) {
+        return;
+    }
+    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_AGENT_DISCONNECTED);
 }
 
 static void main_channel_marshall_agent_disconnected(SpiceMarshaller *m)
@@ -374,10 +386,15 @@ static void main_channel_marshall_agent_disconnected(SpiceMarshaller *m)
 
 void main_channel_push_tokens(Channel *channel, uint32_t num_tokens)
 {
+    TokensPipeItem *item;
     MainChannel *main_chan = channel->data;
-    TokensPipeItem *item = main_tokens_item_new(main_chan, num_tokens);
+    RedChannelClient *rcc = main_chan->base.rcc;
 
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    if (!rcc) {
+        return;
+    }
+    item = main_tokens_item_new(rcc, num_tokens);
+    red_channel_client_pipe_add_push(rcc, &item->base);
 }
 
 static void main_channel_marshall_tokens(SpiceMarshaller *m, uint32_t num_tokens)
@@ -392,10 +409,11 @@ void main_channel_push_agent_data(Channel *channel, uint8_t* data, size_t len,
            spice_marshaller_item_free_func free_data, void *opaque)
 {
     MainChannel *main_chan = channel->data;
+    RedChannelClient *rcc = main_chan->base.rcc;
     AgentDataPipeItem *item;
 
-    item = main_agent_data_item_new(main_chan, data, len, free_data, opaque);
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    item = main_agent_data_item_new(rcc, data, len, free_data, opaque);
+    red_channel_client_pipe_add_push(rcc, &item->base);
 }
 
 static void main_channel_marshall_agent_data(SpiceMarshaller *m,
@@ -407,9 +425,12 @@ static void main_channel_marshall_agent_data(SpiceMarshaller *m,
 
 static void main_channel_push_migrate_data_item(MainChannel *main_chan)
 {
-    RedsOutItem *item = main_pipe_item_new(main_chan, SPICE_MSG_MIGRATE_DATA);
+    RedChannelClient *rcc = main_chan->base.rcc;
 
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    if (!rcc) {
+        return;
+    }
+    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MIGRATE_DATA);
 }
 
 static void main_channel_marshall_migrate_data_item(SpiceMarshaller *m, int serial, int ping_id)
@@ -456,11 +477,12 @@ void main_channel_push_init(Channel *channel, int connection_id,
 {
     InitPipeItem *item;
     MainChannel *main_chan = channel->data;
+    RedChannelClient *rcc = main_chan->base.rcc;
 
-    item = main_init_item_new(main_chan,
+    item = main_init_item_new(rcc,
              connection_id, display_channels_hint, current_mouse_mode,
              is_client_mouse_allowed, multi_media_time, ram_hint);
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    red_channel_client_pipe_add_push(rcc, &item->base);
 }
 
 static void main_channel_marshall_init(SpiceMarshaller *m,
@@ -485,9 +507,11 @@ static void main_channel_marshall_init(SpiceMarshaller *m,
 void main_channel_push_notify(Channel *channel, uint8_t *mess, const int mess_len)
 {
     MainChannel *main_chan = channel->data;
-    NotifyPipeItem *item = main_notify_item_new(main_chan, mess, mess_len);
+    RedChannelClient *rcc = main_chan->base.rcc;
+    NotifyPipeItem *item;
 
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    item = main_notify_item_new(rcc, mess, mess_len);
+    red_channel_client_pipe_add_push(rcc, &item->base);
 }
 
 static uint64_t get_time_stamp(void)
@@ -515,10 +539,12 @@ void main_channel_push_migrate_begin(Channel *channel, int port, int sport,
     uint8_t *cert_pub_key)
 {
     MainChannel *main_chan = channel->data;
-    MigrateBeginPipeItem *item = main_migrate_begin_item_new(main_chan, port,
-        sport, host, cert_pub_key_type, cert_pub_key_len, cert_pub_key);
+    RedChannelClient *rcc = main_chan->base.rcc;
+    MigrateBeginPipeItem *item;
 
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    item = main_migrate_begin_item_new(rcc, port,
+        sport, host, cert_pub_key_type, cert_pub_key_len, cert_pub_key);
+    red_channel_client_pipe_add_push(rcc, &item->base);
 }
 
 static void main_channel_marshall_migrate_begin(SpiceMarshaller *m,
@@ -539,9 +565,12 @@ static void main_channel_marshall_migrate_begin(SpiceMarshaller *m,
 void main_channel_push_migrate(Channel *channel)
 {
     MainChannel *main_chan = channel->data;
-    RedsOutItem *item = main_pipe_item_new(main_chan, SPICE_MSG_MIGRATE);
+    RedChannelClient *rcc = main_chan->base.rcc;
 
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    if (!rcc) {
+        return;
+    }
+    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MIGRATE);
 }
 
 static void main_channel_marshall_migrate(SpiceMarshaller *m)
@@ -555,19 +584,22 @@ static void main_channel_marshall_migrate(SpiceMarshaller *m)
 void main_channel_push_migrate_cancel(Channel *channel)
 {
     MainChannel *main_chan = channel->data;
-    RedsOutItem *item = main_pipe_item_new(main_chan,
-                                           SPICE_MSG_MAIN_MIGRATE_CANCEL);
+    RedChannelClient *rcc = main_chan->base.rcc;
 
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    if (!rcc) {
+        return;
+    }
+    red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_MIGRATE_CANCEL);
 }
 
 void main_channel_push_multi_media_time(Channel *channel, int time)
 {
     MainChannel *main_chan = channel->data;
+    RedChannelClient *rcc = main_chan->base.rcc;
+    MultiMediaTimePipeItem *item;
 
-    MultiMediaTimePipeItem *item =
-        main_multi_media_time_item_new(main_chan, time);
-    red_channel_pipe_add_push(&main_chan->base, &item->base);
+    item =main_multi_media_time_item_new(rcc, time);
+    red_channel_client_pipe_add_push(rcc, &item->base);
 }
 
 static PipeItem *main_migrate_switch_item_new(MainChannel *main_chan)
@@ -582,8 +614,9 @@ static PipeItem *main_migrate_switch_item_new(MainChannel *main_chan)
 void main_channel_push_migrate_switch(Channel *channel)
 {
     MainChannel *main_chan = channel->data;
+    RedChannelClient *rcc = main_chan->base.rcc;
 
-    red_channel_pipe_add_push(&main_chan->base,
+    red_channel_client_pipe_add_push(rcc,
         main_migrate_switch_item_new(main_chan));
 }
 
@@ -697,7 +730,7 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
     case SPICE_MSGC_MAIN_AGENT_TOKEN:
         break;
     case SPICE_MSGC_MAIN_ATTACH_CHANNELS:
-        main_channel_push_channels(main_chan);
+        main_channel_push_channels(rcc);
         break;
     case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
         red_printf("connected");
diff --git a/server/red_channel.c b/server/red_channel.c
index 8bbc6c9..1927f47 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -267,13 +267,12 @@ static void red_channel_client_reset_send_data(RedChannelClient *rcc)
 
 void red_channel_client_push_set_ack(RedChannelClient *rcc)
 {
-    red_channel_pipe_add_type(rcc->channel, PIPE_ITEM_TYPE_SET_ACK);
+    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_SET_ACK);
 }
 
 void red_channel_push_set_ack(RedChannel *channel)
 {
-    // TODO - MC, should replace with add_type_all (or whatever I'll name it)
-    red_channel_pipe_add_type(channel, PIPE_ITEM_TYPE_SET_ACK);
+    red_channel_pipes_add_type(channel, PIPE_ITEM_TYPE_SET_ACK);
 }
 
 static void red_channel_client_send_set_ack(RedChannelClient *rcc)
@@ -347,6 +346,12 @@ static void red_channel_peer_on_out_msg_done(void *opaque)
     }
 }
 
+static void red_channel_client_pipe_remove(RedChannelClient *rcc, PipeItem *item)
+{
+    rcc->pipe_size--;
+    ring_remove(&item->link);
+}
+
 static void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc)
 {
     ASSERT(rcc);
@@ -381,9 +386,13 @@ RedChannelClient *red_channel_client_create(
         goto error;
     }
 
+    ring_init(&rcc->pipe);
+    rcc->pipe_size = 0;
+
     stream->watch = channel->core->watch_add(stream->socket,
                                            SPICE_WATCH_EVENT_READ,
                                            red_channel_client_event, rcc);
+    rcc->id = 0;
     red_channel_add_client(channel, rcc);
     return rcc;
 error:
@@ -425,7 +434,6 @@ RedChannel *red_channel_create(int size,
 
     channel->core = core;
     channel->migrate = migrate;
-    ring_init(&channel->pipe);
 
     channel->incoming_cb.alloc_msg_buf = (alloc_msg_recv_buf_proc)alloc_recv_buf;
     channel->incoming_cb.release_msg_buf = (release_msg_recv_buf_proc)release_recv_buf;
@@ -501,6 +509,7 @@ void red_channel_destroy(RedChannel *channel)
     if (!channel) {
         return;
     }
+    red_channel_pipes_clear(channel);
     if (channel->rcc) {
         red_channel_client_destroy(channel->rcc);
     }
@@ -524,7 +533,7 @@ void red_channel_shutdown(RedChannel *channel)
     if (channel->rcc) {
         red_channel_client_shutdown(channel->rcc);
     }
-    red_channel_pipe_clear(channel);
+    red_channel_pipes_clear(channel);
 }
 
 void red_channel_client_send(RedChannelClient *rcc)
@@ -545,23 +554,20 @@ static inline int red_channel_client_waiting_for_ack(RedChannelClient *rcc)
             (rcc->ack_data.messages_window > rcc->ack_data.client_window * 2));
 }
 
-// TODO: add refs and target to PipeItem. Right now this only works for a
-// single client (or actually, it's worse - first come first served)
-static inline PipeItem *red_channel_client_pipe_get(RedChannelClient *rcc)
+static inline PipeItem *red_channel_client_pipe_item_get(RedChannelClient *rcc)
 {
     PipeItem *item;
 
     if (!rcc || rcc->send_data.blocked
              || red_channel_client_waiting_for_ack(rcc)
-             || !(item = (PipeItem *)ring_get_tail(&rcc->channel->pipe))) {
+             || !(item = (PipeItem *)ring_get_tail(&rcc->pipe))) {
         return NULL;
     }
-    --rcc->channel->pipe_size;
-    ring_remove(&item->link);
+    red_channel_client_pipe_remove(rcc, item);
     return item;
 }
 
-static void red_channel_client_push(RedChannelClient *rcc)
+void red_channel_client_push(RedChannelClient *rcc)
 {
     PipeItem *pipe_item;
 
@@ -575,7 +581,7 @@ static void red_channel_client_push(RedChannelClient *rcc)
         red_channel_client_send(rcc);
     }
 
-    while ((pipe_item = red_channel_client_pipe_get(rcc))) {
+    while ((pipe_item = red_channel_client_pipe_item_get(rcc))) {
         red_channel_client_send_item(rcc, pipe_item);
     }
     rcc->during_send = FALSE;
@@ -599,13 +605,15 @@ static void red_channel_client_init_outgoing_messages_window(RedChannelClient *r
 // specific
 void red_channel_init_outgoing_messages_window(RedChannel *channel)
 {
-    red_channel_client_init_outgoing_messages_window(channel->rcc);
+    if (channel->rcc) {
+        red_channel_client_init_outgoing_messages_window(channel->rcc);
+    }
 }
 
-static void red_channel_handle_migrate_flush_mark(RedChannel *channel)
+static void red_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
 {
-    if (channel->handle_migrate_flush_mark) {
-        channel->handle_migrate_flush_mark(channel->rcc);
+    if (rcc->channel->handle_migrate_flush_mark) {
+        rcc->channel->handle_migrate_flush_mark(rcc);
     }
 }
 
@@ -647,7 +655,7 @@ int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size,
     case SPICE_MSGC_DISCONNECTING:
         break;
     case SPICE_MSGC_MIGRATE_FLUSH_MARK:
-        red_channel_handle_migrate_flush_mark(rcc->channel);
+        red_channel_handle_migrate_flush_mark(rcc);
         break;
     case SPICE_MSGC_MIGRATE_DATA:
         red_channel_handle_migrate_data(rcc, size, message);
@@ -715,64 +723,86 @@ void red_channel_pipe_item_init(RedChannel *channel, PipeItem *item, int type)
     item->type = type;
 }
 
-void red_channel_pipe_add(RedChannel *channel, PipeItem *item)
+void red_channel_client_pipe_add(RedChannelClient *rcc, PipeItem *item)
 {
-    ASSERT(channel);
-
-    channel->pipe_size++;
-    ring_add(&channel->pipe, &item->link);
+    ASSERT(rcc && item);
+    rcc->pipe_size++;
+    ring_add(&rcc->pipe, &item->link);
 }
 
-void red_channel_pipe_add_push(RedChannel *channel, PipeItem *item)
+void red_channel_client_pipe_add_push(RedChannelClient *rcc, PipeItem *item)
 {
-    ASSERT(channel);
-
-    channel->pipe_size++;
-    ring_add(&channel->pipe, &item->link);
-    red_channel_push(channel);
+    red_channel_client_pipe_add(rcc, item);
+    red_channel_client_push(rcc);
 }
 
-void red_channel_pipe_add_after(RedChannel *channel, PipeItem *item, PipeItem *pos)
+void red_channel_client_pipe_add_after(RedChannelClient *rcc,
+                                       PipeItem *item, PipeItem *pos)
 {
-    ASSERT(channel);
+    ASSERT(rcc);
     ASSERT(pos);
     ASSERT(item);
 
-    channel->pipe_size++;
+    rcc->pipe_size++;
     ring_add_after(&item->link, &pos->link);
 }
 
-int red_channel_pipe_item_is_linked(RedChannel *channel, PipeItem *item)
+int red_channel_client_pipe_item_is_linked(RedChannelClient *rcc,
+                                           PipeItem *item)
 {
     return ring_item_is_linked(&item->link);
 }
 
-void red_channel_pipe_item_remove(RedChannel *channel, PipeItem *item)
+int red_channel_pipe_item_is_linked(RedChannel *channel, PipeItem *item)
 {
-    ring_remove(&item->link);
+    return channel->rcc && red_channel_client_pipe_item_is_linked(channel->rcc, item);
 }
 
-void red_channel_pipe_add_tail(RedChannel *channel, PipeItem *item)
+void red_channel_client_pipe_add_tail_no_push(RedChannelClient *rcc,
+                                              PipeItem *item)
 {
-    ASSERT(channel);
-    channel->pipe_size++;
-    ring_add_before(&item->link, &channel->pipe);
+    ASSERT(rcc);
+    rcc->pipe_size++;
+    ring_add_before(&item->link, &rcc->pipe);
+}
 
-    red_channel_push(channel);
+void red_channel_client_pipe_add_tail(RedChannelClient *rcc, PipeItem *item)
+{
+    ASSERT(rcc);
+    rcc->pipe_size++;
+    ring_add_before(&item->link, &rcc->pipe);
+    red_channel_client_push(rcc);
 }
 
-void red_channel_pipe_add_type(RedChannel *channel, int pipe_item_type)
+void red_channel_client_pipe_add_type(RedChannelClient *rcc, int pipe_item_type)
 {
     PipeItem *item = spice_new(PipeItem, 1);
-    red_channel_pipe_item_init(channel, item, pipe_item_type);
-    red_channel_pipe_add(channel, item);
 
-    red_channel_push(channel);
+    red_channel_pipe_item_init(rcc->channel, item, pipe_item_type);
+    red_channel_client_pipe_add(rcc, item);
+    red_channel_client_push(rcc);
+}
+
+void red_channel_pipes_add_type(RedChannel *channel, int pipe_item_type)
+{
+    if (channel->rcc) {
+        red_channel_client_pipe_add_type(channel->rcc, pipe_item_type);
+    }
+}
+
+int red_channel_client_is_connected(RedChannelClient *rcc)
+{
+    return rcc->stream != NULL;
+}
+
+void red_channel_pipe_item_remove(RedChannel *channel, PipeItem *item)
+{
+    ring_remove(&item->link);
 }
 
 int red_channel_is_connected(RedChannel *channel)
 {
-    return channel->rcc != NULL;
+    return (channel->rcc != NULL) && red_channel_client_is_connected(channel->rcc);
 }
 
 void red_channel_client_clear_sent_item(RedChannelClient *rcc)
@@ -785,19 +815,26 @@ void red_channel_client_clear_sent_item(RedChannelClient *rcc)
     rcc->send_data.size = 0;
 }
 
-void red_channel_pipe_clear(RedChannel *channel)
+void red_channel_client_pipe_clear(RedChannelClient *rcc)
 {
     PipeItem *item;
 
-    ASSERT(channel);
-    if (channel->rcc) {
-        red_channel_client_clear_sent_item(channel->rcc);
+    if (rcc) {
+        red_channel_client_clear_sent_item(rcc);
     }
-    while ((item = (PipeItem *)ring_get_head(&channel->pipe))) {
+    while ((item = (PipeItem *)ring_get_head(&rcc->pipe))) {
         ring_remove(&item->link);
-        red_channel_client_release_item(channel->rcc, item, FALSE);
+        red_channel_client_release_item(rcc, item, FALSE);
+    }
+    rcc->pipe_size = 0;
+}
+
+void red_channel_pipes_clear(RedChannel *channel)
+{
+    if (!channel || !channel->rcc) {
+        return;
     }
-    channel->pipe_size = 0;
+    red_channel_client_pipe_clear(channel->rcc);
 }
 
 void red_channel_client_ack_zero_messages_window(RedChannelClient *rcc)
@@ -839,7 +876,7 @@ void red_channel_client_disconnect(RedChannelClient *rcc)
 
 void red_channel_disconnect(RedChannel *channel)
 {
-    red_channel_pipe_clear(channel);
+    red_channel_pipes_clear(channel);
     if (channel->rcc) {
         red_channel_client_disconnect(channel->rcc);
     }
@@ -936,12 +973,6 @@ int red_channel_client_no_item_being_sent(RedChannelClient *rcc)
     return !rcc || (rcc->send_data.size == 0);
 }
 
-static void red_channel_client_pipe_remove(RedChannelClient *rcc, PipeItem *item)
-{
-    rcc->channel->pipe_size--;
-    ring_remove(&item->link);
-}
-
 void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc,
                                                 PipeItem *item)
 {
diff --git a/server/red_channel.h b/server/red_channel.h
index e7a83d3..2bd3054 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -161,6 +161,9 @@ struct RedChannelClient {
     OutgoingHandler outgoing;
     IncomingHandler incoming;
     int during_send;
+    int id; // debugging purposes
+    Ring pipe;
+    uint32_t pipe_size;
 };
 
 struct RedChannel {
@@ -170,9 +173,6 @@ struct RedChannel {
 
     RedChannelClient *rcc;
 
-    Ring pipe;
-    uint32_t pipe_size;
-
     OutgoingHandlerInterface outgoing_cb;
     IncomingHandlerInterface incoming_cb;
 
@@ -258,15 +258,16 @@ void red_channel_client_set_message_serial(RedChannelClient *channel, uint64_t);
 void red_channel_client_begin_send_message(RedChannelClient *rcc);
 
 void red_channel_pipe_item_init(RedChannel *channel, PipeItem *item, int type);
-void red_channel_pipe_add_push(RedChannel *channel, PipeItem *item);
-void red_channel_pipe_add(RedChannel *channel, PipeItem *item);
-void red_channel_pipe_add_after(RedChannel *channel, PipeItem *item, PipeItem *pos);
+void red_channel_client_pipe_add_push(RedChannelClient *rcc, PipeItem *item);
+void red_channel_client_pipe_add(RedChannelClient *rcc, PipeItem *item);
+void red_channel_client_pipe_add_after(RedChannelClient *rcc, PipeItem *item, PipeItem *pos);
 int red_channel_pipe_item_is_linked(RedChannel *channel, PipeItem *item);
 void red_channel_pipe_item_remove(RedChannel *channel, PipeItem *item);
 void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc, PipeItem *item);
-void red_channel_pipe_add_tail(RedChannel *channel, PipeItem *item);
+void red_channel_client_pipe_add_tail(RedChannelClient *rcc, PipeItem *item);
 /* for types that use this routine -> the pipe item should be freed */
-void red_channel_pipe_add_type(RedChannel *channel, int pipe_item_type);
+void red_channel_client_pipe_add_type(RedChannelClient *rcc, int pipe_item_type);
+void red_channel_pipes_add_type(RedChannel *channel, int pipe_item_type);
 
 void red_channel_client_ack_zero_messages_window(RedChannelClient *rcc);
 void red_channel_client_ack_set_client_window(RedChannelClient *rcc, int client_window);
@@ -296,19 +297,24 @@ int red_channel_client_send_message_pending(RedChannelClient *rcc);
  * hasn't even begun, i.e. no one called begin_send_)
  * */
 int red_channel_item_being_sent(RedChannel *channel, PipeItem *item);
+int red_channel_client_item_being_sent(RedChannelClient *rcc, PipeItem *item);
 
 int red_channel_no_item_being_sent(RedChannel *channel);
 int red_channel_client_no_item_being_sent(RedChannelClient *rcc);
 
+void red_channel_pipes_remove(RedChannel *channel, PipeItem *item);
+
 // TODO: unstaticed for display/cursor channels. they do some specific pushes not through
 // adding elements or on events. but not sure if this is actually required (only result
 // should be that they ""try"" a little harder, but if the event system is correct it
 // should not make any difference.
 void red_channel_push(RedChannel *channel);
+void red_channel_client_push(RedChannelClient *rcc);
 // TODO: again - what is the context exactly? this happens in channel disconnect. but our
 // current red_channel_shutdown also closes the socket - is there a socket to close?
 // are we reading from an fd here? arghh
-void red_channel_pipe_clear(RedChannel *channel);
+void red_channel_pipes_clear(RedChannel *channel);
+void red_channel_client_pipe_clear(RedChannelClient *rcc);
 // Again, used in various places outside of event handler context (or in other event handler
 // contexts):
 //  flush_display_commands/flush_cursor_commands
diff --git a/server/red_client_cache.h b/server/red_client_cache.h
index fae4b2a..6c17ba6 100644
--- a/server/red_client_cache.h
+++ b/server/red_client_cache.h
@@ -24,6 +24,7 @@
 #define FUNC_NAME(name) red_cursor_cache_##name
 #define VAR_NAME(name) cursor_cache_##name
 #define CHANNEL CursorChannel
+#define CHANNELCLIENT RedChannelClient
 
 #elif defined(CLIENT_PALETTE_CACHE)
 
@@ -34,12 +35,15 @@
 #define FUNC_NAME(name) red_palette_cache_##name
 #define VAR_NAME(name) palette_cache_##name
 #define CHANNEL DisplayChannel
+#define CHANNELCLIENT RedChannelClient
 #else
 
 #error "no cache type."
 
 #endif
 
+#define CHANNEL_FROM_RCC(rcc) SPICE_CONTAINEROF((rcc)->channel, CHANNEL, common.base);
+
 static CacheItem *FUNC_NAME(find)(CHANNEL *channel, uint64_t id)
 {
     CacheItem *item = channel->CACHE_NAME[CACHE_HASH_KEY(id)];
@@ -55,9 +59,10 @@ static CacheItem *FUNC_NAME(find)(CHANNEL *channel, uint64_t id)
     return item;
 }
 
-static void FUNC_NAME(remove)(CHANNEL *channel, CacheItem *item)
+static void FUNC_NAME(remove)(CHANNELCLIENT *channel_client, CacheItem *item)
 {
     CacheItem **now;
+    CHANNEL *channel = CHANNEL_FROM_RCC(channel_client);
     ASSERT(item);
 
     now = &channel->CACHE_NAME[CACHE_HASH_KEY(item->id)];
@@ -74,11 +79,12 @@ static void FUNC_NAME(remove)(CHANNEL *channel, CacheItem *item)
     channel->VAR_NAME(available) += item->size;
 
     red_channel_pipe_item_init(&channel->common.base, &item->u.pipe_data, PIPE_ITEM_TYPE_INVAL_ONE);
-    red_channel_pipe_add_tail(&channel->common.base, &item->u.pipe_data); // for now
+    red_channel_client_pipe_add_tail(channel_client, &item->u.pipe_data); // for now
 }
 
-static int FUNC_NAME(add)(CHANNEL *channel, uint64_t id, size_t size)
+static int FUNC_NAME(add)(CHANNELCLIENT *channel_client, uint64_t id, size_t size)
 {
+    CHANNEL *channel = CHANNEL_FROM_RCC(channel_client);
     CacheItem *item;
     int key;
 
@@ -92,7 +98,7 @@ static int FUNC_NAME(add)(CHANNEL *channel, uint64_t id, size_t size)
             free(item);
             return FALSE;
         }
-        FUNC_NAME(remove)(channel, tail);
+        FUNC_NAME(remove)(channel_client, tail);
     }
     ++channel->VAR_NAME(items);
     item->u.cache_data.next = channel->CACHE_NAME[(key = CACHE_HASH_KEY(id))];
@@ -130,4 +136,4 @@ static void FUNC_NAME(reset)(CHANNEL *channel, long size)
 #undef FUNC_NAME
 #undef VAR_NAME
 #undef CHANNEL
-
+#undef CHANNEL_FROM_RCC
diff --git a/server/red_client_shared_cache.h b/server/red_client_shared_cache.h
index dddccc6..6b1f975 100644
--- a/server/red_client_shared_cache.h
+++ b/server/red_client_shared_cache.h
@@ -97,7 +97,7 @@ static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, R
 
     if (cache->generation != channel->CACH_GENERATION) {
         if (!channel->pending_pixmaps_sync) {
-            red_channel_pipe_add_type((RedChannel *)channel, PIPE_ITEM_TYPE_PIXMAP_SYNC);
+            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_PIXMAP_SYNC);
             channel->pending_pixmaps_sync = TRUE;
         }
         pthread_mutex_unlock(&cache->lock);
@@ -235,4 +235,3 @@ static void FUNC_NAME(destroy)(CACHE *cache)
 #undef VAR_NAME
 #undef CHANNEL
 #undef CHANNEL_FROM_RCC
-
diff --git a/server/red_tunnel_worker.c b/server/red_tunnel_worker.c
index 5caa8b9..f10ab48 100644
--- a/server/red_tunnel_worker.c
+++ b/server/red_tunnel_worker.c
@@ -734,7 +734,7 @@ static inline void __process_rcv_buf_tokens(TunnelChannel *channel, RedSocket *s
     if ((sckt->in_data.num_tokens >= SOCKET_TOKENS_TO_SEND) ||
         (!sckt->in_data.client_total_num_tokens && !sckt->in_data.ready_chunks_queue.head)) {
         sckt->out_data.token_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_TOKEN;
-        red_channel_pipe_add(&channel->base, &sckt->out_data.token_pipe_item);
+        red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.token_pipe_item);
     }
 }
 
@@ -1094,7 +1094,7 @@ static inline TunnelService *__tunnel_worker_add_service(TunnelWorker *worker, u
 #endif
     if (!virt_ip) {
         new_service->pipe_item.type = PIPE_ITEM_TYPE_SERVICE_IP_MAP;
-        red_channel_pipe_add(&worker->channel->base, &new_service->pipe_item);
+        red_channel_client_pipe_add(worker->channel->base.rcc, &new_service->pipe_item);
     }
 
     return new_service;
@@ -1347,7 +1347,7 @@ static inline void __tunnel_socket_add_fin_to_pipe(TunnelChannel *channel, RedSo
 {
     ASSERT(!red_channel_pipe_item_is_linked(&channel->base, &sckt->out_data.status_pipe_item));
     sckt->out_data.status_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_FIN;
-    red_channel_pipe_add(&channel->base, &sckt->out_data.status_pipe_item);
+    red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.status_pipe_item);
 }
 
 static inline void __tunnel_socket_add_close_to_pipe(TunnelChannel *channel, RedSocket *sckt)
@@ -1361,7 +1361,7 @@ static inline void __tunnel_socket_add_close_to_pipe(TunnelChannel *channel, Red
     }
     sckt->pushed_close = TRUE;
     sckt->out_data.status_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_CLOSE;
-    red_channel_pipe_add(&channel->base, &sckt->out_data.status_pipe_item);
+    red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.status_pipe_item);
 }
 
 static inline void __tunnel_socket_add_close_ack_to_pipe(TunnelChannel *channel, RedSocket *sckt)
@@ -1375,7 +1375,7 @@ static inline void __tunnel_socket_add_close_ack_to_pipe(TunnelChannel *channel,
     }
 
     sckt->out_data.status_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_CLOSED_ACK;
-    red_channel_pipe_add(&channel->base, &sckt->out_data.status_pipe_item);
+    red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.status_pipe_item);
 }
 
 /*
@@ -1639,7 +1639,7 @@ static int tunnel_channel_handle_socket_token(TunnelChannel *channel, RedSocket
         !red_channel_pipe_item_is_linked(&channel->base, &sckt->out_data.data_pipe_item)) {
         // data is pending to be sent
         sckt->out_data.data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-        red_channel_pipe_add(&channel->base, &sckt->out_data.data_pipe_item);
+        red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.data_pipe_item);
     }
 
     return TRUE;
@@ -1809,7 +1809,7 @@ static int tunnel_channel_handle_migrate_mark(RedChannelClient *rcc)
         }
     }
 
-    red_channel_pipe_add((RedChannel *)channel, &migrate_item->base);
+    red_channel_client_pipe_add(channel->base.rcc, &migrate_item->base);
 
     return TRUE;
 error:
@@ -2113,7 +2113,7 @@ static void tunnel_channel_restore_socket_state(TunnelChannel *channel, RedSocke
         if (!red_channel_pipe_item_is_linked(
                 &channel->base, &sckt->out_data.data_pipe_item)) {
             sckt->out_data.data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-            red_channel_pipe_add(&channel->base, &sckt->out_data.data_pipe_item);
+            red_channel_client_pipe_add(channel->base.rcc, &sckt->out_data.data_pipe_item);
         }
     }
 
@@ -2795,7 +2795,7 @@ static void tunnel_worker_release_socket_out_data(TunnelWorker *worker, PipeItem
                 if (!red_channel_pipe_item_is_linked(
                         &worker->channel->base, &sckt_out_data->data_pipe_item)) {
                     sckt_out_data->data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-                    red_channel_pipe_add(&worker->channel->base, &sckt_out_data->data_pipe_item);
+                    red_channel_client_pipe_add(worker->channel->base.rcc, &sckt_out_data->data_pipe_item);
                 }
             } else if ((sckt->slirp_status == SLIRP_SCKT_STATUS_SHUTDOWN_SEND) ||
                        (sckt->slirp_status == SLIRP_SCKT_STATUS_WAIT_CLOSE)) {
@@ -2959,7 +2959,7 @@ static int tunnel_socket_connect(SlirpUsrNetworkInterface *usr_interface,
 #endif
     *o_usr_s = sckt;
     sckt->out_data.status_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_OPEN;
-    red_channel_pipe_add(&worker->channel->base, &sckt->out_data.status_pipe_item);
+    red_channel_client_pipe_add(worker->channel->base.rcc, &sckt->out_data.status_pipe_item);
 
     errno = EINPROGRESS;
     return -1;
@@ -3048,7 +3048,7 @@ static int tunnel_socket_send(SlirpUsrNetworkInterface *usr_interface, UserSocke
             !red_channel_pipe_item_is_linked(&worker->channel->base,
                                              &sckt->out_data.data_pipe_item)) {
             sckt->out_data.data_pipe_item.type = PIPE_ITEM_TYPE_SOCKET_DATA;
-            red_channel_pipe_add(&worker->channel->base, &sckt->out_data.data_pipe_item);
+            red_channel_client_pipe_add(worker->channel->base.rcc, &sckt->out_data.data_pipe_item);
         }
     }
 
@@ -3426,7 +3426,7 @@ static void on_new_tunnel_channel(TunnelChannel *channel)
         channel->expect_migrate_data = TRUE;
     } else {
         red_channel_init_outgoing_messages_window(&channel->base);
-        red_channel_pipe_add_type(&channel->base, PIPE_ITEM_TYPE_TUNNEL_INIT);
+        red_channel_client_pipe_add_type(channel->base.rcc, PIPE_ITEM_TYPE_TUNNEL_INIT);
     }
 }
 
@@ -3484,6 +3484,6 @@ static void handle_tunnel_channel_migrate(struct Channel *channel)
     TunnelChannel *tunnel_channel = ((TunnelWorker *)channel->data)->channel;
     tunnel_channel->mig_inprogress = TRUE;
     net_slirp_freeze();
-    red_channel_pipe_add_type(&tunnel_channel->base, PIPE_ITEM_TYPE_MIGRATE);
+    red_channel_client_pipe_add_type(tunnel_channel->base.rcc, PIPE_ITEM_TYPE_MIGRATE);
 }
 
diff --git a/server/red_worker.c b/server/red_worker.c
index 68d1a2f..699820c 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -1134,16 +1134,26 @@ static inline void pipe_item_remove(PipeItem *item)
     ring_remove(&item->link);
 }
 
-static void red_pipe_add_verb(RedChannel* channel, uint16_t verb)
+static void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb)
 {
     VerbItem *item = spice_new(VerbItem, 1);
-    red_channel_pipe_item_init(channel, &item->base, PIPE_ITEM_TYPE_VERB);
+
+    red_channel_pipe_item_init(rcc->channel, &item->base, PIPE_ITEM_TYPE_VERB);
     item->verb = verb;
-    red_channel_pipe_add(channel, &item->base);
+    red_channel_client_pipe_add(rcc, &item->base);
 }
 
 static inline void red_create_surface_item(RedWorker *worker, int surface_id);
 static void red_add_surface_image(RedWorker *worker, int surface_id);
+static void red_pipes_add_verb(RedChannel *channel, uint16_t verb)
+{
+    RedChannelClient *rcc = channel->rcc;
+
+    if (!rcc) {
+        return;
+    }
+    red_pipe_add_verb(rcc, verb);
+}
 
 static inline void red_handle_drawable_surfaces_client_synced(RedWorker *worker, Drawable *drawable)
 {
@@ -1193,7 +1203,7 @@ static inline void red_pipe_add_drawable(RedWorker *worker, Drawable *drawable)
     red_handle_drawable_surfaces_client_synced(worker, drawable);
 
     drawable->refs++;
-    red_channel_pipe_add(&worker->display_channel->common.base, &drawable->pipe_item);
+    red_channel_client_pipe_add(worker->display_channel->common.base.rcc, &drawable->pipe_item);
 }
 
 static inline void red_pipe_add_drawable_to_tail(RedWorker *worker, Drawable *drawable)
@@ -1203,7 +1213,7 @@ static inline void red_pipe_add_drawable_to_tail(RedWorker *worker, Drawable *dr
     }
     red_handle_drawable_surfaces_client_synced(worker, drawable);
     drawable->refs++;
-    red_channel_pipe_add_tail(&worker->display_channel->common.base, &drawable->pipe_item);
+    red_channel_client_pipe_add_tail(worker->display_channel->common.base.rcc, &drawable->pipe_item);
 }
 
 static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *drawable,
@@ -1219,7 +1229,7 @@ static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *draw
     }
     red_handle_drawable_surfaces_client_synced(worker, drawable);
     drawable->refs++;
-    red_channel_pipe_add_after(&worker->display_channel->common.base, &drawable->pipe_item, &pos_after->pipe_item);
+    red_channel_client_pipe_add_after(worker->display_channel->common.base.rcc, &drawable->pipe_item, &pos_after->pipe_item);
 }
 
 static inline PipeItem *red_pipe_get_tail(RedWorker *worker)
@@ -1228,17 +1238,16 @@ static inline PipeItem *red_pipe_get_tail(RedWorker *worker)
         return NULL;
     }
 
-    return (PipeItem*)ring_get_tail(&worker->display_channel->common.base.pipe);
+    return (PipeItem*)ring_get_tail(&worker->display_channel->common.base.rcc->pipe);
 }
 
 static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id);
 
 static inline void red_pipe_remove_drawable(RedWorker *worker, Drawable *drawable)
 {
-    if (ring_item_is_linked(&drawable->pipe_item.link)) {
-        worker->display_channel->common.base.pipe_size--;
-        ring_remove(&drawable->pipe_item.link);
-        release_drawable(worker, drawable);
+    if (pipe_item_is_linked(&drawable->pipe_item)) {
+        red_channel_client_pipe_remove_and_release(
+            worker->display_channel->common.base.rcc, &drawable->pipe_item);
     }
 }
 
@@ -1248,7 +1257,7 @@ static inline void red_pipe_add_image_item(RedWorker *worker, ImageItem *item)
         return;
     }
     item->refs++;
-    red_channel_pipe_add(&worker->display_channel->common.base, &item->link);
+    red_channel_client_pipe_add(worker->display_channel->common.base.rcc, &item->link);
 }
 
 static inline void red_pipe_add_image_item_after(RedWorker *worker, ImageItem *item,
@@ -1258,7 +1267,7 @@ static inline void red_pipe_add_image_item_after(RedWorker *worker, ImageItem *i
         return;
     }
     item->refs++;
-    red_channel_pipe_add_after(&worker->display_channel->common.base, &item->link, pos);
+    red_channel_client_pipe_add_after(worker->display_channel->common.base.rcc, &item->link, pos);
 }
 
 static void release_image_item(ImageItem *item)
@@ -1368,7 +1377,7 @@ static inline void red_destroy_surface_item(RedWorker *worker, uint32_t surface_
     worker->display_channel->surface_client_created[surface_id] = FALSE;
     channel = &worker->display_channel->common.base;
     destroy = get_surface_destroy_item(channel, surface_id);
-    red_channel_pipe_add(channel, &destroy->pipe_item);
+    red_channel_client_pipe_add(channel->rcc, &destroy->pipe_item);
 }
 
 static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
@@ -1684,7 +1693,7 @@ static void red_clear_surface_drawables_from_pipe(RedWorker *worker, int surface
     /* removing the newest drawables that their destination is surface_id and
        no other drawable depends on them */
 
-    ring = &worker->display_channel->common.base.pipe;
+    ring = &worker->display_channel->common.base.rcc->pipe;
     item = (PipeItem *) ring;
     while ((item = (PipeItem *)ring_next(ring, (RingItem *)item))) {
         Drawable *drawable;
@@ -1703,7 +1712,7 @@ static void red_clear_surface_drawables_from_pipe(RedWorker *worker, int surface
             ring_remove(&tmp_item->link);
             worker->display_channel->common.base.release_item(
                 worker->display_channel->common.base.rcc, tmp_item, FALSE);
-            worker->display_channel->common.base.pipe_size--;
+            worker->display_channel->common.base.rcc->pipe_size--;
 
             if (!item) {
                 item = (PipeItem *)ring;
@@ -2163,7 +2172,7 @@ static void push_stream_clip_by_drawable(DisplayChannel* channel, StreamAgent *a
         item->rects->num_rects = n_rects;
         region_ret_rects(&drawable->tree_item.base.rgn, item->rects->rects, n_rects);
     }
-    red_channel_pipe_add((RedChannel*)channel, (PipeItem *)item);
+    red_channel_client_pipe_add(channel->common.base.rcc, (PipeItem *)item);
 }
 
 static void push_stream_clip(DisplayChannel* channel, StreamAgent *agent)
@@ -2181,7 +2190,7 @@ static void push_stream_clip(DisplayChannel* channel, StreamAgent *agent)
     item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects));
     item->rects->num_rects = n_rects;
     region_ret_rects(&agent->vis_region, item->rects->rects, n_rects);
-    red_channel_pipe_add((RedChannel*)channel, (PipeItem *)item);
+    red_channel_client_pipe_add(channel->common.base.rcc, (PipeItem *)item);
 }
 
 static void red_display_release_stream_clip(DisplayChannel* channel, StreamClipItem *item)
@@ -2226,7 +2235,7 @@ static void red_stop_stream(RedWorker *worker, Stream *stream)
         region_clear(&stream_agent->vis_region);
         ASSERT(!pipe_item_is_linked(&stream_agent->destroy_item));
         stream->refs++;
-        red_channel_pipe_add(&channel->common.base, &stream_agent->destroy_item);
+        red_channel_client_pipe_add(channel->common.base.rcc, &stream_agent->destroy_item);
     }
     ring_remove(&stream->link);
     red_release_stream(worker, stream);
@@ -2251,7 +2260,7 @@ static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *strea
         upgrade_item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects));
         upgrade_item->rects->num_rects = n_rects;
         region_ret_rects(&upgrade_item->drawable->tree_item.base.rgn, upgrade_item->rects->rects, n_rects);
-        red_channel_pipe_add((RedChannel *)channel, &upgrade_item->base);
+        red_channel_client_pipe_add(channel->common.base.rcc, &upgrade_item->base);
     }
     red_detach_stream(worker, stream);
 }
@@ -2404,7 +2413,7 @@ static void red_display_create_stream(DisplayChannel *display, Stream *stream)
     agent->drops = 0;
     agent->fps = MAX_FPS;
     reset_rate(agent);
-    red_channel_pipe_add(&display->common.base, &agent->create_item);
+    red_channel_client_pipe_add(display->common.base.rcc, &agent->create_item);
 }
 
 static void red_create_stream(RedWorker *worker, Drawable *drawable)
@@ -4171,7 +4180,7 @@ static void qxl_process_cursor(RedWorker *worker, RedCursorCmd *cursor_cmd, uint
 
     if (worker->cursor_channel && (worker->mouse_mode == SPICE_MOUSE_MODE_SERVER ||
                                    cursor_cmd->type != QXL_CURSOR_MOVE || cursor_show)) {
-        red_channel_pipe_add(&worker->cursor_channel->common.base, &item->pipe_data);
+        red_channel_client_pipe_add(worker->cursor_channel->common.base.rcc, &item->pipe_data);
     } else {
         red_release_cursor(worker, item);
     }
@@ -4197,7 +4206,8 @@ static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ri
     }
 
     *ring_is_empty = FALSE;
-    while (!cursor_is_connected(worker) || worker->cursor_channel->common.base.pipe_size <= max_pipe_size) {
+    while (!cursor_is_connected(worker) ||
+           worker->cursor_channel->common.base.rcc->pipe_size <= max_pipe_size) {
         if (!worker->qxl->st->qif->get_cursor_command(worker->qxl, &ext_cmd)) {
             *ring_is_empty = TRUE;
             if (worker->repoll_cursor_ring < CMD_RING_POLL_RETRIES) {
@@ -4242,7 +4252,8 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
     }
 
     *ring_is_empty = FALSE;
-    while (!display_is_connected(worker) || worker->display_channel->common.base.pipe_size <= max_pipe_size) {
+    while (!display_is_connected(worker)
+           || worker->display_channel->common.base.rcc->pipe_size <= max_pipe_size) {
         if (!worker->qxl->st->qif->get_command(worker->qxl, &ext_cmd)) {
             *ring_is_empty = TRUE;;
             if (worker->repoll_cmd_ring < CMD_RING_POLL_RETRIES) {
@@ -4500,7 +4511,7 @@ static inline void fill_palette(DisplayChannel *display_channel,
             *flags |= SPICE_BITMAP_FLAGS_PAL_FROM_CACHE;
             return;
         }
-        if (red_palette_cache_add(display_channel, palette->unique, 1)) {
+        if (red_palette_cache_add(display_channel->common.base.rcc, palette->unique, 1)) {
             *flags |= SPICE_BITMAP_FLAGS_PAL_CACHE_ME;
         }
     }
@@ -5924,7 +5935,7 @@ static void fill_cursor(CursorChannel *cursor_channel, SpiceCursor *red_cursor,
             red_cursor->flags |= SPICE_CURSOR_FLAGS_FROM_CACHE;
             return;
         }
-        if (red_cursor_cache_add(cursor_channel, red_cursor->header.unique, 1)) {
+        if (red_cursor_cache_add(cursor_channel->common.base.rcc, red_cursor->header.unique, 1)) {
             red_cursor->flags |= SPICE_CURSOR_FLAGS_CACHE_ME;
         }
     }
@@ -6164,7 +6175,7 @@ static int pipe_rendered_drawables_intersect_with_areas(RedWorker *worker,
     Ring *pipe;
 
     ASSERT(num_surfaces);
-    pipe = &display_channel->common.base.pipe;
+    pipe = &display_channel->common.base.rcc->pipe;
 
     for (pipe_item = (PipeItem *)ring_get_head(pipe);
          pipe_item;
@@ -6202,7 +6213,7 @@ static void red_pipe_replace_rendered_drawables_with_images(RedWorker *worker,
     resent_areas[0] = *first_area;
     num_resent = 1;
 
-    pipe = &display_channel->common.base.pipe;
+    pipe = &display_channel->common.base.rcc->pipe;
 
     // going from the oldest to the newest
     for (pipe_item = (PipeItem *)ring_get_tail(pipe);
@@ -8119,8 +8130,9 @@ void red_disconnect_all_display_TODO_remove_me(RedChannel *channel)
 static void red_migrate_display(RedWorker *worker)
 {
     if (worker->display_channel) {
-        red_pipe_add_verb(&worker->display_channel->common.base, SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL);
-        red_channel_pipe_add_type(&worker->display_channel->common.base, PIPE_ITEM_TYPE_MIGRATE);
+        red_pipes_add_verb(&worker->display_channel->common.base,
+                           SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL);
+        red_channel_pipes_add_type(&worker->display_channel->common.base, PIPE_ITEM_TYPE_MIGRATE);
     }
 }
 
@@ -8249,7 +8261,7 @@ static inline void __red_create_surface_item(RedWorker *worker, int surface_id,
 
     worker->display_channel->surface_client_created[surface_id] = TRUE;
 
-    red_channel_pipe_add(&worker->display_channel->common.base, &create->pipe_item);
+    red_channel_client_pipe_add(worker->display_channel->common.base.rcc, &create->pipe_item);
 }
 
 static inline void red_create_surface_item(RedWorker *worker, int surface_id)
@@ -8349,7 +8361,7 @@ static inline void flush_display_commands(RedWorker *worker)
         for (;;) {
             red_channel_push(&worker->display_channel->common.base);
             if (!display_is_connected(worker) ||
-                 worker->display_channel->common.base.pipe_size <= MAX_PIPE_SIZE) {
+                 worker->display_channel->common.base.rcc->pipe_size <= MAX_PIPE_SIZE) {
                 break;
             }
             RedChannel *channel = (RedChannel *)worker->display_channel;
@@ -8392,8 +8404,8 @@ static inline void flush_cursor_commands(RedWorker *worker)
         int sleep_count = 0;
         for (;;) {
             red_channel_push(&worker->cursor_channel->common.base);
-            if (!cursor_is_connected(worker) ||
-                                        worker->cursor_channel->common.base.pipe_size <= MAX_PIPE_SIZE) {
+            if (!cursor_is_connected(worker)
+                || worker->cursor_channel->common.base.rcc->pipe_size <= MAX_PIPE_SIZE) {
                 break;
             }
             RedChannel *channel = (RedChannel *)worker->cursor_channel;
@@ -8423,7 +8435,7 @@ static void push_new_primary_surface(RedWorker *worker)
     DisplayChannel *display_channel;
 
     if ((display_channel = worker->display_channel)) {
-        red_channel_pipe_add_type(&display_channel->common.base, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+        red_channel_client_pipe_add_type(display_channel->common.base.rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
         if (!display_channel->common.base.migrate) {
             red_create_surface_item(worker, 0);
         }
@@ -8481,7 +8493,7 @@ static void on_new_display_channel(RedWorker *worker)
         push_new_primary_surface(worker);
         red_add_surface_image(worker, 0);
         if (red_channel_is_connected(&display_channel->common.base)) {
-            red_pipe_add_verb(&display_channel->common.base, SPICE_MSG_DISPLAY_MARK);
+            red_pipe_add_verb(display_channel->common.base.rcc, SPICE_MSG_DISPLAY_MARK);
             red_disply_start_streams(display_channel);
         }
     }
@@ -8719,7 +8731,7 @@ static int display_channel_handle_migrate_mark(RedChannelClient *rcc)
         return FALSE;
     }
     channel->expect_migrate_mark = FALSE;
-    red_channel_pipe_add_type((RedChannel *)channel, PIPE_ITEM_TYPE_MIGRATE_DATA);
+    red_channel_client_pipe_add_type(channel->common.base.rcc, PIPE_ITEM_TYPE_MIGRATE_DATA);
     return TRUE;
 }
 
@@ -8773,7 +8785,7 @@ static uint64_t display_channel_handle_migrate_data(RedChannelClient *rcc, uint3
 
     if (migrate_data->pixmap_cache_freezer) {
         channel->pixmap_cache->size = migrate_data->pixmap_cache_size;
-        red_channel_pipe_add_type((RedChannel *)channel, PIPE_ITEM_TYPE_PIXMAP_RESET);
+        red_channel_client_pipe_add_type(channel->common.base.rcc, PIPE_ITEM_TYPE_PIXMAP_RESET);
     }
 
     if (display_channel_handle_migrate_glz_dictionary(channel, migrate_data)) {
@@ -8786,7 +8798,7 @@ static uint64_t display_channel_handle_migrate_data(RedChannelClient *rcc, uint3
         PANIC("restoring global lz dictionary failed");
     }
 
-    red_channel_pipe_add_type((RedChannel *)channel, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+    red_channel_client_pipe_add_type(channel->common.base.rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
     red_channel_ack_zero_messages_window(&channel->common.base);
     return TRUE;
 }
@@ -9146,9 +9158,11 @@ static void red_disconnect_cursor(RedChannel *channel)
 
 static void red_migrate_cursor(RedWorker *worker)
 {
-    if (worker->cursor_channel) {
-        red_channel_pipe_add_type(&worker->cursor_channel->common.base, PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
-        red_channel_pipe_add_type(&worker->cursor_channel->common.base, PIPE_ITEM_TYPE_MIGRATE);
+    if (cursor_is_connected(worker)) {
+        red_channel_pipes_add_type(&worker->cursor_channel->common.base,
+                                   PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
+        red_channel_pipes_add_type(&worker->cursor_channel->common.base,
+                                   PIPE_ITEM_TYPE_MIGRATE);
     }
 }
 
@@ -9160,7 +9174,7 @@ static void on_new_cursor_channel(RedWorker *worker)
     red_channel_ack_zero_messages_window(&channel->common.base);
     red_channel_push_set_ack(&channel->common.base);
     if (worker->surfaces[0].context.canvas && !channel->common.base.migrate) {
-        red_channel_pipe_add_type(&worker->cursor_channel->common.base, PIPE_ITEM_TYPE_CURSOR_INIT);
+        red_channel_client_pipe_add_type(worker->cursor_channel->common.base.rcc, PIPE_ITEM_TYPE_CURSOR_INIT);
     }
 }
 
@@ -9481,9 +9495,9 @@ static inline void red_cursor_reset(RedWorker *worker)
     worker->cursor_trail_length = worker->cursor_trail_frequency = 0;
 
     if (worker->cursor_channel) {
-        red_channel_pipe_add_type(&worker->cursor_channel->common.base, PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
+        red_channel_pipes_add_type(&worker->cursor_channel->common.base, PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
         if (!worker->cursor_channel->common.base.migrate) {
-            red_pipe_add_verb(&worker->cursor_channel->common.base, SPICE_MSG_CURSOR_RESET);
+            red_pipes_add_verb(&worker->cursor_channel->common.base, SPICE_MSG_CURSOR_RESET);
         }
         red_wait_outgoing_item((RedChannel *)worker->cursor_channel);
         ASSERT(red_channel_no_item_being_sent(&worker->cursor_channel->common.base));
@@ -9509,8 +9523,8 @@ static inline void handle_dev_destroy_surfaces(RedWorker *worker)
     ASSERT(ring_is_empty(&worker->streams));
 
     if (worker->display_channel) {
-        red_channel_pipe_add_type(&worker->display_channel->common.base, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
-        red_pipe_add_verb(&worker->display_channel->common.base, SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL);
+        red_channel_client_pipe_add_type(worker->display_channel->common.base.rcc, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+        red_pipe_add_verb(worker->display_channel->common.base.rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL);
     }
 
     red_display_clear_glz_drawables(worker->display_channel);
@@ -9541,13 +9555,15 @@ static inline void handle_dev_create_primary_surface(RedWorker *worker)
     red_create_surface(worker, 0, surface.width, surface.height, surface.stride, surface.format,
                        line_0, surface.flags & QXL_SURF_FLAG_KEEP_DATA, TRUE);
 
-    if (worker->display_channel) {
-        red_pipe_add_verb(&worker->display_channel->common.base, SPICE_MSG_DISPLAY_MARK);
+    if (display_is_connected(worker)) {
+        red_pipes_add_verb(&worker->display_channel->common.base,
+                           SPICE_MSG_DISPLAY_MARK);
         red_channel_push(&worker->display_channel->common.base);
     }
 
-    if (worker->cursor_channel) {
-        red_channel_pipe_add_type(&worker->cursor_channel->common.base, PIPE_ITEM_TYPE_CURSOR_INIT);
+    if (cursor_is_connected(worker)) {
+        red_channel_pipes_add_type(&worker->cursor_channel->common.base,
+                                   PIPE_ITEM_TYPE_CURSOR_INIT);
     }
 }
 
@@ -9673,7 +9689,7 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
         if (worker->qxl->st->qif->flush_resources(worker->qxl) == 0) {
             red_printf("oom current %u pipe %u", worker->current_size,
                        worker->display_channel ?
-                       display_red_channel->pipe_size : 0);
+                       display_red_channel->rcc->pipe_size : 0);
             red_free_some(worker);
             worker->qxl->st->qif->flush_resources(worker->qxl);
         }
diff --git a/server/smartcard.c b/server/smartcard.c
index a8a7beb..eda73fe 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -342,7 +342,7 @@ static void smartcard_channel_disconnect(RedChannelClient *rcc)
  * so no mutex is required. */
 static void smartcard_channel_pipe_add_push(RedChannelClient *rcc, PipeItem *item)
 {
-    red_channel_pipe_add_push(rcc->channel, item);
+    red_channel_client_pipe_add_push(rcc, item);
 }
 
 static void smartcard_push_error(RedChannelClient *rcc, uint32_t reader_id, VSCErrorCode error)
diff --git a/server/usbredir.c b/server/usbredir.c
index f6b0fe8..8806702 100644
--- a/server/usbredir.c
+++ b/server/usbredir.c
@@ -75,8 +75,8 @@ static void usbredir_chardev_wakeup(SpiceCharDeviceInstance *sin)
                       sizeof(state->pipe_item->buf));
         if (n > 0) {
             state->pipe_item->buf_used = n;
-            red_channel_pipe_add_push(state->red_channel,
-                                      &state->pipe_item->base);
+            red_channel_client_pipe_add_push(state->rcc,
+                                             &state->pipe_item->base);
             state->pipe_item = NULL;
         }
     } while (n > 0);
commit b6cf68604f2d184367d0f89834ebcce88e00bad8
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Nov 22 16:56:58 2010 +0200

    server/red_worker: introduce {display,cursor}_is_connected
    
    Instead of checking for worker->{display,cursor}_channel directly.

diff --git a/server/red_worker.c b/server/red_worker.c
index bbb0155..68d1a2f 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -1172,9 +1172,21 @@ static inline void red_handle_drawable_surfaces_client_synced(RedWorker *worker,
     red_add_surface_image(worker, drawable->surface_id);
 }
 
+static int display_is_connected(RedWorker *worker)
+{
+    return (worker->display_channel && red_channel_is_connected(
+        &worker->display_channel->common.base));
+}
+
+static int cursor_is_connected(RedWorker *worker)
+{
+    return (worker->cursor_channel && red_channel_is_connected(
+        &worker->cursor_channel->common.base));
+}
+
 static inline void red_pipe_add_drawable(RedWorker *worker, Drawable *drawable)
 {
-    if (!worker->display_channel) {
+    if (!display_is_connected(worker)) {
         return;
     }
 
@@ -1186,7 +1198,7 @@ static inline void red_pipe_add_drawable(RedWorker *worker, Drawable *drawable)
 
 static inline void red_pipe_add_drawable_to_tail(RedWorker *worker, Drawable *drawable)
 {
-    if (!worker->display_channel) {
+    if (!display_is_connected(worker)) {
         return;
     }
     red_handle_drawable_surfaces_client_synced(worker, drawable);
@@ -1197,7 +1209,7 @@ static inline void red_pipe_add_drawable_to_tail(RedWorker *worker, Drawable *dr
 static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *drawable,
                                                Drawable *pos_after)
 {
-    if (!worker->display_channel) {
+    if (!display_is_connected(worker)) {
         return;
     }
 
@@ -1212,7 +1224,7 @@ static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *draw
 
 static inline PipeItem *red_pipe_get_tail(RedWorker *worker)
 {
-    if (!worker->display_channel) {
+    if (!display_is_connected(worker)) {
         return NULL;
     }
 
@@ -1232,7 +1244,7 @@ static inline void red_pipe_remove_drawable(RedWorker *worker, Drawable *drawabl
 
 static inline void red_pipe_add_image_item(RedWorker *worker, ImageItem *item)
 {
-    if (!worker->display_channel) {
+    if (!display_is_connected(worker)) {
         return;
     }
     item->refs++;
@@ -1242,7 +1254,7 @@ static inline void red_pipe_add_image_item(RedWorker *worker, ImageItem *item)
 static inline void red_pipe_add_image_item_after(RedWorker *worker, ImageItem *item,
                                                  PipeItem *pos)
 {
-    if (!worker->display_channel) {
+    if (!display_is_connected(worker)) {
         return;
     }
     item->refs++;
@@ -1350,7 +1362,7 @@ static inline void red_destroy_surface_item(RedWorker *worker, uint32_t surface_
     SurfaceDestroyItem *destroy;
     RedChannel *channel;
 
-    if (!worker->display_channel) {
+    if (!display_is_connected(worker)) {
         return;
     }
     worker->display_channel->surface_client_created[surface_id] = FALSE;
@@ -1665,7 +1677,7 @@ static void red_clear_surface_drawables_from_pipe(RedWorker *worker, int surface
     PipeItem *item;
     int x;
 
-    if (!worker->display_channel) {
+    if (!display_is_connected(worker)) {
         return;
     }
 
@@ -2273,11 +2285,11 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region)
 
 static void red_streams_update_clip(RedWorker *worker, Drawable *drawable)
 {
-    DisplayChannel *channel;
+    DisplayChannel *channel = worker->display_channel;
     Ring *ring;
     RingItem *item;
 
-    if (!(channel = worker->display_channel)) {
+    if (!display_is_connected(worker)) {
         return;
     }
 
@@ -2548,7 +2560,7 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
 {
     ASSERT(stream->current);
 
-    if (!worker->display_channel || !IS_LOW_BANDWIDTH()) {
+    if (!display_is_connected(worker) || !IS_LOW_BANDWIDTH()) {
         return;
     }
 
@@ -4131,9 +4143,11 @@ static CursorItem *get_cursor_item(RedWorker *worker, RedCursorCmd *cmd, uint32_
 
 static void qxl_process_cursor(RedWorker *worker, RedCursorCmd *cursor_cmd, uint32_t group_id)
 {
-    CursorItem *item = get_cursor_item(worker, cursor_cmd, group_id);
+    CursorItem *item;
     int cursor_show = FALSE;
 
+    item = get_cursor_item(worker, cursor_cmd, group_id);
+
     switch (cursor_cmd->type) {
     case QXL_CURSOR_SET:
         worker->cursor_visible = cursor_cmd->u.set.visible;
@@ -4183,7 +4197,7 @@ static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ri
     }
 
     *ring_is_empty = FALSE;
-    while (!worker->cursor_channel || worker->cursor_channel->common.base.pipe_size <= max_pipe_size) {
+    while (!cursor_is_connected(worker) || worker->cursor_channel->common.base.pipe_size <= max_pipe_size) {
         if (!worker->qxl->st->qif->get_cursor_command(worker->qxl, &ext_cmd)) {
             *ring_is_empty = TRUE;
             if (worker->repoll_cursor_ring < CMD_RING_POLL_RETRIES) {
@@ -4228,7 +4242,7 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
     }
 
     *ring_is_empty = FALSE;
-    while (!worker->display_channel || worker->display_channel->common.base.pipe_size <= max_pipe_size) {
+    while (!display_is_connected(worker) || worker->display_channel->common.base.pipe_size <= max_pipe_size) {
         if (!worker->qxl->st->qif->get_command(worker->qxl, &ext_cmd)) {
             *ring_is_empty = TRUE;;
             if (worker->repoll_cmd_ring < CMD_RING_POLL_RETRIES) {
@@ -4406,7 +4420,7 @@ static void red_add_surface_image(RedWorker *worker, int surface_id)
 
     surface = &worker->surfaces[surface_id];
 
-    if (!worker->display_channel || !surface->context.canvas) {
+    if (!display_is_connected(worker) || !surface->context.canvas) {
         return;
     }
 
@@ -8223,7 +8237,7 @@ static inline void __red_create_surface_item(RedWorker *worker, int surface_id,
     RedSurface *surface;
     SurfaceCreateItem *create;
 
-    if (!worker->display_channel) {
+    if (!display_is_connected(worker)) {
         return;
     }
 
@@ -8334,7 +8348,7 @@ static inline void flush_display_commands(RedWorker *worker)
         int sleep_count = 0;
         for (;;) {
             red_channel_push(&worker->display_channel->common.base);
-            if (!worker->display_channel ||
+            if (!display_is_connected(worker) ||
                  worker->display_channel->common.base.pipe_size <= MAX_PIPE_SIZE) {
                 break;
             }
@@ -8378,7 +8392,7 @@ static inline void flush_cursor_commands(RedWorker *worker)
         int sleep_count = 0;
         for (;;) {
             red_channel_push(&worker->cursor_channel->common.base);
-            if (!worker->cursor_channel ||
+            if (!cursor_is_connected(worker) ||
                                         worker->cursor_channel->common.base.pipe_size <= MAX_PIPE_SIZE) {
                 break;
             }
commit 7e8e13593ee681cf04c349bca57dd225d7802494
Author: Alon Levy <alevy at redhat.com>
Date:   Sat Nov 13 13:23:02 2010 +0200

    server/red_channel (all): introduce RedChannelClient
    
    This commit adds a RedChannelClient that now owns the stream connection,
    but still doesn't own the pipe. There is only a single RCC per RC
    right now (and RC still means RedChannel, RedClient will be introduced
    later). All internal api changes are in server/red_channel.h, hence
    the need to update all channels. red_worker.c is affected the most because
    it makes use of direct access to some of RedChannel still.
    
    API changes:
    
     1. red_channel_client_create added.
      rec_channel_create -> (red_channel_create, red_channel_client_create)
     2. two way connection: rcc->channel, channel->rcc (later channel will
      hold a list, and there will be a RedClient to hold the list of channels
      per client)
     3. seperation of channel disconnect and channel_client_disconnect
    
    TODO:
     usbredir added untested.

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index 0cc53a2..3a4a0d0 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -164,9 +164,9 @@ const VDAgentMouseState *inputs_get_mouse_state(void)
     return &g_inputs_channel->mouse_state;
 }
 
-static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header)
+static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header)
 {
-    InputsChannel *inputs_channel = SPICE_CONTAINEROF(channel, InputsChannel, base);
+    InputsChannel *inputs_channel = SPICE_CONTAINEROF(rcc->channel, InputsChannel, base);
 
     if (msg_header->size > RECEIVE_BUF_SIZE) {
         red_printf("error: too large incoming message");
@@ -175,7 +175,7 @@ static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataH
     return inputs_channel->recv_buf;
 }
 
-static void inputs_channel_release_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header,
+static void inputs_channel_release_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header,
                                                uint8_t *msg)
 {
 }
@@ -249,17 +249,17 @@ static void inputs_pipe_add_type(InputsChannel *channel, int type)
     red_channel_pipe_add_push(&channel->base, &pipe_item->base);
 }
 
-static void inputs_channel_release_pipe_item(RedChannel *channel,
+static void inputs_channel_release_pipe_item(RedChannelClient *rcc,
     PipeItem *base, int item_pushed)
 {
     free(base);
 }
 
-static void inputs_channel_send_item(RedChannel *channel, PipeItem *base)
+static void inputs_channel_send_item(RedChannelClient *rcc, PipeItem *base)
 {
-    SpiceMarshaller *m = red_channel_get_marshaller(channel);
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
 
-    red_channel_init_send_data(channel, base->type, base);
+    red_channel_client_init_send_data(rcc, base->type, base);
     switch (base->type) {
         case PIPE_ITEM_KEY_MODIFIERS:
         {
@@ -288,12 +288,12 @@ static void inputs_channel_send_item(RedChannel *channel, PipeItem *base)
         default:
             break;
     }
-    red_channel_begin_send_message(channel);
+    red_channel_client_begin_send_message(rcc);
 }
 
-static int inputs_channel_handle_parsed(RedChannel *channel, uint32_t size, uint16_t type, void *message)
+static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
 {
-    InputsChannel *inputs_channel = (InputsChannel *)channel;
+    InputsChannel *inputs_channel = (InputsChannel *)rcc->channel;
     uint8_t *buf = (uint8_t *)message;
 
     ASSERT(g_inputs_channel == inputs_channel);
@@ -446,10 +446,10 @@ static void inputs_relase_keys(void)
     kbd_push_scan(keyboard, 0x38 | 0x80); //LALT
 }
 
-static void inputs_channel_on_error(RedChannel *channel)
+static void inputs_channel_on_error(RedChannelClient *rcc)
 {
     inputs_relase_keys();
-    reds_disconnect();
+    red_channel_client_destroy(rcc);
 }
 
 static void inputs_shutdown(Channel *channel)
@@ -485,11 +485,11 @@ static void inputs_pipe_add_init(InputsChannel *inputs_channel)
     red_channel_pipe_add_push(&inputs_channel->base, &item->base);
 }
 
-static int inputs_channel_config_socket(RedChannel *channel)
+static int inputs_channel_config_socket(RedChannelClient *rcc)
 {
     int flags;
     int delay_val = 1;
-    RedsStream *stream = red_channel_get_stream(channel);
+    RedsStream *stream = red_channel_client_get_stream(rcc);
 
     if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY,
             &delay_val, sizeof(delay_val)) == -1) {
@@ -505,7 +505,7 @@ static int inputs_channel_config_socket(RedChannel *channel)
     return TRUE;
 }
 
-static void inputs_channel_hold_pipe_item(RedChannel *channel, PipeItem *item)
+static void inputs_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
 }
 
@@ -514,11 +514,13 @@ static void inputs_link(Channel *channel, RedsStream *stream, int migration,
                         uint32_t *caps)
 {
     InputsChannel *inputs_channel;
-    red_printf("");
+    RedChannelClient *rcc;
+
     ASSERT(channel->data == NULL);
 
+    red_printf("input channel create");
     g_inputs_channel = inputs_channel = (InputsChannel*)red_channel_create_parser(
-        sizeof(*inputs_channel), stream, core, migration, FALSE /* handle_acks */
+        sizeof(*inputs_channel), core, migration, FALSE /* handle_acks */
         ,inputs_channel_config_socket
         ,spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL)
         ,inputs_channel_handle_parsed
@@ -533,6 +535,9 @@ static void inputs_link(Channel *channel, RedsStream *stream, int migration,
         ,NULL
         ,NULL);
     ASSERT(inputs_channel);
+    red_printf("input channel client create");
+    rcc = red_channel_client_create(sizeof(RedChannelClient), &g_inputs_channel->base, stream);
+    ASSERT(rcc);
     channel->data = inputs_channel;
     inputs_pipe_add_init(inputs_channel);
 }
diff --git a/server/main_channel.c b/server/main_channel.c
index 1a6a89c..0718f88 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -421,7 +421,8 @@ static void main_channel_marshall_migrate_data_item(SpiceMarshaller *m, int seri
     data->ping_id = ping_id;
 }
 
-static uint64_t main_channel_handle_migrate_data_get_serial(RedChannel *base,
+static uint64_t main_channel_handle_migrate_data_get_serial(
+    RedChannelClient *rcc,
     uint32_t size, void *message)
 {
     MainMigrateData *data = message;
@@ -433,10 +434,10 @@ static uint64_t main_channel_handle_migrate_data_get_serial(RedChannel *base,
     return data->serial;
 }
 
-static uint64_t main_channel_handle_migrate_data(RedChannel *base,
+static uint64_t main_channel_handle_migrate_data(RedChannelClient *rcc,
     uint32_t size, void *message)
 {
-    MainChannel *main_chan = SPICE_CONTAINEROF(base, MainChannel, base);
+    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
     MainMigrateData *data = message;
 
     if (size < sizeof(*data)) {
@@ -607,12 +608,12 @@ static void main_channel_marshall_multi_media_time(SpiceMarshaller *m,
     spice_marshall_msg_main_multi_media_time(m, &time_mes);
 }
 
-static void main_channel_send_item(RedChannel *channel, PipeItem *base)
+static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
 {
-    SpiceMarshaller *m = red_channel_get_marshaller(channel);
-    MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base);
+    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
 
-    red_channel_init_send_data(channel, base->type, base);
+    red_channel_client_init_send_data(rcc, base->type, base);
     switch (base->type) {
         case SPICE_MSG_MAIN_CHANNELS_LIST:
             main_channel_marshall_channels(m);
@@ -642,7 +643,7 @@ static void main_channel_send_item(RedChannel *channel, PipeItem *base)
             break;
         case SPICE_MSG_MIGRATE_DATA:
             main_channel_marshall_migrate_data_item(m,
-                red_channel_get_message_serial(&main_chan->base),
+                red_channel_client_get_message_serial(rcc),
                 main_chan->ping_id);
             break;
         case SPICE_MSG_MAIN_INIT:
@@ -668,18 +669,18 @@ static void main_channel_send_item(RedChannel *channel, PipeItem *base)
             main_channel_marshall_migrate_switch(m);
             break;
     };
-    red_channel_begin_send_message(channel);
+    red_channel_client_begin_send_message(rcc);
 }
 
-static void main_channel_release_pipe_item(RedChannel *channel,
+static void main_channel_release_pipe_item(RedChannelClient *rcc,
     PipeItem *base, int item_pushed)
 {
     free(base);
 }
 
-static int main_channel_handle_parsed(RedChannel *channel, uint32_t size, uint16_t type, void *message)
+static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
 {
-    MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base);
+    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
 
     switch (type) {
     case SPICE_MSGC_MAIN_AGENT_START:
@@ -770,35 +771,36 @@ static int main_channel_handle_parsed(RedChannel *channel, uint32_t size, uint16
     return TRUE;
 }
 
-static void main_channel_on_error(RedChannel *channel)
+static void main_channel_on_error(RedChannelClient *rcc)
 {
     reds_disconnect();
 }
 
-static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header)
+static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header)
 {
-    MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base);
+    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
 
     return main_chan->recv_buf;
 }
 
-static void main_channel_release_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header,
+static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header,
                                                uint8_t *msg)
 {
 }
 
-static int main_channel_config_socket(RedChannel *channel)
+static int main_channel_config_socket(RedChannelClient *rcc)
 {
     return TRUE;
 }
 
-static void main_channel_hold_pipe_item(RedChannel *channel, PipeItem *item)
+static void main_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
 }
 
-static int main_channel_handle_migrate_flush_mark(RedChannel *base)
+static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
 {
-    main_channel_push_migrate_data_item(SPICE_CONTAINEROF(base, MainChannel, base));
+    main_channel_push_migrate_data_item(SPICE_CONTAINEROF(rcc->channel,
+                                        MainChannel, base));
     return TRUE;
 }
 
@@ -807,26 +809,30 @@ static void main_channel_link(Channel *channel, RedsStream *stream, int migratio
                         uint32_t *caps)
 {
     MainChannel *main_chan;
-    red_printf("");
     ASSERT(channel->data == NULL);
 
-    main_chan = (MainChannel*)red_channel_create_parser(
-        sizeof(*main_chan), stream, core, migration, FALSE /* handle_acks */
-        ,main_channel_config_socket
-        ,spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL)
-        ,main_channel_handle_parsed
-        ,main_channel_alloc_msg_rcv_buf
-        ,main_channel_release_msg_rcv_buf
-        ,main_channel_hold_pipe_item
-        ,main_channel_send_item
-        ,main_channel_release_pipe_item
-        ,main_channel_on_error
-        ,main_channel_on_error
-        ,main_channel_handle_migrate_flush_mark
-        ,main_channel_handle_migrate_data
-        ,main_channel_handle_migrate_data_get_serial);
-    ASSERT(main_chan);
-    channel->data = main_chan;
+    if (channel->data == NULL) {
+        red_printf("create main channel");
+        channel->data = red_channel_create_parser(
+            sizeof(*main_chan), core, migration, FALSE /* handle_acks */
+            ,main_channel_config_socket
+            ,spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL)
+            ,main_channel_handle_parsed
+            ,main_channel_alloc_msg_rcv_buf
+            ,main_channel_release_msg_rcv_buf
+            ,main_channel_hold_pipe_item
+            ,main_channel_send_item
+            ,main_channel_release_pipe_item
+            ,main_channel_on_error
+            ,main_channel_on_error
+            ,main_channel_handle_migrate_flush_mark
+            ,main_channel_handle_migrate_data
+            ,main_channel_handle_migrate_data_get_serial);
+        ASSERT(channel->data);
+    }
+    main_chan = (MainChannel*)channel->data;
+    red_printf("add main channel client");
+    red_channel_client_create(sizeof(RedChannelClient), channel->data, stream);
 }
 
 int main_channel_getsockname(Channel *channel, struct sockaddr *sa, socklen_t *salen)
diff --git a/server/red_channel.c b/server/red_channel.c
index 9ecc7ef..8bbc6c9 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -33,8 +33,7 @@
 #include "red_channel.h"
 #include "generated_marshallers.h"
 
-static PipeItem *red_channel_pipe_get(RedChannel *channel);
-static void red_channel_event(int fd, int event, void *data);
+static void red_channel_client_event(int fd, int event, void *data);
 
 /* return the number of bytes read. -1 in case of error */
 static int red_peer_receive(RedsStream *stream, uint8_t *buf, uint32_t size)
@@ -152,9 +151,14 @@ static void red_peer_handle_incoming(RedsStream *stream, IncomingHandler *handle
     }
 }
 
+void red_channel_client_receive(RedChannelClient *rcc)
+{
+    red_peer_handle_incoming(rcc->stream, &rcc->incoming);
+}
+
 void red_channel_receive(RedChannel *channel)
 {
-    red_peer_handle_incoming(channel->stream, &channel->incoming);
+    red_channel_client_receive(channel->rcc);
 }
 
 static void red_peer_handle_outgoing(RedsStream *stream, OutgoingHandler *handler)
@@ -201,124 +205,194 @@ static void red_peer_handle_outgoing(RedsStream *stream, OutgoingHandler *handle
     }
 }
 
-static void red_channel_on_output(void *opaque, int n)
+static void red_channel_client_on_output(void *opaque, int n)
 {
-    RedChannel *channel = opaque;
+    RedChannelClient *rcc = opaque;
 
-    stat_inc_counter(channel->out_bytes_counter, n);
+    stat_inc_counter(rcc->channel->out_bytes_counter, n);
 }
 
-void red_channel_default_peer_on_error(RedChannel *channel)
+void red_channel_client_default_peer_on_error(RedChannelClient *rcc)
 {
-    channel->disconnect(channel);
+    rcc->channel->disconnect(rcc);
 }
 
-static void red_channel_peer_on_incoming_error(RedChannel *channel)
+static void red_channel_peer_on_incoming_error(RedChannelClient *rcc)
 {
-    channel->on_incoming_error(channel);
+    rcc->channel->on_incoming_error(rcc);
 }
 
-static void red_channel_peer_on_outgoing_error(RedChannel *channel)
+static void red_channel_peer_on_outgoing_error(RedChannelClient *rcc)
 {
-    channel->on_outgoing_error(channel);
+    rcc->channel->on_outgoing_error(rcc);
 }
 
-static int red_channel_peer_get_out_msg_size(void *opaque)
+static int red_channel_client_peer_get_out_msg_size(void *opaque)
 {
-    RedChannel *channel = (RedChannel *)opaque;
+    RedChannelClient *rcc = (RedChannelClient *)opaque;
 
-    return channel->send_data.size;
+    return rcc->send_data.size;
 }
 
-static void red_channel_peer_prepare_out_msg(void *opaque, struct iovec *vec, int *vec_size, int pos)
+static void red_channel_client_peer_prepare_out_msg(
+    void *opaque, struct iovec *vec, int *vec_size, int pos)
 {
-    RedChannel *channel = (RedChannel *)opaque;
+    RedChannelClient *rcc = (RedChannelClient *)opaque;
 
-    *vec_size = spice_marshaller_fill_iovec(channel->send_data.marshaller,
+    *vec_size = spice_marshaller_fill_iovec(rcc->send_data.marshaller,
                                             vec, MAX_SEND_VEC, pos);
 }
 
-static void red_channel_peer_on_out_block(void *opaque)
+static void red_channel_client_peer_on_out_block(void *opaque)
 {
-    RedChannel *channel = (RedChannel *)opaque;
+    RedChannelClient *rcc = (RedChannelClient *)opaque;
 
-    channel->send_data.blocked = TRUE;
-    channel->core->watch_update_mask(channel->stream->watch,
+    rcc->send_data.blocked = TRUE;
+    rcc->channel->core->watch_update_mask(rcc->stream->watch,
                                      SPICE_WATCH_EVENT_READ |
                                      SPICE_WATCH_EVENT_WRITE);
 }
 
-static void red_channel_reset_send_data(RedChannel *channel)
+static void red_channel_client_reset_send_data(RedChannelClient *rcc)
+{
+    spice_marshaller_reset(rcc->send_data.marshaller);
+    rcc->send_data.header = (SpiceDataHeader *)
+        spice_marshaller_reserve_space(rcc->send_data.marshaller, sizeof(SpiceDataHeader));
+    spice_marshaller_set_base(rcc->send_data.marshaller, sizeof(SpiceDataHeader));
+    rcc->send_data.header->type = 0;
+    rcc->send_data.header->size = 0;
+    rcc->send_data.header->sub_list = 0;
+    rcc->send_data.header->serial = ++rcc->send_data.serial;
+}
+
+void red_channel_client_push_set_ack(RedChannelClient *rcc)
 {
-    spice_marshaller_reset(channel->send_data.marshaller);
-    channel->send_data.header = (SpiceDataHeader *)
-        spice_marshaller_reserve_space(channel->send_data.marshaller, sizeof(SpiceDataHeader));
-    spice_marshaller_set_base(channel->send_data.marshaller, sizeof(SpiceDataHeader));
-    channel->send_data.header->type = 0;
-    channel->send_data.header->size = 0;
-    channel->send_data.header->sub_list = 0;
-    channel->send_data.header->serial = ++channel->send_data.serial;
+    red_channel_pipe_add_type(rcc->channel, PIPE_ITEM_TYPE_SET_ACK);
 }
 
 void red_channel_push_set_ack(RedChannel *channel)
 {
+    // TODO - MC, should replace with add_type_all (or whatever I'll name it)
     red_channel_pipe_add_type(channel, PIPE_ITEM_TYPE_SET_ACK);
 }
 
-static void red_channel_send_set_ack(RedChannel *channel)
+static void red_channel_client_send_set_ack(RedChannelClient *rcc)
 {
     SpiceMsgSetAck ack;
 
-    ASSERT(channel);
-    red_channel_init_send_data(channel, SPICE_MSG_SET_ACK, NULL);
-    ack.generation = ++channel->ack_data.generation;
-    ack.window = channel->ack_data.client_window;
-    channel->ack_data.messages_window = 0;
+    ASSERT(rcc);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_SET_ACK, NULL);
+    ack.generation = ++rcc->ack_data.generation;
+    ack.window = rcc->ack_data.client_window;
+    rcc->ack_data.messages_window = 0;
 
-    spice_marshall_msg_set_ack(channel->send_data.marshaller, &ack);
+    spice_marshall_msg_set_ack(rcc->send_data.marshaller, &ack);
 
-    red_channel_begin_send_message(channel);
+    red_channel_client_begin_send_message(rcc);
 }
 
-static void red_channel_send_item(RedChannel *channel, PipeItem *item)
+static void red_channel_client_send_item(RedChannelClient *rcc, PipeItem *item)
 {
-    red_channel_reset_send_data(channel);
+    int handled = TRUE;
+
+    ASSERT(red_channel_client_no_item_being_sent(rcc));
+    red_channel_client_reset_send_data(rcc);
     switch (item->type) {
         case PIPE_ITEM_TYPE_SET_ACK:
-            red_channel_send_set_ack(channel);
-            return;
+            red_channel_client_send_set_ack(rcc);
+            break;
+        default:
+            handled = FALSE;
+    }
+    if (!handled) {
+        rcc->channel->send_item(rcc, item);
     }
-    /* only reached if not handled here */
-    channel->send_item(channel, item);
 }
 
-static void red_channel_release_item(RedChannel *channel, PipeItem *item, int item_pushed)
+static void red_channel_client_release_item(RedChannelClient *rcc, PipeItem *item, int item_pushed)
 {
+    int handled = TRUE;
+
     switch (item->type) {
         case PIPE_ITEM_TYPE_SET_ACK:
             free(item);
-            return;
+            break;
+        default:
+            handled = FALSE;
+    }
+    if (!handled) {
+        rcc->channel->release_item(rcc, item, item_pushed);
     }
-    /* only reached if not handled here */
-    channel->release_item(channel, item, item_pushed);
 }
 
-static void red_channel_peer_on_out_msg_done(void *opaque)
+static inline void red_channel_client_release_sent_item(RedChannelClient *rcc)
 {
-    RedChannel *channel = (RedChannel *)opaque;
-    channel->send_data.size = 0;
-    if (channel->send_data.item) {
-        red_channel_release_item(channel, channel->send_data.item, TRUE);
-        channel->send_data.item = NULL;
+    if (rcc->send_data.item) {
+        red_channel_client_release_item(rcc,
+                                        rcc->send_data.item, TRUE);
+        rcc->send_data.item = NULL;
     }
-    if (channel->send_data.blocked) {
-        channel->send_data.blocked = FALSE;
-        channel->core->watch_update_mask(channel->stream->watch,
+}
+
+static void red_channel_peer_on_out_msg_done(void *opaque)
+{
+    RedChannelClient *rcc = (RedChannelClient *)opaque;
+
+    rcc->send_data.size = 0;
+    red_channel_client_release_sent_item(rcc);
+    if (rcc->send_data.blocked) {
+        rcc->send_data.blocked = FALSE;
+        rcc->channel->core->watch_update_mask(rcc->stream->watch,
                                          SPICE_WATCH_EVENT_READ);
     }
 }
 
-RedChannel *red_channel_create(int size, RedsStream *stream,
+static void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc)
+{
+    ASSERT(rcc);
+	channel->rcc = rcc;
+}
+
+RedChannelClient *red_channel_client_create(
+    int size,
+    RedChannel *channel,
+    RedsStream *stream)
+{
+    RedChannelClient *rcc = NULL;
+
+    ASSERT(stream && channel && size >= sizeof(RedChannelClient));
+    rcc = spice_malloc0(size);
+    rcc->stream = stream;
+    rcc->channel = channel;
+    rcc->ack_data.messages_window = ~0;  // blocks send message (maybe use send_data.blocked +
+                                             // block flags)
+    rcc->ack_data.client_generation = ~0;
+    rcc->ack_data.client_window = CLIENT_ACK_WINDOW;
+    rcc->send_data.marshaller = spice_marshaller_new();
+
+    rcc->incoming.opaque = rcc;
+    rcc->incoming.cb = &channel->incoming_cb;
+
+    rcc->outgoing.opaque = rcc;
+    rcc->outgoing.cb = &channel->outgoing_cb;
+    rcc->outgoing.pos = 0;
+    rcc->outgoing.size = 0;
+    if (!channel->config_socket(rcc)) {
+        goto error;
+    }
+
+    stream->watch = channel->core->watch_add(stream->socket,
+                                           SPICE_WATCH_EVENT_READ,
+                                           red_channel_client_event, rcc);
+    red_channel_add_client(channel, rcc);
+    return rcc;
+error:
+    free(rcc);
+    reds_stream_free(stream);
+    return NULL;
+}
+
+RedChannel *red_channel_create(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
                                channel_configure_socket_proc config_socket,
@@ -339,7 +413,6 @@ RedChannel *red_channel_create(int size, RedsStream *stream,
     ASSERT(config_socket && disconnect && handle_message && alloc_recv_buf &&
            release_item);
     channel = spice_malloc0(size);
-
     channel->handle_acks = handle_acks;
     channel->disconnect = disconnect;
     channel->send_item = send_item;
@@ -348,69 +421,40 @@ RedChannel *red_channel_create(int size, RedsStream *stream,
     channel->handle_migrate_flush_mark = handle_migrate_flush_mark;
     channel->handle_migrate_data = handle_migrate_data;
     channel->handle_migrate_data_get_serial = handle_migrate_data_get_serial;
+    channel->config_socket = config_socket;
 
-    channel->stream = stream;
     channel->core = core;
-    channel->ack_data.messages_window = ~0;  // blocks send message (maybe use send_data.blocked +
-                                             // block flags)
-    channel->ack_data.client_generation = ~0;
-    channel->ack_data.client_window = CLIENT_ACK_WINDOW;
-
     channel->migrate = migrate;
     ring_init(&channel->pipe);
-    channel->send_data.marshaller = spice_marshaller_new();
 
-    channel->incoming.opaque = channel;
     channel->incoming_cb.alloc_msg_buf = (alloc_msg_recv_buf_proc)alloc_recv_buf;
     channel->incoming_cb.release_msg_buf = (release_msg_recv_buf_proc)release_recv_buf;
     channel->incoming_cb.handle_message = (handle_message_proc)handle_message;
-    channel->incoming_cb.on_error = (on_incoming_error_proc)red_channel_default_peer_on_error;
-
-    channel->outgoing.opaque = channel;
-    channel->outgoing.pos = 0;
-    channel->outgoing.size = 0;
-
-    channel->outgoing_cb.get_msg_size = red_channel_peer_get_out_msg_size;
-    channel->outgoing_cb.prepare = red_channel_peer_prepare_out_msg;
-    channel->outgoing_cb.on_block = red_channel_peer_on_out_block;
-    channel->outgoing_cb.on_error = (on_outgoing_error_proc)red_channel_default_peer_on_error;
+    channel->incoming_cb.on_error =
+        (on_incoming_error_proc)red_channel_client_default_peer_on_error;
+    channel->outgoing_cb.get_msg_size = red_channel_client_peer_get_out_msg_size;
+    channel->outgoing_cb.prepare = red_channel_client_peer_prepare_out_msg;
+    channel->outgoing_cb.on_block = red_channel_client_peer_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_peer_on_out_msg_done;
-    channel->outgoing_cb.on_output = red_channel_on_output;
-
-    channel->incoming.cb = &channel->incoming_cb;
-    channel->outgoing.cb = &channel->outgoing_cb;
+    channel->outgoing_cb.on_output = red_channel_client_on_output;
 
     channel->shut = 0; // came here from inputs, perhaps can be removed? XXX
     channel->out_bytes_counter = 0;
-
-    if (!config_socket(channel)) {
-        goto error;
-    }
-
-    channel->stream->watch = channel->core->watch_add(channel->stream->socket,
-                                                    SPICE_WATCH_EVENT_READ,
-                                                    red_channel_event, channel);
-
     return channel;
-
-error:
-    spice_marshaller_destroy(channel->send_data.marshaller);
-    free(channel);
-    reds_stream_free(stream);
-
-    return NULL;
 }
 
-static void do_nothing_disconnect(RedChannel *red_channel)
+static void do_nothing_disconnect(RedChannelClient *rcc)
 {
 }
 
-static int do_nothing_handle_message(RedChannel *red_channel, SpiceDataHeader *header, uint8_t *msg)
+static int do_nothing_handle_message(RedChannelClient *rcc, SpiceDataHeader *header, uint8_t *msg)
 {
     return TRUE;
 }
 
-RedChannel *red_channel_create_parser(int size, RedsStream *stream,
+RedChannel *red_channel_create_parser(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
                                channel_configure_socket_proc config_socket,
@@ -427,7 +471,7 @@ RedChannel *red_channel_create_parser(int size, RedsStream *stream,
                                channel_handle_migrate_data_proc handle_migrate_data,
                                channel_handle_migrate_data_get_serial_proc handle_migrate_data_get_serial)
 {
-    RedChannel *channel = red_channel_create(size, stream,
+    RedChannel *channel = red_channel_create(size,
         core, migrate, handle_acks, config_socket, do_nothing_disconnect,
         do_nothing_handle_message, alloc_recv_buf, release_recv_buf, hold_item,
         send_item, release_item, handle_migrate_flush_mark, handle_migrate_data,
@@ -438,62 +482,152 @@ RedChannel *red_channel_create_parser(int size, RedsStream *stream,
     }
     channel->incoming_cb.handle_parsed = (handle_parsed_proc)handle_parsed;
     channel->incoming_cb.parser = parser;
-    channel->on_incoming_error = incoming_error;
-    channel->on_outgoing_error = outgoing_error;
     channel->incoming_cb.on_error = (on_incoming_error_proc)red_channel_peer_on_incoming_error;
     channel->outgoing_cb.on_error = (on_outgoing_error_proc)red_channel_peer_on_outgoing_error;
+    channel->on_incoming_error = incoming_error;
+    channel->on_outgoing_error = outgoing_error;
     return channel;
 }
 
+void red_channel_client_destroy(RedChannelClient *rcc)
+{
+    red_channel_client_disconnect(rcc);
+    spice_marshaller_destroy(rcc->send_data.marshaller);
+    free(rcc);
+}
+
 void red_channel_destroy(RedChannel *channel)
 {
     if (!channel) {
         return;
     }
-    red_channel_pipe_clear(channel);
-    reds_stream_free(channel->stream);
-    spice_marshaller_destroy(channel->send_data.marshaller);
+    if (channel->rcc) {
+        red_channel_client_destroy(channel->rcc);
+    }
     free(channel);
 }
 
+static void red_channel_client_shutdown(RedChannelClient *rcc)
+{
+    if (rcc->stream && !rcc->stream->shutdown) {
+        rcc->channel->core->watch_remove(rcc->stream->watch);
+        rcc->stream->watch = NULL;
+        shutdown(rcc->stream->socket, SHUT_RDWR);
+        rcc->stream->shutdown = TRUE;
+        rcc->incoming.shut = TRUE;
+    }
+    red_channel_client_release_sent_item(rcc);
+}
+
 void red_channel_shutdown(RedChannel *channel)
 {
-    red_printf("");
-    if (channel->stream && !channel->stream->shutdown) {
-        channel->core->watch_update_mask(channel->stream->watch,
-                                         SPICE_WATCH_EVENT_READ);
-        red_channel_pipe_clear(channel);
-        shutdown(channel->stream->socket, SHUT_RDWR);
-        channel->stream->shutdown = TRUE;
-        channel->incoming.shut = TRUE;
+    if (channel->rcc) {
+        red_channel_client_shutdown(channel->rcc);
+    }
+    red_channel_pipe_clear(channel);
+}
+
+void red_channel_client_send(RedChannelClient *rcc)
+{
+    red_peer_handle_outgoing(rcc->stream, &rcc->outgoing);
+}
+
+void red_channel_send(RedChannel *channel)
+{
+    if (channel->rcc) {
+        red_channel_client_send(channel->rcc);
     }
 }
 
+static inline int red_channel_client_waiting_for_ack(RedChannelClient *rcc)
+{
+    return (rcc->channel->handle_acks &&
+            (rcc->ack_data.messages_window > rcc->ack_data.client_window * 2));
+}
+
+// TODO: add refs and target to PipeItem. Right now this only works for a
+// single client (or actually, it's worse - first come first served)
+static inline PipeItem *red_channel_client_pipe_get(RedChannelClient *rcc)
+{
+    PipeItem *item;
+
+    if (!rcc || rcc->send_data.blocked
+             || red_channel_client_waiting_for_ack(rcc)
+             || !(item = (PipeItem *)ring_get_tail(&rcc->channel->pipe))) {
+        return NULL;
+    }
+    --rcc->channel->pipe_size;
+    ring_remove(&item->link);
+    return item;
+}
+
+static void red_channel_client_push(RedChannelClient *rcc)
+{
+    PipeItem *pipe_item;
+
+    if (!rcc->during_send) {
+        rcc->during_send = TRUE;
+    } else {
+        return;
+    }
+
+    if (rcc->send_data.blocked) {
+        red_channel_client_send(rcc);
+    }
+
+    while ((pipe_item = red_channel_client_pipe_get(rcc))) {
+        red_channel_client_send_item(rcc, pipe_item);
+    }
+    rcc->during_send = FALSE;
+}
+
+void red_channel_push(RedChannel *channel)
+{
+    if (!channel || !channel->rcc) {
+        return;
+    }
+    red_channel_client_push(channel->rcc);
+}
+
+static void red_channel_client_init_outgoing_messages_window(RedChannelClient *rcc)
+{
+    rcc->ack_data.messages_window = 0;
+    red_channel_client_push(rcc);
+}
+
+// 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)
 {
-    channel->ack_data.messages_window = 0;
-    red_channel_push(channel);
+    red_channel_client_init_outgoing_messages_window(channel->rcc);
 }
 
 static void red_channel_handle_migrate_flush_mark(RedChannel *channel)
 {
     if (channel->handle_migrate_flush_mark) {
-        channel->handle_migrate_flush_mark(channel);
+        channel->handle_migrate_flush_mark(channel->rcc);
     }
 }
 
-static void red_channel_handle_migrate_data(RedChannel *channel, uint32_t size, void *message)
+// TODO: the whole migration is broken with multiple clients. What do we want to do?
+// basically just
+//  1) source send mark to all
+//  2) source gets at various times the data (waits for all)
+//  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)
 {
-    if (!channel->handle_migrate_data) {
+    if (!rcc->channel->handle_migrate_data) {
         return;
     }
-    ASSERT(red_channel_get_message_serial(channel) == 0);
-    red_channel_set_message_serial(channel,
-        channel->handle_migrate_data_get_serial(channel, size, message));
-    channel->handle_migrate_data(channel, size, message);
+    ASSERT(red_channel_client_get_message_serial(rcc) == 0);
+    red_channel_client_set_message_serial(rcc,
+        rcc->channel->handle_migrate_data_get_serial(rcc, size, message));
+    rcc->channel->handle_migrate_data(rcc, size, message);
 }
 
-int red_channel_handle_message(RedChannel *channel, uint32_t size,
+int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size,
                                uint16_t type, void *message)
 {
     switch (type) {
@@ -502,21 +636,21 @@ int red_channel_handle_message(RedChannel *channel, uint32_t size,
             red_printf("bad message size");
             return FALSE;
         }
-        channel->ack_data.client_generation = *(uint32_t *)(message);
+        rcc->ack_data.client_generation = *(uint32_t *)(message);
         break;
     case SPICE_MSGC_ACK:
-        if (channel->ack_data.client_generation == channel->ack_data.generation) {
-            channel->ack_data.messages_window -= channel->ack_data.client_window;
-            red_channel_push(channel);
+        if (rcc->ack_data.client_generation == rcc->ack_data.generation) {
+            rcc->ack_data.messages_window -= rcc->ack_data.client_window;
+            red_channel_client_push(rcc);
         }
         break;
     case SPICE_MSGC_DISCONNECTING:
         break;
     case SPICE_MSGC_MIGRATE_FLUSH_MARK:
-        red_channel_handle_migrate_flush_mark(channel);
+        red_channel_handle_migrate_flush_mark(rcc->channel);
         break;
     case SPICE_MSGC_MIGRATE_DATA:
-        red_channel_handle_migrate_data(channel, size, message);
+        red_channel_handle_migrate_data(rcc, size, message);
         break;
     default:
         red_printf("invalid message type %u", type);
@@ -525,75 +659,54 @@ int red_channel_handle_message(RedChannel *channel, uint32_t size,
     return TRUE;
 }
 
-static void red_channel_event(int fd, int event, void *data)
+static void red_channel_client_event(int fd, int event, void *data)
 {
-    RedChannel *channel = (RedChannel *)data;
+    RedChannelClient *rcc = (RedChannelClient *)data;
 
     if (event & SPICE_WATCH_EVENT_READ) {
-        red_channel_receive(channel);
+        red_channel_client_receive(rcc);
     }
     if (event & SPICE_WATCH_EVENT_WRITE) {
-        red_channel_push(channel);
+        red_channel_client_push(rcc);
     }
 }
 
-void red_channel_init_send_data(RedChannel *channel, uint16_t msg_type, PipeItem *item)
+void red_channel_client_init_send_data(RedChannelClient *rcc, uint16_t msg_type, PipeItem *item)
 {
-    ASSERT(channel->send_data.item == NULL);
-    channel->send_data.header->type = msg_type;
-    channel->send_data.item = item;
+    ASSERT(red_channel_client_no_item_being_sent(rcc));
+    ASSERT(msg_type != 0);
+    rcc->send_data.header->type = msg_type;
+    rcc->send_data.item = item;
     if (item) {
-        channel->hold_item(channel, item);
+        rcc->channel->hold_item(rcc, item);
     }
 }
 
-void red_channel_send(RedChannel *channel)
-{
-    red_peer_handle_outgoing(channel->stream, &channel->outgoing);
-}
-
-void red_channel_begin_send_message(RedChannel *channel)
-{
-    spice_marshaller_flush(channel->send_data.marshaller);
-    channel->send_data.size = spice_marshaller_get_total_size(channel->send_data.marshaller);
-    channel->send_data.header->size =  channel->send_data.size - sizeof(SpiceDataHeader);
-    channel->ack_data.messages_window++;
-    channel->send_data.header = NULL; /* avoid writing to this until we have a new message */
-    red_channel_send(channel);
-}
-
-void red_channel_push(RedChannel *channel)
+void red_channel_client_begin_send_message(RedChannelClient *rcc)
 {
-    PipeItem *pipe_item;
-
-    if (!channel) {
-        return;
-    }
+    SpiceMarshaller *m = rcc->send_data.marshaller;
 
-    if (!channel->during_send) {
-        channel->during_send = TRUE;
-    } else {
+    // TODO - better check: type in channel_allowed_types. Better: type in channel_allowed_types(channel_state)
+    if (rcc->send_data.header->type == 0) {
+        red_printf("BUG: header->type == 0");
         return;
     }
-
-    if (channel->send_data.blocked) {
-        red_channel_send(channel);
-    }
-
-    while ((pipe_item = red_channel_pipe_get(channel))) {
-        red_channel_send_item(channel, pipe_item);
-    }
-    channel->during_send = FALSE;
+    spice_marshaller_flush(m);
+    rcc->send_data.size = spice_marshaller_get_total_size(m);
+    rcc->send_data.header->size = rcc->send_data.size - sizeof(SpiceDataHeader);
+    rcc->ack_data.messages_window++;
+    rcc->send_data.header = NULL; /* avoid writing to this until we have a new message */
+    red_channel_client_send(rcc);
 }
 
-uint64_t red_channel_get_message_serial(RedChannel *channel)
+uint64_t red_channel_client_get_message_serial(RedChannelClient *rcc)
 {
-    return channel->send_data.serial;
+    return rcc->send_data.serial;
 }
 
-void red_channel_set_message_serial(RedChannel *channel, uint64_t serial)
+void red_channel_client_set_message_serial(RedChannelClient *rcc, uint64_t serial)
 {
-    channel->send_data.serial = serial;
+    rcc->send_data.serial = serial;
 }
 
 void red_channel_pipe_item_init(RedChannel *channel, PipeItem *item, int type)
@@ -657,28 +770,19 @@ void red_channel_pipe_add_type(RedChannel *channel, int pipe_item_type)
     red_channel_push(channel);
 }
 
-static inline int red_channel_waiting_for_ack(RedChannel *channel)
+int red_channel_is_connected(RedChannel *channel)
 {
-    return (channel->handle_acks && (channel->ack_data.messages_window > channel->ack_data.client_window * 2));
+    return channel->rcc != NULL;
 }
 
-static inline PipeItem *red_channel_pipe_get(RedChannel *channel)
+void red_channel_client_clear_sent_item(RedChannelClient *rcc)
 {
-    PipeItem *item;
-
-    if (!channel || channel->send_data.blocked ||
-        red_channel_waiting_for_ack(channel) ||
-        !(item = (PipeItem *)ring_get_tail(&channel->pipe))) {
-        return NULL;
+    if (rcc->send_data.item) {
+        red_channel_client_release_item(rcc, rcc->send_data.item, TRUE);
+        rcc->send_data.item = NULL;
     }
-    --channel->pipe_size;
-    ring_remove(&item->link);
-    return item;
-}
-
-int red_channel_is_connected(RedChannel *channel)
-{
-    return !!channel->stream;
+    rcc->send_data.blocked = FALSE;
+    rcc->send_data.size = 0;
 }
 
 void red_channel_pipe_clear(RedChannel *channel)
@@ -686,82 +790,161 @@ void red_channel_pipe_clear(RedChannel *channel)
     PipeItem *item;
 
     ASSERT(channel);
-    if (channel->send_data.item) {
-        red_channel_release_item(channel, channel->send_data.item, TRUE);
-        channel->send_data.item = NULL;
+    if (channel->rcc) {
+        red_channel_client_clear_sent_item(channel->rcc);
     }
     while ((item = (PipeItem *)ring_get_head(&channel->pipe))) {
         ring_remove(&item->link);
-        red_channel_release_item(channel, item, FALSE);
+        red_channel_client_release_item(channel->rcc, item, FALSE);
     }
     channel->pipe_size = 0;
 }
 
+void red_channel_client_ack_zero_messages_window(RedChannelClient *rcc)
+{
+    rcc->ack_data.messages_window = 0;
+}
+
 void red_channel_ack_zero_messages_window(RedChannel *channel)
 {
-    channel->ack_data.messages_window = 0;
+    red_channel_client_ack_zero_messages_window(channel->rcc);
 }
 
-void red_channel_ack_set_client_window(RedChannel *channel, int client_window)
+void red_channel_client_ack_set_client_window(RedChannelClient *rcc, int client_window)
 {
-    channel->ack_data.client_window = client_window;
+    rcc->ack_data.client_window = client_window;
+}
+
+void red_channel_ack_set_client_window(RedChannel* channel, int client_window)
+{
+    if (channel->rcc) {
+        red_channel_client_ack_set_client_window(channel->rcc, client_window);
+    }
+}
+
+void red_channel_client_disconnect(RedChannelClient *rcc)
+{
+    red_printf("%p (channel %p)", rcc, rcc->channel);
+
+    if (rcc->send_data.item) {
+        rcc->channel->release_item(rcc, rcc->send_data.item, FALSE);
+    }
+    // TODO: clear our references from the pipe
+    reds_stream_free(rcc->stream);
+    rcc->send_data.item = NULL;
+    rcc->send_data.blocked = FALSE;
+    rcc->send_data.size = 0;
+    rcc->channel->rcc = NULL;
+}
+
+void red_channel_disconnect(RedChannel *channel)
+{
+    red_channel_pipe_clear(channel);
+    if (channel->rcc) {
+        red_channel_client_disconnect(channel->rcc);
+    }
+}
+
+int red_channel_all_clients_serials_are_zero(RedChannel *channel)
+{
+    return (!channel->rcc || channel->rcc->send_data.serial == 0);
+}
+
+void red_channel_apply_clients(RedChannel *channel, channel_client_visitor v)
+{
+    if (channel->rcc) {
+        v(channel->rcc);
+    }
+}
+
+void red_channel_apply_clients_data(RedChannel *channel, channel_client_visitor_data v, void *data)
+{
+    if (channel->rcc) {
+        v(channel->rcc, data);
+    }
+}
+
+void red_channel_set_shut(RedChannel *channel)
+{
+    if (channel->rcc) {
+        channel->rcc->incoming.shut = TRUE;
+    }
 }
 
 int red_channel_all_blocked(RedChannel *channel)
 {
-    return channel->send_data.blocked;
+    return !channel || !channel->rcc || channel->rcc->send_data.blocked;
 }
 
 int red_channel_any_blocked(RedChannel *channel)
 {
-    return channel->send_data.blocked;
+    return !channel || !channel->rcc || channel->rcc->send_data.blocked;
 }
 
-int red_channel_send_message_pending(RedChannel *channel)
+int red_channel_client_blocked(RedChannelClient *rcc)
 {
-    return channel->send_data.header->type != 0;
+    return rcc && rcc->send_data.blocked;
 }
 
-/* accessors for RedChannel */
-SpiceMarshaller *red_channel_get_marshaller(RedChannel *channel)
+int red_channel_client_send_message_pending(RedChannelClient *rcc)
 {
-    return channel->send_data.marshaller;
+    return rcc->send_data.header->type != 0;
 }
 
-RedsStream *red_channel_get_stream(RedChannel *channel)
+/* accessors for RedChannelClient */
+SpiceMarshaller *red_channel_client_get_marshaller(RedChannelClient *rcc)
 {
-    return channel->stream;
+    return rcc->send_data.marshaller;
 }
 
-SpiceDataHeader *red_channel_get_header(RedChannel *channel)
+RedsStream *red_channel_client_get_stream(RedChannelClient *rcc)
 {
-    return channel->send_data.header;
+    return rcc->stream;
+}
+
+SpiceDataHeader *red_channel_client_get_header(RedChannelClient *rcc)
+{
+    return rcc->send_data.header;
 }
 /* end of accessors */
 
 int red_channel_get_first_socket(RedChannel *channel)
 {
-    if (!channel->stream) {
+    if (!channel->rcc || !channel->rcc->stream) {
         return -1;
     }
-    return channel->stream->socket;
+    return channel->rcc->stream->socket;
+}
+
+int red_channel_client_item_being_sent(RedChannelClient *rcc, PipeItem *item)
+{
+    return rcc->send_data.item == item;
 }
 
 int red_channel_item_being_sent(RedChannel *channel, PipeItem *item)
 {
-    return channel->send_data.item == item;
+    return channel->rcc && red_channel_client_item_being_sent(channel->rcc, item);
 }
 
 int red_channel_no_item_being_sent(RedChannel *channel)
 {
-    return channel->send_data.item == NULL;
+    return !channel->rcc || red_channel_client_no_item_being_sent(channel->rcc);
 }
 
-void red_channel_disconnect(RedChannel *channel)
+int red_channel_client_no_item_being_sent(RedChannelClient *rcc)
 {
-    red_channel_pipe_clear(channel);
-    reds_stream_free(channel->stream);
-    channel->stream = NULL;
-    channel->send_data.blocked = FALSE;
-    channel->send_data.size = 0;
+    return !rcc || (rcc->send_data.size == 0);
+}
+
+static void red_channel_client_pipe_remove(RedChannelClient *rcc, PipeItem *item)
+{
+    rcc->channel->pipe_size--;
+    ring_remove(&item->link);
+}
+
+void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc,
+                                                PipeItem *item)
+{
+    red_channel_client_pipe_remove(rcc, item);
+    red_channel_client_release_item(rcc, item, FALSE);
 }
diff --git a/server/red_channel.h b/server/red_channel.h
index d05722c..e7a83d3 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -97,6 +97,9 @@ typedef struct BufDescriptor {
     uint8_t *data;
 } BufDescriptor;
 
+typedef struct RedChannel RedChannel;
+typedef struct RedChannelClient RedChannelClient;
+
 /* Messages handled by red_channel
  * SET_ACK - sent to client on channel connection
  * Note that the numbers don't have to correspond to spice message types,
@@ -112,37 +115,33 @@ typedef struct PipeItem {
     int type;
 } PipeItem;
 
-typedef struct RedChannel RedChannel;
-
-typedef uint8_t *(*channel_alloc_msg_recv_buf_proc)(RedChannel *channel,
+typedef uint8_t *(*channel_alloc_msg_recv_buf_proc)(RedChannelClient *channel,
                                                     SpiceDataHeader *msg_header);
-typedef int (*channel_handle_parsed_proc)(RedChannel *channel, uint32_t size, uint16_t type,
+typedef int (*channel_handle_parsed_proc)(RedChannelClient *rcc, uint32_t size, uint16_t type,
                                         void *message);
-typedef int (*channel_handle_message_proc)(RedChannel *channel,
+typedef int (*channel_handle_message_proc)(RedChannelClient *rcc,
                                            SpiceDataHeader *header, uint8_t *msg);
-typedef void (*channel_release_msg_recv_buf_proc)(RedChannel *channel,
+typedef void (*channel_release_msg_recv_buf_proc)(RedChannelClient *channel,
                                                   SpiceDataHeader *msg_header, uint8_t *msg);
-typedef void (*channel_disconnect_proc)(RedChannel *channel);
-typedef int (*channel_configure_socket_proc)(RedChannel *channel);
-typedef void (*channel_send_pipe_item_proc)(RedChannel *channel, PipeItem *item);
-typedef void (*channel_hold_pipe_item_proc)(RedChannel *channel, PipeItem *item);
-typedef void (*channel_release_pipe_item_proc)(RedChannel *channel,
+typedef void (*channel_disconnect_proc)(RedChannelClient *rcc);
+typedef int (*channel_configure_socket_proc)(RedChannelClient *rcc);
+typedef void (*channel_send_pipe_item_proc)(RedChannelClient *rcc, PipeItem *item);
+typedef void (*channel_hold_pipe_item_proc)(RedChannelClient *rcc, PipeItem *item);
+typedef void (*channel_release_pipe_item_proc)(RedChannelClient *rcc,
                                                PipeItem *item, int item_pushed);
-typedef void (*channel_on_incoming_error_proc)(RedChannel *channel);
-typedef void (*channel_on_outgoing_error_proc)(RedChannel *channel);
+typedef void (*channel_on_incoming_error_proc)(RedChannelClient *rcc);
+typedef void (*channel_on_outgoing_error_proc)(RedChannelClient *rcc);
 
-typedef int (*channel_handle_migrate_flush_mark_proc)(RedChannel *channel);
-typedef uint64_t (*channel_handle_migrate_data_proc)(RedChannel *channel,
+typedef int (*channel_handle_migrate_flush_mark_proc)(RedChannelClient *base);
+typedef uint64_t (*channel_handle_migrate_data_proc)(RedChannelClient *base,
                                                 uint32_t size, void *message);
-typedef uint64_t (*channel_handle_migrate_data_get_serial_proc)(RedChannel *channel,
+typedef uint64_t (*channel_handle_migrate_data_get_serial_proc)(RedChannelClient *base,
                                             uint32_t size, void *message);
 
-struct RedChannel {
+struct RedChannelClient {
+    RingItem channel_link;
+    RedChannel *channel;
     RedsStream *stream;
-    SpiceCoreInterface *core;
-    int migrate;
-    int handle_acks;
-
     struct {
         uint32_t generation;
         uint32_t client_generation;
@@ -150,9 +149,6 @@ struct RedChannel {
         uint32_t client_window;
     } ack_data;
 
-    Ring pipe;
-    uint32_t pipe_size;
-
     struct {
         SpiceMarshaller *marshaller;
         SpiceDataHeader *header;
@@ -164,16 +160,28 @@ struct RedChannel {
 
     OutgoingHandler outgoing;
     IncomingHandler incoming;
+    int during_send;
+};
+
+struct RedChannel {
+    SpiceCoreInterface *core;
+    int migrate;
+    int handle_acks;
+
+    RedChannelClient *rcc;
+
+    Ring pipe;
+    uint32_t pipe_size;
 
     OutgoingHandlerInterface outgoing_cb;
     IncomingHandlerInterface incoming_cb;
 
+    channel_configure_socket_proc config_socket;
     channel_disconnect_proc disconnect;
     channel_send_pipe_item_proc send_item;
     channel_hold_pipe_item_proc hold_item;
     channel_release_pipe_item_proc release_item;
 
-    int during_send;
     /* Stuff below added for Main and Inputs channels switch to RedChannel
      * (might be removed later) */
     channel_on_incoming_error_proc on_incoming_error; /* alternative to disconnect */
@@ -190,7 +198,7 @@ struct RedChannel {
 
 /* 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, RedsStream *stream,
+RedChannel *red_channel_create(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
                                channel_configure_socket_proc config_socket,
@@ -207,7 +215,7 @@ RedChannel *red_channel_create(int size, RedsStream *stream,
 
 /* alternative constructor, meant for marshaller based (inputs,main) channels,
  * will become default eventually */
-RedChannel *red_channel_create_parser(int size, RedsStream *stream,
+RedChannel *red_channel_create_parser(int size,
                                SpiceCoreInterface *core,
                                int migrate, int handle_acks,
                                channel_configure_socket_proc config_socket,
@@ -223,29 +231,31 @@ RedChannel *red_channel_create_parser(int size, RedsStream *stream,
                                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);
-
+RedChannelClient *red_channel_client_create(int size, RedChannel *channel,
+                                            RedsStream *stream);
 int red_channel_is_connected(RedChannel *channel);
 
+void red_channel_client_destroy(RedChannelClient *rcc);
 void red_channel_destroy(RedChannel *channel);
 
 /* should be called when a new channel is ready to send messages */
 void red_channel_init_outgoing_messages_window(RedChannel *channel);
 
 /* handles general channel msgs from the client */
-int red_channel_handle_message(RedChannel *channel, uint32_t size,
+int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size,
                                uint16_t type, void *message);
 
 /* default error handler that disconnects channel */
-void red_channel_default_peer_on_error(RedChannel *channel);
+void red_channel_client_default_peer_on_error(RedChannelClient *rcc);
 
 /* when preparing send_data: should call init and then use marshaller */
-void red_channel_init_send_data(RedChannel *channel, uint16_t msg_type, PipeItem *item);
+void red_channel_client_init_send_data(RedChannelClient *rcc, uint16_t msg_type, PipeItem *item);
 
-uint64_t red_channel_get_message_serial(RedChannel *channel);
-void red_channel_set_message_serial(RedChannel *channel, uint64_t);
+uint64_t red_channel_client_get_message_serial(RedChannelClient *channel);
+void red_channel_client_set_message_serial(RedChannelClient *channel, uint64_t);
 
-/* when sending a msg. should first call red_channel_begin_send_message */
-void red_channel_begin_send_message(RedChannel *channel);
+/* when sending a msg. should first call red_channel_client_begin_send_message */
+void red_channel_client_begin_send_message(RedChannelClient *rcc);
 
 void red_channel_pipe_item_init(RedChannel *channel, PipeItem *item, int type);
 void red_channel_pipe_add_push(RedChannel *channel, PipeItem *item);
@@ -253,14 +263,19 @@ void red_channel_pipe_add(RedChannel *channel, PipeItem *item);
 void red_channel_pipe_add_after(RedChannel *channel, PipeItem *item, PipeItem *pos);
 int red_channel_pipe_item_is_linked(RedChannel *channel, PipeItem *item);
 void red_channel_pipe_item_remove(RedChannel *channel, PipeItem *item);
+void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc, PipeItem *item);
 void red_channel_pipe_add_tail(RedChannel *channel, PipeItem *item);
 /* for types that use this routine -> the pipe item should be freed */
 void red_channel_pipe_add_type(RedChannel *channel, int pipe_item_type);
 
+void red_channel_client_ack_zero_messages_window(RedChannelClient *rcc);
+void red_channel_client_ack_set_client_window(RedChannelClient *rcc, int client_window);
+void red_channel_client_push_set_ack(RedChannelClient *rcc);
 void red_channel_ack_zero_messages_window(RedChannel *channel);
 void red_channel_ack_set_client_window(RedChannel *channel, int client_window);
 void red_channel_push_set_ack(RedChannel *channel);
 
+/* TODO: This sets all clients to shut state - probably we want to close per channel */
 void red_channel_shutdown(RedChannel *channel);
 
 int red_channel_get_first_socket(RedChannel *channel);
@@ -271,8 +286,10 @@ int red_channel_all_blocked(RedChannel *channel);
 /* return TRUE if any of the connected clients to this channel are blocked */
 int red_channel_any_blocked(RedChannel *channel);
 
+int red_channel_client_blocked(RedChannelClient *rcc);
+
 /* helper for channels that have complex logic that can possibly ready a send */
-int red_channel_send_message_pending(RedChannel *channel);
+int red_channel_client_send_message_pending(RedChannelClient *rcc);
 
 /* returns TRUE if item is being sent by one of the channel clients. This will
  * be true if someone called init_send_data but send has not completed (or perhaps
@@ -281,6 +298,7 @@ int red_channel_send_message_pending(RedChannel *channel);
 int red_channel_item_being_sent(RedChannel *channel, PipeItem *item);
 
 int red_channel_no_item_being_sent(RedChannel *channel);
+int red_channel_client_no_item_being_sent(RedChannelClient *rcc);
 
 // TODO: unstaticed for display/cursor channels. they do some specific pushes not through
 // adding elements or on events. but not sure if this is actually required (only result
@@ -299,14 +317,18 @@ void red_channel_pipe_clear(RedChannel *channel);
 //  red_wait_pipe_item_sent
 //  handle_channel_events - this is the only one that was used before, and was in red_channel.c
 void red_channel_receive(RedChannel *channel);
+void red_channel_client_receive(RedChannelClient *rcc);
+// For red_worker
 void red_channel_send(RedChannel *channel);
+void red_channel_client_send(RedChannelClient *rcc);
 // For red_worker
 void red_channel_disconnect(RedChannel *channel);
+void red_channel_client_disconnect(RedChannelClient *rcc);
 
-/* accessors for RedChannel */
+/* accessors for RedChannelClient */
 /* Note: the valid times to call red_channel_get_marshaller are just during send_item callback. */
-SpiceMarshaller *red_channel_get_marshaller(RedChannel *channel);
-RedsStream *red_channel_get_stream(RedChannel *channel);
+SpiceMarshaller *red_channel_client_get_marshaller(RedChannelClient *rcc);
+RedsStream *red_channel_client_get_stream(RedChannelClient *rcc);
 
 /* this is a convenience function for sending messages, sometimes (migration only?)
  * the serial from the header needs to be available for sending. Note that the header
@@ -314,5 +336,12 @@ RedsStream *red_channel_get_stream(RedChannel *channel);
  * red_channel_begin_send_message. red_channel_init_send_data changes the header (sets
  * the type in it) as a convenience function. It is preffered to do that through it and
  * not via the below accessor and direct header manipulation. */
-SpiceDataHeader *red_channel_get_header(RedChannel *channel);
+SpiceDataHeader *red_channel_client_get_header(RedChannelClient *rcc);
+
+/* apply given function to all connected clients */
+typedef void (*channel_client_visitor)(RedChannelClient *rcc);
+typedef void (*channel_client_visitor_data)(RedChannelClient *rcc, void *data);
+void red_channel_apply_clients(RedChannel *channel, channel_client_visitor v);
+void red_channel_apply_clients_data(RedChannel *channel, channel_client_visitor_data v, void *data);
+
 #endif
diff --git a/server/red_client_shared_cache.h b/server/red_client_shared_cache.h
index 74553c0..dddccc6 100644
--- a/server/red_client_shared_cache.h
+++ b/server/red_client_shared_cache.h
@@ -26,6 +26,7 @@
 #define FUNC_NAME(name) pixmap_cache_##name
 #define PRIVATE_FUNC_NAME(name) __pixmap_cache_##name
 #define CHANNEL DisplayChannel
+#define CHANNEL_FROM_RCC(rcc) SPICE_CONTAINEROF(rcc->channel, CHANNEL, common.base);
 #define CACH_GENERATION pixmap_cache_generation
 #define INVAL_ALL_VERB SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS
 #else
@@ -35,12 +36,13 @@
 #endif
 
 
-static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, CHANNEL *channel)
+static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, RedChannelClient *rcc)
 {
+    CHANNEL *channel = CHANNEL_FROM_RCC(rcc);
     NewCacheItem *item;
     uint64_t serial;
 
-    serial = red_channel_get_message_serial((RedChannel *)channel);
+    serial = red_channel_client_get_message_serial(rcc);
     pthread_mutex_lock(&cache->lock);
     item = cache->hash_table[CACHE_HASH_KEY(id)];
 
@@ -79,8 +81,9 @@ static int FUNC_NAME(set_lossy)(CACHE *cache, uint64_t id, int lossy)
     return !!item;
 }
 
-static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, CHANNEL *channel)
+static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, RedChannelClient *rcc)
 {
+    CHANNEL *channel = CHANNEL_FROM_RCC(rcc);
     NewCacheItem *item;
     uint64_t serial;
     int key;
@@ -88,7 +91,7 @@ static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, C
     ASSERT(size > 0);
 
     item = spice_new(NewCacheItem, 1);
-    serial = red_channel_get_message_serial((RedChannel *)channel);
+    serial = red_channel_client_get_message_serial(rcc);
 
     pthread_mutex_lock(&cache->lock);
 
@@ -166,13 +169,14 @@ static void PRIVATE_FUNC_NAME(clear)(CACHE *cache)
     cache->items = 0;
 }
 
-static void FUNC_NAME(reset)(CACHE *cache, CHANNEL *channel, SpiceMsgWaitForChannels* sync_data)
+static void FUNC_NAME(reset)(CACHE *cache, RedChannelClient *rcc, SpiceMsgWaitForChannels* sync_data)
 {
+    CHANNEL *channel = CHANNEL_FROM_RCC(rcc);
     uint8_t wait_count;
     uint64_t serial;
     uint32_t i;
 
-    serial = red_channel_get_message_serial((RedChannel *)channel);
+    serial = red_channel_client_get_message_serial(rcc);
     pthread_mutex_lock(&cache->lock);
     PRIVATE_FUNC_NAME(clear)(cache);
 
@@ -230,4 +234,5 @@ static void FUNC_NAME(destroy)(CACHE *cache)
 #undef FUNC_NAME
 #undef VAR_NAME
 #undef CHANNEL
+#undef CHANNEL_FROM_RCC
 
diff --git a/server/red_tunnel_worker.c b/server/red_tunnel_worker.c
index b649c60..5caa8b9 100644
--- a/server/red_tunnel_worker.c
+++ b/server/red_tunnel_worker.c
@@ -1645,9 +1645,11 @@ static int tunnel_channel_handle_socket_token(TunnelChannel *channel, RedSocket
     return TRUE;
 }
 
-static uint8_t *tunnel_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header)
+static uint8_t *tunnel_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
+                                                 SpiceDataHeader *msg_header)
 {
-    TunnelChannel *tunnel_channel = (TunnelChannel *)channel;
+    TunnelChannel *tunnel_channel = (TunnelChannel *)rcc->channel;
+
     if (msg_header->type == SPICE_MSGC_TUNNEL_SOCKET_DATA) {
         return (__tunnel_worker_alloc_socket_rcv_buf(tunnel_channel->worker)->buf);
     } else if ((msg_header->type == SPICE_MSGC_MIGRATE_DATA) ||
@@ -1659,10 +1661,11 @@ static uint8_t *tunnel_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataH
 }
 
 // called by the receive routine of the channel, before the buffer was assigned to a socket
-static void tunnel_channel_release_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header,
+static void tunnel_channel_release_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header,
                                                uint8_t *msg)
 {
-    TunnelChannel *tunnel_channel = (TunnelChannel *)channel;
+    TunnelChannel *tunnel_channel = (TunnelChannel *)rcc->channel;
+
     if (msg_header->type == SPICE_MSGC_TUNNEL_SOCKET_DATA) {
         ASSERT(!(SPICE_CONTAINEROF(msg, RedSocketRawRcvBuf, buf)->base.usr_opaque));
         __tunnel_worker_free_socket_rcv_buf(tunnel_channel->worker,
@@ -1744,9 +1747,9 @@ static void __tunnel_channel_fill_socket_migrate_item(TunnelChannel *channel, Re
 }
 
 static void release_migrate_item(TunnelMigrateItem *item);
-static int tunnel_channel_handle_migrate_mark(RedChannel *base)
+static int tunnel_channel_handle_migrate_mark(RedChannelClient *rcc)
 {
-    TunnelChannel *channel = SPICE_CONTAINEROF(base, TunnelChannel, base);
+    TunnelChannel *channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
     TunnelMigrateItem *migrate_item = NULL;
     TunnelService *service;
     TunnelMigrateServiceItem *mig_service;
@@ -2159,7 +2162,7 @@ static inline void tunnel_channel_activate_migrated_sockets(TunnelChannel *chann
     }
 }
 
-static uint64_t tunnel_channel_handle_migrate_data_get_serial(RedChannel *base,
+static uint64_t tunnel_channel_handle_migrate_data_get_serial(RedChannelClient *rcc,
                                               uint32_t size, void *msg)
 {
     TunnelMigrateData *migrate_data = msg;
@@ -2172,10 +2175,10 @@ static uint64_t tunnel_channel_handle_migrate_data_get_serial(RedChannel *base,
     return migrate_data->message_serial;
 }
 
-static uint64_t tunnel_channel_handle_migrate_data(RedChannel *base,
+static uint64_t tunnel_channel_handle_migrate_data(RedChannelClient *rcc,
                                               uint32_t size, void *msg)
 {
-    TunnelChannel *channel = SPICE_CONTAINEROF(base, TunnelChannel, base);
+    TunnelChannel *channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
     TunnelMigrateSocketList *sockets_list;
     TunnelMigrateServicesList *services_list;
     TunnelMigrateData *migrate_data = msg;
@@ -2242,9 +2245,9 @@ error:
 }
 
 //  msg was allocated by tunnel_channel_alloc_msg_rcv_buf
-static int tunnel_channel_handle_message(RedChannel *channel, SpiceDataHeader *header, uint8_t *msg)
+static int tunnel_channel_handle_message(RedChannelClient *rcc, SpiceDataHeader *header, uint8_t *msg)
 {
-    TunnelChannel *tunnel_channel = (TunnelChannel *)channel;
+    TunnelChannel *tunnel_channel = (TunnelChannel *)rcc->channel;
     RedSocket *sckt = NULL;
     // retrieve the sckt
     switch (header->type) {
@@ -2268,7 +2271,7 @@ static int tunnel_channel_handle_message(RedChannel *channel, SpiceDataHeader *h
         }
         break;
     default:
-        return red_channel_handle_message(channel, header->size, header->type, msg);
+        return red_channel_client_handle_message(rcc, header->size, header->type, msg);
     }
 
     switch (header->type) {
@@ -2337,7 +2340,7 @@ static int tunnel_channel_handle_message(RedChannel *channel, SpiceDataHeader *h
         return tunnel_channel_handle_socket_token(tunnel_channel, sckt,
                                                   (SpiceMsgcTunnelSocketTokens *)msg);
     default:
-        return red_channel_handle_message(channel, header->size, header->type, msg);
+        return red_channel_client_handle_message(rcc, header->size, header->type, msg);
     }
     return TRUE;
 }
@@ -2346,13 +2349,16 @@ static int tunnel_channel_handle_message(RedChannel *channel, SpiceDataHeader *h
 /* outgoing msgs
 ********************************/
 
-static void tunnel_channel_marshall_migrate(TunnelChannel *tunnel_channel, SpiceMarshaller *m, PipeItem *item)
+static void tunnel_channel_marshall_migrate(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    ASSERT(tunnel_channel);
+    TunnelChannel *tunnel_channel;
+
+    ASSERT(rcc);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
     tunnel_channel->send_data.u.migrate.flags =
         SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER;
     tunnel_channel->expect_migrate_mark = TRUE;
-    red_channel_init_send_data(&tunnel_channel->base, SPICE_MSG_MIGRATE, item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE, item);
     spice_marshaller_add_ref(m,
         (uint8_t*)&tunnel_channel->send_data.u.migrate,
         sizeof(SpiceMsgMigrate));
@@ -2492,20 +2498,23 @@ static int __tunnel_channel_marshall_socket_migrate_data(TunnelChannel *channel,
     return (cur_offset - offset);
 }
 
-static void tunnel_channel_marshall_migrate_data(TunnelChannel *channel,
+static void tunnel_channel_marshall_migrate_data(RedChannelClient *rcc,
                                         SpiceMarshaller *m, PipeItem *item)
 {
-    TunnelMigrateData *migrate_data = &channel->send_data.u.migrate_data;
+    TunnelChannel *tunnel_channel;
+    TunnelMigrateData *migrate_data;
     TunnelMigrateItem *migrate_item = (TunnelMigrateItem *)item;
     int i;
 
     uint32_t data_buf_offset = 0; // current location in data[0] field
-    ASSERT(channel);
+    ASSERT(rcc);
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    migrate_data = &tunnel_channel->send_data.u.migrate_data;
 
     migrate_data->magic = TUNNEL_MIGRATE_DATA_MAGIC;
     migrate_data->version = TUNNEL_MIGRATE_DATA_VERSION;
-    migrate_data->message_serial = red_channel_get_message_serial(&channel->base);
-    red_channel_init_send_data(&channel->base, SPICE_MSG_MIGRATE_DATA, item);
+    migrate_data->message_serial = red_channel_client_get_message_serial(rcc);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
     spice_marshaller_add_ref(m, (uint8_t*)migrate_data, sizeof(*migrate_data));
 
     migrate_data->slirp_state = data_buf_offset;
@@ -2519,7 +2528,7 @@ static void tunnel_channel_marshall_migrate_data(TunnelChannel *channel,
 
     for (i = 0; i < migrate_item->services_list->num_services; i++) {
         migrate_item->services_list->services[i] = data_buf_offset;
-        data_buf_offset += __tunnel_channel_marshall_service_migrate_data(channel, m,
+        data_buf_offset += __tunnel_channel_marshall_service_migrate_data(tunnel_channel, m,
                                                                       migrate_item->services + i,
                                                                       data_buf_offset);
     }
@@ -2532,83 +2541,93 @@ static void tunnel_channel_marshall_migrate_data(TunnelChannel *channel,
 
     for (i = 0; i < migrate_item->sockets_list->num_sockets; i++) {
         migrate_item->sockets_list->sockets[i] = data_buf_offset;
-        data_buf_offset += __tunnel_channel_marshall_socket_migrate_data(channel, m,
+        data_buf_offset += __tunnel_channel_marshall_socket_migrate_data(tunnel_channel, m,
                                                                      migrate_item->sockets_data + i,
                                                                      data_buf_offset);
     }
 }
 
-static void tunnel_channel_marshall_init(TunnelChannel *channel, SpiceMarshaller *m, PipeItem *item)
+static void tunnel_channel_marshall_init(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
-    ASSERT(channel);
+    TunnelChannel *channel;
 
+    ASSERT(rcc);
+    channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
     channel->send_data.u.init.max_socket_data_size = MAX_SOCKET_DATA_SIZE;
     channel->send_data.u.init.max_num_of_sockets = MAX_SOCKETS_NUM;
 
-    red_channel_init_send_data(&channel->base, SPICE_MSG_TUNNEL_INIT, item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_TUNNEL_INIT, item);
     spice_marshaller_add_ref(m, (uint8_t*)&channel->send_data.u.init, sizeof(SpiceMsgTunnelInit));
 }
 
-static void tunnel_channel_marshall_service_ip_map(TunnelChannel *channel, SpiceMarshaller *m, PipeItem *item)
+static void tunnel_channel_marshall_service_ip_map(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
+    TunnelChannel *tunnel_channel;
     TunnelService *service = SPICE_CONTAINEROF(item, TunnelService, pipe_item);
 
-    channel->send_data.u.service_ip.service_id = service->id;
-    channel->send_data.u.service_ip.virtual_ip.type = SPICE_TUNNEL_IP_TYPE_IPv4;
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel->send_data.u.service_ip.service_id = service->id;
+    tunnel_channel->send_data.u.service_ip.virtual_ip.type = SPICE_TUNNEL_IP_TYPE_IPv4;
 
-    red_channel_init_send_data(&channel->base, SPICE_MSG_TUNNEL_SERVICE_IP_MAP, item);
-    spice_marshaller_add_ref(m, (uint8_t*)&channel->send_data.u.service_ip,
+    red_channel_client_init_send_data(rcc, SPICE_MSG_TUNNEL_SERVICE_IP_MAP, item);
+    spice_marshaller_add_ref(m, (uint8_t*)&tunnel_channel->send_data.u.service_ip,
                         sizeof(SpiceMsgTunnelServiceIpMap));
     spice_marshaller_add_ref(m, (uint8_t*)&service->virt_ip.s_addr, sizeof(SpiceTunnelIPv4));
 }
 
-static void tunnel_channel_marshall_socket_open(TunnelChannel *channel, SpiceMarshaller *m, PipeItem *item)
+static void tunnel_channel_marshall_socket_open(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
+    TunnelChannel *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, status_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
-    channel->send_data.u.socket_open.connection_id = sckt->connection_id;
-    channel->send_data.u.socket_open.service_id = sckt->far_service->id;
-    channel->send_data.u.socket_open.tokens = SOCKET_WINDOW_SIZE;
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel->send_data.u.socket_open.connection_id = sckt->connection_id;
+    tunnel_channel->send_data.u.socket_open.service_id = sckt->far_service->id;
+    tunnel_channel->send_data.u.socket_open.tokens = SOCKET_WINDOW_SIZE;
 
     sckt->in_data.client_total_num_tokens = SOCKET_WINDOW_SIZE;
     sckt->in_data.num_tokens = 0;
-    red_channel_init_send_data(&channel->base, SPICE_MSG_TUNNEL_SOCKET_OPEN, item);
-    spice_marshaller_add_ref(m, (uint8_t*)&channel->send_data.u.socket_open,
-                        sizeof(channel->send_data.u.socket_open));
+    red_channel_client_init_send_data(rcc, SPICE_MSG_TUNNEL_SOCKET_OPEN, item);
+    spice_marshaller_add_ref(m, (uint8_t*)&tunnel_channel->send_data.u.socket_open,
+                        sizeof(tunnel_channel->send_data.u.socket_open));
 #ifdef DEBUG_NETWORK
     PRINT_SCKT(sckt);
 #endif
 }
 
-static void tunnel_channel_marshall_socket_fin(TunnelChannel *channel, SpiceMarshaller *m, PipeItem *item)
+static void tunnel_channel_marshall_socket_fin(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
+    TunnelChannel *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, status_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
     ASSERT(!sckt->out_data.ready_chunks_queue.head);
 
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
     if (sckt->out_data.process_queue->head) {
         red_printf("socket sent FIN but there are still buffers in outgoing process queue"
                    "(local_port=%d, service_id=%d)",
                    ntohs(sckt->local_port), sckt->far_service->id);
     }
 
-    channel->send_data.u.socket_fin.connection_id = sckt->connection_id;
+    tunnel_channel->send_data.u.socket_fin.connection_id = sckt->connection_id;
 
-    red_channel_init_send_data(&channel->base, SPICE_MSG_TUNNEL_SOCKET_FIN, item);
-    spice_marshaller_add_ref(m, (uint8_t*)&channel->send_data.u.socket_fin,
-                        sizeof(channel->send_data.u.socket_fin));
+    red_channel_client_init_send_data(rcc, SPICE_MSG_TUNNEL_SOCKET_FIN, item);
+    spice_marshaller_add_ref(m, (uint8_t*)&tunnel_channel->send_data.u.socket_fin,
+                        sizeof(tunnel_channel->send_data.u.socket_fin));
 #ifdef DEBUG_NETWORK
     PRINT_SCKT(sckt);
 #endif
 }
 
-static void tunnel_channel_marshall_socket_close(TunnelChannel *channel, SpiceMarshaller *m, PipeItem *item)
+static void tunnel_channel_marshall_socket_close(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
+    TunnelChannel *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, status_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
     // can happen when it is a forced close
     if (sckt->out_data.ready_chunks_queue.head) {
         red_printf("socket closed but there are still buffers in outgoing ready queue"
@@ -2623,65 +2642,71 @@ static void tunnel_channel_marshall_socket_close(TunnelChannel *channel, SpiceMa
                    ntohs(sckt->local_port), sckt->far_service->id);
     }
 
-    channel->send_data.u.socket_close.connection_id = sckt->connection_id;
+    tunnel_channel->send_data.u.socket_close.connection_id = sckt->connection_id;
 
-    red_channel_init_send_data(&channel->base, SPICE_MSG_TUNNEL_SOCKET_CLOSE, item);
-    spice_marshaller_add_ref(m, (uint8_t*)&channel->send_data.u.socket_close,
-                        sizeof(channel->send_data.u.socket_close));
+    red_channel_client_init_send_data(rcc, SPICE_MSG_TUNNEL_SOCKET_CLOSE, item);
+    spice_marshaller_add_ref(m, (uint8_t*)&tunnel_channel->send_data.u.socket_close,
+                        sizeof(tunnel_channel->send_data.u.socket_close));
 #ifdef DEBUG_NETWORK
     PRINT_SCKT(sckt);
 #endif
 }
 
-static void tunnel_channel_marshall_socket_closed_ack(TunnelChannel *channel, SpiceMarshaller *m, PipeItem *item)
+static void tunnel_channel_marshall_socket_closed_ack(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
+    TunnelChannel *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, status_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
-    channel->send_data.u.socket_close_ack.connection_id = sckt->connection_id;
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel->send_data.u.socket_close_ack.connection_id = sckt->connection_id;
 
     // pipe item is null because we free the sckt.
-    red_channel_init_send_data(&channel->base, SPICE_MSG_TUNNEL_SOCKET_CLOSED_ACK, NULL);
-    spice_marshaller_add_ref(m, (uint8_t*)&channel->send_data.u.socket_close_ack,
-                        sizeof(channel->send_data.u.socket_close_ack));
+    red_channel_client_init_send_data(rcc, SPICE_MSG_TUNNEL_SOCKET_CLOSED_ACK, NULL);
+    spice_marshaller_add_ref(m, (uint8_t*)&tunnel_channel->send_data.u.socket_close_ack,
+                        sizeof(tunnel_channel->send_data.u.socket_close_ack));
 #ifdef DEBUG_NETWORK
     PRINT_SCKT(sckt);
 #endif
 
     ASSERT(sckt->client_waits_close_ack && (sckt->client_status == CLIENT_SCKT_STATUS_CLOSED));
-    tunnel_worker_free_socket(channel->worker, sckt);
-    if (CHECK_TUNNEL_ERROR(channel)) {
-        tunnel_shutdown(channel->worker);
+    tunnel_worker_free_socket(tunnel_channel->worker, sckt);
+    if (CHECK_TUNNEL_ERROR(tunnel_channel)) {
+        tunnel_shutdown(tunnel_channel->worker);
     }
 }
 
-static void tunnel_channel_marshall_socket_token(TunnelChannel *channel, SpiceMarshaller *m, PipeItem *item)
+static void tunnel_channel_marshall_socket_token(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
+    TunnelChannel *tunnel_channel;
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, token_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
 
     /* notice that the num of tokens sent can be > SOCKET_TOKENS_TO_SEND, since
        the sending is performed after the pipe item was pushed */
 
-    channel->send_data.u.socket_token.connection_id = sckt->connection_id;
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
+    tunnel_channel->send_data.u.socket_token.connection_id = sckt->connection_id;
 
     if (sckt->in_data.num_tokens > 0) {
-        channel->send_data.u.socket_token.num_tokens = sckt->in_data.num_tokens;
+        tunnel_channel->send_data.u.socket_token.num_tokens = sckt->in_data.num_tokens;
     } else {
         ASSERT(!sckt->in_data.client_total_num_tokens && !sckt->in_data.ready_chunks_queue.head);
-        channel->send_data.u.socket_token.num_tokens = SOCKET_TOKENS_TO_SEND_FOR_PROCESS;
+        tunnel_channel->send_data.u.socket_token.num_tokens = SOCKET_TOKENS_TO_SEND_FOR_PROCESS;
     }
-    sckt->in_data.num_tokens -= channel->send_data.u.socket_token.num_tokens;
-    sckt->in_data.client_total_num_tokens += channel->send_data.u.socket_token.num_tokens;
+    sckt->in_data.num_tokens -= tunnel_channel->send_data.u.socket_token.num_tokens;
+    sckt->in_data.client_total_num_tokens += tunnel_channel->send_data.u.socket_token.num_tokens;
     ASSERT(sckt->in_data.client_total_num_tokens <= SOCKET_WINDOW_SIZE);
 
-    red_channel_init_send_data(&channel->base, SPICE_MSG_TUNNEL_SOCKET_TOKEN, item);
-    spice_marshaller_add_ref(m, (uint8_t*)&channel->send_data.u.socket_token,
-                        sizeof(channel->send_data.u.socket_token));
+    red_channel_client_init_send_data(rcc, SPICE_MSG_TUNNEL_SOCKET_TOKEN, item);
+    spice_marshaller_add_ref(m, (uint8_t*)&tunnel_channel->send_data.u.socket_token,
+                        sizeof(tunnel_channel->send_data.u.socket_token));
 }
 
-static void tunnel_channel_marshall_socket_out_data(TunnelChannel *channel, SpiceMarshaller *m, PipeItem *item)
+static void tunnel_channel_marshall_socket_out_data(RedChannelClient *rcc, SpiceMarshaller *m, PipeItem *item)
 {
+    TunnelChannel *tunnel_channel;
+    tunnel_channel = SPICE_CONTAINEROF(rcc->channel, TunnelChannel, base);
     RedSocketOutData *sckt_out_data = SPICE_CONTAINEROF(item, RedSocketOutData, data_pipe_item);
     RedSocket *sckt = SPICE_CONTAINEROF(sckt_out_data, RedSocket, out_data);
     ReadyTunneledChunk *chunk;
@@ -2701,11 +2726,11 @@ static void tunnel_channel_marshall_socket_out_data(TunnelChannel *channel, Spic
     ASSERT(!sckt->out_data.push_tail);
     ASSERT(sckt->out_data.ready_chunks_queue.head->size <= MAX_SOCKET_DATA_SIZE);
 
-    channel->send_data.u.socket_data.connection_id = sckt->connection_id;
+    tunnel_channel->send_data.u.socket_data.connection_id = sckt->connection_id;
 
-    red_channel_init_send_data(&channel->base, SPICE_MSG_TUNNEL_SOCKET_DATA, item);
-    spice_marshaller_add_ref(m, (uint8_t*)&channel->send_data.u.socket_data,
-                        sizeof(channel->send_data.u.socket_data));
+    red_channel_client_init_send_data(rcc, SPICE_MSG_TUNNEL_SOCKET_DATA, item);
+    spice_marshaller_add_ref(m, (uint8_t*)&tunnel_channel->send_data.u.socket_data,
+                        sizeof(tunnel_channel->send_data.u.socket_data));
     pushed_bufs_num++;
 
     // the first chunk is in a valid size
@@ -2790,52 +2815,51 @@ static void tunnel_worker_release_socket_out_data(TunnelWorker *worker, PipeItem
     }
 }
 
-static void tunnel_channel_send_item(RedChannel *channel, PipeItem *item)
+static void tunnel_channel_send_item(RedChannelClient *rcc, PipeItem *item)
 {
-    TunnelChannel *tunnel_channel = (TunnelChannel *)channel;
-    SpiceMarshaller *m = red_channel_get_marshaller(channel);
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
 
     switch (item->type) {
     case PIPE_ITEM_TYPE_TUNNEL_INIT:
-        tunnel_channel_marshall_init(tunnel_channel, m, item);
+        tunnel_channel_marshall_init(rcc, m, item);
         break;
     case PIPE_ITEM_TYPE_SERVICE_IP_MAP:
-        tunnel_channel_marshall_service_ip_map(tunnel_channel, m, item);
+        tunnel_channel_marshall_service_ip_map(rcc, m, item);
         break;
     case PIPE_ITEM_TYPE_SOCKET_OPEN:
-        tunnel_channel_marshall_socket_open(tunnel_channel, m, item);
+        tunnel_channel_marshall_socket_open(rcc, m, item);
         break;
     case PIPE_ITEM_TYPE_SOCKET_DATA:
-        tunnel_channel_marshall_socket_out_data(tunnel_channel, m, item);
+        tunnel_channel_marshall_socket_out_data(rcc, m, item);
         break;
     case PIPE_ITEM_TYPE_SOCKET_FIN:
-        tunnel_channel_marshall_socket_fin(tunnel_channel, m, item);
+        tunnel_channel_marshall_socket_fin(rcc, m, item);
         break;
     case PIPE_ITEM_TYPE_SOCKET_CLOSE:
-        tunnel_channel_marshall_socket_close(tunnel_channel, m, item);
+        tunnel_channel_marshall_socket_close(rcc, m, item);
         break;
     case PIPE_ITEM_TYPE_SOCKET_CLOSED_ACK:
-        tunnel_channel_marshall_socket_closed_ack(tunnel_channel, m, item);
+        tunnel_channel_marshall_socket_closed_ack(rcc, m, item);
         break;
     case PIPE_ITEM_TYPE_SOCKET_TOKEN:
-        tunnel_channel_marshall_socket_token(tunnel_channel, m, item);
+        tunnel_channel_marshall_socket_token(rcc, m, item);
         break;
     case PIPE_ITEM_TYPE_MIGRATE:
-        tunnel_channel_marshall_migrate(tunnel_channel, m, item);
+        tunnel_channel_marshall_migrate(rcc, m, item);
         break;
     case PIPE_ITEM_TYPE_MIGRATE_DATA:
-        tunnel_channel_marshall_migrate_data(tunnel_channel, m, item);
+        tunnel_channel_marshall_migrate_data(rcc, m, item);
         break;
     default:
         red_error("invalid pipe item type");
     }
-    red_channel_begin_send_message(channel);
+    red_channel_client_begin_send_message(rcc);
 }
 
 /* param item_pushed: distinguishes between a pipe item that was pushed for sending, and
    a pipe item that is still in the pipe and is released due to disconnection.
    see red_pipe_item_clear */
-static void tunnel_channel_release_pipe_item(RedChannel *channel, PipeItem *item, int item_pushed)
+static void tunnel_channel_release_pipe_item(RedChannelClient *rcc, PipeItem *item, int item_pushed)
 {
     if (!item) { // e.g. when acking closed socket
         return;
@@ -2852,7 +2876,7 @@ static void tunnel_channel_release_pipe_item(RedChannel *channel, PipeItem *item
         break;
     case PIPE_ITEM_TYPE_SOCKET_DATA:
         if (item_pushed) {
-            tunnel_worker_release_socket_out_data(((TunnelChannel *)channel)->worker, item);
+            tunnel_worker_release_socket_out_data(((TunnelChannel *)rcc->channel)->worker, item);
         }
         break;
     case PIPE_ITEM_TYPE_MIGRATE:
@@ -3321,11 +3345,11 @@ static void arm_timer(SlirpUsrNetworkInterface *usr_interface, UserTimer *timer,
 * channel interface and other related procedures
 ************************************************/
 
-static int tunnel_channel_config_socket(RedChannel *channel)
+static int tunnel_channel_config_socket(RedChannelClient *rcc)
 {
     int flags;
     int delay_val;
-    RedsStream *stream = red_channel_get_stream(channel);
+    RedsStream *stream = red_channel_client_get_stream(rcc);
 
     if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
         red_printf("accept failed, %s", strerror(errno)); // can't we just use red_error?
@@ -3386,6 +3410,12 @@ static void tunnel_channel_disconnect(RedChannel *channel)
     worker->channel = NULL;
 }
 
+// TODO - not MC friendly, remove
+static void tunnel_channel_disconnect_client(RedChannelClient *rcc)
+{
+    tunnel_channel_disconnect(rcc->channel);
+}
+
 /* interface for reds */
 
 static void on_new_tunnel_channel(TunnelChannel *channel)
@@ -3400,7 +3430,7 @@ static void on_new_tunnel_channel(TunnelChannel *channel)
     }
 }
 
-static void tunnel_channel_hold_pipe_item(RedChannel *channel, PipeItem *item)
+static void tunnel_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
 {
 }
 
@@ -3415,10 +3445,10 @@ static void handle_tunnel_channel_link(Channel *channel, RedsStream *stream, int
     }
 
     tunnel_channel =
-        (TunnelChannel *)red_channel_create(sizeof(*tunnel_channel), stream, worker->core_interface,
+        (TunnelChannel *)red_channel_create(sizeof(*tunnel_channel), worker->core_interface,
                                             migration, TRUE,
                                             tunnel_channel_config_socket,
-                                            tunnel_channel_disconnect,
+                                            tunnel_channel_disconnect_client,
                                             tunnel_channel_handle_message,
                                             tunnel_channel_alloc_msg_rcv_buf,
                                             tunnel_channel_release_msg_rcv_buf,
@@ -3432,7 +3462,7 @@ static void handle_tunnel_channel_link(Channel *channel, RedsStream *stream, int
     if (!tunnel_channel) {
         return;
     }
-
+    red_channel_client_create(sizeof(RedChannelClient), &tunnel_channel->base, stream);
 
     tunnel_channel->worker = worker;
     tunnel_channel->worker->channel = tunnel_channel;
diff --git a/server/red_worker.c b/server/red_worker.c
index c0d03f3..bbb0155 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -922,7 +922,7 @@ static void red_display_release_stream(DisplayChannel *display, StreamAgent *age
 static inline void red_detach_stream(RedWorker *worker, Stream *stream);
 static void red_stop_stream(RedWorker *worker, Stream *stream);
 static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate, Drawable *sect);
-static inline void display_begin_send_message(DisplayChannel *channel, SpiceMarshaller *base_marshaller);
+static inline void display_begin_send_message(RedChannelClient *rcc, SpiceMarshaller *base_marshaller);
 static void red_release_pixmap_cache(DisplayChannel *channel);
 static void red_release_glz(DisplayChannel *channel);
 static void red_freeze_glz(DisplayChannel *channel);
@@ -1265,16 +1265,16 @@ static void release_upgrade_item(RedWorker* worker, UpgradeItem *item)
     }
 }
 
-static uint8_t *common_alloc_recv_buf(RedChannel *channel, SpiceDataHeader *msg_header)
+static uint8_t *common_alloc_recv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header)
 {
-    CommonChannel *common = SPICE_CONTAINEROF(channel, CommonChannel, base);
+    CommonChannel *common = SPICE_CONTAINEROF(rcc->channel, CommonChannel, base);
 
     return common->recv_buf;
 }
 
-static void common_release_recv_buf(RedChannel *channel, SpiceDataHeader *msg_header, uint8_t* msg)
+static void common_release_recv_buf(RedChannelClient *rcc,
+                                    SpiceDataHeader *msg_header, uint8_t* msg)
 {
-    return;
 }
 
 #define CLIENT_PIXMAPS_CACHE
@@ -1690,7 +1690,7 @@ static void red_clear_surface_drawables_from_pipe(RedWorker *worker, int surface
             item = (PipeItem *)ring_prev(ring, (RingItem *)item);
             ring_remove(&tmp_item->link);
             worker->display_channel->common.base.release_item(
-                &worker->display_channel->common.base, tmp_item, FALSE);
+                worker->display_channel->common.base.rcc, tmp_item, FALSE);
             worker->display_channel->common.base.pipe_size--;
 
             if (!item) {
@@ -5701,16 +5701,18 @@ static inline int red_compress_image(DisplayChannel *display_channel,
     }
 }
 
-static inline void red_display_add_image_to_pixmap_cache(DisplayChannel *display_channel,
+static inline void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc,
                                                          SpiceImage *image, SpiceImage *io_image,
                                                          int is_lossy)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+
     if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
         ASSERT(image->descriptor.width * image->descriptor.height > 0);
         if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME)) {
             if (pixmap_cache_add(display_channel->pixmap_cache, image->descriptor.id,
                                  image->descriptor.width * image->descriptor.height, is_lossy,
-                                 display_channel)) {
+                                 rcc)) {
                 io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
                 stat_inc_counter(display_channel->add_to_cache_counter, 1);
             }
@@ -5733,9 +5735,10 @@ typedef enum {
 
 /* if the number of times fill_bits can be called per one qxl_drawable increases -
    MAX_LZ_DRAWABLE_INSTANCES must be increased as well */
-static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *m,
+static FillBitsType fill_bits(RedChannelClient *rcc, SpiceMarshaller *m,
                               SpiceImage *simage, Drawable *drawable, int can_lossy)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedWorker *worker = display_channel->common.worker;
     SpiceImage image;
     compress_send_data_t comp_send_data = {0};
@@ -5751,7 +5754,7 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
     if ((simage->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
         int lossy_cache_item;
         if (pixmap_cache_hit(display_channel->pixmap_cache, image.descriptor.id,
-                             &lossy_cache_item, display_channel)) {
+                             &lossy_cache_item, rcc)) {
             if (can_lossy || !lossy_cache_item) {
                 if (!display_channel->enable_jpeg || lossy_cache_item) {
                     image.descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
@@ -5808,7 +5811,7 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
                                 drawable, can_lossy, &comp_send_data)) {
             SpicePalette *palette;
 
-            red_display_add_image_to_pixmap_cache(display_channel, simage, &image, FALSE);
+            red_display_add_image_to_pixmap_cache(rcc, simage, &image, FALSE);
 
             *bitmap = simage->u.bitmap;
             bitmap->flags = bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
@@ -5826,7 +5829,7 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
             spice_marshaller_add_ref_chunks(m, bitmap->data);
             return FILL_BITS_TYPE_BITMAP;
         } else {
-            red_display_add_image_to_pixmap_cache(display_channel, simage, &image,
+            red_display_add_image_to_pixmap_cache(rcc, simage, &image,
                                                   comp_send_data.is_lossy);
 
             spice_marshall_Image(m, &image,
@@ -5847,7 +5850,7 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
         break;
     }
     case SPICE_IMAGE_TYPE_QUIC:
-        red_display_add_image_to_pixmap_cache(display_channel, simage, &image, FALSE);
+        red_display_add_image_to_pixmap_cache(rcc, simage, &image, FALSE);
         image.u.quic = simage->u.quic;
         spice_marshall_Image(m, &image,
                              &bitmap_palette_out, &lzplt_palette_out);
@@ -5860,23 +5863,25 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
     }
 }
 
-static void fill_mask(DisplayChannel *display_channel, SpiceMarshaller *m,
+static void fill_mask(RedChannelClient *rcc, SpiceMarshaller *m,
                       SpiceImage *mask_bitmap, Drawable *drawable)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+
     if (mask_bitmap && m) {
         if (display_channel->common.worker->image_compression != SPICE_IMAGE_COMPRESS_OFF) {
             spice_image_compression_t save_img_comp =
                 display_channel->common.worker->image_compression;
             display_channel->common.worker->image_compression = SPICE_IMAGE_COMPRESS_OFF;
-            fill_bits(display_channel, m, mask_bitmap, drawable, FALSE);
+            fill_bits(rcc, m, mask_bitmap, drawable, FALSE);
             display_channel->common.worker->image_compression = save_img_comp;
         } else {
-            fill_bits(display_channel, m, mask_bitmap, drawable, FALSE);
+            fill_bits(rcc, m, mask_bitmap, drawable, FALSE);
         }
     }
 }
 
-static void fill_attr(DisplayChannel *display_channel, SpiceMarshaller *m, SpiceLineAttr *attr, uint32_t group_id)
+static void fill_attr(SpiceMarshaller *m, SpiceLineAttr *attr, uint32_t group_id)
 {
     int i;
 
@@ -5966,9 +5971,11 @@ static int is_surface_area_lossy(DisplayChannel *display_channel, uint32_t surfa
    to the client, returns false. "area" is for surfaces. If area = NULL,
    all the surface is considered. out_lossy_data will hold info about the bitmap, and its lossy
    area in case it is lossy and part of a surface. */
-static int is_bitmap_lossy(DisplayChannel *display_channel, SpiceImage *image, SpiceRect *area,
+static int is_bitmap_lossy(RedChannelClient *rcc, SpiceImage *image, SpiceRect *area,
                            Drawable *drawable, BitmapData *out_data)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+
     if (image == NULL) {
         // self bitmap
         out_data->type = BITMAP_DATA_TYPE_BITMAP;
@@ -5980,7 +5987,7 @@ static int is_bitmap_lossy(DisplayChannel *display_channel, SpiceImage *image, S
 
         out_data->id = image->descriptor.id;
         if (pixmap_cache_hit(display_channel->pixmap_cache, image->descriptor.id,
-                             &is_hit_lossy, display_channel)) {
+                             &is_hit_lossy, rcc)) {
             out_data->type = BITMAP_DATA_TYPE_CACHE;
             if (is_hit_lossy) {
                 return TRUE;
@@ -6010,11 +6017,11 @@ static int is_bitmap_lossy(DisplayChannel *display_channel, SpiceImage *image, S
     }
 }
 
-static int is_brush_lossy(DisplayChannel *display_channel, SpiceBrush *brush,
+static int is_brush_lossy(RedChannelClient *rcc, SpiceBrush *brush,
                           Drawable *drawable, BitmapData *out_data)
 {
     if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
-        return is_bitmap_lossy(display_channel, brush->u.pattern.pat, NULL,
+        return is_bitmap_lossy(rcc, brush->u.pattern.pat, NULL,
                                drawable, out_data);
     } else {
         out_data->type = BITMAP_DATA_TYPE_INVALID;
@@ -6289,17 +6296,16 @@ static void red_add_lossless_drawable_dependencies(RedWorker *worker,
 }
 
 static void red_marshall_qxl_draw_fill(RedWorker *worker,
-                                   DisplayChannel *display_channel,
+                                   RedChannelClient *rcc,
                                    SpiceMarshaller *base_marshaller,
                                    Drawable *item)
 {
-    RedChannel *channel = &display_channel->common.base;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *mask_bitmap_out;
     SpiceFill fill;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_FILL, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL, &item->pipe_item);
     fill_base(base_marshaller, item);
     fill = drawable->u.fill;
     spice_marshall_Fill(base_marshaller,
@@ -6308,18 +6314,19 @@ static void red_marshall_qxl_draw_fill(RedWorker *worker,
                         &mask_bitmap_out);
 
     if (brush_pat_out) {
-        fill_bits(display_channel, brush_pat_out, fill.brush.u.pattern.pat, item, FALSE);
+        fill_bits(rcc, brush_pat_out, fill.brush.u.pattern.pat, item, FALSE);
     }
 
-    fill_mask(display_channel, mask_bitmap_out, fill.mask.bitmap, item);
+    fill_mask(rcc, mask_bitmap_out, fill.mask.bitmap, item);
 }
 
 
-static void red_lossy_send_qxl_draw_fill(RedWorker *worker,
-                                         DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_fill(RedWorker *worker,
+                                         RedChannelClient *rcc,
                                          SpiceMarshaller *m,
                                          Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
 
     int dest_allowed_lossy = FALSE;
@@ -6335,7 +6342,7 @@ static void red_lossy_send_qxl_draw_fill(RedWorker *worker,
                            (rop & SPICE_ROPD_OP_AND) ||
                            (rop & SPICE_ROPD_OP_XOR));
 
-    brush_is_lossy = is_brush_lossy(display_channel, &drawable->u.fill.brush, item,
+    brush_is_lossy = is_brush_lossy(rcc, &drawable->u.fill.brush, item,
                                     &brush_bitmap_data);
     if (!dest_allowed_lossy) {
         dest_is_lossy = is_surface_area_lossy(display_channel, item->surface_id, &drawable->bbox,
@@ -6346,8 +6353,7 @@ static void red_lossy_send_qxl_draw_fill(RedWorker *worker,
         !(brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE))) {
         int has_mask = !!drawable->u.fill.mask.bitmap;
 
-        red_marshall_qxl_draw_fill(worker, display_channel, m, item);
-
+        red_marshall_qxl_draw_fill(worker, rcc, m, item);
         // either the brush operation is opaque, or the dest is not lossy
         surface_lossy_region_update(worker, display_channel, item, has_mask, FALSE);
     } else {
@@ -6373,11 +6379,10 @@ static void red_lossy_send_qxl_draw_fill(RedWorker *worker,
 }
 
 static FillBitsType red_marshall_qxl_draw_opaque(RedWorker *worker,
-                                             DisplayChannel *display_channel,
+                                             RedChannelClient *rcc,
                                              SpiceMarshaller *base_marshaller,
                                              Drawable *item, int src_allowed_lossy)
 {
-    RedChannel *channel = &display_channel->common.base;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *src_bitmap_out;
@@ -6385,7 +6390,7 @@ static FillBitsType red_marshall_qxl_draw_opaque(RedWorker *worker,
     SpiceOpaque opaque;
     FillBitsType src_send_type;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_OPAQUE, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_OPAQUE, &item->pipe_item);
     fill_base(base_marshaller, item);
     opaque = drawable->u.opaque;
     spice_marshall_Opaque(base_marshaller,
@@ -6394,22 +6399,23 @@ static FillBitsType red_marshall_qxl_draw_opaque(RedWorker *worker,
                           &brush_pat_out,
                           &mask_bitmap_out);
 
-    src_send_type = fill_bits(display_channel, src_bitmap_out, opaque.src_bitmap, item,
+    src_send_type = fill_bits(rcc, src_bitmap_out, opaque.src_bitmap, item,
                               src_allowed_lossy);
 
     if (brush_pat_out) {
-        fill_bits(display_channel, brush_pat_out, opaque.brush.u.pattern.pat, item, FALSE);
+        fill_bits(rcc, brush_pat_out, opaque.brush.u.pattern.pat, item, FALSE);
     }
-    fill_mask(display_channel, mask_bitmap_out, opaque.mask.bitmap, item);
+    fill_mask(rcc, mask_bitmap_out, opaque.mask.bitmap, item);
 
     return src_send_type;
 }
 
-static void red_lossy_send_qxl_draw_opaque(RedWorker *worker,
-                                           DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_opaque(RedWorker *worker,
+                                           RedChannelClient *rcc,
                                            SpiceMarshaller *m,
                                            Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
 
     int src_allowed_lossy;
@@ -6424,11 +6430,11 @@ static void red_lossy_send_qxl_draw_opaque(RedWorker *worker,
                           (rop & SPICE_ROPD_OP_AND) ||
                           (rop & SPICE_ROPD_OP_XOR));
 
-    brush_is_lossy = is_brush_lossy(display_channel, &drawable->u.opaque.brush, item,
+    brush_is_lossy = is_brush_lossy(rcc, &drawable->u.opaque.brush, item,
                                     &brush_bitmap_data);
 
     if (!src_allowed_lossy) {
-        src_is_lossy = is_bitmap_lossy(display_channel, drawable->u.opaque.src_bitmap,
+        src_is_lossy = is_bitmap_lossy(rcc, drawable->u.opaque.src_bitmap,
                                        &drawable->u.opaque.src_area,
                                        item,
                                        &src_bitmap_data);
@@ -6439,8 +6445,7 @@ static void red_lossy_send_qxl_draw_opaque(RedWorker *worker,
         FillBitsType src_send_type;
         int has_mask = !!drawable->u.opaque.mask.bitmap;
 
-        src_send_type = red_marshall_qxl_draw_opaque(worker, display_channel, m, item, src_allowed_lossy);
-
+        src_send_type = red_marshall_qxl_draw_opaque(worker, rcc, m, item, src_allowed_lossy);
         if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) {
             src_is_lossy = TRUE;
         } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) {
@@ -6471,18 +6476,17 @@ static void red_lossy_send_qxl_draw_opaque(RedWorker *worker,
 }
 
 static FillBitsType red_marshall_qxl_draw_copy(RedWorker *worker,
-                                           DisplayChannel *display_channel,
+                                           RedChannelClient *rcc,
                                            SpiceMarshaller *base_marshaller,
                                            Drawable *item, int src_allowed_lossy)
 {
-    RedChannel *channel = &display_channel->common.base;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceMarshaller *mask_bitmap_out;
     SpiceCopy copy;
     FillBitsType src_send_type;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_COPY, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->pipe_item);
     fill_base(base_marshaller, item);
     copy = drawable->u.copy;
     spice_marshall_Copy(base_marshaller,
@@ -6490,27 +6494,28 @@ static FillBitsType red_marshall_qxl_draw_copy(RedWorker *worker,
                         &src_bitmap_out,
                         &mask_bitmap_out);
 
-    src_send_type = fill_bits(display_channel, src_bitmap_out, copy.src_bitmap, item, src_allowed_lossy);
-    fill_mask(display_channel, mask_bitmap_out, copy.mask.bitmap, item);
+    src_send_type = fill_bits(rcc, src_bitmap_out, copy.src_bitmap, item, src_allowed_lossy);
+    fill_mask(rcc, mask_bitmap_out, copy.mask.bitmap, item);
 
     return src_send_type;
 }
 
-static void red_lossy_send_qxl_draw_copy(RedWorker *worker,
-                                         DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_copy(RedWorker *worker,
+                                         RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
                                          Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int has_mask = !!drawable->u.copy.mask.bitmap;
     int src_is_lossy;
     BitmapData src_bitmap_data;
     FillBitsType src_send_type;
 
-    src_is_lossy = is_bitmap_lossy(display_channel, drawable->u.copy.src_bitmap,
+    src_is_lossy = is_bitmap_lossy(rcc, drawable->u.copy.src_bitmap,
                                    &drawable->u.copy.src_area, item, &src_bitmap_data);
 
-    src_send_type = red_marshall_qxl_draw_copy(worker, display_channel, base_marshaller, item, TRUE);
+    src_send_type = red_marshall_qxl_draw_copy(worker, rcc, base_marshaller, item, TRUE);
 
     if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) {
         src_is_lossy = TRUE;
@@ -6523,39 +6528,38 @@ static void red_lossy_send_qxl_draw_copy(RedWorker *worker,
 }
 
 static void red_marshall_qxl_draw_transparent(RedWorker *worker,
-                                          DisplayChannel *display_channel,
+                                          RedChannelClient *rcc,
                                           SpiceMarshaller *base_marshaller,
                                           Drawable *item)
 {
-    RedChannel *channel = &display_channel->common.base;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceTransparent transparent;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_TRANSPARENT, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TRANSPARENT, &item->pipe_item);
     fill_base(base_marshaller, item);
     transparent = drawable->u.transparent;
     spice_marshall_Transparent(base_marshaller,
                                &transparent,
                                &src_bitmap_out);
-    fill_bits(display_channel, src_bitmap_out, transparent.src_bitmap, item, FALSE);
+    fill_bits(rcc, src_bitmap_out, transparent.src_bitmap, item, FALSE);
 }
 
-static void red_lossy_send_qxl_draw_transparent(RedWorker *worker,
-                                                DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_transparent(RedWorker *worker,
+                                                RedChannelClient *rcc,
                                                 SpiceMarshaller *base_marshaller,
                                                 Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
     BitmapData src_bitmap_data;
 
-    src_is_lossy = is_bitmap_lossy(display_channel, drawable->u.transparent.src_bitmap,
+    src_is_lossy = is_bitmap_lossy(rcc, drawable->u.transparent.src_bitmap,
                                    &drawable->u.transparent.src_area, item, &src_bitmap_data);
 
     if (!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) {
-        red_marshall_qxl_draw_transparent(worker, display_channel, base_marshaller, item);
-
+        red_marshall_qxl_draw_transparent(worker, rcc, base_marshaller, item);
         // don't update surface lossy region since transperent areas might be lossy
     } else {
         int resend_surface_ids[1];
@@ -6570,42 +6574,42 @@ static void red_lossy_send_qxl_draw_transparent(RedWorker *worker,
 }
 
 static FillBitsType red_marshall_qxl_draw_alpha_blend(RedWorker *worker,
-                                                  DisplayChannel *display_channel,
+                                                  RedChannelClient *rcc,
                                                   SpiceMarshaller *base_marshaller,
                                                   Drawable *item,
                                                   int src_allowed_lossy)
 {
-    RedChannel *channel = &display_channel->common.base;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceAlphaBlend alpha_blend;
     FillBitsType src_send_type;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND, &item->pipe_item);
     fill_base(base_marshaller, item);
     alpha_blend = drawable->u.alpha_blend;
     spice_marshall_AlphaBlend(base_marshaller,
                               &alpha_blend,
                               &src_bitmap_out);
-    src_send_type = fill_bits(display_channel, src_bitmap_out, alpha_blend.src_bitmap, item, src_allowed_lossy);
+    src_send_type = fill_bits(rcc, src_bitmap_out, alpha_blend.src_bitmap, item, src_allowed_lossy);
 
     return src_send_type;
 }
 
-static void red_lossy_send_qxl_draw_alpha_blend(RedWorker *worker,
-                                                DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_alpha_blend(RedWorker *worker,
+                                                RedChannelClient *rcc,
                                                 SpiceMarshaller *base_marshaller,
                                                 Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
     BitmapData src_bitmap_data;
     FillBitsType src_send_type;
 
-    src_is_lossy = is_bitmap_lossy(display_channel, drawable->u.alpha_blend.src_bitmap,
+    src_is_lossy = is_bitmap_lossy(rcc, drawable->u.alpha_blend.src_bitmap,
                                    &drawable->u.alpha_blend.src_area, item, &src_bitmap_data);
 
-    src_send_type = red_marshall_qxl_draw_alpha_blend(worker, display_channel, base_marshaller, item, TRUE);
+    src_send_type = red_marshall_qxl_draw_alpha_blend(worker, rcc, base_marshaller, item, TRUE);
 
     if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) {
         src_is_lossy = TRUE;
@@ -6619,26 +6623,26 @@ static void red_lossy_send_qxl_draw_alpha_blend(RedWorker *worker,
 }
 
 static void red_marshall_qxl_copy_bits(RedWorker *worker,
-                                   DisplayChannel *display_channel,
+                                   RedChannelClient *rcc,
                                    SpiceMarshaller *base_marshaller,
                                    Drawable *item)
 {
-    RedChannel *channel = &display_channel->common.base;
     RedDrawable *drawable = item->red_drawable;
     SpicePoint copy_bits;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_COPY_BITS, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_COPY_BITS, &item->pipe_item);
     fill_base(base_marshaller, item);
     copy_bits = drawable->u.copy_bits.src_pos;
     spice_marshall_Point(base_marshaller,
                          &copy_bits);
 }
 
-static void red_lossy_send_qxl_copy_bits(RedWorker *worker,
-                                         DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_copy_bits(RedWorker *worker,
+                                         RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
                                          Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     SpiceRect src_rect;
     int horz_offset;
@@ -6646,7 +6650,7 @@ static void red_lossy_send_qxl_copy_bits(RedWorker *worker,
     int src_is_lossy;
     SpiceRect src_lossy_area;
 
-    red_marshall_qxl_copy_bits(worker, display_channel, base_marshaller, item);
+    red_marshall_qxl_copy_bits(worker, rcc, base_marshaller, item);
 
     horz_offset = drawable->u.copy_bits.src_pos.x - drawable->bbox.left;
     vert_offset = drawable->u.copy_bits.src_pos.y - drawable->bbox.top;
@@ -6664,17 +6668,16 @@ static void red_lossy_send_qxl_copy_bits(RedWorker *worker,
 }
 
 static void red_marshall_qxl_draw_blend(RedWorker *worker,
-                                    DisplayChannel *display_channel,
+                                    RedChannelClient *rcc,
                                     SpiceMarshaller *base_marshaller,
                                     Drawable *item)
 {
-    RedChannel *channel = &display_channel->common.base;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *src_bitmap_out;
     SpiceMarshaller *mask_bitmap_out;
     SpiceBlend blend;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_BLEND, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLEND, &item->pipe_item);
     fill_base(base_marshaller, item);
     blend = drawable->u.blend;
     spice_marshall_Blend(base_marshaller,
@@ -6682,30 +6685,31 @@ static void red_marshall_qxl_draw_blend(RedWorker *worker,
                          &src_bitmap_out,
                          &mask_bitmap_out);
 
-    fill_bits(display_channel, src_bitmap_out, blend.src_bitmap, item, FALSE);
+    fill_bits(rcc, src_bitmap_out, blend.src_bitmap, item, FALSE);
 
-    fill_mask(display_channel, mask_bitmap_out, blend.mask.bitmap, item);
+    fill_mask(rcc, mask_bitmap_out, blend.mask.bitmap, item);
 }
 
-static void red_lossy_send_qxl_draw_blend(RedWorker *worker,
-                                          DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_blend(RedWorker *worker,
+                                          RedChannelClient *rcc,
                                           SpiceMarshaller *base_marshaller,
                                           Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
     BitmapData src_bitmap_data;
     int dest_is_lossy;
     SpiceRect dest_lossy_area;
 
-    src_is_lossy = is_bitmap_lossy(display_channel, drawable->u.blend.src_bitmap,
+    src_is_lossy = is_bitmap_lossy(rcc, drawable->u.blend.src_bitmap,
                                    &drawable->u.blend.src_area, item, &src_bitmap_data);
     dest_is_lossy = is_surface_area_lossy(display_channel, drawable->surface_id,
                                           &drawable->bbox, &dest_lossy_area);
 
     if (!dest_is_lossy &&
         (!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) {
-        red_marshall_qxl_draw_blend(worker, display_channel, base_marshaller, item);
+        red_marshall_qxl_draw_blend(worker, rcc, base_marshaller, item);
     } else {
         int resend_surface_ids[2];
         SpiceRect *resend_areas[2];
@@ -6729,16 +6733,15 @@ static void red_lossy_send_qxl_draw_blend(RedWorker *worker,
 }
 
 static void red_marshall_qxl_draw_blackness(RedWorker *worker,
-                                        DisplayChannel *display_channel,
+                                        RedChannelClient *rcc,
                                         SpiceMarshaller *base_marshaller,
                                         Drawable *item)
 {
-    RedChannel *channel = &display_channel->common.base;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *mask_bitmap_out;
     SpiceBlackness blackness;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_BLACKNESS, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLACKNESS, &item->pipe_item);
     fill_base(base_marshaller, item);
     blackness = drawable->u.blackness;
 
@@ -6746,33 +6749,33 @@ static void red_marshall_qxl_draw_blackness(RedWorker *worker,
                              &blackness,
                              &mask_bitmap_out);
 
-    fill_mask(display_channel, mask_bitmap_out, blackness.mask.bitmap, item);
+    fill_mask(rcc, mask_bitmap_out, blackness.mask.bitmap, item);
 }
 
-static void red_lossy_send_qxl_draw_blackness(RedWorker *worker,
-                                              DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_blackness(RedWorker *worker,
+                                              RedChannelClient *rcc,
                                               SpiceMarshaller *base_marshaller,
                                               Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int has_mask = !!drawable->u.blackness.mask.bitmap;
 
-    red_marshall_qxl_draw_blackness(worker, display_channel, base_marshaller, item);
+    red_marshall_qxl_draw_blackness(worker, rcc, base_marshaller, item);
 
     surface_lossy_region_update(worker, display_channel, item, has_mask, FALSE);
 }
 
 static void red_marshall_qxl_draw_whiteness(RedWorker *worker,
-                                        DisplayChannel *display_channel,
+                                        RedChannelClient *rcc,
                                         SpiceMarshaller *base_marshaller,
                                         Drawable *item)
 {
-    RedChannel *channel = &display_channel->common.base;
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *mask_bitmap_out;
     SpiceWhiteness whiteness;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_WHITENESS, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_WHITENESS, &item->pipe_item);
     fill_base(base_marshaller, item);
     whiteness = drawable->u.whiteness;
 
@@ -6780,33 +6783,33 @@ static void red_marshall_qxl_draw_whiteness(RedWorker *worker,
                              &whiteness,
                              &mask_bitmap_out);
 
-    fill_mask(display_channel, mask_bitmap_out, whiteness.mask.bitmap, item);
+    fill_mask(rcc, mask_bitmap_out, whiteness.mask.bitmap, item);
 }
 
-static void red_lossy_send_qxl_draw_whiteness(RedWorker *worker,
-                                              DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_whiteness(RedWorker *worker,
+                                              RedChannelClient *rcc,
                                               SpiceMarshaller *base_marshaller,
                                               Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int has_mask = !!drawable->u.whiteness.mask.bitmap;
 
-    red_marshall_qxl_draw_whiteness(worker, display_channel, base_marshaller, item);
+    red_marshall_qxl_draw_whiteness(worker, rcc, base_marshaller, item);
 
     surface_lossy_region_update(worker, display_channel, item, has_mask, FALSE);
 }
 
 static void red_marshall_qxl_draw_inverse(RedWorker *worker,
-                                        DisplayChannel *display_channel,
+                                        RedChannelClient *rcc,
                                         SpiceMarshaller *base_marshaller,
                                         Drawable *item)
 {
     RedDrawable *drawable = item->red_drawable;
     SpiceMarshaller *mask_bitmap_out;
-    RedChannel *channel = &display_channel->common.base;
     SpiceInvers inverse;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_INVERS, NULL);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_INVERS, NULL);
     fill_base(base_marshaller, item);
     inverse = drawable->u.invers;
 
@@ -6814,30 +6817,29 @@ static void red_marshall_qxl_draw_inverse(RedWorker *worker,
                           &inverse,
                           &mask_bitmap_out);
 
-    fill_mask(display_channel, mask_bitmap_out, inverse.mask.bitmap, item);
+    fill_mask(rcc, mask_bitmap_out, inverse.mask.bitmap, item);
 }
 
-static void red_lossy_send_qxl_draw_inverse(RedWorker *worker,
-                                            DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_inverse(RedWorker *worker,
+                                            RedChannelClient *rcc,
                                             SpiceMarshaller *base_marshaller,
                                             Drawable *item)
 {
-    red_marshall_qxl_draw_inverse(worker, display_channel, base_marshaller, item);
+    red_marshall_qxl_draw_inverse(worker, rcc, base_marshaller, item);
 }
 
 static void red_marshall_qxl_draw_rop3(RedWorker *worker,
-                                   DisplayChannel *display_channel,
+                                   RedChannelClient *rcc,
                                    SpiceMarshaller *base_marshaller,
                                    Drawable *item)
 {
     RedDrawable *drawable = item->red_drawable;
-    RedChannel *channel = &display_channel->common.base;
     SpiceRop3 rop3;
     SpiceMarshaller *src_bitmap_out;
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *mask_bitmap_out;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_ROP3, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ROP3, &item->pipe_item);
     fill_base(base_marshaller, item);
     rop3 = drawable->u.rop3;
     spice_marshall_Rop3(base_marshaller,
@@ -6846,19 +6848,20 @@ static void red_marshall_qxl_draw_rop3(RedWorker *worker,
                         &brush_pat_out,
                         &mask_bitmap_out);
 
-    fill_bits(display_channel, src_bitmap_out, rop3.src_bitmap, item, FALSE);
+    fill_bits(rcc, src_bitmap_out, rop3.src_bitmap, item, FALSE);
 
     if (brush_pat_out) {
-        fill_bits(display_channel, brush_pat_out, rop3.brush.u.pattern.pat, item, FALSE);
+        fill_bits(rcc, brush_pat_out, rop3.brush.u.pattern.pat, item, FALSE);
     }
-    fill_mask(display_channel, mask_bitmap_out, rop3.mask.bitmap, item);
+    fill_mask(rcc, mask_bitmap_out, rop3.mask.bitmap, item);
 }
 
-static void red_lossy_send_qxl_draw_rop3(RedWorker *worker,
-                                         DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_rop3(RedWorker *worker,
+                                         RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
                                          Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int src_is_lossy;
     BitmapData src_bitmap_data;
@@ -6867,9 +6870,9 @@ static void red_lossy_send_qxl_draw_rop3(RedWorker *worker,
     int dest_is_lossy;
     SpiceRect dest_lossy_area;
 
-    src_is_lossy = is_bitmap_lossy(display_channel, drawable->u.rop3.src_bitmap,
+    src_is_lossy = is_bitmap_lossy(rcc, drawable->u.rop3.src_bitmap,
                                    &drawable->u.rop3.src_area, item, &src_bitmap_data);
-    brush_is_lossy = is_brush_lossy(display_channel, &drawable->u.rop3.brush, item,
+    brush_is_lossy = is_brush_lossy(rcc, &drawable->u.rop3.brush, item,
                                     &brush_bitmap_data);
     dest_is_lossy = is_surface_area_lossy(display_channel, drawable->surface_id,
                                           &drawable->bbox, &dest_lossy_area);
@@ -6878,8 +6881,7 @@ static void red_lossy_send_qxl_draw_rop3(RedWorker *worker,
         (!brush_is_lossy || (brush_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) &&
         !dest_is_lossy) {
         int has_mask = !!drawable->u.rop3.mask.bitmap;
-        red_marshall_qxl_draw_rop3(worker, display_channel, base_marshaller, item);
-
+        red_marshall_qxl_draw_rop3(worker, rcc, base_marshaller, item);
         surface_lossy_region_update(worker, display_channel, item, has_mask, FALSE);
     } else {
         int resend_surface_ids[3];
@@ -6910,17 +6912,16 @@ static void red_lossy_send_qxl_draw_rop3(RedWorker *worker,
 }
 
 static void red_marshall_qxl_draw_stroke(RedWorker *worker,
-                                     DisplayChannel *display_channel,
+                                     RedChannelClient *rcc,
                                      SpiceMarshaller *base_marshaller,
                                      Drawable *item)
 {
     RedDrawable *drawable = item->red_drawable;
-    RedChannel *channel = &display_channel->common.base;
     SpiceStroke stroke;
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *style_out;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_STROKE, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_STROKE, &item->pipe_item);
     fill_base(base_marshaller, item);
     stroke = drawable->u.stroke;
     spice_marshall_Stroke(base_marshaller,
@@ -6928,17 +6929,18 @@ static void red_marshall_qxl_draw_stroke(RedWorker *worker,
                           &style_out,
                           &brush_pat_out);
 
-    fill_attr(display_channel, style_out, &stroke.attr, item->group_id);
+    fill_attr(style_out, &stroke.attr, item->group_id);
     if (brush_pat_out) {
-        fill_bits(display_channel, brush_pat_out, stroke.brush.u.pattern.pat, item, FALSE);
+        fill_bits(rcc, brush_pat_out, stroke.brush.u.pattern.pat, item, FALSE);
     }
 }
 
-static void red_lossy_send_qxl_draw_stroke(RedWorker *worker,
-                                           DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_stroke(RedWorker *worker,
+                                           RedChannelClient *rcc,
                                            SpiceMarshaller *base_marshaller,
                                            Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int brush_is_lossy;
     BitmapData brush_bitmap_data;
@@ -6946,7 +6948,7 @@ static void red_lossy_send_qxl_draw_stroke(RedWorker *worker,
     SpiceRect dest_lossy_area;
     int rop;
 
-    brush_is_lossy = is_brush_lossy(display_channel, &drawable->u.stroke.brush, item,
+    brush_is_lossy = is_brush_lossy(rcc, &drawable->u.stroke.brush, item,
                                     &brush_bitmap_data);
 
     // back_mode is not used at the client. Ignoring.
@@ -6964,7 +6966,7 @@ static void red_lossy_send_qxl_draw_stroke(RedWorker *worker,
     if (!dest_is_lossy &&
         (!brush_is_lossy || (brush_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)))
     {
-        red_marshall_qxl_draw_stroke(worker, display_channel, base_marshaller, item);
+        red_marshall_qxl_draw_stroke(worker, rcc, base_marshaller, item);
     } else {
         int resend_surface_ids[2];
         SpiceRect *resend_areas[2];
@@ -6986,21 +6988,19 @@ static void red_lossy_send_qxl_draw_stroke(RedWorker *worker,
         red_add_lossless_drawable_dependencies(worker, display_channel, item,
                                                resend_surface_ids, resend_areas, num_resend);
     }
-
 }
 
 static void red_marshall_qxl_draw_text(RedWorker *worker,
-                                   DisplayChannel *display_channel,
+                                   RedChannelClient *rcc,
                                    SpiceMarshaller *base_marshaller,
                                    Drawable *item)
 {
     RedDrawable *drawable = item->red_drawable;
-    RedChannel *channel = &display_channel->common.base;
     SpiceText text;
     SpiceMarshaller *brush_pat_out;
     SpiceMarshaller *back_brush_pat_out;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_TEXT, &item->pipe_item);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TEXT, &item->pipe_item);
     fill_base(base_marshaller, item);
     text = drawable->u.text;
     spice_marshall_Text(base_marshaller,
@@ -7009,18 +7009,19 @@ static void red_marshall_qxl_draw_text(RedWorker *worker,
                         &back_brush_pat_out);
 
     if (brush_pat_out) {
-        fill_bits(display_channel, brush_pat_out, text.fore_brush.u.pattern.pat, item, FALSE);
+        fill_bits(rcc, brush_pat_out, text.fore_brush.u.pattern.pat, item, FALSE);
     }
     if (back_brush_pat_out) {
-        fill_bits(display_channel, back_brush_pat_out, text.back_brush.u.pattern.pat, item, FALSE);
+        fill_bits(rcc, back_brush_pat_out, text.back_brush.u.pattern.pat, item, FALSE);
     }
 }
 
-static void red_lossy_send_qxl_draw_text(RedWorker *worker,
-                                         DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_draw_text(RedWorker *worker,
+                                         RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
                                          Drawable *item)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     RedDrawable *drawable = item->red_drawable;
     int fg_is_lossy;
     BitmapData fg_bitmap_data;
@@ -7030,9 +7031,9 @@ static void red_lossy_send_qxl_draw_text(RedWorker *worker,
     SpiceRect dest_lossy_area;
     int rop = 0;
 
-    fg_is_lossy = is_brush_lossy(display_channel, &drawable->u.text.fore_brush, item,
+    fg_is_lossy = is_brush_lossy(rcc, &drawable->u.text.fore_brush, item,
                                  &fg_bitmap_data);
-    bg_is_lossy = is_brush_lossy(display_channel, &drawable->u.text.back_brush, item,
+    bg_is_lossy = is_brush_lossy(rcc, &drawable->u.text.back_brush, item,
                                  &bg_bitmap_data);
 
     // assuming that if the brush type is solid, the destination can
@@ -7054,7 +7055,7 @@ static void red_lossy_send_qxl_draw_text(RedWorker *worker,
     if (!dest_is_lossy &&
         (!fg_is_lossy || (fg_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) &&
         (!bg_is_lossy || (bg_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) {
-        red_marshall_qxl_draw_text(worker, display_channel, base_marshaller, item);
+        red_marshall_qxl_draw_text(worker, rcc, base_marshaller, item);
     } else {
         int resend_surface_ids[3];
         SpiceRect *resend_areas[3];
@@ -7082,98 +7083,98 @@ static void red_lossy_send_qxl_draw_text(RedWorker *worker,
     }
 }
 
-static void red_lossy_marshall_qxl_drawable(RedWorker *worker, DisplayChannel *display_channel,
+static void red_lossy_marshall_qxl_drawable(RedWorker *worker, RedChannelClient *rcc,
                                         SpiceMarshaller *base_marshaller, Drawable *item)
 {
     switch (item->red_drawable->type) {
     case QXL_DRAW_FILL:
-        red_lossy_send_qxl_draw_fill(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_fill(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_OPAQUE:
-        red_lossy_send_qxl_draw_opaque(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_opaque(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_COPY:
-        red_lossy_send_qxl_draw_copy(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_copy(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_TRANSPARENT:
-        red_lossy_send_qxl_draw_transparent(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_transparent(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_ALPHA_BLEND:
-        red_lossy_send_qxl_draw_alpha_blend(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_alpha_blend(worker, rcc, base_marshaller, item);
         break;
     case QXL_COPY_BITS:
-        red_lossy_send_qxl_copy_bits(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_copy_bits(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_BLEND:
-        red_lossy_send_qxl_draw_blend(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_blend(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_BLACKNESS:
-        red_lossy_send_qxl_draw_blackness(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_blackness(worker, rcc, base_marshaller, item);
         break;
      case QXL_DRAW_WHITENESS:
-        red_lossy_send_qxl_draw_whiteness(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_whiteness(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_INVERS:
-        red_lossy_send_qxl_draw_inverse(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_inverse(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_ROP3:
-        red_lossy_send_qxl_draw_rop3(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_rop3(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_STROKE:
-        red_lossy_send_qxl_draw_stroke(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_stroke(worker, rcc, base_marshaller, item);
         break;
     case QXL_DRAW_TEXT:
-        red_lossy_send_qxl_draw_text(worker, display_channel, base_marshaller, item);
+        red_lossy_marshall_qxl_draw_text(worker, rcc, base_marshaller, item);
         break;
     default:
         red_error("invalid type");
     }
 }
 
-static inline void red_marshall_qxl_drawable(RedWorker *worker, DisplayChannel *display_channel,
-                                         SpiceMarshaller *m, Drawable *item)
+static inline void red_marshall_qxl_drawable(RedWorker *worker, RedChannelClient *rcc,
+                                SpiceMarshaller *m, Drawable *item)
 {
     RedDrawable *drawable = item->red_drawable;
 
     switch (drawable->type) {
     case QXL_DRAW_FILL:
-        red_marshall_qxl_draw_fill(worker, display_channel, m, item);
+        red_marshall_qxl_draw_fill(worker, rcc, m, item);
         break;
     case QXL_DRAW_OPAQUE:
-        red_marshall_qxl_draw_opaque(worker, display_channel, m, item, FALSE);
+        red_marshall_qxl_draw_opaque(worker, rcc, m, item, FALSE);
         break;
     case QXL_DRAW_COPY:
-        red_marshall_qxl_draw_copy(worker, display_channel, m, item, FALSE);
+        red_marshall_qxl_draw_copy(worker, rcc, m, item, FALSE);
         break;
     case QXL_DRAW_TRANSPARENT:
-        red_marshall_qxl_draw_transparent(worker, display_channel, m, item);
+        red_marshall_qxl_draw_transparent(worker, rcc, m, item);
         break;
     case QXL_DRAW_ALPHA_BLEND:
-        red_marshall_qxl_draw_alpha_blend(worker, display_channel, m, item, FALSE);
+        red_marshall_qxl_draw_alpha_blend(worker, rcc, m, item, FALSE);
         break;
     case QXL_COPY_BITS:
-        red_marshall_qxl_copy_bits(worker, display_channel, m, item);
+        red_marshall_qxl_copy_bits(worker, rcc, m, item);
         break;
     case QXL_DRAW_BLEND:
-        red_marshall_qxl_draw_blend(worker, display_channel, m, item);
+        red_marshall_qxl_draw_blend(worker, rcc, m, item);
         break;
     case QXL_DRAW_BLACKNESS:
-        red_marshall_qxl_draw_blackness(worker, display_channel, m, item);
+        red_marshall_qxl_draw_blackness(worker, rcc, m, item);
         break;
     case QXL_DRAW_WHITENESS:
-        red_marshall_qxl_draw_whiteness(worker, display_channel, m, item);
+        red_marshall_qxl_draw_whiteness(worker, rcc, m, item);
         break;
     case QXL_DRAW_INVERS:
-        red_marshall_qxl_draw_inverse(worker, display_channel, m, item);
+        red_marshall_qxl_draw_inverse(worker, rcc, m, item);
         break;
     case QXL_DRAW_ROP3:
-        red_marshall_qxl_draw_rop3(worker, display_channel, m, item);
+        red_marshall_qxl_draw_rop3(worker, rcc, m, item);
         break;
     case QXL_DRAW_STROKE:
-        red_marshall_qxl_draw_stroke(worker, display_channel, m, item);
+        red_marshall_qxl_draw_stroke(worker, rcc, m, item);
         break;
     case QXL_DRAW_TEXT:
-        red_marshall_qxl_draw_text(worker, display_channel, m, item);
+        red_marshall_qxl_draw_text(worker, rcc, m, item);
         break;
     default:
         red_error("invalid type");
@@ -7204,10 +7205,11 @@ static void display_channel_push_release(DisplayChannel *channel, uint8_t type,
     free_list->res->resources[free_list->res->count++].id = id;
 }
 
-static inline void display_begin_send_message(DisplayChannel *channel, SpiceMarshaller *base_marshaller)
+static inline void display_begin_send_message(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
 {
-    FreeList *free_list = &channel->send_data.free_list;
-    SpiceDataHeader *header = red_channel_get_header(&channel->common.base);
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    FreeList *free_list = &display_channel->send_data.free_list;
+    SpiceDataHeader *header = red_channel_client_get_header(rcc);
 
     if (free_list->res->count) {
         int sub_list_len = 1;
@@ -7225,7 +7227,7 @@ static inline void display_begin_send_message(DisplayChannel *channel, SpiceMars
         spice_marshall_msg_display_inval_list(inval_m, free_list->res);
 
         for (i = 0; i < MAX_CACHE_CLIENTS; i++) {
-            if (i != channel->common.id && free_list->sync[i] != 0) {
+            if (i != display_channel->common.id && free_list->sync[i] != 0) {
                 free_list->wait.header.wait_list[sync_count].channel_type = SPICE_CHANNEL_DISPLAY;
                 free_list->wait.header.wait_list[sync_count].channel_id = i;
                 free_list->wait.header.wait_list[sync_count++].message_serial = free_list->sync[i];
@@ -7252,7 +7254,7 @@ static inline void display_begin_send_message(DisplayChannel *channel, SpiceMars
         spice_marshaller_add_uint32(sub_list_m, spice_marshaller_get_offset(inval_m));
         header->sub_list = spice_marshaller_get_offset(sub_list_m);
     }
-    red_channel_begin_send_message((RedChannel *)channel);
+    red_channel_client_begin_send_message(rcc);
 }
 
 static inline RedChannel *red_ref_channel(RedChannel *channel)
@@ -7341,19 +7343,18 @@ static int encode_frame (RedWorker *worker, const SpiceRect *src,
     return TRUE;
 }
 
-static inline int red_send_stream_data(DisplayChannel *display_channel,
+static inline int red_marshall_stream_data(RedChannelClient *rcc,
                   SpiceMarshaller *base_marshaller, Drawable *drawable)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     Stream *stream = drawable->stream;
     SpiceImage *image;
-    RedChannel *channel;
     RedWorker* worker;
     int n;
 
     ASSERT(stream);
     ASSERT(drawable->red_drawable->type == QXL_DRAW_COPY);
 
-    channel = &display_channel->common.base;
     worker = display_channel->common.worker;
     image = drawable->red_drawable->u.copy.src_bitmap;
 
@@ -7382,7 +7383,7 @@ static inline int red_send_stream_data(DisplayChannel *display_channel,
     n = mjpeg_encoder_end_frame(stream->mjpeg_encoder);
     display_channel->send_data.stream_outbuf_size = outbuf_size;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
 
     SpiceMsgDisplayStreamData stream_data;
 
@@ -7396,72 +7397,63 @@ static inline int red_send_stream_data(DisplayChannel *display_channel,
     return TRUE;
 }
 
-static inline void marshall_qxl_drawable(DisplayChannel *display_channel,
+static inline void marshall_qxl_drawable(RedChannelClient *rcc,
     SpiceMarshaller *m, Drawable *item)
 {
-    ASSERT(display_channel);
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
 
-    if (item->stream && red_send_stream_data(display_channel, m, item)) {
+    ASSERT(display_channel && rcc);
+    if (item->stream && red_marshall_stream_data(rcc, m, item)) {
         return;
     }
     if (!display_channel->enable_jpeg)
-        red_marshall_qxl_drawable(display_channel->common.worker, display_channel, m, item);
+        red_marshall_qxl_drawable(display_channel->common.worker, rcc, m, item);
     else
-        red_lossy_marshall_qxl_drawable(display_channel->common.worker, display_channel, m, item);
+        red_lossy_marshall_qxl_drawable(display_channel->common.worker, rcc, m, item);
 }
 
-static inline void red_marshall_verb(RedChannel *channel, uint16_t verb)
+static inline void red_marshall_verb(RedChannelClient *rcc, uint16_t verb)
 {
-    ASSERT(channel);
-    red_channel_init_send_data(channel, verb, NULL);
+    ASSERT(rcc);
+    red_channel_client_init_send_data(rcc, verb, NULL);
 }
 
-static inline void display_marshall_verb(DisplayChannel *channel, uint16_t verb)
-{
-    ASSERT(channel);
-    red_channel_init_send_data(&channel->common.base, verb, NULL);
-}
-
-static inline void red_marshall_inval(RedChannel *channel,
+static inline void red_marshall_inval(RedChannelClient *rcc,
         SpiceMarshaller *base_marshaller, CacheItem *cach_item)
 {
     SpiceMsgDisplayInvalOne inval_one;
 
-    red_channel_init_send_data(channel, cach_item->inval_type, NULL);
+    red_channel_client_init_send_data(rcc, cach_item->inval_type, NULL);
     inval_one.id = *(uint64_t *)&cach_item->id;
 
     spice_marshall_msg_cursor_inval_one(base_marshaller, &inval_one);
 }
 
-static void red_display_marshall_inval(DisplayChannel *display_channel,
-                SpiceMarshaller *base_marshaller, CacheItem *cach_item)
-{
-    red_marshall_inval((RedChannel *)display_channel, base_marshaller, cach_item);
-}
-
-static void display_channel_marshall_migrate(DisplayChannel *display_channel, SpiceMarshaller *base_marshaller)
+static void display_channel_marshall_migrate(RedChannelClient *rcc,
+    SpiceMarshaller *base_marshaller)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     SpiceMsgMigrate migrate;
 
-    red_channel_init_send_data(&display_channel->common.base, SPICE_MSG_MIGRATE, NULL);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE, NULL);
     migrate.flags = SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER;
     spice_marshall_msg_migrate(base_marshaller, &migrate);
     display_channel->expect_migrate_mark = TRUE;
 }
 
-static void display_channel_marshall_migrate_data(DisplayChannel *display_channel,
-                                                  SpiceMarshaller *base_marshaller)
+static void display_channel_marshall_migrate_data(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     DisplayChannelMigrateData display_data;
 
-    red_channel_init_send_data(&display_channel->common.base, SPICE_MSG_MIGRATE_DATA, NULL);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, NULL);
 
     ASSERT(display_channel->pixmap_cache);
     display_data.magic = DISPLAY_MIGRATE_DATA_MAGIC;
     ASSERT(MAX_CACHE_CLIENTS == 4); //MIGRATE_DATA_VERSION dependent
     display_data.version = DISPLAY_MIGRATE_DATA_VERSION;
 
-    display_data.message_serial = red_channel_get_message_serial((RedChannel *)display_channel);
+    display_data.message_serial = red_channel_client_get_message_serial(rcc);
 
     display_data.pixmap_cache_freezer = pixmap_cache_freeze(display_channel->pixmap_cache);
     display_data.pixmap_cache_id = display_channel->pixmap_cache->id;
@@ -7480,13 +7472,13 @@ static void display_channel_marshall_migrate_data(DisplayChannel *display_channe
                              (uint8_t *)&display_data, sizeof(display_data));
 }
 
-static void display_channel_marshall_pixmap_sync(DisplayChannel *display_channel,
-                                                 SpiceMarshaller *base_marshaller)
+static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     SpiceMsgWaitForChannels wait;
     PixmapCache *pixmap_cache;
 
-    red_channel_init_send_data(&display_channel->common.base, SPICE_MSG_WAIT_FOR_CHANNELS, NULL);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_WAIT_FOR_CHANNELS, NULL);
     pixmap_cache = display_channel->pixmap_cache;
 
     pthread_mutex_lock(&pixmap_cache->lock);
@@ -7503,21 +7495,21 @@ static void display_channel_marshall_pixmap_sync(DisplayChannel *display_channel
     spice_marshall_msg_wait_for_channels(base_marshaller, &wait);
 }
 
-static void display_channel_marshall_reset_cache(DisplayChannel *display_channel,
-                                                 SpiceMarshaller *base_marshaller)
+static void display_channel_marshall_reset_cache(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
 {
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     SpiceMsgWaitForChannels wait;
 
-    red_channel_init_send_data(&display_channel->common.base, SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS, NULL);
-    pixmap_cache_reset(display_channel->pixmap_cache, display_channel, &wait);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS, NULL);
+    pixmap_cache_reset(display_channel->pixmap_cache, rcc, &wait);
 
     spice_marshall_msg_display_inval_all_pixmaps(base_marshaller,
                                                  &wait);
 }
 
-static void red_marshall_image(DisplayChannel *display_channel, SpiceMarshaller *m, ImageItem *item)
+static void red_marshall_image( RedChannelClient *rcc, SpiceMarshaller *m, ImageItem *item)
 {
-    RedChannel *channel;
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     SpiceImage red_image;
     RedWorker *worker;
     SpiceBitmap bitmap;
@@ -7531,8 +7523,7 @@ static void red_marshall_image(DisplayChannel *display_channel, SpiceMarshaller
     SpiceMarshaller *src_bitmap_out, *mask_bitmap_out;
     SpiceMarshaller *bitmap_palette_out, *lzplt_palette_out;
 
-    ASSERT(display_channel && item);
-    channel = &display_channel->common.base;
+    ASSERT(rcc && display_channel && item);
     worker = display_channel->common.worker;
 
     QXL_SET_IMAGE_ID(&red_image, QXL_IMAGE_GROUP_RED, ++worker->bits_unique);
@@ -7555,7 +7546,7 @@ static void red_marshall_image(DisplayChannel *display_channel, SpiceMarshaller
     chunks = spice_chunks_new_linear(item->data, bitmap.stride * bitmap.y);
     bitmap.data = chunks;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_COPY, &item->link);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->link);
 
     copy.base.surface_id = item->surface_id;
     copy.base.box.left = item->pos.x;
@@ -7649,17 +7640,15 @@ static void red_marshall_image(DisplayChannel *display_channel, SpiceMarshaller
     spice_chunks_destroy(chunks);
 }
 
-static void red_display_marshall_upgrade(DisplayChannel *display_channel, SpiceMarshaller *m, UpgradeItem *item)
+static void red_display_marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller *m, UpgradeItem *item)
 {
-    RedChannel *channel;
     RedDrawable *red_drawable;
     SpiceMsgDisplayDrawCopy copy;
     SpiceMarshaller *src_bitmap_out, *mask_bitmap_out;
 
-    ASSERT(display_channel && item && item->drawable);
-    channel = &display_channel->common.base;
+    ASSERT(rcc && rcc->channel && item && item->drawable);
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_DRAW_COPY, &item->base);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->base);
 
     red_drawable = item->drawable->red_drawable;
     ASSERT(red_drawable->type == QXL_DRAW_COPY);
@@ -7675,18 +7664,18 @@ static void red_display_marshall_upgrade(DisplayChannel *display_channel, SpiceM
     spice_marshall_msg_display_draw_copy(m, &copy,
                                          &src_bitmap_out, &mask_bitmap_out);
 
-    fill_bits(display_channel, src_bitmap_out, copy.data.src_bitmap, item->drawable, FALSE);
+    fill_bits(rcc, src_bitmap_out, copy.data.src_bitmap, item->drawable, FALSE);
 }
 
-static void red_display_marshall_stream_start(DisplayChannel *display_channel,
+static void red_display_marshall_stream_start(RedChannelClient *rcc,
                      SpiceMarshaller *base_marshaller, StreamAgent *agent)
 {
-    RedChannel *channel = &display_channel->common.base;
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     Stream *stream = agent->stream;
 
     agent->last_send_time = 0;
     ASSERT(stream);
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_STREAM_CREATE,
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CREATE,
         stream->current ? &stream->current->pipe_item : NULL);
     SpiceMsgDisplayStreamCreate stream_create;
     SpiceClipRects clip_rects;
@@ -7714,18 +7703,17 @@ static void red_display_marshall_stream_start(DisplayChannel *display_channel,
     spice_marshall_msg_display_stream_create(base_marshaller, &stream_create);
 }
 
-static void red_display_marshall_stream_clip(DisplayChannel *display_channel,
+static void red_display_marshall_stream_clip(RedChannelClient *rcc,
                                          SpiceMarshaller *base_marshaller,
                                          StreamClipItem *item)
 {
-    RedChannel *channel = &display_channel->common.base;
-
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     StreamAgent *agent = item->stream_agent;
     Stream *stream = agent->stream;
 
     ASSERT(stream);
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_STREAM_CLIP, &item->base);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CLIP, &item->base);
     SpiceMsgDisplayStreamClip stream_clip;
 
     stream_clip.id = agent - display_channel->stream_agents;
@@ -7735,67 +7723,66 @@ static void red_display_marshall_stream_clip(DisplayChannel *display_channel,
     spice_marshall_msg_display_stream_clip(base_marshaller, &stream_clip);
 }
 
-static void red_display_marshall_stream_end(DisplayChannel *display_channel,
+static void red_display_marshall_stream_end(RedChannelClient *rcc,
                    SpiceMarshaller *base_marshaller, StreamAgent* agent)
 {
-    RedChannel *channel = &display_channel->common.base;
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     SpiceMsgDisplayStreamDestroy destroy;
 
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL);
     destroy.id = agent - display_channel->stream_agents;
 
     spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy);
 }
 
-static void red_cursor_marshall_inval(CursorChannel *channel,
+static void red_cursor_marshall_inval(RedChannelClient *rcc,
                 SpiceMarshaller *m, CacheItem *cach_item)
 {
-    ASSERT(channel);
-    red_marshall_inval((RedChannel *)channel, m, cach_item);
+    ASSERT(rcc);
+    red_marshall_inval(rcc, m, cach_item);
 }
 
-static void red_marshall_cursor_init(CursorChannel *channel, SpiceMarshaller *base_marshaller)
+static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
 {
+    CursorChannel *cursor_channel;
     RedWorker *worker;
     SpiceMsgCursorInit msg;
     AddBufInfo info;
 
-    ASSERT(channel);
-
-    worker = channel->common.worker;
+    ASSERT(rcc);
+    cursor_channel = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base);
+    worker = cursor_channel->common.worker;
 
-    red_channel_init_send_data(&channel->common.base, SPICE_MSG_CURSOR_INIT, &worker->cursor->pipe_data);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_INIT, &worker->cursor->pipe_data);
     msg.visible = worker->cursor_visible;
     msg.position = worker->cursor_position;
     msg.trail_length = worker->cursor_trail_length;
     msg.trail_frequency = worker->cursor_trail_frequency;
 
-    fill_cursor(channel, &msg.cursor, worker->cursor, &info);
+    fill_cursor(cursor_channel, &msg.cursor, worker->cursor, &info);
     spice_marshall_msg_cursor_init(base_marshaller, &msg);
     add_buf_from_info(base_marshaller, &info);
 }
 
-static void cursor_channel_marshall_migrate(CursorChannel *cursor_channel,
-                                            SpiceMarshaller *base_marshaller)
+static void cursor_channel_marshall_migrate(RedChannelClient *rcc, SpiceMarshaller *base_marshaller)
 {
     SpiceMsgMigrate migrate;
 
-    red_channel_init_send_data(&cursor_channel->common.base, SPICE_MSG_MIGRATE, NULL);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE, NULL);
     migrate.flags = 0;
 
     spice_marshall_msg_migrate(base_marshaller, &migrate);
 }
 
-static void red_marshall_cursor(CursorChannel *cursor_channel,
+static void red_marshall_cursor(RedChannelClient *rcc,
                    SpiceMarshaller *m, CursorItem *cursor)
 {
-    RedChannel *channel;
+    CursorChannel *cursor_channel = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base);
     RedCursorCmd *cmd;
     RedWorker *worker;
 
     ASSERT(cursor_channel);
 
-    channel = &cursor_channel->common.base;
     worker = cursor_channel->common.worker;
 
     cmd = cursor->red_cursor;
@@ -7803,7 +7790,7 @@ static void red_marshall_cursor(CursorChannel *cursor_channel,
     case QXL_CURSOR_MOVE:
         {
             SpiceMsgCursorMove cursor_move;
-            red_channel_init_send_data(channel, SPICE_MSG_CURSOR_MOVE, &cursor->pipe_data);
+            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_MOVE, &cursor->pipe_data);
             cursor_move.position = cmd->u.position;
             spice_marshall_msg_cursor_move(m, &cursor_move);
             break;
@@ -7813,7 +7800,7 @@ static void red_marshall_cursor(CursorChannel *cursor_channel,
             SpiceMsgCursorSet cursor_set;
             AddBufInfo info;
 
-            red_channel_init_send_data(channel, SPICE_MSG_CURSOR_SET, &cursor->pipe_data);
+            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_SET, &cursor->pipe_data);
             cursor_set.position = cmd->u.set.position;
             cursor_set.visible = worker->cursor_visible;
 
@@ -7823,13 +7810,13 @@ static void red_marshall_cursor(CursorChannel *cursor_channel,
             break;
         }
     case QXL_CURSOR_HIDE:
-        red_channel_init_send_data(channel, SPICE_MSG_CURSOR_HIDE, &cursor->pipe_data);
+        red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_HIDE, &cursor->pipe_data);
         break;
     case QXL_CURSOR_TRAIL:
         {
             SpiceMsgCursorTrail cursor_trail;
 
-            red_channel_init_send_data(channel, SPICE_MSG_CURSOR_TRAIL, &cursor->pipe_data);
+            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_TRAIL, &cursor->pipe_data);
             cursor_trail.length = cmd->u.trail.length;
             cursor_trail.frequency = cmd->u.trail.frequency;
             spice_marshall_msg_cursor_trail(m, &cursor_trail);
@@ -7838,106 +7825,103 @@ static void red_marshall_cursor(CursorChannel *cursor_channel,
     default:
         red_error("bad cursor command %d", cmd->type);
     }
-
     red_release_cursor(worker, cursor);
 }
 
-static void red_marshall_surface_create(DisplayChannel *display,
+static void red_marshall_surface_create(RedChannelClient *rcc,
     SpiceMarshaller *base_marshaller, SpiceMsgSurfaceCreate *surface_create)
 {
-    RedChannel *channel;
-
-    ASSERT(display);
-    channel = &display->common.base;
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
 
-    region_init(&display->surface_client_lossy_region[surface_create->surface_id]);
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_SURFACE_CREATE, NULL);
+    ASSERT(display_channel);
+    region_init(&display_channel->surface_client_lossy_region[surface_create->surface_id]);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE, NULL);
 
     spice_marshall_msg_display_surface_create(base_marshaller, surface_create);
 }
 
-static void red_marshall_surface_destroy(DisplayChannel *display,
+static void red_marshall_surface_destroy(RedChannelClient *rcc,
        SpiceMarshaller *base_marshaller, uint32_t surface_id)
 {
-    RedChannel *channel;
+    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
     SpiceMsgSurfaceDestroy surface_destroy;
 
-    ASSERT(display);
-    channel = &display->common.base;
+    ASSERT(display_channel);
 
-    region_destroy(&display->surface_client_lossy_region[surface_id]);
-    red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_SURFACE_DESTROY, NULL);
+    region_destroy(&display_channel->surface_client_lossy_region[surface_id]);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_DESTROY, NULL);
 
     surface_destroy.surface_id = surface_id;
 
     spice_marshall_msg_display_surface_destroy(base_marshaller, &surface_destroy);
 }
 
-static void display_channel_send_item(RedChannel *base, PipeItem *pipe_item)
+static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
 {
-    SpiceMarshaller *m = red_channel_get_marshaller(base);
-    DisplayChannel *display_channel = (DisplayChannel *)red_ref_channel(base);
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
+    DisplayChannel *display_channel = (DisplayChannel *)red_ref_channel(rcc->channel);
 
     red_display_reset_send_data(display_channel);
     switch (pipe_item->type) {
     case PIPE_ITEM_TYPE_DRAW: {
         Drawable *drawable = SPICE_CONTAINEROF(pipe_item, Drawable, pipe_item);
-        marshall_qxl_drawable(display_channel, m, drawable);
+        marshall_qxl_drawable(rcc, m, drawable);
         break;
     }
     case PIPE_ITEM_TYPE_INVAL_ONE:
-        red_display_marshall_inval(display_channel, m, (CacheItem *)pipe_item);
+        red_marshall_inval(rcc, m, (CacheItem *)pipe_item);
         break;
     case PIPE_ITEM_TYPE_STREAM_CREATE: {
         StreamAgent *agent = SPICE_CONTAINEROF(pipe_item, StreamAgent, create_item);
-        red_display_marshall_stream_start(display_channel, m, agent);
+        red_display_marshall_stream_start(rcc, m, agent);
         break;
     }
     case PIPE_ITEM_TYPE_STREAM_CLIP: {
-        red_display_marshall_stream_clip(display_channel, m, (StreamClipItem *)pipe_item);
+        StreamClipItem* clip_item = (StreamClipItem *)pipe_item;
+        red_display_marshall_stream_clip(rcc, m, clip_item);
         break;
     }
     case PIPE_ITEM_TYPE_STREAM_DESTROY: {
         StreamAgent *agent = SPICE_CONTAINEROF(pipe_item, StreamAgent, destroy_item);
-        red_display_marshall_stream_end(display_channel, m, agent);
+        red_display_marshall_stream_end(rcc, m, agent);
         break;
     }
     case PIPE_ITEM_TYPE_UPGRADE:
-        red_display_marshall_upgrade(display_channel, m, (UpgradeItem *)pipe_item);
+        red_display_marshall_upgrade(rcc, m, (UpgradeItem *)pipe_item);
         break;
     case PIPE_ITEM_TYPE_VERB:
-        display_marshall_verb(display_channel, ((VerbItem*)pipe_item)->verb);
+        red_marshall_verb(rcc, ((VerbItem*)pipe_item)->verb);
         break;
     case PIPE_ITEM_TYPE_MIGRATE:
         red_printf("PIPE_ITEM_TYPE_MIGRATE");
-        display_channel_marshall_migrate(display_channel, m);
+        display_channel_marshall_migrate(rcc, m);
         break;
     case PIPE_ITEM_TYPE_MIGRATE_DATA:
-        display_channel_marshall_migrate_data(display_channel, m);
+        display_channel_marshall_migrate_data(rcc, m);
         break;
     case PIPE_ITEM_TYPE_IMAGE:
-        red_marshall_image(display_channel, m, (ImageItem *)pipe_item);
+        red_marshall_image(rcc, m, (ImageItem *)pipe_item);
         break;
     case PIPE_ITEM_TYPE_PIXMAP_SYNC:
-        display_channel_marshall_pixmap_sync(display_channel, m);
+        display_channel_marshall_pixmap_sync(rcc, m);
         break;
     case PIPE_ITEM_TYPE_PIXMAP_RESET:
-        display_channel_marshall_reset_cache(display_channel, m);
+        display_channel_marshall_reset_cache(rcc, m);
         break;
     case PIPE_ITEM_TYPE_INVAL_PALLET_CACHE:
         red_reset_palette_cache(display_channel);
-        red_marshall_verb((RedChannel *)display_channel, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
+        red_marshall_verb(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
         break;
     case PIPE_ITEM_TYPE_CREATE_SURFACE: {
         SurfaceCreateItem *surface_create = SPICE_CONTAINEROF(pipe_item, SurfaceCreateItem,
                                                               pipe_item);
-        red_marshall_surface_create(display_channel, m, &surface_create->surface_create);
+        red_marshall_surface_create(rcc, m, &surface_create->surface_create);
         break;
     }
     case PIPE_ITEM_TYPE_DESTROY_SURFACE: {
         SurfaceDestroyItem *surface_destroy = SPICE_CONTAINEROF(pipe_item, SurfaceDestroyItem,
                                                                 pipe_item);
-        red_marshall_surface_destroy(display_channel, m, surface_destroy->surface_destroy.surface_id);
+        red_marshall_surface_destroy(rcc, m, surface_destroy->surface_destroy.surface_id);
         break;
     }
     default:
@@ -7947,47 +7931,47 @@ static void display_channel_send_item(RedChannel *base, PipeItem *pipe_item)
     display_channel_release_item_before_push(display_channel, pipe_item);
 
     // a message is pending
-    if (red_channel_send_message_pending(&display_channel->common.base)) {
-        display_begin_send_message(display_channel, m);
+    if (red_channel_client_send_message_pending(rcc)) {
+        display_begin_send_message(rcc, m);
     }
     red_unref_channel(&display_channel->common.base);