diff options
Diffstat (limited to 'server/cursor-channel.c')
-rw-r--r-- | server/cursor-channel.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/server/cursor-channel.c b/server/cursor-channel.c new file mode 100644 index 00000000..6cc2b93b --- /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); + 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); + } + } +} |