[Spice-devel] [PATCH 05/11] worker: move some cursor code to cursor-channel.c

Frediano Ziglio fziglio at redhat.com
Tue Oct 27 12:19:09 PDT 2015


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

Signed-off-by: Marc-André Lureau <marcandre.lureau at gmail.com>
Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
 server/Makefile.am      |   1 +
 server/cursor-channel.c | 464 ++++++++++++++++++++++++++++++++++++
 server/cursor-channel.h |  26 +++
 server/red_worker.c     | 611 +++++-------------------------------------------
 server/red_worker.h     |  92 ++++++++
 5 files changed, 635 insertions(+), 559 deletions(-)
 create mode 100644 server/cursor-channel.c

diff --git a/server/Makefile.am b/server/Makefile.am
index 42b222b..19ff3c8 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -108,6 +108,7 @@ libspice_server_la_SOURCES =			\
 	red_worker.c				\
 	red_worker.h				\
 	display-channel.h			\
+	cursor-channel.c			\
 	cursor-channel.h			\
 	reds.c					\
 	reds.h					\
diff --git a/server/cursor-channel.c b/server/cursor-channel.c
new file mode 100644
index 0000000..7ba4a7d
--- /dev/null
+++ b/server/cursor-channel.c
@@ -0,0 +1,464 @@
+/* -*- 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
+
+static inline CursorItem *alloc_cursor_item(void)
+{
+    CursorItem *cursor_item;
+
+    cursor_item = g_slice_new0(CursorItem);
+    cursor_item->refs = 1;
+
+    return cursor_item;
+}
+
+CursorItem *cursor_item_new(RedCursorCmd *cmd, uint32_t group_id)
+{
+    CursorItem *cursor_item;
+
+    spice_return_val_if_fail(cmd != NULL, NULL);
+    spice_warn_if(!(cursor_item = alloc_cursor_item()));
+
+    cursor_item->group_id = group_id;
+    cursor_item->red_cursor = cmd;
+
+    return cursor_item;
+}
+
+void cursor_item_unref(QXLInstance *qxl, 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;
+        qxl->st->qif->release_resource(qxl, release_info_ext);
+        red_put_cursor_cmd(cursor_cmd);
+        free(cursor_cmd);
+
+        g_slice_free(CursorItem, cursor);
+    }
+}
+
+static void cursor_set_item(CursorChannel *cursor, CursorItem *item)
+{
+    if (cursor->item)
+        cursor_item_unref(red_worker_get_qxl(cursor->common.worker), cursor->item);
+
+    if (item)
+        item->refs++;
+
+    cursor->item = 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;
+}
+
+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(RedChannel *channel)
+{
+    if (!channel || !red_channel_is_connected(channel)) {
+        return;
+    }
+    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_assert(pipe_item);
+
+    if (--pipe_item->refs) {
+        return;
+    }
+
+    spice_assert(!pipe_item_is_linked(&pipe_item->base));
+
+    cursor_item_unref(red_worker_get_qxl(ccc->common.worker), 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_channel;
+    CursorChannelClient *ccc = RCC_TO_CCC(rcc);
+    SpiceMsgCursorInit msg;
+    AddBufInfo info;
+
+    spice_assert(rcc);
+    cursor_channel = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base);
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_INIT, NULL);
+    msg.visible = cursor_channel->cursor_visible;
+    msg.position = cursor_channel->cursor_position;
+    msg.trail_length = cursor_channel->cursor_trail_length;
+    msg.trail_frequency = cursor_channel->cursor_trail_frequency;
+
+    cursor_fill(ccc, &msg.cursor, cursor_channel->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_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;
+
+    spice_assert(cursor_channel);
+
+    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 = cursor_channel->cursor_visible;
+
+            cursor_fill(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);
+    }
+}
+
+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 red_cursor_marshall_inval(RedChannelClient *rcc,
+                SpiceMarshaller *m, CacheItem *cach_item)
+{
+    spice_assert(rcc);
+    red_marshall_inval(rcc, m, cach_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:
+        cursor_marshall(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 CursorPipeItem *cursor_pipe_item_ref(CursorPipeItem *item)
+{
+    spice_assert(item);
+    item->refs++;
+    return item;
+}
+
+
+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);
+    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, int migrate)
+{
+    CursorChannel* cursor;
+
+    spice_info("create cursor channel");
+    cursor = (CursorChannel *)__new_channel(
+        worker, sizeof(CursorChannel),
+        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);
+
+    cursor->cursor_visible = TRUE;
+    cursor->mouse_mode = SPICE_MOUSE_MODE_SERVER;
+
+    return cursor;
+}
+
+CursorChannelClient *cursor_channel_client_new(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;
+}
+
+void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd *cursor_cmd,
+                                uint32_t group_id)
+{
+    CursorItem *cursor_item;
+    int cursor_show = FALSE;
+
+    cursor_item = cursor_item_new(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_error("invalid cursor command %u", cursor_cmd->type);
+    }
+
+    if (red_channel_is_connected(&cursor->common.base) && (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,
+                                  (void*)cursor_item);
+    }
+    cursor_item_unref(red_worker_get_qxl(cursor->common.worker), cursor_item);
+}
+
+void cursor_channel_reset(CursorChannel *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(&cursor->common.base)) {
+        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
index 4654bcb..2c19859 100644
--- a/server/cursor-channel.h
+++ b/server/cursor-channel.h
@@ -18,7 +18,11 @@
 #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
@@ -59,6 +63,13 @@ typedef struct 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
@@ -66,5 +77,20 @@ typedef struct CursorChannel {
 
 G_STATIC_ASSERT(sizeof(CursorItem) <= QXL_CURSUR_DEVICE_DATA_SIZE);
 
+CursorChannel*       cursor_channel_new         (RedWorker *worker, int migrate);
+void                 cursor_channel_disconnect  (RedChannel *channel);
+void                 cursor_channel_reset       (CursorChannel *cursor);
+void                 cursor_channel_process_cmd (CursorChannel *cursor, RedCursorCmd *cursor_cmd,
+                                                 uint32_t group_id);
+
+CursorItem*          cursor_item_new            (RedCursorCmd *cmd, uint32_t group_id);
+void                 cursor_item_unref          (QXLInstance *qxl, CursorItem *cursor);
+
+
+CursorChannelClient *cursor_channel_client_new(CommonChannel *common,
+                                               RedClient *client, RedsStream *stream,
+                                               int mig_target,
+                                               uint32_t *common_caps, int num_common_caps,
+                                               uint32_t *caps, int num_caps);
 
 #endif /* CURSOR_CHANNEL_H_ */
diff --git a/server/red_worker.c b/server/red_worker.c
index 5e982db..8ae0878 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -265,33 +265,6 @@ struct SpiceWatch {
     void *watch_func_opaque;
 };
 
-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_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,
-    PIPE_ITEM_TYPE_MONITORS_CONFIG,
-    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 {
@@ -637,12 +610,6 @@ 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;
 
@@ -654,8 +621,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;
@@ -732,7 +697,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 cursor_item_unref(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);
@@ -751,15 +715,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);
 
@@ -769,21 +729,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)                       \
@@ -911,6 +856,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++;
@@ -1066,28 +1018,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)
 {
@@ -1320,10 +1253,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
@@ -1333,11 +1262,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;
@@ -4231,123 +4155,6 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac
     validate_area(worker, area, surface_id);
 }
 
-static void cursor_item_unref(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);
-        red_put_cursor_cmd(cursor_cmd);
-        free(cursor_cmd);
-
-        g_slice_free(CursorItem, cursor);
-    }
-}
-
-static void red_set_cursor(RedWorker *worker, CursorItem *cursor)
-{
-    if (worker->cursor) {
-        cursor_item_unref(worker, worker->cursor);
-    }
-    ++cursor->refs;
-    worker->cursor = cursor;
-}
-
-static inline CursorItem *alloc_cursor_item(void)
-{
-    CursorItem *cursor_item;
-
-    cursor_item = g_slice_new0(CursorItem);
-    cursor_item->refs = 1;
-
-    return cursor_item;
-}
-
-static CursorItem *cursor_item_new(RedCursorCmd *cmd, uint32_t group_id)
-{
-    CursorItem *cursor_item;
-
-    spice_return_val_if_fail(cmd != NULL, NULL);
-    spice_warn_if(!(cursor_item = alloc_cursor_item()));
-
-    cursor_item->group_id = group_id;
-    cursor_item->red_cursor = cmd;
-
-    return cursor_item;
-}
-
-static CursorPipeItem *cursor_pipe_item_ref(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));
-
-    cursor_item_unref(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 = cursor_item_new(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);
-    }
-    cursor_item_unref(worker, cursor_item);
-}
-
 static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ring_is_empty)
 {
     QXLCommandExt ext_cmd;
@@ -4386,7 +4193,7 @@ static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ri
                 break;
             }
 
-            qxl_process_cursor(worker, cursor, ext_cmd.group_id);
+            cursor_channel_process_cmd(worker->cursor_channel, cursor, ext_cmd.group_id);
             break;
         }
         default:
@@ -4650,11 +4457,6 @@ static void red_push_surface_image(DisplayChannelClient *dcc, int surface_id)
     red_channel_client_push(&dcc->common.base);
 }
 
-typedef struct {
-    void *data;
-    uint32_t size;
-} AddBufInfo;
-
 static void marshaller_add_compressed(SpiceMarshaller *m,
                                       RedCompressBuf *comp_buf, size_t size)
 {
@@ -4670,13 +4472,6 @@ static void marshaller_add_compressed(SpiceMarshaller *m,
 }
 
 
-static void add_buf_from_info(SpiceMarshaller *m, AddBufInfo *info)
-{
-    if (info->data) {
-        spice_marshaller_add_ref(m, info->data, info->size);
-    }
-}
-
 static void fill_base(SpiceMarshaller *base_marshaller, Drawable *drawable)
 {
     SpiceMsgDisplayBase base;
@@ -6368,36 +6163,6 @@ static void fill_attr(SpiceMarshaller *m, SpiceLineAttr *attr, uint32_t group_id
     }
 }
 
-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 inline void red_display_reset_send_data(DisplayChannelClient *dcc)
 {
     red_display_reset_compress_buf(dcc);
@@ -8153,23 +7918,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)
@@ -8545,93 +8293,6 @@ 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;
-
-    cursor_fill(ccc, &msg.cursor, worker->cursor, &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_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;
-
-            cursor_fill(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);
-    }
-}
-
 static void red_marshall_surface_create(RedChannelClient *rcc,
     SpiceMarshaller *base_marshaller, SpiceMsgSurfaceCreate *surface_create)
 {
@@ -8700,6 +8361,17 @@ static void red_marshall_stream_activate_report(RedChannelClient *rcc,
     spice_marshall_msg_display_stream_activate_report(base_marshaller, &msg);
 }
 
+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_send_item(RedChannelClient *rcc, PipeItem *pipe_item)
 {
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
@@ -8789,37 +8461,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:
-        cursor_marshall(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) {
@@ -9267,7 +8908,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(channel);
+                worker->cursor_channel = NULL;
             } else {
                 sleep_count++;
                 usleep(DISPLAY_CLIENT_RETRY_INTERVAL);
@@ -9878,16 +9520,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_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)
 {
     RedChannelClient *rcc =
         red_channel_client_create(size, &common->base, client, stream, monitor_latency,
@@ -9930,40 +9572,16 @@ 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 *__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 *channel = NULL;
     CommonChannel *common;
@@ -10288,28 +9906,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)) {
@@ -10333,84 +9929,6 @@ 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;
-    spice_assert(item);
-    cursor_pipe_item = SPICE_CONTAINEROF(item, CursorPipeItem, base);
-    cursor_pipe_item_ref(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_new(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,
@@ -10425,7 +9943,7 @@ static void red_connect_cursor(RedWorker *worker, RedClient *client, RedsStream
     }
     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->common, client, stream,
                                     migrate,
                                     common_caps, num_common_caps,
                                     caps, num_caps);
@@ -10590,31 +10108,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) {
-        cursor_item_unref(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*/
@@ -10645,7 +10138,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)
@@ -10835,7 +10328,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)
@@ -11005,7 +10498,7 @@ void handle_dev_oom(void *opaque, void *payload)
 
 void handle_dev_reset_cursor(void *opaque, void *payload)
 {
-    red_cursor_reset((RedWorker *)opaque);
+    cursor_channel_reset(((RedWorker*)opaque)->cursor_channel);
 }
 
 void handle_dev_reset_image_cache(void *opaque, void *payload)
@@ -11150,7 +10643,9 @@ void handle_dev_cursor_channel_create(void *opaque, void *payload)
     RedChannel *red_channel;
 
     // TODO: handle seemless migration. Temp, setting migrate to FALSE
-    cursor_channel_new(worker, FALSE);
+    if (!worker->cursor_channel) {
+        worker->cursor_channel = cursor_channel_new(worker, FALSE);
+    }
     red_channel = &worker->cursor_channel->common.base;
     send_data(worker->channel, &red_channel, sizeof(RedChannel *));
 }
@@ -11266,8 +10761,8 @@ 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);
+    worker->cursor_channel->mouse_mode = msg->mode;
+    spice_info("mouse mode %u", worker->cursor_channel->mouse_mode);
 }
 
 void handle_dev_add_memslot_async(void *opaque, void *payload)
@@ -11304,7 +10799,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);
@@ -11581,12 +11076,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;
diff --git a/server/red_worker.h b/server/red_worker.h
index 0f5fac7..502283e 100644
--- a/server/red_worker.h
+++ b/server/red_worker.h
@@ -32,6 +32,8 @@ 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 +47,97 @@ typedef struct CommonChannel {
                                   of the primary surface) */
 } CommonChannel;
 
+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_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,
+    PIPE_ITEM_TYPE_MONITORS_CONFIG,
+    PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT,
+};
+
+typedef struct VerbItem {
+    PipeItem base;
+    uint16_t verb;
+} VerbItem;
+
+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_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);
+
+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);
+
+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);
 
 #endif
-- 
2.4.3



More information about the Spice-devel mailing list