[Spice-devel] [PATCH 06/14] server: move some cursor code to cursor-channel.c

Frediano Ziglio fziglio at redhat.com
Fri Oct 23 05:13:30 PDT 2015


From: Marc-André Lureau <marcandre.lureau at gmail.com>

Also fix warning due to unexpected pipe item type

The specific item type that was not being handled was
PIPE_ITEM_TYPE_INVAL_ONE (#102). This item type is used by the cursor
channel, but the analogous item for the display channel is
PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE. Use this value instead.

The exact warning follows:

    (/usr/bin/qemu-kvm:24458): Spice-Warning **:
    ../../server/dcc-send.c:2442:dcc_send_item: should not be reached
    (/usr/bin/qemu-kvm:24458): Spice-CRITICAL **:
    ../../server/dcc.c:1595:release_item_before_push: invalid item type

Author:    Marc-André Lureau <marcandre.lureau at gmail.com>
Date:      Thu Feb 27 19:38:58 2014 +0200
---
 server/Makefile.am       |   3 +-
 server/cache_item.tmpl.c |   4 +-
 server/cursor-channel.c  | 489 ++++++++++++++++++++++++++++++
 server/cursor-channel.h  | 103 +++++++
 server/cursor_channel.h  |  62 ----
 server/red_dispatcher.c  |   1 +
 server/red_worker.c      | 751 ++++++-----------------------------------------
 server/red_worker.h      |  71 +++++
 8 files changed, 759 insertions(+), 725 deletions(-)
 create mode 100644 server/cursor-channel.c
 create mode 100644 server/cursor-channel.h
 delete mode 100644 server/cursor_channel.h

diff --git a/server/Makefile.am b/server/Makefile.am
index 013188b..43bbf6f 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -108,7 +108,8 @@ libspice_server_la_SOURCES =			\
 	red_worker.c				\
 	red_worker.h				\
 	display-channel.h			\
-	cursor_channel.h			\
+	cursor-channel.c			\
+	cursor-channel.h			\
 	reds.c					\
 	reds.h					\
 	reds-private.h				\
diff --git a/server/cache_item.tmpl.c b/server/cache_item.tmpl.c
index dc314c0..ad2b579 100644
--- a/server/cache_item.tmpl.c
+++ b/server/cache_item.tmpl.c
@@ -21,6 +21,7 @@
 #define CACHE_HASH_KEY CURSOR_CACHE_HASH_KEY
 #define CACHE_HASH_SIZE CURSOR_CACHE_HASH_SIZE
 #define CACHE_INVAL_TYPE SPICE_MSG_CURSOR_INVAL_ONE
+#define PIPE_ITEM_TYPE PIPE_ITEM_TYPE_INVAL_ONE
 #define FUNC_NAME(name) red_cursor_cache_##name
 #define VAR_NAME(name) cursor_cache_##name
 #define CHANNEL CursorChannel
@@ -32,6 +33,7 @@
 #define CACHE_HASH_KEY PALETTE_CACHE_HASH_KEY
 #define CACHE_HASH_SIZE PALETTE_CACHE_HASH_SIZE
 #define CACHE_INVAL_TYPE SPICE_MSG_DISPLAY_INVAL_PALETTE
+#define PIPE_ITEM_TYPE PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE
 #define FUNC_NAME(name) red_palette_cache_##name
 #define VAR_NAME(name) palette_cache_##name
 #define CHANNEL DisplayChannel
@@ -78,7 +80,7 @@ static void FUNC_NAME(remove)(CHANNELCLIENT *channel_client, CacheItem *item)
     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_pipe_item_init(&channel->common.base, &item->u.pipe_data, PIPE_ITEM_TYPE);
     red_channel_client_pipe_add_tail(&channel_client->common.base, &item->u.pipe_data); // for now
 }
 
diff --git a/server/cursor-channel.c b/server/cursor-channel.c
new file mode 100644
index 0000000..adf7d78
--- /dev/null
+++ b/server/cursor-channel.c
@@ -0,0 +1,489 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include <glib.h>
+#include "common/generated_server_marshallers.h"
+#include "cursor-channel.h"
+
+#define RCC_TO_CCC(rcc) SPICE_CONTAINEROF((rcc), CursorChannelClient, common.base)
+
+#define CLIENT_CURSOR_CACHE
+#include "cache_item.tmpl.c"
+#undef CLIENT_CURSOR_CACHE
+
+CursorItem *cursor_item_new(QXLInstance *qxl, RedCursorCmd *cmd, uint32_t group_id)
+{
+    CursorItem *cursor_item;
+
+    spice_return_val_if_fail(cmd != NULL, NULL);
+
+    cursor_item = g_slice_new0(CursorItem);
+    cursor_item->qxl = qxl;
+    cursor_item->refs = 1;
+    cursor_item->group_id = group_id;
+    cursor_item->red_cursor = cmd;
+
+    return cursor_item;
+}
+
+CursorItem *cursor_item_ref(CursorItem *item)
+{
+    spice_return_val_if_fail(item != NULL, NULL);
+    spice_return_val_if_fail(item->refs > 0, NULL);
+
+    item->refs++;
+
+    return item;
+}
+
+void cursor_item_unref(CursorItem *item)
+{
+    QXLReleaseInfoExt release_info_ext;
+    RedCursorCmd *cursor_cmd;
+
+    spice_return_if_fail(item != NULL);
+
+    if (--item->refs)
+        return;
+
+    cursor_cmd = item->red_cursor;
+    release_info_ext.group_id = item->group_id;
+    release_info_ext.info = cursor_cmd->release_info;
+    item->qxl->st->qif->release_resource(item->qxl, release_info_ext);
+    red_put_cursor_cmd(cursor_cmd);
+    free(cursor_cmd);
+
+    g_slice_free(CursorItem, item);
+
+}
+
+static void cursor_set_item(CursorChannel *cursor, CursorItem *item)
+{
+    if (cursor->item)
+        cursor_item_unref(cursor->item);
+
+    cursor->item = item ? cursor_item_ref(item) : NULL;
+}
+
+#ifdef DEBUG_CURSORS
+static int _cursor_count = 0;
+#endif
+
+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;
+}
+
+typedef struct {
+    void *data;
+    uint32_t size;
+} AddBufInfo;
+
+static void add_buf_from_info(SpiceMarshaller *m, AddBufInfo *info)
+{
+    if (info->data) {
+        spice_marshaller_add_ref(m, info->data, info->size);
+    }
+}
+
+static void cursor_fill(CursorChannelClient *ccc, SpiceCursor *red_cursor,
+                        CursorItem *cursor, AddBufInfo *addbuf)
+{
+    RedCursorCmd *cursor_cmd;
+    addbuf->data = NULL;
+
+    if (!cursor) {
+        red_cursor->flags = SPICE_CURSOR_FLAGS_NONE;
+        return;
+    }
+
+    cursor_cmd = cursor->red_cursor;
+    *red_cursor = cursor_cmd->u.set.shape;
+
+    if (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(ccc, red_cursor->header.unique, 1)) {
+            red_cursor->flags |= SPICE_CURSOR_FLAGS_CACHE_ME;
+        }
+    }
+
+    if (red_cursor->data_size) {
+        addbuf->data = red_cursor->data;
+        addbuf->size = red_cursor->data_size;
+    }
+}
+
+
+static void red_reset_cursor_cache(RedChannelClient *rcc)
+{
+    red_cursor_cache_reset(RCC_TO_CCC(rcc), CLIENT_CURSOR_CACHE_SIZE);
+}
+
+void cursor_channel_disconnect(CursorChannel *cursor)
+{
+    RedChannel *channel = (RedChannel *)cursor;
+
+    if (!channel || !red_channel_is_connected(channel)) {
+        return;
+    }
+
+    /* TODO: use a class vdispose */
+    red_channel_apply_clients(channel, red_reset_cursor_cache);
+    red_channel_disconnect(channel);
+}
+
+
+static void put_cursor_pipe_item(CursorChannelClient *ccc, CursorPipeItem *pipe_item)
+{
+    spice_return_if_fail(pipe_item);
+    spice_return_if_fail(pipe_item->refs > 0);
+
+    if (--pipe_item->refs) {
+        return;
+    }
+
+    spice_assert(!pipe_item_is_linked(&pipe_item->base));
+
+    cursor_item_unref(pipe_item->cursor_item);
+    free(pipe_item);
+}
+
+static void cursor_channel_client_on_disconnect(RedChannelClient *rcc)
+{
+    if (!rcc) {
+        return;
+    }
+    red_reset_cursor_cache(rcc);
+}
+
+// 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: {
+        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_CURSOR_INIT:
+    case PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE:
+        free(item);
+        break;
+    default:
+        spice_error("invalid pipe item type");
+    }
+}
+
+static void cursor_channel_client_release_item_after_push(CursorChannelClient *ccc,
+                                                          PipeItem *item)
+{
+    switch (item->type) {
+        case PIPE_ITEM_TYPE_CURSOR: {
+            CursorPipeItem *cursor_pipe_item = SPICE_CONTAINEROF(item, CursorPipeItem, base);
+            put_cursor_pipe_item(ccc, cursor_pipe_item);
+            break;
+        }
+        default:
+            spice_critical("invalid item type");
+    }
+}
+
+static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *base_marshaller,
+                                     PipeItem *pipe_item)
+{
+    CursorChannel *cursor;
+    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
+    SpiceMsgCursorInit msg;
+    AddBufInfo info;
+
+    spice_assert(rcc);
+    cursor = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base);
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_INIT, NULL);
+    msg.visible = cursor->cursor_visible;
+    msg.position = cursor->cursor_position;
+    msg.trail_length = cursor->cursor_trail_length;
+    msg.trail_frequency = cursor->cursor_trail_frequency;
+
+    cursor_fill(ccc, &msg.cursor, cursor->item, &info);
+    spice_marshall_msg_cursor_init(base_marshaller, &msg);
+    add_buf_from_info(base_marshaller, &info);
+}
+
+static void cursor_marshall(RedChannelClient *rcc,
+                            SpiceMarshaller *m, CursorPipeItem *cursor_pipe_item)
+{
+    CursorChannel *cursor = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base);
+    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
+    CursorItem *item = cursor_pipe_item->cursor_item;
+    PipeItem *pipe_item = &cursor_pipe_item->base;
+    RedCursorCmd *cmd;
+
+    spice_return_if_fail(cursor);
+
+    cmd = item->red_cursor;
+    switch (cmd->type) {
+    case QXL_CURSOR_MOVE:
+        {
+            SpiceMsgCursorMove cursor_move;
+            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;
+        }
+    case QXL_CURSOR_SET:
+        {
+            SpiceMsgCursorSet cursor_set;
+            AddBufInfo info;
+
+            red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_SET, pipe_item);
+            cursor_set.position = cmd->u.set.position;
+            cursor_set.visible = cursor->cursor_visible;
+
+            cursor_fill(ccc, &cursor_set.cursor, item, &info);
+            spice_marshall_msg_cursor_set(m, &cursor_set);
+            add_buf_from_info(m, &info);
+            break;
+        }
+    case QXL_CURSOR_HIDE:
+        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, pipe_item);
+            cursor_trail.length = cmd->u.trail.length;
+            cursor_trail.frequency = cmd->u.trail.frequency;
+            spice_marshall_msg_cursor_trail(m, &cursor_trail);
+        }
+        break;
+    default:
+        spice_error("bad cursor command %d", cmd->type);
+    }
+}
+
+static inline void red_marshall_inval(RedChannelClient *rcc,
+                                      SpiceMarshaller *base_marshaller, CacheItem *cach_item)
+{
+    SpiceMsgDisplayInvalOne inval_one;
+
+    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 cursor_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
+{
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
+    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
+
+    switch (pipe_item->type) {
+    case PIPE_ITEM_TYPE_CURSOR:
+        cursor_marshall(rcc, m, SPICE_CONTAINEROF(pipe_item, CursorPipeItem, base));
+        break;
+    case PIPE_ITEM_TYPE_INVAL_ONE:
+        red_marshall_inval(rcc, m, (CacheItem *)pipe_item);
+        break;
+    case PIPE_ITEM_TYPE_VERB:
+        red_marshall_verb(rcc, (VerbItem*)pipe_item);
+        break;
+    case PIPE_ITEM_TYPE_CURSOR_INIT:
+        red_reset_cursor_cache(rcc);
+        red_marshall_cursor_init(rcc, m, pipe_item);
+        break;
+    case PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE:
+        red_reset_cursor_cache(rcc);
+        red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_INVAL_ALL, NULL);
+        break;
+    default:
+        spice_error("invalid pipe item type");
+    }
+
+    cursor_channel_client_release_item_before_push(ccc, pipe_item);
+    red_channel_client_begin_send_message(rcc);
+}
+
+static CursorPipeItem *cursor_pipe_item_ref(CursorPipeItem *item)
+{
+    spice_return_val_if_fail(item, NULL);
+    spice_return_val_if_fail(item->refs > 0, NULL);
+
+    item->refs++;
+    return item;
+}
+
+
+static void cursor_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
+{
+    CursorPipeItem *cursor_pipe_item;
+
+    spice_return_if_fail(item);
+
+    cursor_pipe_item = SPICE_CONTAINEROF(item, CursorPipeItem, base);
+    /* TODO: refcnt at PipeItem instead ? */
+    cursor_pipe_item_ref(cursor_pipe_item);
+}
+
+static void cursor_channel_release_item(RedChannelClient *rcc, PipeItem *item, int item_pushed)
+{
+    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
+
+    spice_assert(item);
+
+    if (item_pushed) {
+        cursor_channel_client_release_item_after_push(ccc, item);
+    } else {
+        spice_debug("not pushed (%d)", item->type);
+        cursor_channel_client_release_item_before_push(ccc, item);
+    }
+}
+
+CursorChannel* cursor_channel_new(RedWorker *worker)
+{
+    CursorChannel *cursor;
+    RedChannel *channel = NULL;
+    ChannelCbs cbs = {
+        .on_disconnect =  cursor_channel_client_on_disconnect,
+        .send_item = cursor_channel_send_item,
+        .hold_item = cursor_channel_hold_pipe_item,
+        .release_item = cursor_channel_release_item
+    };
+
+    spice_info("create cursor channel");
+    channel = red_worker_new_channel(worker, sizeof(CursorChannel),
+                                     SPICE_CHANNEL_CURSOR, 0,
+                                     &cbs, red_channel_client_handle_message);
+
+    cursor = (CursorChannel *)channel;
+    cursor->cursor_visible = TRUE;
+    cursor->mouse_mode = SPICE_MOUSE_MODE_SERVER;
+
+    return cursor;
+}
+
+CursorChannelClient* cursor_channel_client_new(CursorChannel *cursor, RedClient *client, RedsStream *stream,
+                                               int mig_target,
+                                               uint32_t *common_caps, int num_common_caps,
+                                               uint32_t *caps, int num_caps)
+{
+    spice_return_val_if_fail(cursor, NULL);
+    spice_return_val_if_fail(client, NULL);
+    spice_return_val_if_fail(stream, NULL);
+    spice_return_val_if_fail(!num_common_caps || common_caps, NULL);
+    spice_return_val_if_fail(!num_caps || caps, NULL);
+
+    CursorChannelClient *ccc =
+        (CursorChannelClient*)common_channel_new_client(&cursor->common,
+                                                        sizeof(CursorChannelClient),
+                                                        client, stream,
+                                                        mig_target,
+                                                        FALSE,
+                                                        common_caps,
+                                                        num_common_caps,
+                                                        caps,
+                                                        num_caps);
+    spice_return_val_if_fail(ccc != NULL, NULL);
+
+    ring_init(&ccc->cursor_cache_lru);
+    ccc->cursor_cache_available = CLIENT_CURSOR_CACHE_SIZE;
+
+
+    return ccc;
+}
+
+void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd *cursor_cmd,
+                                uint32_t group_id)
+{
+    CursorItem *cursor_item;
+    int cursor_show = FALSE;
+
+    spice_return_if_fail(cursor);
+    spice_return_if_fail(cursor_cmd);
+
+    cursor_item = cursor_item_new(red_worker_get_qxl(cursor->common.worker),
+                                  cursor_cmd, group_id);
+
+    switch (cursor_cmd->type) {
+    case QXL_CURSOR_SET:
+        cursor->cursor_visible = cursor_cmd->u.set.visible;
+        cursor_set_item(cursor, cursor_item);
+        break;
+    case QXL_CURSOR_MOVE:
+        cursor_show = !cursor->cursor_visible;
+        cursor->cursor_visible = TRUE;
+        cursor->cursor_position = cursor_cmd->u.position;
+        break;
+    case QXL_CURSOR_HIDE:
+        cursor->cursor_visible = FALSE;
+        break;
+    case QXL_CURSOR_TRAIL:
+        cursor->cursor_trail_length = cursor_cmd->u.trail.length;
+        cursor->cursor_trail_frequency = cursor_cmd->u.trail.frequency;
+        break;
+    default:
+        spice_warning("invalid cursor command %u", cursor_cmd->type);
+        return;
+    }
+
+    if (cursor->mouse_mode == SPICE_MOUSE_MODE_SERVER
+        || cursor_cmd->type != QXL_CURSOR_MOVE
+        || cursor_show) {
+        red_channel_pipes_new_add(&cursor->common.base,
+                                  new_cursor_pipe_item, cursor_item);
+    }
+
+    cursor_item_unref(cursor_item);
+}
+
+void cursor_channel_reset(CursorChannel *cursor)
+{
+    RedChannel *channel = (RedChannel *)cursor;
+
+    spice_return_if_fail(cursor);
+
+    cursor_set_item(cursor, NULL);
+    cursor->cursor_visible = TRUE;
+    cursor->cursor_position.x = cursor->cursor_position.y = 0;
+    cursor->cursor_trail_length = cursor->cursor_trail_frequency = 0;
+
+    if (red_channel_is_connected(channel)) {
+        red_channel_pipes_add_type(&cursor->common.base, PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
+        if (!cursor->common.during_target_migrate) {
+            red_pipes_add_verb(&cursor->common.base, SPICE_MSG_CURSOR_RESET);
+        }
+        if (!red_channel_wait_all_sent(&cursor->common.base,
+                                       DISPLAY_CLIENT_TIMEOUT)) {
+            red_channel_apply_clients(&cursor->common.base,
+                                      red_channel_client_disconnect_if_pending_send);
+        }
+    }
+}
diff --git a/server/cursor-channel.h b/server/cursor-channel.h
new file mode 100644
index 0000000..e1ef4b6
--- /dev/null
+++ b/server/cursor-channel.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CURSOR_CHANNEL_H_
+# define CURSOR_CHANNEL_H_
+
+#include "spice.h"
+#include "reds.h"
+#include "red_worker.h"
+#include "red_parse_qxl.h"
+#include "cache-item.h"
+#include "stat.h"
+
+#define CLIENT_CURSOR_CACHE_SIZE 256
+
+#define CURSOR_CACHE_HASH_SHIFT 8
+#define CURSOR_CACHE_HASH_SIZE (1 << CURSOR_CACHE_HASH_SHIFT)
+#define CURSOR_CACHE_HASH_MASK (CURSOR_CACHE_HASH_SIZE - 1)
+#define CURSOR_CACHE_HASH_KEY(id) ((id) & CURSOR_CACHE_HASH_MASK)
+
+enum {
+    PIPE_ITEM_TYPE_CURSOR = PIPE_ITEM_TYPE_COMMON_LAST,
+    PIPE_ITEM_TYPE_CURSOR_INIT,
+    PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE,
+};
+
+typedef struct CursorItem {
+    QXLInstance *qxl;
+    uint32_t group_id;
+    int refs;
+    RedCursorCmd *red_cursor;
+} CursorItem;
+
+typedef struct CursorPipeItem {
+    PipeItem base;
+    CursorItem *cursor_item;
+    int refs;
+} CursorPipeItem;
+
+typedef struct LocalCursor {
+    CursorItem base;
+    SpicePoint16 position;
+    uint32_t data_size;
+    SpiceCursor red_cursor;
+} LocalCursor;
+
+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
+
+    CursorItem *item;
+    int cursor_visible;
+    SpicePoint16 cursor_position;
+    uint16_t cursor_trail_length;
+    uint16_t cursor_trail_frequency;
+    uint32_t mouse_mode;
+
+#ifdef RED_STATISTICS
+    StatNodeRef stat;
+#endif
+} CursorChannel;
+
+G_STATIC_ASSERT(sizeof(CursorItem) <= QXL_CURSUR_DEVICE_DATA_SIZE);
+
+CursorChannel*       cursor_channel_new         (RedWorker *worker);
+void                 cursor_channel_disconnect  (CursorChannel *cursor);
+void                 cursor_channel_reset       (CursorChannel *cursor);
+void                 cursor_channel_process_cmd (CursorChannel *cursor, RedCursorCmd *cursor_cmd,
+                                                 uint32_t group_id);
+
+CursorChannelClient* cursor_channel_client_new  (CursorChannel *cursor,
+                                                 RedClient *client, RedsStream *stream,
+                                                 int mig_target,
+                                                 uint32_t *common_caps, int num_common_caps,
+                                                 uint32_t *caps, int num_caps);
+
+CursorItem*          cursor_item_new            (QXLInstance *qxl, RedCursorCmd *cmd, uint32_t group_id);
+CursorItem*          cursor_item_ref            (CursorItem *cursor);
+void                 cursor_item_unref          (CursorItem *cursor);
+
+#endif /* CURSOR_CHANNEL_H_ */
diff --git a/server/cursor_channel.h b/server/cursor_channel.h
deleted file mode 100644
index 0104988..0000000
--- a/server/cursor_channel.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef CURSOR_CHANNEL_H_
-# define CURSOR_CHANNEL_H_
-
-#include "red_worker.h"
-#include "stat.h"
-
-#define CLIENT_CURSOR_CACHE_SIZE 256
-
-#define CURSOR_CACHE_HASH_SHIFT 8
-#define CURSOR_CACHE_HASH_SIZE (1 << CURSOR_CACHE_HASH_SHIFT)
-#define CURSOR_CACHE_HASH_MASK (CURSOR_CACHE_HASH_SIZE - 1)
-#define CURSOR_CACHE_HASH_KEY(id) ((id) & CURSOR_CACHE_HASH_MASK)
-
-typedef struct CursorItem {
-    uint32_t group_id;
-    int refs;
-    RedCursorCmd *red_cursor;
-} CursorItem;
-
-typedef struct CursorPipeItem {
-    PipeItem base;
-    CursorItem *cursor_item;
-    int refs;
-} CursorPipeItem;
-
-typedef struct LocalCursor {
-    CursorItem base;
-    SpicePoint16 position;
-    uint32_t data_size;
-    SpiceCursor red_cursor;
-} LocalCursor;
-
-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;
-#endif
-} CursorChannel;
-
-typedef struct _CursorItem _CursorItem;
-
-struct _CursorItem {
-    union {
-        CursorItem cursor_item;
-        _CursorItem *next;
-    } u;
-};
-
-G_STATIC_ASSERT(sizeof(CursorItem) <= QXL_CURSUR_DEVICE_DATA_SIZE);
-
-
-#endif /* CURSOR_CHANNEL_H_ */
diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
index 5e9b5bf..ecfef48 100644
--- a/server/red_dispatcher.c
+++ b/server/red_dispatcher.c
@@ -160,6 +160,7 @@ static void red_dispatcher_set_cursor_peer(RedChannel *channel, RedClient *clien
     memcpy(payload.common_caps, common_caps, sizeof(uint32_t)*num_common_caps);
     memcpy(payload.caps, caps, sizeof(uint32_t)*num_caps);
 
+    /* TODO serialize it all, no dangling pointers */
     dispatcher_send_message(&dispatcher->dispatcher,
                             RED_WORKER_MESSAGE_CURSOR_CONNECT,
                             &payload);
diff --git a/server/red_worker.c b/server/red_worker.c
index 4b69a38..9741758 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -94,7 +94,7 @@
 #include "spice_image_cache.h"
 #include "pixmap-cache.h"
 #include "display-channel.h"
-#include "cursor_channel.h"
+#include "cursor-channel.h"
 
 //#define COMPRESS_STAT
 //#define DUMP_BITMAP
@@ -266,24 +266,15 @@ struct SpiceWatch {
 };
 
 enum {
-    BUF_TYPE_RAW = 1,
-};
-
-enum {
-    PIPE_ITEM_TYPE_DRAW = PIPE_ITEM_TYPE_CHANNEL_BASE,
-    PIPE_ITEM_TYPE_INVAL_ONE,
-    PIPE_ITEM_TYPE_CURSOR,
-    PIPE_ITEM_TYPE_CURSOR_INIT,
+    PIPE_ITEM_TYPE_DRAW = PIPE_ITEM_TYPE_COMMON_LAST,
     PIPE_ITEM_TYPE_IMAGE,
     PIPE_ITEM_TYPE_STREAM_CREATE,
     PIPE_ITEM_TYPE_STREAM_CLIP,
     PIPE_ITEM_TYPE_STREAM_DESTROY,
     PIPE_ITEM_TYPE_UPGRADE,
-    PIPE_ITEM_TYPE_VERB,
     PIPE_ITEM_TYPE_MIGRATE_DATA,
     PIPE_ITEM_TYPE_PIXMAP_SYNC,
     PIPE_ITEM_TYPE_PIXMAP_RESET,
-    PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE,
     PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE,
     PIPE_ITEM_TYPE_CREATE_SURFACE,
     PIPE_ITEM_TYPE_DESTROY_SURFACE,
@@ -291,11 +282,6 @@ enum {
     PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT,
 };
 
-typedef struct VerbItem {
-    PipeItem base;
-    uint16_t verb;
-} VerbItem;
-
 #define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
 
 typedef struct SurfaceCreateItem {
@@ -639,18 +625,9 @@ typedef struct RedWorker {
 
     uint32_t bits_unique;
 
-    CursorItem *cursor;
-    int cursor_visible;
-    SpicePoint16 cursor_position;
-    uint16_t cursor_trail_length;
-    uint16_t cursor_trail_frequency;
-
     _Drawable drawables[NUM_DRAWABLES];
     _Drawable *free_drawables;
 
-    _CursorItem cursor_items[NUM_CURSORS];
-    _CursorItem *free_cursor_items;
-
     RedMemSlotInfo mem_slots;
 
     ImageCache image_cache;
@@ -659,8 +636,6 @@ typedef struct RedWorker {
     spice_wan_compression_t jpeg_state;
     spice_wan_compression_t zlib_glz_state;
 
-    uint32_t mouse_mode;
-
     uint32_t streaming_video;
     Stream streams_buf[NUM_STREAMS];
     Stream *free_streams;
@@ -737,7 +712,6 @@ static void red_draw_drawable(RedWorker *worker, Drawable *item);
 static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id);
 static void red_update_area_till(RedWorker *worker, const SpiceRect *area, int surface_id,
                                  Drawable *last);
-static void red_release_cursor(RedWorker *worker, CursorItem *cursor);
 static inline void release_drawable(RedWorker *worker, Drawable *item);
 static void red_display_release_stream(RedWorker *worker, StreamAgent *agent);
 static inline void red_detach_stream(RedWorker *worker, Stream *stream, int detach_sized);
@@ -756,16 +730,11 @@ static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surf
 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 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_push_monitors_config(DisplayChannelClient *dcc);
 
 /*
@@ -774,21 +743,6 @@ static void red_push_monitors_config(DisplayChannelClient *dcc);
  *  given a channel, iterate over it's clients
  */
 
-/* a generic safe for loop macro  */
-#define SAFE_FOREACH(link, next, cond, ring, data, get_data)               \
-    for ((((link) = ((cond) ? ring_get_head(ring) : NULL)), \
-          ((next) = ((link) ? ring_next((ring), (link)) : NULL)),          \
-          ((data) = ((link)? (get_data) : NULL)));                         \
-         (link);                                                           \
-         (((link) = (next)),                                               \
-          ((next) = ((link) ? ring_next((ring), (link)) : NULL)),          \
-          ((data) = ((link)? (get_data) : NULL))))
-
-#define LINK_TO_RCC(ptr) SPICE_CONTAINEROF(ptr, RedChannelClient, channel_link)
-#define RCC_FOREACH_SAFE(link, next, rcc, channel) \
-    SAFE_FOREACH(link, next, channel,  &(channel)->clients, rcc, LINK_TO_RCC(link))
-
-
 #define LINK_TO_DCC(ptr) SPICE_CONTAINEROF(ptr, DisplayChannelClient,  \
                                       common.base.channel_link)
 #define DCC_FOREACH_SAFE(link, next, dcc, channel)                       \
@@ -824,7 +778,6 @@ static void red_push_monitors_config(DisplayChannelClient *dcc);
                                          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)
 
 
 
@@ -916,6 +869,13 @@ static void print_compress_stats(DisplayChannel *display_channel)
 
 #endif
 
+QXLInstance* red_worker_get_qxl(RedWorker *worker)
+{
+    spice_return_val_if_fail(worker != NULL, NULL);
+
+    return worker->qxl;
+}
+
 static MonitorsConfig *monitors_config_getref(MonitorsConfig *monitors_config)
 {
     monitors_config->refs++;
@@ -1071,28 +1031,9 @@ static void show_draw_item(RedWorker *worker, DrawItem *draw_item, const char *p
            draw_item->base.rgn.extents.y2);
 }
 
-static void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb)
-{
-    VerbItem *item = spice_new(VerbItem, 1);
-
-    red_channel_pipe_item_init(rcc->channel, &item->base, PIPE_ITEM_TYPE_VERB);
-    item->verb = verb;
-    red_channel_client_pipe_add(rcc, &item->base);
-}
-
 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;
-    RingItem *link, *next;
-
-    RCC_FOREACH_SAFE(link, next, rcc, channel) {
-        red_pipe_add_verb(rcc, verb);
-    }
-}
-
 static inline void red_handle_drawable_surfaces_client_synced(
                         DisplayChannelClient *dcc, Drawable *drawable)
 {
@@ -1325,10 +1266,6 @@ static void common_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32
     }
 }
 
-#define CLIENT_CURSOR_CACHE
-#include "cache_item.tmpl.c"
-#undef CLIENT_CURSOR_CACHE
-
 #define CLIENT_PALETTE_CACHE
 #include "cache_item.tmpl.c"
 #undef CLIENT_PALETTE_CACHE
@@ -1338,11 +1275,6 @@ static void red_reset_palette_cache(DisplayChannelClient *dcc)
     red_palette_cache_reset(dcc, CLIENT_PALETTE_CACHE_SIZE);
 }
 
-static void red_reset_cursor_cache(RedChannelClient *rcc)
-{
-    red_cursor_cache_reset(RCC_TO_CCC(rcc), CLIENT_CURSOR_CACHE_SIZE);
-}
-
 static inline Drawable *alloc_drawable(RedWorker *worker)
 {
     Drawable *drawable;
@@ -4241,153 +4173,6 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac
     validate_area(worker, area, surface_id);
 }
 
-static inline void free_cursor_item(RedWorker *worker, CursorItem *item);
-
-static void red_release_cursor(RedWorker *worker, CursorItem *cursor)
-{
-    if (!--cursor->refs) {
-        QXLReleaseInfoExt release_info_ext;
-        RedCursorCmd *cursor_cmd;
-
-        cursor_cmd = cursor->red_cursor;
-        release_info_ext.group_id = cursor->group_id;
-        release_info_ext.info = cursor_cmd->release_info;
-        worker->qxl->st->qif->release_resource(worker->qxl, release_info_ext);
-        free_cursor_item(worker, cursor);
-        red_put_cursor_cmd(cursor_cmd);
-        free(cursor_cmd);
-    }
-}
-
-static void red_set_cursor(RedWorker *worker, CursorItem *cursor)
-{
-    if (worker->cursor) {
-        red_release_cursor(worker, worker->cursor);
-    }
-    ++cursor->refs;
-    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;
-}
-
-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;
-    spice_assert(_cursor_count <= NUM_CURSORS);
-#endif
-}
-
-static void cursor_items_init(RedWorker *worker)
-{
-    int i;
-
-    worker->free_cursor_items = NULL;
-    for (i = 0; i < NUM_CURSORS; i++) {
-        free_cursor_item(worker, &worker->cursor_items[i].u.cursor_item);
-    }
-}
-
-static CursorItem *get_cursor_item(RedWorker *worker, RedCursorCmd *cmd, uint32_t group_id)
-{
-    CursorItem *cursor_item;
-
-    spice_warn_if(!(cursor_item = alloc_cursor_item(worker)));
-
-    cursor_item->refs = 1;
-    cursor_item->group_id = group_id;
-    cursor_item->red_cursor = cmd;
-
-    return cursor_item;
-}
-
-static CursorPipeItem *ref_cursor_pipe_item(CursorPipeItem *item)
-{
-    spice_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)
-{
-    spice_assert(pipe_item);
-
-    if (--pipe_item->refs) {
-        return;
-    }
-
-    spice_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;
-    int cursor_show = FALSE;
-
-    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, cursor_item);
-        break;
-    case QXL_CURSOR_MOVE:
-        cursor_show = !worker->cursor_visible;
-        worker->cursor_visible = TRUE;
-        worker->cursor_position = cursor_cmd->u.position;
-        break;
-    case QXL_CURSOR_HIDE:
-        worker->cursor_visible = FALSE;
-        break;
-    case QXL_CURSOR_TRAIL:
-        worker->cursor_trail_length = cursor_cmd->u.trail.length;
-        worker->cursor_trail_frequency = cursor_cmd->u.trail.frequency;
-        break;
-    default:
-        spice_error("invalid cursor command %u", cursor_cmd->type);
-    }
-
-    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, new_cursor_pipe_item,
-                                  (void*)cursor_item);
-    }
-    red_release_cursor(worker, cursor_item);
-}
-
 static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ring_is_empty)
 {
     QXLCommandExt ext_cmd;
@@ -4425,12 +4210,11 @@ static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ri
                 free(cursor);
                 break;
             }
-
-            qxl_process_cursor(worker, cursor, ext_cmd.group_id);
+            cursor_channel_process_cmd(worker->cursor_channel, cursor, ext_cmd.group_id);
             break;
         }
         default:
-            spice_error("bad command type");
+            spice_warning("bad command type");
         }
         n++;
     }
@@ -4690,12 +4474,6 @@ static void red_push_surface_image(DisplayChannelClient *dcc, int surface_id)
     red_channel_client_push(&dcc->common.base);
 }
 
-typedef struct {
-    uint32_t type;
-    void *data;
-    uint32_t size;
-} AddBufInfo;
-
 static void marshaller_add_compressed(SpiceMarshaller *m,
                                       RedCompressBuf *comp_buf, size_t size)
 {
@@ -4710,18 +4488,6 @@ static void marshaller_add_compressed(SpiceMarshaller *m,
     } while (max);
 }
 
-
-static void add_buf_from_info(SpiceMarshaller *m, AddBufInfo *info)
-{
-    if (info->data) {
-        switch (info->type) {
-        case BUF_TYPE_RAW:
-            spice_marshaller_add_ref(m, info->data, info->size);
-            break;
-        }
-    }
-}
-
 static void fill_base(SpiceMarshaller *base_marshaller, Drawable *drawable)
 {
     SpiceMsgDisplayBase base;
@@ -6413,37 +6179,6 @@ static void fill_attr(SpiceMarshaller *m, SpiceLineAttr *attr, uint32_t group_id
     }
 }
 
-static void fill_cursor(CursorChannelClient *ccc, SpiceCursor *red_cursor,
-                        CursorItem *cursor, AddBufInfo *addbuf)
-{
-    RedCursorCmd *cursor_cmd;
-    addbuf->data = NULL;
-
-    if (!cursor) {
-        red_cursor->flags = SPICE_CURSOR_FLAGS_NONE;
-        return;
-    }
-
-    cursor_cmd = cursor->red_cursor;
-    *red_cursor = cursor_cmd->u.set.shape;
-
-    if (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(ccc, red_cursor->header.unique, 1)) {
-            red_cursor->flags |= SPICE_CURSOR_FLAGS_CACHE_ME;
-        }
-    }
-
-    if (red_cursor->data_size) {
-        addbuf->type = BUF_TYPE_RAW;
-        addbuf->data = red_cursor->data;
-        addbuf->size = red_cursor->data_size;
-    }
-}
-
 static inline void red_display_reset_send_data(DisplayChannelClient *dcc)
 {
     red_display_reset_compress_buf(dcc);
@@ -8199,23 +7934,6 @@ static inline void marshall_qxl_drawable(RedChannelClient *rcc,
         red_lossy_marshall_qxl_drawable(display_channel->common.worker, rcc, m, dpi);
 }
 
-static inline void red_marshall_verb(RedChannelClient *rcc, uint16_t verb)
-{
-    spice_assert(rcc);
-    red_channel_client_init_send_data(rcc, verb, NULL);
-}
-
-static inline void red_marshall_inval(RedChannelClient *rcc,
-        SpiceMarshaller *base_marshaller, CacheItem *cach_item)
-{
-    SpiceMsgDisplayInvalOne inval_one;
-
-    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 display_channel_marshall_migrate_data_surfaces(DisplayChannelClient *dcc,
                                                            SpiceMarshaller *m,
                                                            int lossy)
@@ -8591,92 +8309,7 @@ static void red_display_marshall_stream_end(RedChannelClient *rcc,
     spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy);
 }
 
-static void red_cursor_marshall_inval(RedChannelClient *rcc,
-                SpiceMarshaller *m, CacheItem *cach_item)
-{
-    spice_assert(rcc);
-    red_marshall_inval(rcc, m, cach_item);
-}
-
-static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *base_marshaller,
-                                     PipeItem *pipe_item)
-{
-    CursorChannel *cursor_channel;
-    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
-    RedWorker *worker;
-    SpiceMsgCursorInit msg;
-    AddBufInfo info;
-
-    spice_assert(rcc);
-    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, NULL);
-    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(ccc, &msg.cursor, worker->cursor, &info);
-    spice_marshall_msg_cursor_init(base_marshaller, &msg);
-    add_buf_from_info(base_marshaller, &info);
-}
-
-static void red_marshall_cursor(RedChannelClient *rcc,
-                   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;
-
-    spice_assert(cursor_channel);
-
-    worker = cursor_channel->common.worker;
-
-    cmd = cursor->red_cursor;
-    switch (cmd->type) {
-    case QXL_CURSOR_MOVE:
-        {
-            SpiceMsgCursorMove cursor_move;
-            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;
-        }
-    case QXL_CURSOR_SET:
-        {
-            SpiceMsgCursorSet cursor_set;
-            AddBufInfo info;
-
-            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;
-
-            fill_cursor(ccc, &cursor_set.cursor, cursor, &info);
-            spice_marshall_msg_cursor_set(m, &cursor_set);
-            add_buf_from_info(m, &info);
-            break;
-        }
-    case QXL_CURSOR_HIDE:
-        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, pipe_item);
-            cursor_trail.length = cmd->u.trail.length;
-            cursor_trail.frequency = cmd->u.trail.frequency;
-            spice_marshall_msg_cursor_trail(m, &cursor_trail);
-        }
-        break;
-    default:
-        spice_error("bad cursor command %d", cmd->type);
-    }
-}
+#define RCC_TO_CCC(rcc) SPICE_CONTAINEROF((rcc), CursorChannelClient, common.base)
 
 static void red_marshall_surface_create(RedChannelClient *rcc,
     SpiceMarshaller *base_marshaller, SpiceMsgSurfaceCreate *surface_create)
@@ -8758,9 +8391,6 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
         marshall_qxl_drawable(rcc, m, dpi);
         break;
     }
-    case PIPE_ITEM_TYPE_INVAL_ONE:
-        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(rcc, m, agent);
@@ -8780,7 +8410,7 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
         red_display_marshall_upgrade(rcc, m, (UpgradeItem *)pipe_item);
         break;
     case PIPE_ITEM_TYPE_VERB:
-        red_marshall_verb(rcc, ((VerbItem*)pipe_item)->verb);
+        red_marshall_verb(rcc, (VerbItem*)pipe_item);
         break;
     case PIPE_ITEM_TYPE_MIGRATE_DATA:
         display_channel_marshall_migrate_data(rcc, m);
@@ -8796,7 +8426,7 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
         break;
     case PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE:
         red_reset_palette_cache(dcc);
-        red_marshall_verb(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES, NULL);
         break;
     case PIPE_ITEM_TYPE_CREATE_SURFACE: {
         SurfaceCreateItem *surface_create = SPICE_CONTAINEROF(pipe_item, SurfaceCreateItem,
@@ -8835,37 +8465,6 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item
     }
 }
 
-static void cursor_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
-{
-    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
-
-    switch (pipe_item->type) {
-    case PIPE_ITEM_TYPE_CURSOR:
-        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);
-        break;
-    case PIPE_ITEM_TYPE_VERB:
-        red_marshall_verb(rcc, ((VerbItem*)pipe_item)->verb);
-        break;
-    case PIPE_ITEM_TYPE_CURSOR_INIT:
-        red_reset_cursor_cache(rcc);
-        red_marshall_cursor_init(rcc, m, pipe_item);
-        break;
-    case PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE:
-        red_reset_cursor_cache(rcc);
-        red_marshall_verb(rcc, SPICE_MSG_CURSOR_INVAL_ALL);
-        break;
-    default:
-        spice_error("invalid pipe item type");
-    }
-
-    cursor_channel_client_release_item_before_push(ccc, pipe_item);
-    red_channel_client_begin_send_message(rcc);
-}
-
 static inline void red_push(RedWorker *worker)
 {
     if (worker->cursor_channel) {
@@ -9313,7 +8912,8 @@ static inline void flush_cursor_commands(RedWorker *worker)
             red_channel_send(channel);
             if (red_now() >= end_time) {
                 spice_warning("flush cursor timeout");
-                red_disconnect_cursor(channel);
+                cursor_channel_disconnect(worker->cursor_channel);
+                worker->cursor_channel = NULL;
             } else {
                 sleep_count++;
                 usleep(DISPLAY_CLIENT_RETRY_INTERVAL);
@@ -9924,16 +9524,16 @@ SpiceCoreInterface worker_core = {
     .watch_remove = worker_watch_remove,
 };
 
-static CommonChannelClient *common_channel_client_create(int size,
-                                                         CommonChannel *common,
-                                                         RedClient *client,
-                                                         RedsStream *stream,
-                                                         int mig_target,
-                                                         int monitor_latency,
-                                                         uint32_t *common_caps,
-                                                         int num_common_caps,
-                                                         uint32_t *caps,
-                                                         int num_caps)
+CommonChannelClient *common_channel_new_client(CommonChannel *common,
+                                               int size,
+                                               RedClient *client,
+                                               RedsStream *stream,
+                                               int mig_target,
+                                               int monitor_latency,
+                                               uint32_t *common_caps,
+                                               int num_common_caps,
+                                               uint32_t *caps,
+                                               int num_caps)
 {
     RedChannelClient *rcc =
         red_channel_client_create(size, &common->base, client, stream, monitor_latency,
@@ -9961,8 +9561,8 @@ DisplayChannelClient *display_channel_client_create(CommonChannel *common,
                                                     uint32_t *caps, int num_caps)
 {
     DisplayChannelClient *dcc =
-        (DisplayChannelClient*)common_channel_client_create(
-            sizeof(DisplayChannelClient), common, client, stream,
+        (DisplayChannelClient*)common_channel_new_client(
+            common, sizeof(DisplayChannelClient), client, stream,
             mig_target,
             TRUE,
             common_caps, num_common_caps,
@@ -9976,62 +9576,30 @@ DisplayChannelClient *display_channel_client_create(CommonChannel *common,
     return dcc;
 }
 
-CursorChannelClient *cursor_channel_create_rcc(CommonChannel *common,
-                                               RedClient *client, RedsStream *stream,
-                                               int mig_target,
-                                               uint32_t *common_caps, int num_common_caps,
-                                               uint32_t *caps, int num_caps)
-{
-    CursorChannelClient *ccc =
-        (CursorChannelClient*)common_channel_client_create(
-            sizeof(CursorChannelClient), common, client, stream,
-            mig_target,
-            FALSE,
-            common_caps,
-            num_common_caps,
-            caps,
-            num_caps);
-
-    if (!ccc) {
-        return NULL;
-    }
-    ring_init(&ccc->cursor_cache_lru);
-    ccc->cursor_cache_available = CLIENT_CURSOR_CACHE_SIZE;
-    return ccc;
-}
-
-static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_type,
-                                 int migration_flags,
-                                 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_handle_migrate_flush_mark_proc handle_migrate_flush_mark,
-                                 channel_handle_migrate_data_proc handle_migrate_data,
-                                 channel_handle_migrate_data_get_serial_proc migrate_get_serial)
+RedChannel *red_worker_new_channel(RedWorker *worker, int size,
+                                   uint32_t channel_type, int migration_flags,
+                                   ChannelCbs *channel_cbs,
+                                   channel_handle_parsed_proc handle_parsed)
 {
     RedChannel *channel = NULL;
     CommonChannel *common;
-    ChannelCbs channel_cbs = { NULL, };
-
-    channel_cbs.config_socket = common_channel_config_socket;
-    channel_cbs.on_disconnect = on_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 = migrate_get_serial;
+
+    spice_return_val_if_fail(worker, NULL);
+    spice_return_val_if_fail(channel_cbs, NULL);
+    spice_return_val_if_fail(!channel_cbs->config_socket, NULL);
+    spice_return_val_if_fail(!channel_cbs->alloc_recv_buf, NULL);
+    spice_return_val_if_fail(!channel_cbs->release_recv_buf, NULL);
+
+    channel_cbs->config_socket = common_channel_config_socket;
+    channel_cbs->alloc_recv_buf = common_alloc_recv_buf;
+    channel_cbs->release_recv_buf = common_release_recv_buf;
 
     channel = red_channel_create_parser(size, &worker_core,
                                         channel_type, worker->id,
                                         TRUE /* handle_acks */,
                                         spice_get_client_channel_parser(channel_type, NULL),
                                         handle_parsed,
-                                        &channel_cbs,
+                                        channel_cbs,
                                         migration_flags);
     common = (CommonChannel *)channel;
     if (!channel) {
@@ -10151,7 +9719,6 @@ static void display_channel_client_release_item_before_push(DisplayChannelClient
         free(item);
         break;
     }
-    case PIPE_ITEM_TYPE_INVAL_ONE:
     case PIPE_ITEM_TYPE_VERB:
     case PIPE_ITEM_TYPE_MIGRATE_DATA:
     case PIPE_ITEM_TYPE_PIXMAP_SYNC:
@@ -10181,25 +9748,26 @@ static void display_channel_release_item(RedChannelClient *rcc, PipeItem *item,
 static void display_channel_create(RedWorker *worker, int migrate)
 {
     DisplayChannel *display_channel;
+    ChannelCbs cbs = {
+        .on_disconnect = display_channel_client_on_disconnect,
+        .send_item = display_channel_send_item,
+        .hold_item = display_channel_hold_pipe_item,
+        .release_item = display_channel_release_item,
+        .handle_migrate_flush_mark = display_channel_handle_migrate_mark,
+        .handle_migrate_data = display_channel_handle_migrate_data,
+        .handle_migrate_data_get_serial = display_channel_handle_migrate_data_get_serial
+    };
 
     if (worker->display_channel) {
         return;
     }
 
     spice_info("create display channel");
-    if (!(worker->display_channel = (DisplayChannel *)__new_channel(
+    if (!(worker->display_channel = (DisplayChannel *)red_worker_new_channel(
             worker, sizeof(*display_channel),
             SPICE_CHANNEL_DISPLAY,
             SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER,
-            display_channel_client_on_disconnect,
-            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
-            ))) {
+            &cbs, display_channel_handle_message))) {
         spice_warning("failed to create display channel");
         return;
     }
@@ -10334,129 +9902,6 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
     on_new_display_channel_client(dcc);
 }
 
-static void cursor_channel_client_on_disconnect(RedChannelClient *rcc)
-{
-    if (!rcc) {
-        return;
-    }
-    red_reset_cursor_cache(rcc);
-}
-
-static void red_disconnect_cursor(RedChannel *channel)
-{
-    CommonChannel *common;
-
-    if (!channel || !red_channel_is_connected(channel)) {
-        return;
-    }
-    common = SPICE_CONTAINEROF(channel, CommonChannel, base);
-    spice_assert(channel == (RedChannel *)common->worker->cursor_channel);
-    common->worker->cursor_channel = NULL;
-    red_channel_apply_clients(channel, red_reset_cursor_cache);
-    red_channel_disconnect(channel);
-}
-
-static void red_migrate_cursor(RedWorker *worker, RedChannelClient *rcc)
-{
-    if (red_channel_client_is_connected(rcc)) {
-        red_channel_client_pipe_add_type(rcc,
-                                         PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
-        red_channel_client_default_migrate(rcc);
-    }
-}
-
-static void on_new_cursor_channel(RedWorker *worker, RedChannelClient *rcc)
-{
-    CursorChannel *channel = worker->cursor_channel;
-
-    spice_assert(channel);
-    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.during_target_migrate) {
-        red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_CURSOR_INIT);
-    }
-}
-
-static void cursor_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
-{
-    CursorPipeItem *cursor_pipe_item;
-    spice_assert(item);
-    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: {
-        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_CURSOR_INIT:
-    case PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE:
-        free(item);
-        break;
-    default:
-        spice_error("invalid pipe item type");
-    }
-}
-
-static void cursor_channel_client_release_item_after_push(CursorChannelClient *ccc,
-                                                          PipeItem *item)
-{
-    switch (item->type) {
-        case PIPE_ITEM_TYPE_CURSOR: {
-            CursorPipeItem *cursor_pipe_item = SPICE_CONTAINEROF(item, CursorPipeItem, base);
-            put_cursor_pipe_item(ccc, cursor_pipe_item);
-            break;
-        }
-        default:
-            spice_critical("invalid item type");
-    }
-}
-
-static void cursor_channel_release_item(RedChannelClient *rcc, PipeItem *item, int item_pushed)
-{
-    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
-
-    spice_assert(item);
-
-    if (item_pushed) {
-        cursor_channel_client_release_item_after_push(ccc, item);
-    } else {
-        spice_debug("not pushed (%d)", item->type);
-        cursor_channel_client_release_item_before_push(ccc, item);
-    }
-}
-
-static void cursor_channel_create(RedWorker *worker, int migrate)
-{
-    if (worker->cursor_channel != NULL) {
-        return;
-    }
-    spice_info("create cursor channel");
-    worker->cursor_channel = (CursorChannel *)__new_channel(
-        worker, sizeof(*worker->cursor_channel),
-        SPICE_CHANNEL_CURSOR,
-        0,
-        cursor_channel_client_on_disconnect,
-        cursor_channel_send_item,
-        cursor_channel_hold_pipe_item,
-        cursor_channel_release_item,
-        red_channel_client_handle_message,
-        NULL,
-        NULL,
-        NULL);
-}
-
 static void red_connect_cursor(RedWorker *worker, RedClient *client, RedsStream *stream,
                                int migrate,
                                uint32_t *common_caps, int num_common_caps,
@@ -10465,24 +9910,28 @@ static void red_connect_cursor(RedWorker *worker, RedClient *client, RedsStream
     CursorChannel *channel;
     CursorChannelClient *ccc;
 
-    if (worker->cursor_channel == NULL) {
-        spice_warning("cursor channel was not created");
-        return;
-    }
+    spice_return_if_fail(worker->cursor_channel != NULL);
+
     channel = worker->cursor_channel;
     spice_info("add cursor channel client");
-    ccc = cursor_channel_create_rcc(&channel->common, client, stream,
+    ccc = cursor_channel_client_new(channel, client, stream,
                                     migrate,
                                     common_caps, num_common_caps,
                                     caps, num_caps);
-    if (!ccc) {
-        return;
-    }
+    spice_return_if_fail(ccc != NULL);
 #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
-    on_new_cursor_channel(worker, &ccc->common.base);
+
+    RedChannelClient *rcc = &ccc->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.during_target_migrate) {
+        red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_CURSOR_INIT);
+    }
 }
 
 static void surface_dirty_region_to_rects(RedSurface *surface,
@@ -10636,31 +10085,6 @@ void handle_dev_destroy_surface_wait(void *opaque, void *payload)
     dev_destroy_surface_wait(worker, msg->surface_id);
 }
 
-static inline void red_cursor_reset(RedWorker *worker)
-{
-    if (worker->cursor) {
-        red_release_cursor(worker, worker->cursor);
-        worker->cursor = NULL;
-    }
-
-    worker->cursor_visible = TRUE;
-    worker->cursor_position.x = worker->cursor_position.y = 0;
-    worker->cursor_trail_length = worker->cursor_trail_frequency = 0;
-
-    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.during_target_migrate) {
-            red_pipes_add_verb(&worker->cursor_channel->common.base, SPICE_MSG_CURSOR_RESET);
-        }
-        if (!red_channel_wait_all_sent(&worker->cursor_channel->common.base,
-                                       DISPLAY_CLIENT_TIMEOUT)) {
-            red_channel_apply_clients(&worker->cursor_channel->common.base,
-                               red_channel_client_disconnect_if_pending_send);
-        }
-    }
-}
-
 /* called upon device reset */
 
 /* TODO: split me*/
@@ -10691,7 +10115,7 @@ static inline void dev_destroy_surfaces(RedWorker *worker)
 
     red_display_clear_glz_drawables(worker->display_channel);
 
-    red_cursor_reset(worker);
+    cursor_channel_reset(worker->cursor_channel);
 }
 
 void handle_dev_destroy_surfaces(void *opaque, void *payload)
@@ -10849,10 +10273,9 @@ static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id,
         red_channel_push(&worker->display_channel->common.base);
     }
 
-    if (cursor_is_connected(worker) && !worker->cursor_channel->common.during_target_migrate) {
+    if (!worker->cursor_channel->common.during_target_migrate)
         red_channel_pipes_add_type(&worker->cursor_channel->common.base,
                                    PIPE_ITEM_TYPE_CURSOR_INIT);
-    }
 }
 
 void handle_dev_create_primary_surface(void *opaque, void *payload)
@@ -10881,7 +10304,7 @@ static void dev_destroy_primary_surface(RedWorker *worker, uint32_t surface_id)
 
     spice_assert(!worker->surfaces[surface_id].context.canvas);
 
-    red_cursor_reset(worker);
+    cursor_channel_reset(worker->cursor_channel);
 }
 
 void handle_dev_destroy_primary_surface(void *opaque, void *payload)
@@ -11051,7 +10474,9 @@ void handle_dev_oom(void *opaque, void *payload)
 
 void handle_dev_reset_cursor(void *opaque, void *payload)
 {
-    red_cursor_reset((RedWorker *)opaque);
+    RedWorker *worker = opaque;
+
+    cursor_channel_reset(worker->cursor_channel);
 }
 
 void handle_dev_reset_image_cache(void *opaque, void *payload)
@@ -11195,8 +10620,12 @@ void handle_dev_cursor_channel_create(void *opaque, void *payload)
     RedWorker *worker = opaque;
     RedChannel *red_channel;
 
-    // TODO: handle seemless migration. Temp, setting migrate to FALSE
-    cursor_channel_create(worker, FALSE);
+    if (!worker->cursor_channel) {
+        worker->cursor_channel = cursor_channel_new(worker);
+    } else {
+        spice_warning("cursor channel already created");
+    }
+
     red_channel = &worker->cursor_channel->common.base;
     send_data(worker->channel, &red_channel, sizeof(RedChannel *));
 }
@@ -11223,19 +10652,20 @@ void handle_dev_cursor_disconnect(void *opaque, void *payload)
     RedChannelClient *rcc = msg->rcc;
 
     spice_info("disconnect cursor client");
-    spice_assert(rcc);
     red_channel_client_disconnect(rcc);
 }
 
 void handle_dev_cursor_migrate(void *opaque, void *payload)
 {
     RedWorkerMessageCursorMigrate *msg = payload;
-    RedWorker *worker = opaque;
     RedChannelClient *rcc = msg->rcc;
 
     spice_info("migrate cursor client");
-    spice_assert(rcc);
-    red_migrate_cursor(worker, rcc);
+    if (!red_channel_client_is_connected(rcc))
+        return;
+
+    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE);
+    red_channel_client_default_migrate(rcc);
 }
 
 void handle_dev_set_compression(void *opaque, void *payload)
@@ -11312,8 +10742,10 @@ void handle_dev_set_mouse_mode(void *opaque, void *payload)
     RedWorkerMessageSetMouseMode *msg = payload;
     RedWorker *worker = opaque;
 
-    worker->mouse_mode = msg->mode;
-    spice_info("mouse mode %u", worker->mouse_mode);
+    spice_info("mouse mode %u", msg->mode);
+    spice_return_if_fail(worker->cursor_channel);
+
+    worker->cursor_channel->mouse_mode = msg->mode;
 }
 
 void handle_dev_add_memslot_async(void *opaque, void *payload)
@@ -11350,7 +10782,7 @@ static int loadvm_command(RedWorker *worker, QXLCommandExt *ext)
             free(cursor_cmd);
             return FALSE;
         }
-        qxl_process_cursor(worker, cursor_cmd, ext->group_id);
+        cursor_channel_process_cmd(worker->cursor_channel, cursor_cmd, ext->group_id);
         break;
     case QXL_CMD_SURFACE:
         surface_cmd = spice_new0(RedSurfaceCmd, 1);
@@ -11628,12 +11060,10 @@ RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher)
     if (worker->record_fd) {
         dispatcher_register_universal_handler(dispatcher, worker_dispatcher_record);
     }
-    worker->cursor_visible = TRUE;
     spice_assert(num_renderers > 0);
     worker->num_renderers = num_renderers;
     memcpy(worker->renderers, renderers, sizeof(worker->renderers));
     worker->renderer = RED_RENDERER_INVALID;
-    worker->mouse_mode = SPICE_MOUSE_MODE_SERVER;
     worker->image_compression = image_compression;
     worker->jpeg_state = jpeg_state;
     worker->zlib_glz_state = zlib_glz_state;
@@ -11643,7 +11073,6 @@ RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher)
     image_cache_init(&worker->image_cache);
     image_surface_init(worker);
     drawables_init(worker);
-    cursor_items_init(worker);
     red_init_streams(worker);
     stat_init(&worker->add_stat, add_stat_name);
     stat_init(&worker->exclude_stat, exclude_stat_name);
diff --git a/server/red_worker.h b/server/red_worker.h
index 0f5fac7..60c326a 100644
--- a/server/red_worker.h
+++ b/server/red_worker.h
@@ -32,6 +32,9 @@ typedef struct CommonChannelClient {
     int is_low_bandwidth;
 } CommonChannelClient;
 
+
+#define DISPLAY_CLIENT_TIMEOUT 30000000000ULL //nano
+
 #define CHANNEL_RECEIVE_BUF_SIZE 1024
 typedef struct CommonChannel {
     RedChannel base; // Must be the first thing
@@ -45,7 +48,75 @@ typedef struct CommonChannel {
                                   of the primary surface) */
 } CommonChannel;
 
+enum {
+    PIPE_ITEM_TYPE_VERB = PIPE_ITEM_TYPE_CHANNEL_BASE,
+    PIPE_ITEM_TYPE_INVAL_ONE,
+
+    PIPE_ITEM_TYPE_COMMON_LAST
+};
+
+typedef struct VerbItem {
+    PipeItem base;
+    uint16_t verb;
+} VerbItem;
+
+static inline void red_marshall_verb(RedChannelClient *rcc, VerbItem *item)
+{
+    red_channel_client_init_send_data(rcc, item->verb, NULL);
+}
+
+static inline void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb)
+{
+    VerbItem *item = spice_new(VerbItem, 1);
+
+    red_channel_pipe_item_init(rcc->channel, &item->base, PIPE_ITEM_TYPE_VERB);
+    item->verb = verb;
+    red_channel_client_pipe_add(rcc, &item->base);
+}
+
+/* a generic safe for loop macro  */
+#define SAFE_FOREACH(link, next, cond, ring, data, get_data)               \
+    for ((((link) = ((cond) ? ring_get_head(ring) : NULL)), \
+          ((next) = ((link) ? ring_next((ring), (link)) : NULL)),          \
+          ((data) = ((link)? (get_data) : NULL)));                         \
+         (link);                                                           \
+         (((link) = (next)),                                               \
+          ((next) = ((link) ? ring_next((ring), (link)) : NULL)),          \
+          ((data) = ((link)? (get_data) : NULL))))
+
+#define LINK_TO_RCC(ptr) SPICE_CONTAINEROF(ptr, RedChannelClient, channel_link)
+#define RCC_FOREACH_SAFE(link, next, rcc, channel) \
+    SAFE_FOREACH(link, next, channel,  &(channel)->clients, rcc, LINK_TO_RCC(link))
+
+static inline void red_pipes_add_verb(RedChannel *channel, uint16_t verb)
+{
+    RedChannelClient *rcc;
+    RingItem *link, *next;
+
+    RCC_FOREACH_SAFE(link, next, rcc, channel) {
+        red_pipe_add_verb(rcc, verb);
+    }
+}
+
+
 RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher);
 bool       red_worker_run(RedWorker *worker);
+QXLInstance* red_worker_get_qxl(RedWorker *worker);
+
+RedChannel *red_worker_new_channel(RedWorker *worker, int size,
+                                   uint32_t channel_type, int migration_flags,
+                                   ChannelCbs *channel_cbs,
+                                   channel_handle_parsed_proc handle_parsed);
+
+CommonChannelClient *common_channel_new_client(CommonChannel *common,
+                                               int size,
+                                               RedClient *client,
+                                               RedsStream *stream,
+                                               int mig_target,
+                                               int monitor_latency,
+                                               uint32_t *common_caps,
+                                               int num_common_caps,
+                                               uint32_t *caps,
+                                               int num_caps);
 
 #endif
-- 
2.4.3



More information about the Spice-devel mailing list