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

Christophe Fergeau cfergeau at redhat.com
Mon Oct 26 08:22:23 PDT 2015


On Mon, Oct 26, 2015 at 07:01:11AM -0400, Frediano Ziglio wrote:
> 
> > 
> > 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
> > 
> 
> This patch does a lot of stuff:
> - move code to new files;
> - change cursor structures (for instance to add qxl pointer);
> - renames many functions;

Imo the renaming goes together with the move to the new file (at least
for the few methods I looked at). I agree it would be better to split
this a bit (for example, move CursorItem in a separate patch if at all
possible, and also the other changes you mention).


> - change cursor memory management;
> - fix some bugs.

Christophe
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20151026/fd0357ec/attachment-0001.sig>


More information about the Spice-devel mailing list