When using private structs with GObject, there's a maximum size of (I think) 64k, which was exceeded by the DisplayChannel object. To make this work, I had to make several of the arrays here dynamically allocated rather than statically allocated. --- server/Makefile.am | 5 + server/common-graphics-channel-client.c | 3 +- server/common-graphics-channel-client.h | 3 +- server/common-graphics-channel.c | 181 ++++++++ server/common-graphics-channel.h | 98 +++++ server/cursor-channel.c | 126 +++--- server/cursor-channel.h | 37 +- server/dcc-send.c | 44 +- server/dcc.c | 61 +-- server/dcc.h | 4 +- server/display-channel-private.h | 81 ++++ server/display-channel.c | 726 ++++++++++++++++++++------------ server/display-channel.h | 158 +++---- server/dummy-channel-client.c | 17 +- server/dummy-channel.c | 58 +++ server/dummy-channel.h | 61 +++ server/inputs-channel.c | 262 +++++++----- server/inputs-channel.h | 30 ++ server/main-channel-client.c | 47 +-- server/main-channel-client.h | 4 +- server/main-channel.c | 241 ++++++----- server/main-channel.h | 44 +- server/red-channel-client-private.h | 19 + server/red-channel-client.c | 197 +++++---- server/red-channel-client.h | 6 +- server/red-channel.c | 684 ++++++++++++++++++++---------- server/red-channel.h | 190 ++++----- server/red-parse-qxl.h | 2 + server/red-qxl.c | 21 +- server/red-replay-qxl.c | 2 +- server/red-worker.c | 197 ++------- server/red-worker.h | 64 --- server/reds-private.h | 3 +- server/reds.c | 67 +-- server/smartcard.c | 131 ++++-- server/sound.c | 43 +- server/spicevmc.c | 339 ++++++++++----- server/stream.c | 67 +-- server/stream.h | 3 - 39 files changed, 2753 insertions(+), 1573 deletions(-) create mode 100644 server/common-graphics-channel.c create mode 100644 server/common-graphics-channel.h create mode 100644 server/display-channel-private.h create mode 100644 server/dummy-channel.c create mode 100644 server/dummy-channel.h diff --git a/server/Makefile.am b/server/Makefile.am index b1de055..2679082 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -73,6 +73,8 @@ libserver_la_SOURCES = \ cache-item.h \ char-device.c \ char-device.h \ + common-graphics-channel.c \ + common-graphics-channel.h \ common-graphics-channel-client.c \ common-graphics-channel-client.h \ common-graphics-channel-client-private.h \ @@ -102,6 +104,8 @@ libserver_la_SOURCES = \ red-channel-client.c \ red-channel-client.h \ red-channel-client-private.h \ + dummy-channel.c \ + dummy-channel.h \ dummy-channel-client.c \ dummy-channel-client.h \ red-common.h \ @@ -124,6 +128,7 @@ libserver_la_SOURCES = \ red-worker.h \ display-channel.c \ display-channel.h \ + display-channel-private.h \ cursor-channel-client.c \ cursor-channel-client.h \ cursor-channel.c \ diff --git a/server/common-graphics-channel-client.c b/server/common-graphics-channel-client.c index e83855b..6277d0b 100644 --- a/server/common-graphics-channel-client.c +++ b/server/common-graphics-channel-client.c @@ -19,6 +19,7 @@ #endif #include "common-graphics-channel-client-private.h" +#include "common-graphics-channel.h" #include "dcc.h" #include "red-channel-client.h" @@ -89,7 +90,7 @@ static void common_graphics_channel_client_constructed(GObject *object) self->priv->is_low_bandwidth ? WIDE_CLIENT_ACK_WINDOW : NARROW_CLIENT_ACK_WINDOW); - channel->during_target_migrate = self->priv->migration_target; + common_graphics_channel_set_during_target_migrate(channel, self->priv->migration_target); } static void common_graphics_channel_client_class_init(CommonGraphicsChannelClientClass *klass) diff --git a/server/common-graphics-channel-client.h b/server/common-graphics-channel-client.h index dc1173a..7acb3a2 100644 --- a/server/common-graphics-channel-client.h +++ b/server/common-graphics-channel-client.h @@ -21,6 +21,8 @@ #include "red-common.h" #include "red-channel-client.h" +#define COMMON_CLIENT_TIMEOUT (NSEC_PER_SEC * 30) + G_BEGIN_DECLS #define TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT common_graphics_channel_client_get_type() @@ -49,7 +51,6 @@ struct CommonGraphicsChannelClientClass GType common_graphics_channel_client_get_type(void) G_GNUC_CONST; -typedef struct CommonGraphicsChannel CommonGraphicsChannel; typedef struct RedClient RedClient; typedef struct RedsStream RedsStream; diff --git a/server/common-graphics-channel.c b/server/common-graphics-channel.c new file mode 100644 index 0000000..fe211aa --- /dev/null +++ b/server/common-graphics-channel.c @@ -0,0 +1,181 @@ +/* common-graphics-channel.c */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include "common-graphics-channel.h" +#include "common-graphics-channel-client.h" + +#define CHANNEL_RECEIVE_BUF_SIZE 1024 + +G_DEFINE_ABSTRACT_TYPE(CommonGraphicsChannel, common_graphics_channel, RED_TYPE_CHANNEL) + +#define GRAPHICS_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannelPrivate)) + +struct CommonGraphicsChannelPrivate +{ + QXLInstance *qxl; + uint8_t recv_buf[CHANNEL_RECEIVE_BUF_SIZE]; + uint32_t id_alloc; // bitfield. TODO - use this instead of shift scheme. + int during_target_migrate; /* TRUE when the client that is associated with the channel + is during migration. Turned off when the vm is started. + The flag is used to avoid sending messages that are artifacts + of the transition from stopped vm to loaded vm (e.g., recreation + of the primary surface) */ +}; + +enum { + PROP0, + PROP_QXL +}; + +static void +common_graphics_channel_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + CommonGraphicsChannel *self = COMMON_GRAPHICS_CHANNEL(object); + + switch (property_id) + { + case PROP_QXL: + g_value_set_pointer(value, self->priv->qxl); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +common_graphics_channel_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + CommonGraphicsChannel *self = COMMON_GRAPHICS_CHANNEL(object); + + switch (property_id) + { + case PROP_QXL: + self->priv->qxl = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static int common_channel_config_socket(RedChannelClient *rcc) +{ + RedClient *client = red_channel_client_get_client(rcc); + MainChannelClient *mcc = red_client_get_main(client); + RedsStream *stream = red_channel_client_get_stream(rcc); + CommonGraphicsChannelClient *ccc = COMMON_GRAPHICS_CHANNEL_CLIENT(rcc); + int flags; + int delay_val; + gboolean low_bw; + + if ((flags = fcntl(stream->socket, F_GETFL)) == -1) { + spice_warning("accept failed, %s", strerror(errno)); + return FALSE; + } + + if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) { + spice_warning("accept failed, %s", strerror(errno)); + return FALSE; + } + + // TODO - this should be dynamic, not one time at channel creation + low_bw = main_channel_client_is_low_bandwidth(mcc); + common_graphics_channel_client_set_low_bandwidth(ccc, low_bw); + delay_val = low_bw ? 0 : 1; + /* FIXME: Using Nagle's Algorithm can lead to apparent delays, depending + * on the delayed ack timeout on the other side. + * Instead of using Nagle's, we need to implement message buffering on + * the application level. + * see: http://www.stuartcheshire.org/papers/NagleDelayedAck/ + */ + if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, + sizeof(delay_val)) == -1) { + if (errno != ENOTSUP) { + spice_warning("setsockopt failed, %s", strerror(errno)); + } + } + return TRUE; +} + +static uint8_t *common_alloc_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size) +{ + RedChannel *channel = red_channel_client_get_channel(rcc); + CommonGraphicsChannel *common = COMMON_GRAPHICS_CHANNEL(channel); + + /* SPICE_MSGC_MIGRATE_DATA is the only client message whose size is dynamic */ + if (type == SPICE_MSGC_MIGRATE_DATA) { + return spice_malloc(size); + } + + if (size > CHANNEL_RECEIVE_BUF_SIZE) { + spice_critical("unexpected message size %u (max is %d)", size, CHANNEL_RECEIVE_BUF_SIZE); + return NULL; + } + return common->priv->recv_buf; +} + +static void common_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size, + uint8_t* msg) +{ + if (type == SPICE_MSGC_MIGRATE_DATA) { + free(msg); + } +} + +static void +common_graphics_channel_class_init(CommonGraphicsChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + g_type_class_add_private(klass, sizeof(CommonGraphicsChannelPrivate)); + + object_class->get_property = common_graphics_channel_get_property; + object_class->set_property = common_graphics_channel_set_property; + + channel_class->config_socket = common_channel_config_socket; + channel_class->alloc_recv_buf = common_alloc_recv_buf; + channel_class->release_recv_buf = common_release_recv_buf; + + g_object_class_install_property(object_class, + PROP_QXL, + g_param_spec_pointer("qxl", + "qxl", + "QXLInstance for this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +common_graphics_channel_init(CommonGraphicsChannel *self) +{ + self->priv = GRAPHICS_CHANNEL_PRIVATE(self); +} + +void common_graphics_channel_set_during_target_migrate(CommonGraphicsChannel *self, gboolean value) +{ + self->priv->during_target_migrate = value; +} + +gboolean common_graphics_channel_get_during_target_migrate(CommonGraphicsChannel *self) +{ + return self->priv->during_target_migrate; +} + +QXLInstance* common_graphics_channel_get_qxl(CommonGraphicsChannel *self) +{ + return self->priv->qxl; +} diff --git a/server/common-graphics-channel.h b/server/common-graphics-channel.h new file mode 100644 index 0000000..949470e --- /dev/null +++ b/server/common-graphics-channel.h @@ -0,0 +1,98 @@ +/* common-graphics-channel.h */ +/* + 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 __COMMON_GRAPHICS_CHANNEL_H__ +#define __COMMON_GRAPHICS_CHANNEL_H__ + +#include <glib-object.h> + +#include "red-channel.h" + +G_BEGIN_DECLS + +#define TYPE_COMMON_GRAPHICS_CHANNEL common_graphics_channel_get_type() + +#define COMMON_GRAPHICS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannel)) +#define COMMON_GRAPHICS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannelClass)) +#define COMMON_IS_GRAPHICS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_COMMON_GRAPHICS_CHANNEL)) +#define COMMON_IS_GRAPHICS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COMMON_GRAPHICS_CHANNEL)) +#define COMMON_GRAPHICS_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannelClass)) + +typedef struct CommonGraphicsChannel CommonGraphicsChannel; +typedef struct CommonGraphicsChannelClass CommonGraphicsChannelClass; +typedef struct CommonGraphicsChannelPrivate CommonGraphicsChannelPrivate; + +struct CommonGraphicsChannel +{ + RedChannel parent; + + CommonGraphicsChannelPrivate *priv; +}; + +struct CommonGraphicsChannelClass +{ + RedChannelClass parent_class; +}; + +GType common_graphics_channel_get_type(void) G_GNUC_CONST; + +void common_graphics_channel_set_during_target_migrate(CommonGraphicsChannel *self, gboolean value); +gboolean common_graphics_channel_get_during_target_migrate(CommonGraphicsChannel *self); +QXLInstance* common_graphics_channel_get_qxl(CommonGraphicsChannel *self); + +enum { + RED_PIPE_ITEM_TYPE_VERB = RED_PIPE_ITEM_TYPE_CHANNEL_BASE, + RED_PIPE_ITEM_TYPE_INVAL_ONE, + + RED_PIPE_ITEM_TYPE_COMMON_LAST +}; + +typedef struct RedVerbItem { + RedPipeItem base; + uint16_t verb; +} RedVerbItem; + +static inline void red_marshall_verb(RedChannelClient *rcc, RedVerbItem *item) +{ + red_channel_client_init_send_data(rcc, item->verb, NULL); +} + +static inline void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb) +{ + RedVerbItem *item = spice_new(RedVerbItem, 1); + + red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_VERB); + item->verb = verb; + red_channel_client_pipe_add(rcc, &item->base); +} + +static inline void red_pipe_add_verb_proxy(RedChannelClient *rcc, gpointer data) +{ + uint16_t verb = GPOINTER_TO_UINT(data); + red_pipe_add_verb(rcc, verb); +} + +static inline void red_pipes_add_verb(RedChannel *channel, uint16_t verb) +{ + red_channel_apply_clients_data(channel, red_pipe_add_verb_proxy, GUINT_TO_POINTER(verb)); +} + + +G_END_DECLS + +#endif /* __COMMON_GRAPHICS_CHANNEL_H__ */ diff --git a/server/cursor-channel.c b/server/cursor-channel.c index 032a1e1..26c72d8 100644 --- a/server/cursor-channel.c +++ b/server/cursor-channel.c @@ -24,6 +24,7 @@ #include "common-graphics-channel-client-private.h" #include "cursor-channel.h" #include "reds.h" +#include "red-qxl.h" enum { RED_PIPE_ITEM_TYPE_CURSOR = RED_PIPE_ITEM_TYPE_COMMON_LAST, @@ -45,9 +46,12 @@ typedef struct RedCursorPipeItem { int refs; } RedCursorPipeItem; -struct CursorChannel { - CommonGraphicsChannel common; // Must be the first thing +G_DEFINE_TYPE(CursorChannel, cursor_channel, TYPE_COMMON_GRAPHICS_CHANNEL) +#define CURSOR_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_CURSOR_CHANNEL, CursorChannelPrivate)) + +struct CursorChannelPrivate +{ CursorItem *item; int cursor_visible; SpicePoint16 cursor_position; @@ -104,10 +108,10 @@ static void cursor_item_unref(CursorItem *item) static void cursor_set_item(CursorChannel *cursor, CursorItem *item) { - if (cursor->item) - cursor_item_unref(cursor->item); + if (cursor->priv->item) + cursor_item_unref(cursor->priv->item); - cursor->item = item ? cursor_item_ref(item) : NULL; + cursor->priv->item = item ? cursor_item_ref(item) : NULL; } static RedPipeItem *new_cursor_pipe_item(RedChannelClient *rcc, void *data, int num) @@ -237,12 +241,12 @@ static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *bas cursor_channel = (CursorChannel*)red_channel_client_get_channel(rcc); 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; + msg.visible = cursor_channel->priv->cursor_visible; + msg.position = cursor_channel->priv->cursor_position; + msg.trail_length = cursor_channel->priv->cursor_trail_length; + msg.trail_frequency = cursor_channel->priv->cursor_trail_frequency; - cursor_fill(ccc, &msg.cursor, cursor_channel->item, &info); + cursor_fill(ccc, &msg.cursor, cursor_channel->priv->item, &info); spice_marshall_msg_cursor_init(base_marshaller, &msg); add_buf_from_info(base_marshaller, &info); } @@ -251,8 +255,7 @@ static void cursor_marshall(RedChannelClient *rcc, SpiceMarshaller *m, RedCursorPipeItem *cursor_pipe_item) { - CursorChannel *cursor_channel = SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), - CursorChannel, common.base); + CursorChannel *cursor_channel = CURSOR_CHANNEL(red_channel_client_get_channel(rcc)); CursorChannelClient *ccc = CURSOR_CHANNEL_CLIENT(rcc); CursorItem *item = cursor_pipe_item->cursor_item; RedPipeItem *pipe_item = &cursor_pipe_item->base; @@ -277,7 +280,7 @@ static void cursor_marshall(RedChannelClient *rcc, 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_set.visible = cursor_channel->priv->cursor_visible; cursor_fill(ccc, &cursor_set.cursor, item, &info); spice_marshall_msg_cursor_set(m, &cursor_set); @@ -378,27 +381,17 @@ static void cursor_channel_release_item(RedChannelClient *rcc, RedPipeItem *item } } -CursorChannel* cursor_channel_new(RedWorker *worker) +CursorChannel* cursor_channel_new(SpiceServer *reds, QXLInstance *qxl, + const SpiceCoreInterfaceInternal *core) { - CursorChannel *cursor_channel; - CommonGraphicsChannel *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), "cursor_channel", - SPICE_CHANNEL_CURSOR, 0, - &cbs, red_channel_client_handle_message); - - cursor_channel = (CursorChannel *)channel; - cursor_channel->cursor_visible = TRUE; - cursor_channel->mouse_mode = SPICE_MOUSE_MODE_SERVER; - - return cursor_channel; + return g_object_new(TYPE_CURSOR_CHANNEL, + "spice-server", reds, + "core-interface", core, + "channel-type", SPICE_CHANNEL_CURSOR, + "migration-flags", 0, + "qxl", qxl, + NULL); } void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd *cursor_cmd) @@ -409,35 +402,36 @@ void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd *cursor_cmd) spice_return_if_fail(cursor); spice_return_if_fail(cursor_cmd); - cursor_item = cursor_item_new(cursor->common.qxl, cursor_cmd); + cursor_item = cursor_item_new(common_graphics_channel_get_qxl(COMMON_GRAPHICS_CHANNEL(cursor)), + cursor_cmd); switch (cursor_cmd->type) { case QXL_CURSOR_SET: - cursor->cursor_visible = cursor_cmd->u.set.visible; + cursor->priv->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; + cursor_show = !cursor->priv->cursor_visible; + cursor->priv->cursor_visible = TRUE; + cursor->priv->cursor_position = cursor_cmd->u.position; break; case QXL_CURSOR_HIDE: - cursor->cursor_visible = FALSE; + cursor->priv->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; + cursor->priv->cursor_trail_length = cursor_cmd->u.trail.length; + cursor->priv->cursor_trail_frequency = cursor_cmd->u.trail.frequency; break; default: spice_warning("invalid cursor command %u", cursor_cmd->type); return; } - if (red_channel_is_connected(&cursor->common.base) && - (cursor->mouse_mode == SPICE_MOUSE_MODE_SERVER + if (red_channel_is_connected(RED_CHANNEL(cursor)) && + (cursor->priv->mouse_mode == SPICE_MOUSE_MODE_SERVER || cursor_cmd->type != QXL_CURSOR_MOVE || cursor_show)) { - red_channel_pipes_new_add(&cursor->common.base, + red_channel_pipes_new_add(RED_CHANNEL(cursor), new_cursor_pipe_item, cursor_item); } @@ -446,34 +440,34 @@ void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd *cursor_cmd) void cursor_channel_reset(CursorChannel *cursor) { - RedChannel *channel = &cursor->common.base; + RedChannel *channel = RED_CHANNEL(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; + cursor->priv->cursor_visible = TRUE; + cursor->priv->cursor_position.x = cursor->priv->cursor_position.y = 0; + cursor->priv->cursor_trail_length = cursor->priv->cursor_trail_frequency = 0; if (red_channel_is_connected(channel)) { red_channel_pipes_add_type(channel, RED_PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE); - if (!cursor->common.during_target_migrate) { + if (!common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(cursor))) { red_pipes_add_verb(channel, SPICE_MSG_CURSOR_RESET); } - if (!red_channel_wait_all_sent(&cursor->common.base, + if (!red_channel_wait_all_sent(RED_CHANNEL(cursor), COMMON_CLIENT_TIMEOUT)) { - red_channel_apply_clients(channel, + red_channel_apply_clients(RED_CHANNEL(cursor), red_channel_client_disconnect_if_pending_send); } } } -void cursor_channel_init(CursorChannel *cursor, CursorChannelClient *client) +void cursor_channel_do_init(CursorChannel *cursor, CursorChannelClient *client) { spice_return_if_fail(cursor); - if (!red_channel_is_connected(&cursor->common.base) - || COMMON_GRAPHICS_CHANNEL(cursor)->during_target_migrate) { + if (!red_channel_is_connected(RED_CHANNEL(cursor)) + || common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(cursor))) { spice_debug("during_target_migrate: skip init"); return; } @@ -489,5 +483,29 @@ void cursor_channel_set_mouse_mode(CursorChannel *cursor, uint32_t mode) { spice_return_if_fail(cursor); - cursor->mouse_mode = mode; + cursor->priv->mouse_mode = mode; +} + +static void +cursor_channel_class_init(CursorChannelClass *klass) +{ + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + g_type_class_add_private(klass, sizeof(CursorChannelPrivate)); + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_CURSOR, NULL); + channel_class->handle_parsed = red_channel_client_handle_message; + + channel_class->on_disconnect = cursor_channel_client_on_disconnect; + channel_class->send_item = cursor_channel_send_item; + channel_class->hold_item = cursor_channel_hold_pipe_item; + channel_class->release_item = cursor_channel_release_item; +} + +static void +cursor_channel_init(CursorChannel *self) +{ + self->priv = CURSOR_CHANNEL_PRIVATE(self); + self->priv->cursor_visible = TRUE; + self->priv->mouse_mode = SPICE_MOUSE_MODE_SERVER; } diff --git a/server/cursor-channel.h b/server/cursor-channel.h index 2b09b21..81ad96f 100644 --- a/server/cursor-channel.h +++ b/server/cursor-channel.h @@ -19,16 +19,47 @@ # define CURSOR_CHANNEL_H_ #include "cursor-channel-client.h" -#include "red-worker.h" +#include "common-graphics-channel.h" +#include "red-parse-qxl.h" + +G_BEGIN_DECLS + +#define TYPE_CURSOR_CHANNEL cursor_channel_get_type() + +#define CURSOR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_CURSOR_CHANNEL, CursorChannel)) +#define CURSOR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_CURSOR_CHANNEL, CursorChannelClass)) +#define IS_CURSOR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_CURSOR_CHANNEL)) +#define IS_CURSOR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_CURSOR_CHANNEL)) +#define CURSOR_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_CURSOR_CHANNEL, CursorChannelClass)) typedef struct CursorChannel CursorChannel; +typedef struct CursorChannelClass CursorChannelClass; +typedef struct CursorChannelPrivate CursorChannelPrivate; + +struct CursorChannel +{ + CommonGraphicsChannel parent; + + CursorChannelPrivate *priv; +}; + +struct CursorChannelClass +{ + CommonGraphicsChannelClass parent_class; +}; + +GType cursor_channel_get_type(void) G_GNUC_CONST; + typedef struct CursorItem CursorItem; -CursorChannel* cursor_channel_new (RedWorker *worker); +CursorChannel* cursor_channel_new (SpiceServer *reds, QXLInstance *qxl, + const SpiceCoreInterfaceInternal *core); void cursor_channel_disconnect (CursorChannel *cursor_channel); void cursor_channel_reset (CursorChannel *cursor); -void cursor_channel_init (CursorChannel *cursor, CursorChannelClient* client); +void cursor_channel_do_init (CursorChannel *cursor, CursorChannelClient* client); void cursor_channel_process_cmd (CursorChannel *cursor, RedCursorCmd *cursor_cmd); void cursor_channel_set_mouse_mode(CursorChannel *cursor, uint32_t mode); +G_END_DECLS + #endif /* CURSOR_CHANNEL_H_ */ diff --git a/server/dcc-send.c b/server/dcc-send.c index 2525753..9b9af93 100644 --- a/server/dcc-send.c +++ b/server/dcc-send.c @@ -21,6 +21,7 @@ #include "dcc-private.h" #include "display-channel.h" +#include "display-channel-private.h" #include "common/marshaller.h" #include "common/generated_server_marshallers.h" @@ -94,9 +95,9 @@ static int is_surface_area_lossy(DisplayChannelClient *dcc, uint32_t surface_id, QRegion lossy_region; DisplayChannel *display = DCC_TO_DC(dcc); - spice_return_val_if_fail(validate_surface(display, surface_id), FALSE); + spice_return_val_if_fail(display_channel_validate_surface(display, surface_id), FALSE); - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; surface_lossy_region = &dcc->priv->surface_client_lossy_region[surface_id]; if (!area) { @@ -197,8 +198,7 @@ static void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc, int is_lossy) { DisplayChannel *display_channel = - SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), DisplayChannel, - common.base); + DISPLAY_CHANNEL(red_channel_client_get_channel(rcc)); DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { @@ -210,13 +210,13 @@ static void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc, io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME; dcc->priv->send_data.pixmap_cache_items[dcc->priv->send_data.num_pixmap_cache_items++] = image->descriptor.id; - stat_inc_counter(reds, display_channel->add_to_cache_counter, 1); + stat_inc_counter(reds, display_channel->priv->add_to_cache_counter, 1); } } } if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { - stat_inc_counter(reds, display_channel->non_cache_counter, 1); + stat_inc_counter(reds, display_channel->priv->non_cache_counter, 1); } } @@ -367,7 +367,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, dcc->priv->send_data.pixmap_cache_items[dcc->priv->send_data.num_pixmap_cache_items++] = image.descriptor.id; if (can_lossy || !lossy_cache_item) { - if (!display->enable_jpeg || lossy_cache_item) { + if (!display->priv->enable_jpeg || lossy_cache_item) { image.descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE; } else { // making sure, in multiple monitor scenario, that lossy items that @@ -379,7 +379,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, &bitmap_palette_out, &lzplt_palette_out); spice_assert(bitmap_palette_out == NULL); spice_assert(lzplt_palette_out == NULL); - stat_inc_counter(reds, display->cache_hits_counter, 1); + stat_inc_counter(reds, display->priv->cache_hits_counter, 1); pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); return FILL_BITS_TYPE_CACHE; } else { @@ -396,13 +396,13 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, RedSurface *surface; surface_id = simage->u.surface.surface_id; - if (!validate_surface(display, surface_id)) { + if (!display_channel_validate_surface(display, surface_id)) { spice_warning("Invalid surface in SPICE_IMAGE_TYPE_SURFACE"); pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); return FILL_BITS_TYPE_SURFACE; } - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE; image.descriptor.flags = 0; image.descriptor.width = surface->context.width; @@ -1692,7 +1692,7 @@ static int red_marshall_stream_data(RedChannelClient *rcc, height = stream->height; } - StreamAgent *agent = &dcc->priv->stream_agents[get_stream_id(display, stream)]; + StreamAgent *agent = &dcc->priv->stream_agents[display_channel_get_stream_id(display, stream)]; uint64_t time_now = spice_get_monotonic_time_ns(); size_t outbuf_size; @@ -1739,7 +1739,7 @@ static int red_marshall_stream_data(RedChannelClient *rcc, red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL); - stream_data.base.id = get_stream_id(display, stream); + stream_data.base.id = display_channel_get_stream_id(display, stream); stream_data.base.multi_media_time = frame_mm_time; stream_data.data_size = n; @@ -1749,7 +1749,7 @@ static int red_marshall_stream_data(RedChannelClient *rcc, red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA_SIZED, NULL); - stream_data.base.id = get_stream_id(display, stream); + stream_data.base.id = display_channel_get_stream_id(display, stream); stream_data.base.multi_media_time = frame_mm_time; stream_data.data_size = n; stream_data.width = width; @@ -1822,8 +1822,7 @@ static void display_channel_marshall_migrate_data(RedChannelClient *rcc, DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); SpiceMigrateDataDisplay display_data = {0,}; - display_channel = SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), - DisplayChannel, common.base); + display_channel = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc)); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, NULL); spice_marshaller_add_uint32(base_marshaller, SPICE_MIGRATE_DATA_DISPLAY_MAGIC); @@ -1853,7 +1852,7 @@ static void display_channel_marshall_migrate_data(RedChannelClient *rcc, spice_marshaller_add(base_marshaller, (uint8_t *)&display_data, sizeof(display_data) - sizeof(uint32_t)); display_channel_marshall_migrate_data_surfaces(dcc, base_marshaller, - display_channel->enable_jpeg); + display_channel->priv->enable_jpeg); } static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc, @@ -2132,8 +2131,7 @@ static void marshall_qxl_drawable(RedChannelClient *rcc, Drawable *item = dpi->drawable; DisplayChannel *display = - SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), DisplayChannel, - common.base); + DISPLAY_CHANNEL(red_channel_client_get_channel(rcc)); spice_return_if_fail(display); /* allow sized frames to be streamed, even if they where replaced by another frame, since @@ -2141,7 +2139,7 @@ static void marshall_qxl_drawable(RedChannelClient *rcc, if ((item->stream || item->sized_stream) && red_marshall_stream_data(rcc, m, item)) { return; } - if (display->enable_jpeg) + if (display->priv->enable_jpeg) marshall_lossy_qxl_drawable(rcc, m, dpi); else marshall_lossless_qxl_drawable(rcc, m, dpi); @@ -2160,7 +2158,7 @@ static void marshall_stream_start(RedChannelClient *rcc, SpiceClipRects clip_rects; stream_create.surface_id = 0; - stream_create.id = get_stream_id(DCC_TO_DC(dcc), stream); + stream_create.id = display_channel_get_stream_id(DCC_TO_DC(dcc), stream); stream_create.flags = stream->top_down ? SPICE_STREAM_FLAGS_TOP_DOWN : 0; stream_create.codec_type = SPICE_VIDEO_CODEC_TYPE_MJPEG; @@ -2196,7 +2194,7 @@ static void marshall_stream_clip(RedChannelClient *rcc, red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CLIP, &item->base); SpiceMsgDisplayStreamClip stream_clip; - stream_clip.id = get_stream_id(DCC_TO_DC(dcc), agent->stream); + stream_clip.id = display_channel_get_stream_id(DCC_TO_DC(dcc), agent->stream); stream_clip.clip.type = item->clip_type; stream_clip.clip.rects = item->rects; @@ -2210,7 +2208,7 @@ static void marshall_stream_end(RedChannelClient *rcc, SpiceMsgDisplayStreamDestroy destroy; red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL); - destroy.id = get_stream_id(DCC_TO_DC(dcc), agent->stream); + destroy.id = display_channel_get_stream_id(DCC_TO_DC(dcc), agent->stream); stream_agent_stop(agent); spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy); } @@ -2319,7 +2317,7 @@ static void marshall_gl_scanout(RedChannelClient *rcc, { DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); DisplayChannel *display_channel = DCC_TO_DC(dcc); - QXLInstance* qxl = display_channel->common.qxl; + QXLInstance* qxl = common_graphics_channel_get_qxl(COMMON_GRAPHICS_CHANNEL(display_channel)); SpiceMsgDisplayGlScanoutUnix *scanout = red_qxl_get_gl_scanout(qxl); if (scanout != NULL) { diff --git a/server/dcc.c b/server/dcc.c index 6b5517e..d3c77f4 100644 --- a/server/dcc.c +++ b/server/dcc.c @@ -22,6 +22,7 @@ #include "dcc-private.h" #include "dcc.h" #include "display-channel.h" +#include "display-channel-private.h" #include "red-channel-client-private.h" #include "spice-server-enums.h" @@ -305,12 +306,12 @@ void dcc_create_surface(DisplayChannelClient *dcc, int surface_id) flags = is_primary_surface(DCC_TO_DC(dcc), surface_id) ? SPICE_SURFACE_FLAGS_PRIMARY : 0; /* don't send redundant create surface commands to client */ - if (!dcc || display->common.during_target_migrate || + if (!dcc || common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(display)) || dcc->priv->surface_client_created[surface_id]) { return; } channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(dcc)); - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; create = red_surface_create_item_new(channel, surface_id, surface->context.width, surface->context.height, @@ -327,7 +328,7 @@ RedImageItem *dcc_add_surface_area_image(DisplayChannelClient *dcc, int can_lossy) { DisplayChannel *display = DCC_TO_DC(dcc); - RedSurface *surface = &display->surfaces[surface_id]; + RedSurface *surface = &display->priv->surfaces[surface_id]; SpiceCanvas *canvas = surface->context.canvas; RedImageItem *item; int stride; @@ -393,7 +394,7 @@ void dcc_push_surface_image(DisplayChannelClient *dcc, int surface_id) } display = DCC_TO_DC(dcc); - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; if (!surface->context.canvas) { return; } @@ -495,7 +496,7 @@ static void dcc_init_stream_agents(DisplayChannelClient *dcc) dcc->priv->stream_agents = g_new0(StreamAgent, NUM_STREAMS); for (i = 0; i < NUM_STREAMS; i++) { StreamAgent *agent = &dcc->priv->stream_agents[i]; - agent->stream = &display->streams_buf[i]; + agent->stream = &display->priv->streams_buf[i]; region_init(&agent->vis_region); region_init(&agent->clip); red_pipe_item_init(&agent->create_item, RED_PIPE_ITEM_TYPE_STREAM_CREATE); @@ -552,7 +553,7 @@ DisplayChannelClient *dcc_new(DisplayChannel *display, static void dcc_create_all_streams(DisplayChannelClient *dcc) { - Ring *ring = &DCC_TO_DC(dcc)->streams; + Ring *ring = &DCC_TO_DC(dcc)->priv->streams; RingItem *item = ring; while ((item = ring_next(ring, item))) { @@ -606,7 +607,7 @@ void dcc_start(DisplayChannelClient *dcc) return; red_channel_client_ack_zero_messages_window(RED_CHANNEL_CLIENT(dcc)); - if (display->surfaces[0].context.canvas) { + if (display->priv->surfaces[0].context.canvas) { display_channel_current_flush(display, 0); red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE); dcc_create_surface(dcc, 0); @@ -693,7 +694,7 @@ static RedMonitorsConfigItem *red_monitors_config_item_new(RedChannel* channel, void dcc_push_monitors_config(DisplayChannelClient *dcc) { DisplayChannel *dc = DCC_TO_DC(dcc); - MonitorsConfig *monitors_config = dc->monitors_config; + MonitorsConfig *monitors_config = dc->priv->monitors_config; RedMonitorsConfigItem *mci; RedChannel *channel; @@ -709,7 +710,7 @@ void dcc_push_monitors_config(DisplayChannelClient *dcc) channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(dcc)); mci = red_monitors_config_item_new(channel, - monitors_config_ref(dc->monitors_config)); + monitors_config_ref(dc->priv->monitors_config)); red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &mci->pipe_item); red_channel_client_push(RED_CHANNEL_CLIENT(dcc)); } @@ -777,7 +778,7 @@ void dcc_destroy_surface(DisplayChannelClient *dcc, uint32_t surface_id) display = DCC_TO_DC(dcc); channel = RED_CHANNEL(display); - if (COMMON_GRAPHICS_CHANNEL(display)->during_target_migrate || + if (common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(display)) || !dcc->priv->surface_client_created[surface_id]) { return; } @@ -859,7 +860,7 @@ static int dcc_compress_image_glz(DisplayChannelClient *dcc, { DisplayChannel *display_channel = DCC_TO_DC(dcc); stat_start_time_t start_time; - stat_start_time_init(&start_time, &display_channel->zlib_glz_stat); + stat_start_time_init(&start_time, &display_channel->priv->zlib_glz_stat); spice_assert(bitmap_fmt_is_rgb(src->format)); GlzData *glz_data = &dcc->priv->glz_data; ZlibData *zlib_data; @@ -890,12 +891,12 @@ static int dcc_compress_image_glz(DisplayChannelClient *dcc, glz_drawable_instance, &glz_drawable_instance->context); - stat_compress_add(&display_channel->glz_stat, start_time, src->stride * src->y, glz_size); + stat_compress_add(&display_channel->priv->glz_stat, start_time, src->stride * src->y, glz_size); - if (!display_channel->enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) { + if (!display_channel->priv->enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) { goto glz; } - stat_start_time_init(&start_time, &display_channel->zlib_glz_stat); + stat_start_time_init(&start_time, &display_channel->priv->zlib_glz_stat); zlib_data = &dcc->priv->zlib_data; encoder_data_init(&zlib_data->data, dcc); @@ -922,7 +923,7 @@ static int dcc_compress_image_glz(DisplayChannelClient *dcc, o_comp_data->comp_buf = zlib_data->data.bufs_head; o_comp_data->comp_buf_size = zlib_size; - stat_compress_add(&display_channel->zlib_glz_stat, start_time, glz_size, zlib_size); + stat_compress_add(&display_channel->priv->zlib_glz_stat, start_time, glz_size, zlib_size); return TRUE; glz: dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB; @@ -944,7 +945,7 @@ static int dcc_compress_image_lz(DisplayChannelClient *dcc, int size; // size of the compressed data stat_start_time_t start_time; - stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->lz_stat); + stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->priv->lz_stat); #ifdef COMPRESS_DEBUG spice_info("LZ LOCAL compress"); @@ -995,7 +996,7 @@ static int dcc_compress_image_lz(DisplayChannelClient *dcc, o_comp_data->lzplt_palette = dest->u.lz_plt.palette; } - stat_compress_add(&DCC_TO_DC(dcc)->lz_stat, start_time, src->stride * src->y, + stat_compress_add(&DCC_TO_DC(dcc)->priv->lz_stat, start_time, src->stride * src->y, o_comp_data->comp_buf_size); return TRUE; } @@ -1016,7 +1017,7 @@ static int dcc_compress_image_jpeg(DisplayChannelClient *dcc, SpiceImage *dest, int stride; uint8_t *lz_out_start_byte; stat_start_time_t start_time; - stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->jpeg_alpha_stat); + stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->priv->jpeg_alpha_stat); #ifdef COMPRESS_DEBUG spice_info("JPEG compress"); @@ -1080,7 +1081,7 @@ static int dcc_compress_image_jpeg(DisplayChannelClient *dcc, SpiceImage *dest, o_comp_data->comp_buf_size = jpeg_size; o_comp_data->is_lossy = TRUE; - stat_compress_add(&DCC_TO_DC(dcc)->jpeg_stat, start_time, src->stride * src->y, + stat_compress_add(&DCC_TO_DC(dcc)->priv->jpeg_stat, start_time, src->stride * src->y, o_comp_data->comp_buf_size); return TRUE; } @@ -1122,7 +1123,7 @@ static int dcc_compress_image_jpeg(DisplayChannelClient *dcc, SpiceImage *dest, o_comp_data->comp_buf = jpeg_data->data.bufs_head; o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size; o_comp_data->is_lossy = TRUE; - stat_compress_add(&DCC_TO_DC(dcc)->jpeg_alpha_stat, start_time, src->stride * src->y, + stat_compress_add(&DCC_TO_DC(dcc)->priv->jpeg_alpha_stat, start_time, src->stride * src->y, o_comp_data->comp_buf_size); return TRUE; } @@ -1135,7 +1136,7 @@ static int dcc_compress_image_lz4(DisplayChannelClient *dcc, SpiceImage *dest, Lz4EncoderContext *lz4 = dcc->priv->lz4; int lz4_size = 0; stat_start_time_t start_time; - stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->lz4_stat); + stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->priv->lz4_stat); #ifdef COMPRESS_DEBUG spice_info("LZ4 compress"); @@ -1172,7 +1173,7 @@ static int dcc_compress_image_lz4(DisplayChannelClient *dcc, SpiceImage *dest, o_comp_data->comp_buf = lz4_data->data.bufs_head; o_comp_data->comp_buf_size = lz4_size; - stat_compress_add(&DCC_TO_DC(dcc)->lz4_stat, start_time, src->stride * src->y, + stat_compress_add(&DCC_TO_DC(dcc)->priv->lz4_stat, start_time, src->stride * src->y, o_comp_data->comp_buf_size); return TRUE; } @@ -1186,7 +1187,7 @@ static int dcc_compress_image_quic(DisplayChannelClient *dcc, SpiceImage *dest, volatile QuicImageType type; int size, stride; stat_start_time_t start_time; - stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->quic_stat); + stat_start_time_init(&start_time, &DCC_TO_DC(dcc)->priv->quic_stat); #ifdef COMPRESS_DEBUG spice_info("QUIC compress"); @@ -1246,7 +1247,7 @@ static int dcc_compress_image_quic(DisplayChannelClient *dcc, SpiceImage *dest, o_comp_data->comp_buf = quic_data->data.bufs_head; o_comp_data->comp_buf_size = size << 2; - stat_compress_add(&DCC_TO_DC(dcc)->quic_stat, start_time, src->stride * src->y, + stat_compress_add(&DCC_TO_DC(dcc)->priv->quic_stat, start_time, src->stride * src->y, o_comp_data->comp_buf_size); return TRUE; } @@ -1345,14 +1346,14 @@ int dcc_compress_image(DisplayChannelClient *dcc, stat_start_time_t start_time; int success = FALSE; - stat_start_time_init(&start_time, &display_channel->off_stat); + stat_start_time_init(&start_time, &display_channel->priv->off_stat); image_compression = get_compression_for_bitmap(src, dcc->priv->image_compression, drawable); switch (image_compression) { case SPICE_IMAGE_COMPRESSION_OFF: break; case SPICE_IMAGE_COMPRESSION_QUIC: - if (can_lossy && display_channel->enable_jpeg && + if (can_lossy && display_channel->priv->enable_jpeg && (src->format != SPICE_BITMAP_FMT_RGBA || !bitmap_has_extra_stride(src))) { success = dcc_compress_image_jpeg(dcc, dest, src, o_comp_data); break; @@ -1392,7 +1393,7 @@ lz_compress: if (!success) { uint64_t image_size = src->stride * src->y; - stat_compress_add(&display_channel->off_stat, start_time, image_size, image_size); + stat_compress_add(&display_channel->priv->off_stat, start_time, image_size, image_size); } return success; @@ -1739,15 +1740,15 @@ int dcc_handle_migrate_data(DisplayChannelClient *dcc, uint32_t size, void *mess if (migrate_data->low_bandwidth_setting) { red_channel_client_ack_set_client_window(RED_CHANNEL_CLIENT(dcc), WIDE_CLIENT_ACK_WINDOW); if (dcc->priv->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) { - display->enable_jpeg = TRUE; + display->priv->enable_jpeg = TRUE; } if (dcc->priv->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) { - display->enable_zlib_glz_wrap = TRUE; + display->priv->enable_zlib_glz_wrap = TRUE; } } surfaces = (uint8_t *)message + migrate_data->surfaces_at_client_ptr; - surfaces_restored = display->enable_jpeg ? + surfaces_restored = display->priv->enable_jpeg ? restore_surfaces_lossy(dcc, (MigrateDisplaySurfacesAtClientLossy *)surfaces) : restore_surfaces_lossless(dcc, (MigrateDisplaySurfacesAtClientLossless*)surfaces); diff --git a/server/dcc.h b/server/dcc.h index 9567afd..ab51047 100644 --- a/server/dcc.h +++ b/server/dcc.h @@ -21,7 +21,7 @@ #include <glib-object.h> #include "image-cache.h" #include "pixmap-cache.h" -#include "red-worker.h" +#include "common-graphics-channel-client.h" #include "display-limits.h" G_BEGIN_DECLS @@ -69,7 +69,6 @@ GType display_channel_client_get_type(void) G_GNUC_CONST; #define MAX_PIPE_SIZE 50 -/* FIXME: remove */ typedef struct DisplayChannel DisplayChannel; typedef struct Stream Stream; typedef struct StreamAgent StreamAgent; @@ -87,7 +86,6 @@ typedef struct FreeList { WaitForChannels wait; } FreeList; -#define DCC_TO_WORKER(dcc) ((RedWorker*)((CommonGraphicsChannel*)(red_channel_client_get_channel((RedChannelClient*)dcc)))->worker) #define DCC_TO_DC(dcc) ((DisplayChannel*)red_channel_client_get_channel((RedChannelClient*)dcc)) typedef struct RedSurfaceCreateItem { diff --git a/server/display-channel-private.h b/server/display-channel-private.h new file mode 100644 index 0000000..039a93d --- /dev/null +++ b/server/display-channel-private.h @@ -0,0 +1,81 @@ +/* + Copyright (C) 2009-2015 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 DISPLAY_CHANNEL_PRIVATE_H_ +#define DISPLAY_CHANNEL_PRIVATE_H_ + +struct DisplayChannelPrivate +{ + DisplayChannel *pub; + + uint32_t bits_unique; + + MonitorsConfig *monitors_config; + + uint32_t renderer; + int enable_jpeg; + int enable_zlib_glz_wrap; + + Ring current_list; // of TreeItem + uint32_t current_size; + + uint32_t drawable_count; + _Drawable *drawables; + _Drawable *free_drawables; + + int stream_video; + uint32_t stream_count; + Stream *streams_buf; + Stream *free_streams; + Ring streams; + ItemTrace *items_trace; + uint32_t next_item_trace; + uint64_t streams_size_total; + + RedSurface *surfaces; + uint32_t n_surfaces; + SpiceImageSurfaces image_surfaces; + + ImageCache *image_cache; + RedCompressBuf *free_compress_bufs; + + int gl_draw_async_count; + +/* TODO: some day unify this, make it more runtime.. */ + stat_info_t add_stat; + stat_info_t exclude_stat; + stat_info_t __exclude_stat; +#ifdef RED_WORKER_STAT + uint32_t add_count; + uint32_t add_with_shadow_count; +#endif +#ifdef RED_STATISTICS + uint64_t *cache_hits_counter; + uint64_t *add_to_cache_counter; + uint64_t *non_cache_counter; +#endif + stat_info_t off_stat; + stat_info_t lz_stat; + stat_info_t glz_stat; + stat_info_t quic_stat; + stat_info_t jpeg_stat; + stat_info_t zlib_glz_stat; + stat_info_t jpeg_alpha_stat; + stat_info_t lz4_stat; +}; + +#endif /* DISPLAY_CHANNEL_PRIVATE_H_ */ diff --git a/server/display-channel.c b/server/display-channel.c index 352058a..91b4255 100644 --- a/server/display-channel.c +++ b/server/display-channel.c @@ -19,6 +19,138 @@ #endif #include "display-channel.h" +#include "display-channel-private.h" + +G_DEFINE_TYPE(DisplayChannel, display_channel, TYPE_COMMON_GRAPHICS_CHANNEL) + +#define DISPLAY_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_DISPLAY_CHANNEL, DisplayChannelPrivate)) + +enum { + PROP0, + PROP_N_SURFACES +}; + +static void +display_channel_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + DisplayChannel *self = DISPLAY_CHANNEL(object); + + switch (property_id) + { + case PROP_N_SURFACES: + g_value_set_uint(value, self->priv->n_surfaces); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +display_channel_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + DisplayChannel *self = DISPLAY_CHANNEL(object); + + switch (property_id) + { + case PROP_N_SURFACES: + self->priv->n_surfaces = g_value_get_uint(value); + if (self->priv->surfaces == NULL) + self->priv->surfaces = g_new0(RedSurface, self->priv->n_surfaces); + else + self->priv->surfaces = g_renew(RedSurface, self->priv->surfaces, self->priv->n_surfaces); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +display_channel_finalize(GObject *object) +{ + DisplayChannel *self = DISPLAY_CHANNEL(object); + + G_OBJECT_CLASS(display_channel_parent_class)->finalize(object); + + g_free(self->priv->drawables); + g_free(self->priv->surfaces); + g_free(self->priv->streams_buf); + g_free(self->priv->items_trace); + g_free(self->priv->image_cache); +} + +static void +display_channel_constructed(GObject *object) +{ + DisplayChannel *self = DISPLAY_CHANNEL(object); + + G_OBJECT_CLASS(display_channel_parent_class)->constructed(object); + + self->priv->renderer = RED_RENDERER_INVALID; + + stat_init(&self->priv->add_stat, "add", CLOCK_THREAD_CPUTIME_ID); + stat_init(&self->priv->exclude_stat, "exclude", CLOCK_THREAD_CPUTIME_ID); + stat_init(&self->priv->__exclude_stat, "__exclude", CLOCK_THREAD_CPUTIME_ID); +#ifdef RED_STATISTICS + QXLInstance *qxl = common_graphics_channel_get_qxl(&self->parent); + RedsState *reds = red_qxl_get_server(qxl->st); + RedChannel *channel = RED_CHANNEL(self); + self->priv->cache_hits_counter = + stat_add_counter(reds, red_channel_get_stat_node(channel), + "cache_hits", TRUE); + self->priv->add_to_cache_counter = + stat_add_counter(reds, red_channel_get_stat_node(channel), + "add_to_cache", TRUE); + self->priv->non_cache_counter = + stat_add_counter(reds, red_channel_get_stat_node(channel), + "non_cache", TRUE); +#endif + image_cache_init(self->priv->image_cache); + self->priv->stream_video = SPICE_STREAM_VIDEO_OFF; + display_channel_init_streams(self); +} + +static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces, uint32_t surface_id) +{ + DisplayChannelPrivate *p = SPICE_CONTAINEROF(surfaces, DisplayChannelPrivate, image_surfaces); + DisplayChannel *display = p->pub; + + spice_return_val_if_fail(display_channel_validate_surface(display, surface_id), NULL); + + return p->surfaces[surface_id].context.canvas; +} + +static void drawables_init(DisplayChannel *display); +static void +display_channel_init(DisplayChannel *self) +{ + static SpiceImageSurfacesOps image_surfaces_ops = { + image_surfaces_get, + }; + + self->priv = DISPLAY_CHANNEL_PRIVATE(self); + self->priv->pub = self; + + stat_compress_init(&self->priv->lz_stat, "lz", CLOCK_THREAD_CPUTIME_ID); + stat_compress_init(&self->priv->glz_stat, "glz", CLOCK_THREAD_CPUTIME_ID); + stat_compress_init(&self->priv->quic_stat, "quic", CLOCK_THREAD_CPUTIME_ID); + stat_compress_init(&self->priv->jpeg_stat, "jpeg", CLOCK_THREAD_CPUTIME_ID); + stat_compress_init(&self->priv->zlib_glz_stat, "zlib", CLOCK_THREAD_CPUTIME_ID); + stat_compress_init(&self->priv->jpeg_alpha_stat, "jpeg_alpha", CLOCK_THREAD_CPUTIME_ID); + stat_compress_init(&self->priv->lz4_stat, "lz4", CLOCK_THREAD_CPUTIME_ID); + + ring_init(&self->priv->current_list); + drawables_init(self); + self->priv->image_surfaces.ops = &image_surfaces_ops; + self->priv->streams_buf = g_new0(Stream, NUM_STREAMS); + self->priv->items_trace = g_new0(ItemTrace, NUM_TRACE_ITEMS); + self->priv->image_cache = g_new0(ImageCache, 1); +} static void drawable_draw(DisplayChannel *display, Drawable *drawable); @@ -26,7 +158,7 @@ uint32_t display_channel_generate_uid(DisplayChannel *display) { spice_return_val_if_fail(display != NULL, 0); - return ++display->bits_unique; + return ++display->priv->bits_unique; } #define stat_start(stat, var) \ @@ -36,107 +168,111 @@ void display_channel_compress_stats_reset(DisplayChannel *display) { spice_return_if_fail(display); - stat_reset(&display->off_stat); - stat_reset(&display->quic_stat); - stat_reset(&display->lz_stat); - stat_reset(&display->glz_stat); - stat_reset(&display->jpeg_stat); - stat_reset(&display->zlib_glz_stat); - stat_reset(&display->jpeg_alpha_stat); - stat_reset(&display->lz4_stat); + stat_reset(&display->priv->off_stat); + stat_reset(&display->priv->quic_stat); + stat_reset(&display->priv->lz_stat); + stat_reset(&display->priv->glz_stat); + stat_reset(&display->priv->jpeg_stat); + stat_reset(&display->priv->zlib_glz_stat); + stat_reset(&display->priv->jpeg_alpha_stat); + stat_reset(&display->priv->lz4_stat); } -void display_channel_compress_stats_print(const DisplayChannel *display_channel) +void display_channel_compress_stats_print(DisplayChannel *display_channel) { spice_return_if_fail(display_channel); #ifdef COMPRESS_STAT + DisplayChannelPrivate *priv = display_channel->priv; uint64_t glz_enc_size; + uint32_t id; + + glz_enc_size = priv->enable_zlib_glz_wrap ? + priv->zlib_glz_stat.comp_size : + priv->glz_stat.comp_size; - glz_enc_size = display_channel->enable_zlib_glz_wrap ? - display_channel->zlib_glz_stat.comp_size : - display_channel->glz_stat.comp_size; + g_object_get(display_channel, "id", &id, NULL); - spice_info("==> Compression stats for display %u", display_channel->common.base.id); + spice_info("==> Compression stats for display %u", id); spice_info("Method \t count \torig_size(MB)\tenc_size(MB)\tenc_time(s)"); spice_info("OFF \t%8d\t%13.2f\t%12.2f\t%12.2f", - display_channel->off_stat.count, - stat_byte_to_mega(display_channel->off_stat.orig_size), - stat_byte_to_mega(display_channel->off_stat.comp_size), - stat_cpu_time_to_sec(display_channel->off_stat.total) + priv->off_stat.count, + stat_byte_to_mega(priv->off_stat.orig_size), + stat_byte_to_mega(priv->off_stat.comp_size), + stat_cpu_time_to_sec(priv->off_stat.total) ); spice_info("QUIC \t%8d\t%13.2f\t%12.2f\t%12.2f", - display_channel->quic_stat.count, - stat_byte_to_mega(display_channel->quic_stat.orig_size), - stat_byte_to_mega(display_channel->quic_stat.comp_size), - stat_cpu_time_to_sec(display_channel->quic_stat.total) + priv->quic_stat.count, + stat_byte_to_mega(priv->quic_stat.orig_size), + stat_byte_to_mega(priv->quic_stat.comp_size), + stat_cpu_time_to_sec(priv->quic_stat.total) ); spice_info("GLZ \t%8d\t%13.2f\t%12.2f\t%12.2f", - display_channel->glz_stat.count, - stat_byte_to_mega(display_channel->glz_stat.orig_size), - stat_byte_to_mega(display_channel->glz_stat.comp_size), - stat_cpu_time_to_sec(display_channel->glz_stat.total) + priv->glz_stat.count, + stat_byte_to_mega(priv->glz_stat.orig_size), + stat_byte_to_mega(priv->glz_stat.comp_size), + stat_cpu_time_to_sec(priv->glz_stat.total) ); spice_info("ZLIB GLZ \t%8d\t%13.2f\t%12.2f\t%12.2f", - display_channel->zlib_glz_stat.count, - stat_byte_to_mega(display_channel->zlib_glz_stat.orig_size), - stat_byte_to_mega(display_channel->zlib_glz_stat.comp_size), - stat_cpu_time_to_sec(display_channel->zlib_glz_stat.total) + priv->zlib_glz_stat.count, + stat_byte_to_mega(priv->zlib_glz_stat.orig_size), + stat_byte_to_mega(priv->zlib_glz_stat.comp_size), + stat_cpu_time_to_sec(priv->zlib_glz_stat.total) ); spice_info("LZ \t%8d\t%13.2f\t%12.2f\t%12.2f", - display_channel->lz_stat.count, - stat_byte_to_mega(display_channel->lz_stat.orig_size), - stat_byte_to_mega(display_channel->lz_stat.comp_size), - stat_cpu_time_to_sec(display_channel->lz_stat.total) + priv->lz_stat.count, + stat_byte_to_mega(priv->lz_stat.orig_size), + stat_byte_to_mega(priv->lz_stat.comp_size), + stat_cpu_time_to_sec(priv->lz_stat.total) ); spice_info("JPEG \t%8d\t%13.2f\t%12.2f\t%12.2f", - display_channel->jpeg_stat.count, - stat_byte_to_mega(display_channel->jpeg_stat.orig_size), - stat_byte_to_mega(display_channel->jpeg_stat.comp_size), - stat_cpu_time_to_sec(display_channel->jpeg_stat.total) + priv->jpeg_stat.count, + stat_byte_to_mega(priv->jpeg_stat.orig_size), + stat_byte_to_mega(priv->jpeg_stat.comp_size), + stat_cpu_time_to_sec(priv->jpeg_stat.total) ); spice_info("JPEG-RGBA\t%8d\t%13.2f\t%12.2f\t%12.2f", - display_channel->jpeg_alpha_stat.count, - stat_byte_to_mega(display_channel->jpeg_alpha_stat.orig_size), - stat_byte_to_mega(display_channel->jpeg_alpha_stat.comp_size), - stat_cpu_time_to_sec(display_channel->jpeg_alpha_stat.total) + priv->jpeg_alpha_stat.count, + stat_byte_to_mega(priv->jpeg_alpha_stat.orig_size), + stat_byte_to_mega(priv->jpeg_alpha_stat.comp_size), + stat_cpu_time_to_sec(priv->jpeg_alpha_stat.total) ); spice_info("LZ4 \t%8d\t%13.2f\t%12.2f\t%12.2f", - display_channel->lz4_stat.count, - stat_byte_to_mega(display_channel->lz4_stat.orig_size), - stat_byte_to_mega(display_channel->lz4_stat.comp_size), - stat_cpu_time_to_sec(display_channel->lz4_stat.total) + priv->lz4_stat.count, + stat_byte_to_mega(priv->lz4_stat.orig_size), + stat_byte_to_mega(priv->lz4_stat.comp_size), + stat_cpu_time_to_sec(priv->lz4_stat.total) ); spice_info("-------------------------------------------------------------------"); spice_info("Total \t%8d\t%13.2f\t%12.2f\t%12.2f", - display_channel->lz_stat.count + display_channel->glz_stat.count + - display_channel->off_stat.count + - display_channel->quic_stat.count + - display_channel->jpeg_stat.count + - display_channel->lz4_stat.count + - display_channel->jpeg_alpha_stat.count, - stat_byte_to_mega(display_channel->lz_stat.orig_size + - display_channel->glz_stat.orig_size + - display_channel->off_stat.orig_size + - display_channel->quic_stat.orig_size + - display_channel->jpeg_stat.orig_size + - display_channel->lz4_stat.orig_size + - display_channel->jpeg_alpha_stat.orig_size), - stat_byte_to_mega(display_channel->lz_stat.comp_size + + display_channel->priv->lz_stat.count + display_channel->priv->glz_stat.count + + priv->off_stat.count + + priv->quic_stat.count + + priv->jpeg_stat.count + + priv->lz4_stat.count + + priv->jpeg_alpha_stat.count, + stat_byte_to_mega(priv->lz_stat.orig_size + + priv->glz_stat.orig_size + + priv->off_stat.orig_size + + priv->quic_stat.orig_size + + priv->jpeg_stat.orig_size + + priv->lz4_stat.orig_size + + priv->jpeg_alpha_stat.orig_size), + stat_byte_to_mega(priv->lz_stat.comp_size + glz_enc_size + - display_channel->off_stat.comp_size + - display_channel->quic_stat.comp_size + - display_channel->jpeg_stat.comp_size + - display_channel->lz4_stat.comp_size + - display_channel->jpeg_alpha_stat.comp_size), - stat_cpu_time_to_sec(display_channel->lz_stat.total + - display_channel->glz_stat.total + - display_channel->zlib_glz_stat.total + - display_channel->off_stat.total + - display_channel->quic_stat.total + - display_channel->jpeg_stat.total + - display_channel->lz4_stat.total + - display_channel->jpeg_alpha_stat.total) + priv->off_stat.comp_size + + priv->quic_stat.comp_size + + priv->jpeg_stat.comp_size + + priv->lz4_stat.comp_size + + priv->jpeg_alpha_stat.comp_size), + stat_cpu_time_to_sec(priv->lz_stat.total + + priv->glz_stat.total + + priv->zlib_glz_stat.total + + priv->off_stat.total + + priv->quic_stat.total + + priv->jpeg_stat.total + + priv->lz4_stat.total + + priv->jpeg_alpha_stat.total) ); #endif } @@ -189,7 +325,7 @@ MonitorsConfig* monitors_config_new(QXLHead *heads, ssize_t nheads, ssize_t max) int display_channel_get_streams_timeout(DisplayChannel *display) { int timeout = INT_MAX; - Ring *ring = &display->streams; + Ring *ring = &display->priv->streams; RingItem *item = ring; red_time_t now = spice_get_monotonic_time_ns(); @@ -227,12 +363,18 @@ void display_channel_set_stream_video(DisplayChannel *display, int stream_video) return; } - display->stream_video = stream_video; + display->priv->stream_video = stream_video; +} + +int display_channel_get_stream_video(DisplayChannel *display) +{ + return display->priv->stream_video; } + static void stop_streams(DisplayChannel *display) { - Ring *ring = &display->streams; + Ring *ring = &display->priv->streams; RingItem *item = ring_get_head(ring); while (item) { @@ -245,14 +387,14 @@ static void stop_streams(DisplayChannel *display) } } - display->next_item_trace = 0; - memset(display->items_trace, 0, sizeof(display->items_trace)); + display->priv->next_item_trace = 0; + memset(display->priv->items_trace, 0, NUM_TRACE_ITEMS * sizeof(*display->priv->items_trace)); } void display_channel_surface_unref(DisplayChannel *display, uint32_t surface_id) { - RedSurface *surface = &display->surfaces[surface_id]; - QXLInstance *qxl = display->common.qxl; + RedSurface *surface = &display->priv->surfaces[surface_id]; + QXLInstance *qxl = common_graphics_channel_get_qxl(&display->parent); DisplayChannelClient *dcc; GList *link, *next; @@ -283,6 +425,13 @@ void display_channel_surface_unref(DisplayChannel *display, uint32_t surface_id) spice_warn_if_fail(ring_is_empty(&surface->depend_on_me)); } +/* TODO: perhaps rename to "ready" or "realized" ? */ +bool display_channel_surface_has_canvas(DisplayChannel *display, + uint32_t surface_id) +{ + return display->priv->surfaces[surface_id].context.canvas != NULL; +} + static void streams_update_visible_region(DisplayChannel *display, Drawable *drawable) { Ring *ring; @@ -298,7 +447,7 @@ static void streams_update_visible_region(DisplayChannel *display, Drawable *dra return; } - ring = &display->streams; + ring = &display->priv->streams; item = ring_get_head(ring); while (item) { @@ -312,7 +461,7 @@ static void streams_update_visible_region(DisplayChannel *display, Drawable *dra } FOREACH_DCC(display, link, next, dcc) { - agent = dcc_get_stream_agent(dcc, get_stream_id(display, stream)); + agent = dcc_get_stream_agent(dcc, display_channel_get_stream_id(display, stream)); if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) { region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn); @@ -350,7 +499,7 @@ static void pipes_add_drawable_after(DisplayChannel *display, pipes_add_drawable(display, drawable); return; } - if (num_other_linked != display->common.base.clients_num) { + if (num_other_linked != red_channel_get_n_clients(RED_CHANNEL(display))) { GList *link, *next; spice_debug("TODO: not O(n^2)"); FOREACH_DCC(display, link, next, dcc) { @@ -374,11 +523,11 @@ static void current_add_drawable(DisplayChannel *display, RedSurface *surface; uint32_t surface_id = drawable->surface_id; - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; ring_add_after(&drawable->tree_item.base.siblings_link, pos); - ring_add(&display->current_list, &drawable->list_link); + ring_add(&display->priv->current_list, &drawable->list_link); ring_add(&surface->current_list, &drawable->surface_list_link); - display->current_size++; + display->priv->current_size++; drawable->refs++; } @@ -391,7 +540,7 @@ static void current_remove_drawable(DisplayChannel *display, Drawable *item) ring_remove(&item->list_link); ring_remove(&item->surface_list_link); drawable_unref(item); - display->current_size--; + display->priv->current_size--; } static void drawable_remove_from_pipes(Drawable *drawable) @@ -448,7 +597,7 @@ static void current_remove(DisplayChannel *display, TreeItem *item) static void current_remove_all(DisplayChannel *display, int surface_id) { - Ring *ring = &display->surfaces[surface_id].current; + Ring *ring = &display->priv->surfaces[surface_id].current; RingItem *ring_item; while ((ring_item = ring_get_head(ring))) { @@ -506,7 +655,7 @@ static int current_add_equal(DisplayChannel *display, DrawItem *item, TreeItem * /* sending the drawable to clients that already received * (or will receive) other_drawable */ - link = RED_CHANNEL(display)->clients; + link = red_channel_get_clients(RED_CHANNEL(display)); dpi_ring_item = ring_get_head(&other_drawable->pipes); /* dpi contains a sublist of dcc's, ordered the same */ while (link) { @@ -555,7 +704,7 @@ static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem *item Ring **top_ring, Drawable *frame_candidate) { QRegion and_rgn; - stat_start(&display->__exclude_stat, start_time); + stat_start(&display->priv->__exclude_stat, start_time); region_clone(&and_rgn, rgn); region_and(&and_rgn, &item->rgn); @@ -617,14 +766,14 @@ static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem *item } } region_destroy(&and_rgn); - stat_add(&display->__exclude_stat, start_time); + stat_add(&display->priv->__exclude_stat, start_time); } static void exclude_region(DisplayChannel *display, Ring *ring, RingItem *ring_item, QRegion *rgn, TreeItem **last, Drawable *frame_candidate) { Ring *top_ring; - stat_start(&display->exclude_stat, start_time); + stat_start(&display->priv->exclude_stat, start_time); if (!ring_item) { return; @@ -659,7 +808,7 @@ static void exclude_region(DisplayChannel *display, Ring *ring, RingItem *ring_i } if (region_is_empty(rgn)) { - stat_add(&display->exclude_stat, start_time); + stat_add(&display->priv->exclude_stat, start_time); return; } } @@ -667,7 +816,7 @@ static void exclude_region(DisplayChannel *display, Ring *ring, RingItem *ring_i while ((last && *last == (TreeItem *)ring_item) || !(ring_item = ring_next(ring, ring_item))) { if (ring == top_ring) { - stat_add(&display->exclude_stat, start_time); + stat_add(&display->priv->exclude_stat, start_time); return; } ring_item = &container->base.siblings_link; @@ -679,9 +828,9 @@ static void exclude_region(DisplayChannel *display, Ring *ring, RingItem *ring_i static int current_add_with_shadow(DisplayChannel *display, Ring *ring, Drawable *item) { - stat_start(&display->add_stat, start_time); + stat_start(&display->priv->add_stat, start_time); #ifdef RED_WORKER_STAT - ++display->add_with_shadow_count; + ++display->priv->add_with_shadow_count; #endif RedDrawable *red_drawable = item->red_drawable; @@ -692,7 +841,7 @@ static int current_add_with_shadow(DisplayChannel *display, Ring *ring, Drawable Shadow *shadow = shadow_new(&item->tree_item, &delta); if (!shadow) { - stat_add(&display->add_stat, start_time); + stat_add(&display->priv->add_stat, start_time); return FALSE; } // item and his shadow must initially be placed in the same container. @@ -716,7 +865,7 @@ static int current_add_with_shadow(DisplayChannel *display, Ring *ring, Drawable stream_detach_behind(display, &item->tree_item.base.rgn, item); } } - stat_add(&display->add_stat, start_time); + stat_add(&display->priv->add_stat, start_time); return TRUE; } @@ -726,7 +875,7 @@ static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable) RingItem *now; QRegion exclude_rgn; RingItem *exclude_base = NULL; - stat_start(&display->add_stat, start_time); + stat_start(&display->priv->add_stat, start_time); spice_assert(!region_is_empty(&item->base.rgn)); region_init(&exclude_rgn); @@ -748,7 +897,7 @@ static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable) if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && !(test_res & REGION_TEST_LEFT_EXCLUSIVE) && current_add_equal(display, item, sibling)) { - stat_add(&display->add_stat, start_time); + stat_add(&display->priv->add_stat, start_time); return FALSE; } @@ -834,7 +983,7 @@ static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable) } } region_destroy(&exclude_rgn); - stat_add(&display->add_stat, start_time); + stat_add(&display->priv->add_stat, start_time); return TRUE; } @@ -843,7 +992,7 @@ static bool drawable_can_stream(DisplayChannel *display, Drawable *drawable) RedDrawable *red_drawable = drawable->red_drawable; SpiceImage *image; - if (display->stream_video == SPICE_STREAM_VIDEO_OFF) { + if (display->priv->stream_video == SPICE_STREAM_VIDEO_OFF) { return FALSE; } @@ -863,7 +1012,7 @@ static bool drawable_can_stream(DisplayChannel *display, Drawable *drawable) return FALSE; } - if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) { + if (display->priv->stream_video == SPICE_STREAM_VIDEO_FILTER) { SpiceRect* rect; int size; @@ -880,26 +1029,26 @@ static bool drawable_can_stream(DisplayChannel *display, Drawable *drawable) void display_channel_print_stats(DisplayChannel *display) { #ifdef RED_WORKER_STAT - stat_time_t total = display->add_stat.total; + stat_time_t total = display->priv->add_stat.total; spice_info("add with shadow count %u", - display->add_with_shadow_count); - display->add_with_shadow_count = 0; + display->priv->add_with_shadow_count); + display->priv->add_with_shadow_count = 0; spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f", - display->add_stat.count, + display->priv->add_stat.count, stat_cpu_time_to_sec(total), - display->exclude_stat.count, - stat_cpu_time_to_sec(display->exclude_stat.total), - display->__exclude_stat.count, - stat_cpu_time_to_sec(display->__exclude_stat.total)); + display->priv->exclude_stat.count, + stat_cpu_time_to_sec(display->priv->exclude_stat.total), + display->priv->__exclude_stat.count, + stat_cpu_time_to_sec(display->priv->__exclude_stat.total)); spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%", - (double)(total - display->exclude_stat.total) / total * 100, - (double)(display->exclude_stat.total) / total * 100, - (double)(display->exclude_stat.total - - display->__exclude_stat.total) / display->exclude_stat.total * 100, - (double)(display->__exclude_stat.total) / display->exclude_stat.total * 100); - stat_reset(&display->add_stat); - stat_reset(&display->exclude_stat); - stat_reset(&display->__exclude_stat); + (double)(total - display->priv->exclude_stat.total) / total * 100, + (double)(display->priv->exclude_stat.total) / total * 100, + (double)(display->priv->exclude_stat.total - + display->priv->__exclude_stat.total) / display->priv->exclude_stat.total * 100, + (double)(display->priv->__exclude_stat.total) / display->priv->exclude_stat.total * 100); + stat_reset(&display->priv->add_stat); + stat_reset(&display->priv->exclude_stat); + stat_reset(&display->priv->__exclude_stat); #endif } @@ -914,7 +1063,7 @@ static void drawable_ref_surface_deps(DisplayChannel *display, Drawable *drawabl if (surface_id == -1) { continue; } - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; surface->refs++; } } @@ -923,7 +1072,7 @@ static void surface_read_bits(DisplayChannel *display, int surface_id, const SpiceRect *area, uint8_t *dest, int dest_stride) { SpiceCanvas *canvas; - RedSurface *surface = &display->surfaces[surface_id]; + RedSurface *surface = &display->priv->surfaces[surface_id]; canvas = surface->context.canvas; canvas->ops->read_bits(canvas, dest, dest_stride, area); @@ -941,7 +1090,7 @@ static void handle_self_bitmap(DisplayChannel *display, Drawable *drawable) int bpp; int all_set; - surface = &display->surfaces[drawable->surface_id]; + surface = &display->priv->surfaces[drawable->surface_id]; bpp = SPICE_SURFACE_FMT_DEPTH(surface->context.format) / 8; width = red_drawable->self_bitmap_area.right - red_drawable->self_bitmap_area.left; @@ -993,7 +1142,7 @@ static void surface_add_reverse_dependency(DisplayChannel *display, int surface_ return; } - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; depend_item->drawable = drawable; ring_add(&surface->depend_on_me, &depend_item->ring_item); @@ -1027,7 +1176,7 @@ static void draw_depend_on_me(DisplayChannel *display, uint32_t surface_id) RedSurface *surface; RingItem *ring_item; - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; while ((ring_item = ring_get_tail(&surface->depend_on_me))) { Drawable *drawable; @@ -1045,10 +1194,10 @@ static int validate_drawable_bbox(DisplayChannel *display, RedDrawable *drawable /* surface_id must be validated before calling into * validate_drawable_bbox */ - if (!validate_surface(display, drawable->surface_id)) { + if (!display_channel_validate_surface(display, drawable->surface_id)) { return FALSE; } - context = &display->surfaces[surface_id].context; + context = &display->priv->surfaces[surface_id].context; if (drawable->bbox.top < 0) return FALSE; @@ -1088,7 +1237,7 @@ static Drawable *display_channel_get_drawable(DisplayChannel *display, uint8_t e } for (x = 0; x < 3; ++x) { if (red_drawable->surface_deps[x] != -1 - && !validate_surface(display, red_drawable->surface_deps[x])) { + && !display_channel_validate_surface(display, red_drawable->surface_deps[x])) { return NULL; } } @@ -1102,7 +1251,7 @@ static Drawable *display_channel_get_drawable(DisplayChannel *display, uint8_t e drawable->red_drawable = red_drawable_ref(red_drawable); drawable->surface_id = red_drawable->surface_id; - display->surfaces[drawable->surface_id].refs++; + display->priv->surfaces[drawable->surface_id].refs++; memcpy(drawable->surface_deps, red_drawable->surface_deps, sizeof(drawable->surface_deps)); /* @@ -1152,7 +1301,7 @@ static void display_channel_add_drawable(DisplayChannel *display, Drawable *draw return; } - Ring *ring = &display->surfaces[surface_id].current; + Ring *ring = &display->priv->surfaces[surface_id].current; int add_to_pipe; if (has_shadow(red_drawable)) { add_to_pipe = current_add_with_shadow(display, ring, drawable); @@ -1165,7 +1314,7 @@ static void display_channel_add_drawable(DisplayChannel *display, Drawable *draw pipes_add_drawable(display, drawable); #ifdef RED_WORKER_STAT - if ((++display->add_count % 100) == 0) + if ((++display->priv->add_count % 100) == 0) display_channel_print_stats(display); #endif } @@ -1189,18 +1338,18 @@ void display_channel_process_draw(DisplayChannel *display, RedDrawable *red_draw int display_channel_wait_for_migrate_data(DisplayChannel *display) { uint64_t end_time = spice_get_monotonic_time_ns() + DISPLAY_CLIENT_MIGRATE_DATA_TIMEOUT; - RedChannel *channel = &display->common.base; RedChannelClient *rcc; int ret = FALSE; + GList *clients = red_channel_get_clients(RED_CHANNEL(display));; - if (!red_channel_is_waiting_for_migrate_data(&display->common.base)) { + if (!red_channel_is_waiting_for_migrate_data(RED_CHANNEL(display))) { return FALSE; } spice_debug(NULL); - spice_warn_if_fail(g_list_length(channel->clients) == 1); + spice_warn_if_fail(g_list_length(clients) == 1); - rcc = channel->clients->data; + rcc = clients->data; g_object_ref(rcc); for (;;) { @@ -1228,8 +1377,8 @@ void display_channel_flush_all_surfaces(DisplayChannel *display) { int x; - for (x = 0; x < NUM_SURFACES; ++x) { - if (display->surfaces[x].context.canvas) { + for (x = 0; x < display->priv->n_surfaces; ++x) { + if (display->priv->surfaces[x].context.canvas) { display_channel_current_flush(display, x); } } @@ -1261,7 +1410,7 @@ void display_channel_free_glz_drawables(DisplayChannel *display) static bool free_one_drawable(DisplayChannel *display, int force_glz_free) { - RingItem *ring_item = ring_get_tail(&display->current_list); + RingItem *ring_item = ring_get_tail(&display->priv->current_list); Drawable *drawable; Container *container; @@ -1287,7 +1436,7 @@ static bool free_one_drawable(DisplayChannel *display, int force_glz_free) void display_channel_current_flush(DisplayChannel *display, int surface_id) { - while (!ring_is_empty(&display->surfaces[surface_id].current_list)) { + while (!ring_is_empty(&display->priv->surfaces[surface_id].current_list)) { free_one_drawable(display, FALSE); } current_remove_all(display, surface_id); @@ -1299,7 +1448,7 @@ void display_channel_free_some(DisplayChannel *display) DisplayChannelClient *dcc; GList *link, *next; - spice_debug("#draw=%d, #glz_draw=%d", display->drawable_count, + spice_debug("#draw=%d, #glz_draw=%d", display->priv->drawable_count, display->glz_drawable_count); FOREACH_DCC(display, link, next, dcc) { GlzSharedDictionary *glz_dict = dcc_get_glz_dictionary(dcc); @@ -1312,7 +1461,7 @@ void display_channel_free_some(DisplayChannel *display) } } - while (!ring_is_empty(&display->current_list) && n++ < RED_RELEASE_BUNCH_SIZE) { + while (!ring_is_empty(&display->priv->current_list) && n++ < RED_RELEASE_BUNCH_SIZE) { free_one_drawable(display, TRUE); } @@ -1329,29 +1478,30 @@ static Drawable* drawable_try_new(DisplayChannel *display) { Drawable *drawable; - if (!display->free_drawables) + if (!display->priv->free_drawables) return NULL; - drawable = &display->free_drawables->u.drawable; - display->free_drawables = display->free_drawables->u.next; - display->drawable_count++; + drawable = &display->priv->free_drawables->u.drawable; + display->priv->free_drawables = display->priv->free_drawables->u.next; + display->priv->drawable_count++; return drawable; } static void drawable_free(DisplayChannel *display, Drawable *drawable) { - ((_Drawable *)drawable)->u.next = display->free_drawables; - display->free_drawables = (_Drawable *)drawable; + ((_Drawable *)drawable)->u.next = display->priv->free_drawables; + display->priv->free_drawables = (_Drawable *)drawable; } static void drawables_init(DisplayChannel *display) { int i; - display->free_drawables = NULL; + display->priv->drawables = g_new0(_Drawable, NUM_DRAWABLES); + display->priv->free_drawables = NULL; for (i = 0; i < NUM_DRAWABLES; i++) { - drawable_free(display, &display->drawables[i].u.drawable); + drawable_free(display, &display->priv->drawables[i].u.drawable); } } @@ -1449,11 +1599,12 @@ void drawable_unref(Drawable *drawable) SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->drawable = NULL; ring_remove(item); } + if (drawable->red_drawable) { red_drawable_unref(drawable->red_drawable); } drawable_free(display, drawable); - display->drawable_count--; + display->priv->drawable_count--; } static void drawable_deps_draw(DisplayChannel *display, Drawable *drawable) @@ -1478,11 +1629,11 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) drawable_deps_draw(display, drawable); - surface = &display->surfaces[drawable->surface_id]; + surface = &display->priv->surfaces[drawable->surface_id]; canvas = surface->context.canvas; spice_return_if_fail(canvas); - image_cache_aging(&display->image_cache); + image_cache_aging(display->priv->image_cache); region_add(&surface->draw_dirty_region, &drawable->red_drawable->bbox); @@ -1490,8 +1641,8 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_FILL: { SpiceFill fill = drawable->red_drawable->u.fill; SpiceImage img1, img2; - image_cache_localize_brush(&display->image_cache, &fill.brush, &img1); - image_cache_localize_mask(&display->image_cache, &fill.mask, &img2); + image_cache_localize_brush(display->priv->image_cache, &fill.brush, &img1); + image_cache_localize_mask(display->priv->image_cache, &fill.mask, &img2); canvas->ops->draw_fill(canvas, &drawable->red_drawable->bbox, &clip, &fill); break; @@ -1499,17 +1650,17 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_OPAQUE: { SpiceOpaque opaque = drawable->red_drawable->u.opaque; SpiceImage img1, img2, img3; - image_cache_localize_brush(&display->image_cache, &opaque.brush, &img1); - image_cache_localize(&display->image_cache, &opaque.src_bitmap, &img2, drawable); - image_cache_localize_mask(&display->image_cache, &opaque.mask, &img3); + image_cache_localize_brush(display->priv->image_cache, &opaque.brush, &img1); + image_cache_localize(display->priv->image_cache, &opaque.src_bitmap, &img2, drawable); + image_cache_localize_mask(display->priv->image_cache, &opaque.mask, &img3); canvas->ops->draw_opaque(canvas, &drawable->red_drawable->bbox, &clip, &opaque); break; } case QXL_DRAW_COPY: { SpiceCopy copy = drawable->red_drawable->u.copy; SpiceImage img1, img2; - image_cache_localize(&display->image_cache, ©.src_bitmap, &img1, drawable); - image_cache_localize_mask(&display->image_cache, ©.mask, &img2); + image_cache_localize(display->priv->image_cache, ©.src_bitmap, &img1, drawable); + image_cache_localize_mask(display->priv->image_cache, ©.mask, &img2); canvas->ops->draw_copy(canvas, &drawable->red_drawable->bbox, &clip, ©); break; @@ -1517,7 +1668,7 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_TRANSPARENT: { SpiceTransparent transparent = drawable->red_drawable->u.transparent; SpiceImage img1; - image_cache_localize(&display->image_cache, &transparent.src_bitmap, &img1, drawable); + image_cache_localize(display->priv->image_cache, &transparent.src_bitmap, &img1, drawable); canvas->ops->draw_transparent(canvas, &drawable->red_drawable->bbox, &clip, &transparent); break; @@ -1525,7 +1676,7 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_ALPHA_BLEND: { SpiceAlphaBlend alpha_blend = drawable->red_drawable->u.alpha_blend; SpiceImage img1; - image_cache_localize(&display->image_cache, &alpha_blend.src_bitmap, &img1, drawable); + image_cache_localize(display->priv->image_cache, &alpha_blend.src_bitmap, &img1, drawable); canvas->ops->draw_alpha_blend(canvas, &drawable->red_drawable->bbox, &clip, &alpha_blend); break; @@ -1538,8 +1689,8 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_BLEND: { SpiceBlend blend = drawable->red_drawable->u.blend; SpiceImage img1, img2; - image_cache_localize(&display->image_cache, &blend.src_bitmap, &img1, drawable); - image_cache_localize_mask(&display->image_cache, &blend.mask, &img2); + image_cache_localize(display->priv->image_cache, &blend.src_bitmap, &img1, drawable); + image_cache_localize_mask(display->priv->image_cache, &blend.mask, &img2); canvas->ops->draw_blend(canvas, &drawable->red_drawable->bbox, &clip, &blend); break; @@ -1547,7 +1698,7 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_BLACKNESS: { SpiceBlackness blackness = drawable->red_drawable->u.blackness; SpiceImage img1; - image_cache_localize_mask(&display->image_cache, &blackness.mask, &img1); + image_cache_localize_mask(display->priv->image_cache, &blackness.mask, &img1); canvas->ops->draw_blackness(canvas, &drawable->red_drawable->bbox, &clip, &blackness); break; @@ -1555,7 +1706,7 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_WHITENESS: { SpiceWhiteness whiteness = drawable->red_drawable->u.whiteness; SpiceImage img1; - image_cache_localize_mask(&display->image_cache, &whiteness.mask, &img1); + image_cache_localize_mask(display->priv->image_cache, &whiteness.mask, &img1); canvas->ops->draw_whiteness(canvas, &drawable->red_drawable->bbox, &clip, &whiteness); break; @@ -1563,7 +1714,7 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_INVERS: { SpiceInvers invers = drawable->red_drawable->u.invers; SpiceImage img1; - image_cache_localize_mask(&display->image_cache, &invers.mask, &img1); + image_cache_localize_mask(display->priv->image_cache, &invers.mask, &img1); canvas->ops->draw_invers(canvas, &drawable->red_drawable->bbox, &clip, &invers); break; @@ -1571,9 +1722,9 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_ROP3: { SpiceRop3 rop3 = drawable->red_drawable->u.rop3; SpiceImage img1, img2, img3; - image_cache_localize_brush(&display->image_cache, &rop3.brush, &img1); - image_cache_localize(&display->image_cache, &rop3.src_bitmap, &img2, drawable); - image_cache_localize_mask(&display->image_cache, &rop3.mask, &img3); + image_cache_localize_brush(display->priv->image_cache, &rop3.brush, &img1); + image_cache_localize(display->priv->image_cache, &rop3.src_bitmap, &img2, drawable); + image_cache_localize_mask(display->priv->image_cache, &rop3.mask, &img3); canvas->ops->draw_rop3(canvas, &drawable->red_drawable->bbox, &clip, &rop3); break; @@ -1581,9 +1732,9 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_COMPOSITE: { SpiceComposite composite = drawable->red_drawable->u.composite; SpiceImage src, mask; - image_cache_localize(&display->image_cache, &composite.src_bitmap, &src, drawable); + image_cache_localize(display->priv->image_cache, &composite.src_bitmap, &src, drawable); if (composite.mask_bitmap) - image_cache_localize(&display->image_cache, &composite.mask_bitmap, &mask, drawable); + image_cache_localize(display->priv->image_cache, &composite.mask_bitmap, &mask, drawable); canvas->ops->draw_composite(canvas, &drawable->red_drawable->bbox, &clip, &composite); break; @@ -1591,7 +1742,7 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_STROKE: { SpiceStroke stroke = drawable->red_drawable->u.stroke; SpiceImage img1; - image_cache_localize_brush(&display->image_cache, &stroke.brush, &img1); + image_cache_localize_brush(display->priv->image_cache, &stroke.brush, &img1); canvas->ops->draw_stroke(canvas, &drawable->red_drawable->bbox, &clip, &stroke); break; @@ -1599,8 +1750,8 @@ static void drawable_draw(DisplayChannel *display, Drawable *drawable) case QXL_DRAW_TEXT: { SpiceText text = drawable->red_drawable->u.text; SpiceImage img1, img2; - image_cache_localize_brush(&display->image_cache, &text.fore_brush, &img1); - image_cache_localize_brush(&display->image_cache, &text.back_brush, &img2); + image_cache_localize_brush(display->priv->image_cache, &text.fore_brush, &img1); + image_cache_localize_brush(display->priv->image_cache, &text.back_brush, &img2); canvas->ops->draw_text(canvas, &drawable->red_drawable->bbox, &clip, &text); break; @@ -1692,11 +1843,11 @@ void display_channel_draw_until(DisplayChannel *display, const SpiceRect *area, spice_return_if_fail(last); spice_return_if_fail(ring_item_is_linked(&last->list_link)); - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; if (surface_id != last->surface_id) { // find the nearest older drawable from the appropriate surface - ring = &display->current_list; + ring = &display->priv->current_list; ring_item = &last->list_link; while ((ring_item = ring_next(ring, ring_item))) { now = SPICE_CONTAINEROF(ring_item, Drawable, list_link); @@ -1732,12 +1883,12 @@ void display_channel_draw(DisplayChannel *display, const SpiceRect *area, int su spice_debug("surface %d: area ==>", surface_id); rect_debug(area); - spice_return_if_fail(surface_id >= 0 && surface_id < NUM_SURFACES); + spice_return_if_fail(surface_id >= 0 && surface_id < display->priv->n_surfaces); spice_return_if_fail(area); spice_return_if_fail(area->left >= 0 && area->top >= 0 && area->left < area->right && area->top < area->bottom); - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; last = current_find_intersects_rect(&surface->current_list, NULL, area); if (last) @@ -1769,12 +1920,12 @@ void display_channel_update(DisplayChannel *display, SpiceRect rect; RedSurface *surface; - spice_return_if_fail(validate_surface(display, surface_id)); + spice_return_if_fail(display_channel_validate_surface(display, surface_id)); red_get_rect_ptr(&rect, area); display_channel_draw(display, &rect, surface_id); - surface = &display->surfaces[surface_id]; + surface = &display->priv->surfaces[surface_id]; if (*qxl_dirty_rects == NULL) { *num_dirty_rects = pixman_region32_n_rects(&surface->draw_dirty_region); *qxl_dirty_rects = spice_new0(QXLRect, *num_dirty_rects); @@ -1812,9 +1963,9 @@ void display_channel_destroy_surface(DisplayChannel *display, uint32_t surface_i void display_channel_destroy_surface_wait(DisplayChannel *display, uint32_t surface_id) { - if (!validate_surface(display, surface_id)) + if (!display_channel_validate_surface(display, surface_id)) return; - if (!display->surfaces[surface_id].context.canvas) + if (!display->priv->surfaces[surface_id].context.canvas) return; draw_depend_on_me(display, surface_id); @@ -1833,16 +1984,16 @@ void display_channel_destroy_surfaces(DisplayChannel *display) spice_debug(NULL); //to handle better - for (i = 0; i < NUM_SURFACES; ++i) { - if (display->surfaces[i].context.canvas) { + for (i = 0; i < display->priv->n_surfaces; ++i) { + if (display->priv->surfaces[i].context.canvas) { display_channel_destroy_surface_wait(display, i); - if (display->surfaces[i].context.canvas) { + if (display->priv->surfaces[i].context.canvas) { display_channel_surface_unref(display, i); } - spice_assert(!display->surfaces[i].context.canvas); + spice_assert(!display->priv->surfaces[i].context.canvas); } } - spice_warn_if_fail(ring_is_empty(&display->streams)); + spice_warn_if_fail(ring_is_empty(&display->priv->streams)); if (red_channel_is_connected(RED_CHANNEL(display))) { red_channel_pipes_add_type(RED_CHANNEL(display), RED_PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE); @@ -1873,8 +2024,8 @@ create_canvas_for_surface(DisplayChannel *display, RedSurface *surface, uint32_t case RED_RENDERER_SW: canvas = canvas_create_for_data(surface->context.width, surface->context.height, surface->context.format, surface->context.line_0, surface->context.stride, - &display->image_cache.base, - &display->image_surfaces, NULL, NULL, NULL); + &display->priv->image_cache->base, + &display->priv->image_surfaces, NULL, NULL, NULL); surface->context.top_down = TRUE; surface->context.canvas_draws_on_surface = TRUE; return canvas; @@ -1889,7 +2040,7 @@ void display_channel_create_surface(DisplayChannel *display, uint32_t surface_id uint32_t height, int32_t stride, uint32_t format, void *line_0, int data_is_valid, int send_client) { - RedSurface *surface = &display->surfaces[surface_id]; + RedSurface *surface = &display->priv->surfaces[surface_id]; spice_warn_if_fail(!surface->context.canvas); @@ -1914,21 +2065,21 @@ void display_channel_create_surface(DisplayChannel *display, uint32_t surface_id region_init(&surface->draw_dirty_region); surface->refs = 1; - if (display->renderer == RED_RENDERER_INVALID) { + if (display->priv->renderer == RED_RENDERER_INVALID) { int i; - QXLInstance *qxl = display->common.qxl; + QXLInstance *qxl = common_graphics_channel_get_qxl(&display->parent); RedsState *reds = red_qxl_get_server(qxl->st); GArray *renderers = reds_get_renderers(reds); for (i = 0; i < renderers->len; i++) { uint32_t renderer = g_array_index(renderers, uint32_t, i); surface->context.canvas = create_canvas_for_surface(display, surface, renderer); if (surface->context.canvas) { - display->renderer = renderer; + display->priv->renderer = renderer; break; } } } else { - surface->context.canvas = create_canvas_for_surface(display, surface, display->renderer); + surface->context.canvas = create_canvas_for_surface(display, surface, display->priv->renderer); } spice_return_if_fail(surface->context.canvas); @@ -1952,7 +2103,7 @@ static void on_disconnect(RedChannelClient *rcc) // this was the last channel client spice_debug("#draw=%d, #glz_draw=%d", - display->drawable_count, + display->priv->drawable_count, display->glz_drawable_count); } @@ -2007,72 +2158,27 @@ static int handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *messa return dcc_handle_migrate_data(DISPLAY_CHANNEL_CLIENT(rcc), size, message); } -static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces, uint32_t surface_id) -{ - DisplayChannel *display = SPICE_CONTAINEROF(surfaces, DisplayChannel, image_surfaces); - - spice_return_val_if_fail(validate_surface(display, surface_id), NULL); - - return display->surfaces[surface_id].context.canvas; -} - -DisplayChannel* display_channel_new(SpiceServer *reds, RedWorker *worker, +DisplayChannel* display_channel_new(SpiceServer *reds, + QXLInstance *qxl, + const SpiceCoreInterfaceInternal* core, int migrate, int stream_video, uint32_t n_surfaces) { DisplayChannel *display; - ChannelCbs cbs = { - .on_disconnect = on_disconnect, - .send_item = send_item, - .hold_item = hold_item, - .release_item = release_item, - .handle_migrate_flush_mark = handle_migrate_flush_mark, - .handle_migrate_data = handle_migrate_data, - .handle_migrate_data_get_serial = handle_migrate_data_get_serial - }; - static SpiceImageSurfacesOps image_surfaces_ops = { - image_surfaces_get, - }; + /* FIXME: migrate is not used...? */ spice_info("create display channel"); - display = (DisplayChannel *)red_worker_new_channel( - worker, sizeof(*display), "display_channel", - SPICE_CHANNEL_DISPLAY, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER, - &cbs, dcc_handle_message); - spice_return_val_if_fail(display, NULL); - - clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID; - stat_init(&display->add_stat, "add", stat_clock); - stat_init(&display->exclude_stat, "exclude", stat_clock); - stat_init(&display->__exclude_stat, "__exclude", stat_clock); -#ifdef RED_STATISTICS - RedChannel *channel = RED_CHANNEL(display); - display->cache_hits_counter = stat_add_counter(reds, channel->stat, - "cache_hits", TRUE); - display->add_to_cache_counter = stat_add_counter(reds, channel->stat, - "add_to_cache", TRUE); - display->non_cache_counter = stat_add_counter(reds, channel->stat, - "non_cache", TRUE); -#endif - stat_compress_init(&display->lz_stat, "lz", stat_clock); - stat_compress_init(&display->glz_stat, "glz", stat_clock); - stat_compress_init(&display->quic_stat, "quic", stat_clock); - stat_compress_init(&display->jpeg_stat, "jpeg", stat_clock); - stat_compress_init(&display->zlib_glz_stat, "zlib", stat_clock); - stat_compress_init(&display->jpeg_alpha_stat, "jpeg_alpha", stat_clock); - stat_compress_init(&display->lz4_stat, "lz4", stat_clock); - - display->n_surfaces = n_surfaces; - display->renderer = RED_RENDERER_INVALID; - - ring_init(&display->current_list); - display->image_surfaces.ops = &image_surfaces_ops; - drawables_init(display); - image_cache_init(&display->image_cache); - display->stream_video = stream_video; - display_channel_init_streams(display); - + display = g_object_new(TYPE_DISPLAY_CHANNEL, + "spice-server", reds, + "core-interface", core, + "channel-type", SPICE_CHANNEL_DISPLAY, + "migration-flags", (SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + "qxl", qxl, + "n-surfaces", n_surfaces, + NULL); + if (display) { + display_channel_set_stream_video(display, stream_video); + } return display; } @@ -2084,11 +2190,11 @@ void display_channel_process_surface_cmd(DisplayChannel *display, RedSurfaceCmd uint8_t *data; surface_id = surface->surface_id; - if SPICE_UNLIKELY(surface_id >= display->n_surfaces) { + if SPICE_UNLIKELY(surface_id >= display->priv->n_surfaces) { return; } - red_surface = &display->surfaces[surface_id]; + red_surface = &display->priv->surfaces[surface_id]; switch (surface->type) { case QXL_SURFACE_CMD_CREATE: { @@ -2129,18 +2235,18 @@ void display_channel_update_compression(DisplayChannel *display, DisplayChannelC { gboolean is_low_bw = common_graphics_channel_client_is_low_bandwidth(COMMON_GRAPHICS_CHANNEL_CLIENT(dcc)); if (dcc_get_jpeg_state(dcc) == SPICE_WAN_COMPRESSION_AUTO) { - display->enable_jpeg = is_low_bw; + display->priv->enable_jpeg = is_low_bw; } else { - display->enable_jpeg = (dcc_get_jpeg_state(dcc) == SPICE_WAN_COMPRESSION_ALWAYS); + display->priv->enable_jpeg = (dcc_get_jpeg_state(dcc) == SPICE_WAN_COMPRESSION_ALWAYS); } if (dcc_get_zlib_glz_state(dcc) == SPICE_WAN_COMPRESSION_AUTO) { - display->enable_zlib_glz_wrap = is_low_bw; + display->priv->enable_zlib_glz_wrap = is_low_bw; } else { - display->enable_zlib_glz_wrap = (dcc_get_zlib_glz_state(dcc) == SPICE_WAN_COMPRESSION_ALWAYS); + display->priv->enable_zlib_glz_wrap = (dcc_get_zlib_glz_state(dcc) == SPICE_WAN_COMPRESSION_ALWAYS); } - spice_info("jpeg %s", display->enable_jpeg ? "enabled" : "disabled"); - spice_info("zlib-over-glz %s", display->enable_zlib_glz_wrap ? "enabled" : "disabled"); + spice_info("jpeg %s", display->priv->enable_jpeg ? "enabled" : "disabled"); + spice_info("zlib-over-glz %s", display->priv->enable_zlib_glz_wrap ? "enabled" : "disabled"); } void display_channel_gl_scanout(DisplayChannel *display) @@ -2150,9 +2256,9 @@ void display_channel_gl_scanout(DisplayChannel *display) static void set_gl_draw_async_count(DisplayChannel *display, int num) { - QXLInstance *qxl = display->common.qxl; + QXLInstance *qxl = common_graphics_channel_get_qxl(&display->parent); - display->gl_draw_async_count = num; + display->priv->gl_draw_async_count = num; if (num == 0) { red_qxl_gl_draw_async_complete(qxl); @@ -2163,7 +2269,7 @@ void display_channel_gl_draw(DisplayChannel *display, SpiceMsgDisplayGlDraw *dra { int num; - spice_return_if_fail(display->gl_draw_async_count == 0); + spice_return_if_fail(display->priv->gl_draw_async_count == 0); num = red_channel_pipes_new_add_push(RED_CHANNEL(display), dcc_gl_draw_item_new, draw); set_gl_draw_async_count(display, num); @@ -2171,5 +2277,93 @@ void display_channel_gl_draw(DisplayChannel *display, SpiceMsgDisplayGlDraw *dra void display_channel_gl_draw_done(DisplayChannel *display) { - set_gl_draw_async_count(display, display->gl_draw_async_count - 1); + set_gl_draw_async_count(display, display->priv->gl_draw_async_count - 1); +} + +int display_channel_get_stream_id(DisplayChannel *display, Stream *stream) +{ + return (int)(stream - display->priv->streams_buf); +} + +gboolean display_channel_validate_surface(DisplayChannel *display, uint32_t surface_id) +{ + if SPICE_UNLIKELY(surface_id >= display->priv->n_surfaces) { + spice_warning("invalid surface_id %u", surface_id); + return 0; + } + if (!display->priv->surfaces[surface_id].context.canvas) { + spice_warning("canvas address is %p for %d (and is NULL)\n", + &(display->priv->surfaces[surface_id].context.canvas), surface_id); + spice_warning("failed on %d", surface_id); + return 0; + } + return 1; +} + +void display_channel_update_monitors_config(DisplayChannel *display, + QXLMonitorsConfig *config, + uint16_t count, uint16_t max_allowed) +{ + if (display->priv->monitors_config) + monitors_config_unref(display->priv->monitors_config); + + display->priv->monitors_config = + monitors_config_new(config->heads, count, max_allowed); + +} + +void display_channel_set_monitors_config_to_primary(DisplayChannel *display) +{ + DrawContext *context = &display->priv->surfaces[0].context; + QXLHead head = { 0, }; + + spice_return_if_fail(display->priv->surfaces[0].context.canvas); + + if (display->priv->monitors_config) + monitors_config_unref(display->priv->monitors_config); + + head.width = context->width; + head.height = context->height; + display->priv->monitors_config = monitors_config_new(&head, 1, 1); +} + +void display_channel_reset_image_cache(DisplayChannel *self) +{ + image_cache_reset(self->priv->image_cache); +} + +static void +display_channel_class_init(DisplayChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + g_type_class_add_private(klass, sizeof(DisplayChannelPrivate)); + + object_class->get_property = display_channel_get_property; + object_class->set_property = display_channel_set_property; + object_class->constructed = display_channel_constructed; + object_class->finalize = display_channel_finalize; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL); + channel_class->handle_parsed = dcc_handle_message; + + channel_class->on_disconnect = on_disconnect; + channel_class->send_item = send_item; + channel_class->hold_item = hold_item; + channel_class->release_item = release_item; + channel_class->handle_migrate_flush_mark = handle_migrate_flush_mark; + channel_class->handle_migrate_data = handle_migrate_data; + channel_class->handle_migrate_data_get_serial = handle_migrate_data_get_serial; + + g_object_class_install_property(object_class, + PROP_N_SURFACES, + g_param_spec_uint("n-surfaces", + "number of surfaces", + "Number of surfaces for this channel", + 0, G_MAXUINT, + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); } diff --git a/server/display-channel.h b/server/display-channel.h index cb0a1e3..597d615 100644 --- a/server/display-channel.h +++ b/server/display-channel.h @@ -20,31 +20,63 @@ #include <setjmp.h> -#include "common/rect.h" -#include "reds-stream.h" #include "cache-item.h" -#include "pixmap-cache.h" #include "sw-canvas.h" -#include "stat.h" -#include "reds.h" -#include "memslot.h" -#include "red-parse-qxl.h" -#include "red-record-qxl.h" +#include "common-graphics-channel.h" +#include "common/rect.h" +#include "dcc-encoders.h" +#include "dcc.h" #include "demarshallers.h" -#include "red-channel.h" -#include "red-qxl.h" #include "dispatcher.h" #include "main-channel.h" -#include "migration-protocol.h" #include "main-dispatcher.h" +#include "memslot.h" +#include "migration-protocol.h" +#include "mjpeg-encoder.h" +#include "pixmap-cache.h" +#include "red-channel.h" +#include "red-qxl.h" +#include "red-parse-qxl.h" +#include "red-record-qxl.h" +#include "reds-stream.h" +#include "reds.h" #include "spice-bitmap-utils.h" -#include "image-cache.h" -#include "utils.h" -#include "tree.h" +#include "stat.h" #include "stream.h" -#include "dcc.h" -#include "dcc-encoders.h" +#include "tree.h" +#include "utils.h" + +G_BEGIN_DECLS +#define TYPE_DISPLAY_CHANNEL display_channel_get_type() + +#define DISPLAY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DISPLAY_CHANNEL, DisplayChannel)) +#define DISPLAY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DISPLAY_CHANNEL, DisplayChannelClass)) +#define IS_DISPLAY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DISPLAY_CHANNEL)) +#define IS_DISPLAY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DISPLAY_CHANNEL)) +#define DISPLAY_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DISPLAY_CHANNEL, DisplayChannelClass)) + +typedef struct DisplayChannel DisplayChannel; +typedef struct DisplayChannelClass DisplayChannelClass; +typedef struct DisplayChannelPrivate DisplayChannelPrivate; + +struct DisplayChannel +{ + CommonGraphicsChannel parent; + + uint32_t glz_drawable_count; + + DisplayChannelPrivate *priv; +}; + +struct DisplayChannelClass +{ + CommonGraphicsChannelClass parent_class; +}; + +GType display_channel_get_type(void) G_GNUC_CONST; + +G_END_DECLS typedef struct DependItem { Drawable *drawable; RingItem ring_item; @@ -164,68 +196,8 @@ struct _Drawable { } u; }; -struct DisplayChannel { - CommonGraphicsChannel common; // Must be the first thing - uint32_t bits_unique; - - MonitorsConfig *monitors_config; - - uint32_t renderer; - int enable_jpeg; - int enable_zlib_glz_wrap; - - Ring current_list; // of TreeItem - uint32_t current_size; - - uint32_t drawable_count; - _Drawable drawables[NUM_DRAWABLES]; - _Drawable *free_drawables; - - uint32_t glz_drawable_count; - - int stream_video; - uint32_t stream_count; - Stream streams_buf[NUM_STREAMS]; - Stream *free_streams; - Ring streams; - ItemTrace items_trace[NUM_TRACE_ITEMS]; - uint32_t next_item_trace; - uint64_t streams_size_total; - - RedSurface surfaces[NUM_SURFACES]; - uint32_t n_surfaces; - SpiceImageSurfaces image_surfaces; - - ImageCache image_cache; - RedCompressBuf *free_compress_bufs; - - int gl_draw_async_count; - -/* TODO: some day unify this, make it more runtime.. */ - stat_info_t add_stat; - stat_info_t exclude_stat; - stat_info_t __exclude_stat; -#ifdef RED_WORKER_STAT - uint32_t add_count; - uint32_t add_with_shadow_count; -#endif -#ifdef RED_STATISTICS - uint64_t *cache_hits_counter; - uint64_t *add_to_cache_counter; - uint64_t *non_cache_counter; -#endif - stat_info_t off_stat; - stat_info_t lz_stat; - stat_info_t glz_stat; - stat_info_t quic_stat; - stat_info_t jpeg_stat; - stat_info_t zlib_glz_stat; - stat_info_t jpeg_alpha_stat; - stat_info_t lz4_stat; -}; - #define FOREACH_DCC(channel, _link, _next, _data) \ - for (_link = (channel ? RED_CHANNEL(channel)->clients : NULL), \ + for (_link = (channel ? red_channel_get_clients(RED_CHANNEL(channel)) : NULL), \ _next = (_link ? _link->next : NULL), \ _data = (_link ? _link->data : NULL); \ _link; \ @@ -233,10 +205,7 @@ struct DisplayChannel { _next = (_link ? _link->next : NULL), \ _data = (_link ? _link->data : NULL)) -static inline int get_stream_id(DisplayChannel *display, Stream *stream) -{ - return (int)(stream - display->streams_buf); -} +int display_channel_get_stream_id(DisplayChannel *display, Stream *stream); typedef struct RedSurfaceDestroyItem { SpiceMsgSurfaceDestroy surface_destroy; @@ -251,7 +220,8 @@ typedef struct RedUpgradeItem { DisplayChannel* display_channel_new (SpiceServer *reds, - RedWorker *worker, + QXLInstance *qxl, + const SpiceCoreInterfaceInternal* core, int migrate, int stream_video, uint32_t n_surfaces); @@ -275,13 +245,16 @@ void display_channel_update (DisplayCha void display_channel_free_some (DisplayChannel *display); void display_channel_set_stream_video (DisplayChannel *display, int stream_video); +int display_channel_get_stream_video (DisplayChannel *display); int display_channel_get_streams_timeout (DisplayChannel *display); -void display_channel_compress_stats_print (const DisplayChannel *display); +void display_channel_compress_stats_print (DisplayChannel *display); void display_channel_compress_stats_reset (DisplayChannel *display); Drawable * display_channel_drawable_try_new (DisplayChannel *display, int process_commands_generation); void display_channel_surface_unref (DisplayChannel *display, uint32_t surface_id); +bool display_channel_surface_has_canvas (DisplayChannel *display, + uint32_t surface_id); void display_channel_current_flush (DisplayChannel *display, int surface_id); int display_channel_wait_for_migrate_data (DisplayChannel *display); @@ -306,21 +279,12 @@ void display_channel_gl_scanout (DisplayCha void display_channel_gl_draw (DisplayChannel *display, SpiceMsgDisplayGlDraw *draw); void display_channel_gl_draw_done (DisplayChannel *display); +void display_channel_update_monitors_config(DisplayChannel *display, QXLMonitorsConfig *config, + uint16_t count, uint16_t max_allowed); +void display_channel_set_monitors_config_to_primary(DisplayChannel *display); -static inline int validate_surface(DisplayChannel *display, uint32_t surface_id) -{ - if SPICE_UNLIKELY(surface_id >= display->n_surfaces) { - spice_warning("invalid surface_id %u", surface_id); - return 0; - } - if (!display->surfaces[surface_id].context.canvas) { - spice_warning("canvas address is %p for %d (and is NULL)\n", - &(display->surfaces[surface_id].context.canvas), surface_id); - spice_warning("failed on %d", surface_id); - return 0; - } - return 1; -} +gboolean display_channel_validate_surface(DisplayChannel *display, uint32_t surface_id); +void display_channel_reset_image_cache(DisplayChannel *self); static inline int is_equal_path(SpicePath *path1, SpicePath *path2) { diff --git a/server/dummy-channel-client.c b/server/dummy-channel-client.c index a0354fd..6f0b868 100644 --- a/server/dummy-channel-client.c +++ b/server/dummy-channel-client.c @@ -36,9 +36,11 @@ struct DummyChannelClientPrivate static int dummy_channel_client_pre_create_validate(RedChannel *channel, RedClient *client) { - if (red_client_get_channel(client, channel->type, channel->id)) { + uint32_t type, id; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + if (red_client_get_channel(client, type, id)) { spice_printerr("Error client %p: duplicate channel type %d id %d", - client, channel->type, channel->id); + client, type, id); return FALSE; } return TRUE; @@ -53,6 +55,9 @@ static gboolean dummy_channel_client_initable_init(GInitable *initable, RedChannelClient *rcc = RED_CHANNEL_CLIENT(self); RedClient *client = red_channel_client_get_client(rcc); RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type, id; + + g_object_get(channel, "channel-type", &type, "id", &id, NULL); pthread_mutex_lock(&client->lock); if (!dummy_channel_client_pre_create_validate(channel, client)) { @@ -60,7 +65,7 @@ static gboolean dummy_channel_client_initable_init(GInitable *initable, SPICE_SERVER_ERROR, SPICE_SERVER_ERROR_FAILED, "Client %p: duplicate channel type %d id %d", - client, channel->type, channel->id); + client, type, id); goto cleanup; } @@ -94,10 +99,12 @@ void dummy_channel_client_disconnect(RedChannelClient *rcc) DummyChannelClient *self = DUMMY_CHANNEL_CLIENT(rcc); RedChannel *channel = red_channel_client_get_channel(rcc); GList *link; + uint32_t type, id; - if (channel && (link = g_list_find(channel->clients, rcc))) { + if (channel && (link = g_list_find(red_channel_get_clients(channel), rcc))) { + g_object_get(channel, "channel-type", &type, "id", &id, NULL); spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel, - channel->type, channel->id); + type, id); red_channel_remove_client(channel, link->data); } self->priv->connected = FALSE; diff --git a/server/dummy-channel.c b/server/dummy-channel.c new file mode 100644 index 0000000..6ec7842 --- /dev/null +++ b/server/dummy-channel.c @@ -0,0 +1,58 @@ +/* dummy-channel.c */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "dummy-channel.h" + +G_DEFINE_TYPE(DummyChannel, dummy_channel, RED_TYPE_CHANNEL) + +#define DUMMY_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_DUMMY_CHANNEL, DummyChannelPrivate)) + +struct DummyChannelPrivate +{ + gpointer padding; +}; + +static void +dummy_channel_class_init(DummyChannelClass *klass) +{ + g_type_class_add_private(klass, sizeof(DummyChannelPrivate)); +} + +static void +dummy_channel_init(DummyChannel *self) +{ + self->priv = DUMMY_CHANNEL_PRIVATE(self); +} + +// TODO: red_worker can use this one +static void dummy_watch_update_mask(SpiceWatch *watch, int event_mask) +{ +} + +static SpiceWatch *dummy_watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) +{ + return NULL; // apparently allowed? +} + +static void dummy_watch_remove(SpiceWatch *watch) +{ +} + +// TODO: actually, since I also use channel_client_dummym, no need for core. Can be NULL +static const SpiceCoreInterface dummy_core = { + .watch_update_mask = dummy_watch_update_mask, + .watch_add = dummy_watch_add, + .watch_remove = dummy_watch_remove, +}; + +RedChannel *dummy_channel_new(RedsState *reds, uint32_t type, uint32_t id) +{ + return g_object_new(TYPE_DUMMY_CHANNEL, + "spice-server", reds, + "core-interface", &dummy_core, + "channel-type", type, + "id", id, + NULL); +} diff --git a/server/dummy-channel.h b/server/dummy-channel.h new file mode 100644 index 0000000..dd2f005 --- /dev/null +++ b/server/dummy-channel.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2009-2015 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 __DUMMY_CHANNEL_H__ +#define __DUMMY_CHANNEL_H__ + +#include <glib-object.h> + +#include "red-channel.h" + +G_BEGIN_DECLS + +// TODO: tmp, for channels that don't use RedChannel yet (e.g., snd channel), but +// do use the client callbacks. So the channel clients are not connected (the channel doesn't +// have list of them, but they do have a link to the channel, and the client has a list of them) + +#define TYPE_DUMMY_CHANNEL dummy_channel_get_type() + +#define DUMMY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DUMMY_CHANNEL, DummyChannel)) +#define DUMMY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DUMMY_CHANNEL, DummyChannelClass)) +#define _IS_DUMMY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DUMMY_CHANNEL)) +#define _IS_DUMMY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DUMMY_CHANNEL)) +#define DUMMY_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DUMMY_CHANNEL, DummyChannelClass)) + +typedef struct DummyChannel DummyChannel; +typedef struct DummyChannelClass DummyChannelClass; +typedef struct DummyChannelPrivate DummyChannelPrivate; + +struct DummyChannel +{ + RedChannel parent; + + DummyChannelPrivate *priv; +}; + +struct DummyChannelClass +{ + RedChannelClass parent_class; +}; + +GType dummy_channel_get_type(void) G_GNUC_CONST; + +RedChannel *dummy_channel_new(RedsState *reds, uint32_t type, uint32_t id); + +G_END_DECLS + +#endif /* __DUMMY_CHANNEL_H__ */ diff --git a/server/inputs-channel.c b/server/inputs-channel.c index c0fdc98..32e2cfe 100644 --- a/server/inputs-channel.c +++ b/server/inputs-channel.c @@ -57,6 +57,83 @@ #define RECEIVE_BUF_SIZE \ (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE) +G_DEFINE_TYPE(InputsChannel, inputs_channel, RED_TYPE_CHANNEL) + +#define CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_INPUTS_CHANNEL, InputsChannelPrivate)) + +struct InputsChannelPrivate +{ + uint8_t recv_buf[RECEIVE_BUF_SIZE]; + VDAgentMouseState mouse_state; + int src_during_migrate; + + SpiceKbdInstance *keyboard; + SpiceMouseInstance *mouse; + SpiceTabletInstance *tablet; +}; + + +static void +inputs_channel_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +inputs_channel_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void inputs_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, int migration, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps); +static void inputs_migrate(RedChannelClient *rcc); +static SpiceTimer *key_modifiers_timer; +static void key_modifiers_sender(void *opaque); + +static void +inputs_channel_constructed(GObject *object) +{ + ClientCbs client_cbs = { NULL, }; + InputsChannel *self = INPUTS_CHANNEL(object); + RedsState *reds = red_channel_get_server(RED_CHANNEL(self)); + + G_OBJECT_CLASS(inputs_channel_parent_class)->constructed(object); + + client_cbs.connect = inputs_connect; + client_cbs.migrate = inputs_migrate; + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); + + red_channel_set_cap(RED_CHANNEL(self), SPICE_INPUTS_CAP_KEY_SCANCODE); + reds_register_channel(reds, RED_CHANNEL(self)); + + if (!(key_modifiers_timer = reds_core_timer_add(reds, key_modifiers_sender, self))) { + spice_error("key modifiers timer create failed"); + } +} + +static void +inputs_channel_init(InputsChannel *self) +{ + self->priv = CHANNEL_PRIVATE(self); +} + struct SpiceKbdState { bool push_ext; @@ -101,17 +178,6 @@ RedsState* spice_tablet_state_get_server(SpiceTabletState *st) return st->reds; } -struct InputsChannel { - RedChannel base; - uint8_t recv_buf[RECEIVE_BUF_SIZE]; - VDAgentMouseState mouse_state; - int src_during_migrate; - - SpiceKbdInstance *keyboard; - SpiceMouseInstance *mouse; - SpiceTabletInstance *tablet; -}; - typedef struct RedInputsPipeItem { RedPipeItem base; } RedInputsPipeItem; @@ -126,8 +192,6 @@ typedef struct RedInputsInitPipeItem { uint8_t modifiers; } RedInputsInitPipeItem; -static SpiceTimer *key_modifiers_timer; - #define KEY_MODIFIERS_TTL (MSEC_PER_SEC * 2) @@ -139,26 +203,26 @@ void inputs_channel_set_tablet_logical_size(InputsChannel *inputs, int x_res, in { SpiceTabletInterface *sif; - sif = SPICE_CONTAINEROF(inputs->tablet->base.sif, SpiceTabletInterface, base); - sif->set_logical_size(inputs->tablet, x_res, y_res); + sif = SPICE_CONTAINEROF(inputs->priv->tablet->base.sif, SpiceTabletInterface, base); + sif->set_logical_size(inputs->priv->tablet, x_res, y_res); } VDAgentMouseState *inputs_channel_get_mouse_state(InputsChannel *inputs) { - return &inputs->mouse_state; + return &inputs->priv->mouse_state; } static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size) { - InputsChannel *inputs_channel = (InputsChannel*)red_channel_client_get_channel(rcc); + InputsChannel *inputs = (InputsChannel*)red_channel_client_get_channel(rcc); if (size > RECEIVE_BUF_SIZE) { spice_printerr("error: too large incoming message"); return NULL; } - return inputs_channel->recv_buf; + return inputs->priv->recv_buf; } static void inputs_channel_release_msg_rcv_buf(RedChannelClient *rcc, @@ -276,10 +340,10 @@ static void inputs_channel_send_item(RedChannelClient *rcc, RedPipeItem *base) static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message) { - InputsChannel *inputs_channel = (InputsChannel *)red_channel_client_get_channel(rcc); + InputsChannel *inputs = INPUTS_CHANNEL(red_channel_client_get_channel(rcc)); InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc); uint32_t i; - RedsState *reds = red_channel_get_server(&inputs_channel->base); + RedsState *reds = red_channel_get_server(RED_CHANNEL(inputs)); switch (type) { case SPICE_MSGC_INPUTS_KEY_DOWN: { @@ -297,19 +361,19 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui if (code == 0) { break; } - kbd_push_scan(inputs_channel_get_keyboard(inputs_channel), code); + kbd_push_scan(inputs_channel_get_keyboard(inputs), code); } break; } case SPICE_MSGC_INPUTS_KEY_SCANCODE: { uint8_t *code = message; for (i = 0; i < size; i++) { - kbd_push_scan(inputs_channel_get_keyboard(inputs_channel), code[i]); + kbd_push_scan(inputs_channel_get_keyboard(inputs), code[i]); } break; } case SPICE_MSGC_INPUTS_MOUSE_MOTION: { - SpiceMouseInstance *mouse = inputs_channel_get_mouse(inputs_channel); + SpiceMouseInstance *mouse = inputs_channel_get_mouse(inputs); SpiceMsgcMouseMotion *mouse_motion = message; inputs_channel_client_on_mouse_motion(icc); @@ -324,7 +388,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui } case SPICE_MSGC_INPUTS_MOUSE_POSITION: { SpiceMsgcMousePosition *pos = message; - SpiceTabletInstance *tablet = inputs_channel_get_tablet(inputs_channel); + SpiceTabletInstance *tablet = inputs_channel_get_tablet(inputs); inputs_channel_client_on_mouse_motion(icc); if (reds_get_mouse_mode(reds) != SPICE_MOUSE_MODE_CLIENT) { @@ -337,7 +401,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state)); break; } - VDAgentMouseState *mouse_state = &inputs_channel->mouse_state; + VDAgentMouseState *mouse_state = &inputs->priv->mouse_state; mouse_state->x = pos->x; mouse_state->y = pos->y; mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state); @@ -355,20 +419,20 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui } if (reds_get_mouse_mode(reds) == SPICE_MOUSE_MODE_CLIENT) { if (reds_config_get_agent_mouse(reds) && reds_has_vdagent(reds)) { - inputs_channel->mouse_state.buttons = + inputs->priv->mouse_state.buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) | (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) | (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0); - reds_handle_agent_mouse_event(reds, &inputs_channel->mouse_state); - } else if (inputs_channel_get_tablet(inputs_channel)) { + reds_handle_agent_mouse_event(reds, &inputs->priv->mouse_state); + } else if (inputs_channel_get_tablet(inputs)) { SpiceTabletInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif, SpiceTabletInterface, base); - sif->wheel(inputs_channel_get_tablet(inputs_channel), dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state)); + sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs)->base.sif, SpiceTabletInterface, base); + sif->wheel(inputs_channel_get_tablet(inputs), dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state)); } - } else if (inputs_channel_get_mouse(inputs_channel)) { + } else if (inputs_channel_get_mouse(inputs)) { SpiceMouseInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif, SpiceMouseInterface, base); - sif->motion(inputs_channel_get_mouse(inputs_channel), 0, 0, dz, + sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs)->base.sif, SpiceMouseInterface, base); + sif->motion(inputs_channel_get_mouse(inputs), 0, 0, dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state)); } break; @@ -377,18 +441,18 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui SpiceMsgcMouseRelease *mouse_release = message; if (reds_get_mouse_mode(reds) == SPICE_MOUSE_MODE_CLIENT) { if (reds_config_get_agent_mouse(reds) && reds_has_vdagent(reds)) { - inputs_channel->mouse_state.buttons = + inputs->priv->mouse_state.buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state); - reds_handle_agent_mouse_event(reds, &inputs_channel->mouse_state); - } else if (inputs_channel_get_tablet(inputs_channel)) { + reds_handle_agent_mouse_event(reds, &inputs->priv->mouse_state); + } else if (inputs_channel_get_tablet(inputs)) { SpiceTabletInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif, SpiceTabletInterface, base); - sif->buttons(inputs_channel_get_tablet(inputs_channel), RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state)); + sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs)->base.sif, SpiceTabletInterface, base); + sif->buttons(inputs_channel_get_tablet(inputs), RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state)); } - } else if (inputs_channel_get_mouse(inputs_channel)) { + } else if (inputs_channel_get_mouse(inputs)) { SpiceMouseInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif, SpiceMouseInterface, base); - sif->buttons(inputs_channel_get_mouse(inputs_channel), + sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs)->base.sif, SpiceMouseInterface, base); + sif->buttons(inputs_channel_get_mouse(inputs), RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state)); } break; @@ -396,7 +460,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui case SPICE_MSGC_INPUTS_KEY_MODIFIERS: { SpiceMsgcKeyModifiers *modifiers = message; uint8_t leds; - SpiceKbdInstance *keyboard = inputs_channel_get_keyboard(inputs_channel); + SpiceKbdInstance *keyboard = inputs_channel_get_keyboard(inputs); if (!keyboard) { break; @@ -520,17 +584,17 @@ static void inputs_connect(RedChannel *channel, RedClient *client, static void inputs_migrate(RedChannelClient *rcc) { InputsChannel *inputs = (InputsChannel*)red_channel_client_get_channel(rcc); - inputs->src_during_migrate = TRUE; + inputs->priv->src_during_migrate = TRUE; red_channel_client_default_migrate(rcc); } static void inputs_channel_push_keyboard_modifiers(InputsChannel *inputs, uint8_t modifiers) { - if (!inputs || !red_channel_is_connected(&inputs->base) || - inputs->src_during_migrate) { + if (!inputs || !red_channel_is_connected(RED_CHANNEL(inputs)) || + inputs->priv->src_during_migrate) { return; } - red_channel_pipes_new_add_push(&inputs->base, + red_channel_pipes_new_add_push(RED_CHANNEL(inputs), red_inputs_key_modifiers_item_new, (void*)&modifiers); } @@ -574,117 +638,113 @@ static int inputs_channel_handle_migrate_data(RedChannelClient *rcc, return TRUE; } -InputsChannel* inputs_channel_new(RedsState *reds) +static void +inputs_channel_class_init(InputsChannelClass *klass) { - ChannelCbs channel_cbs = { NULL, }; - ClientCbs client_cbs = { NULL, }; - InputsChannel *inputs; - - channel_cbs.config_socket = inputs_channel_config_socket; - channel_cbs.on_disconnect = inputs_channel_on_disconnect; - channel_cbs.send_item = inputs_channel_send_item; - channel_cbs.hold_item = inputs_channel_hold_pipe_item; - channel_cbs.release_item = inputs_channel_release_pipe_item; - channel_cbs.alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = inputs_channel_release_msg_rcv_buf; - channel_cbs.handle_migrate_data = inputs_channel_handle_migrate_data; - channel_cbs.handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark; - - inputs = (InputsChannel *)red_channel_create_parser( - sizeof(InputsChannel), - reds, - reds_get_core_interface(reds), - SPICE_CHANNEL_INPUTS, 0, - FALSE, /* handle_acks */ - spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL), - inputs_channel_handle_parsed, - &channel_cbs, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER); - - if (!inputs) { - spice_error("failed to allocate Inputs Channel"); - } + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); - client_cbs.connect = inputs_connect; - client_cbs.migrate = inputs_migrate; - red_channel_register_client_cbs(&inputs->base, &client_cbs, NULL); + g_type_class_add_private(klass, sizeof(InputsChannelPrivate)); - red_channel_set_cap(&inputs->base, SPICE_INPUTS_CAP_KEY_SCANCODE); - reds_register_channel(reds, &inputs->base); + object_class->get_property = inputs_channel_get_property; + object_class->set_property = inputs_channel_set_property; + object_class->constructed = inputs_channel_constructed; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL); + channel_class->handle_parsed = inputs_channel_handle_parsed; + + /* channel callbacks */ + channel_class->config_socket = inputs_channel_config_socket; + channel_class->on_disconnect = inputs_channel_on_disconnect; + channel_class->send_item = inputs_channel_send_item; + channel_class->hold_item = inputs_channel_hold_pipe_item; + channel_class->release_item = inputs_channel_release_pipe_item; + channel_class->alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf; + channel_class->release_recv_buf = inputs_channel_release_msg_rcv_buf; + channel_class->handle_migrate_data = inputs_channel_handle_migrate_data; + channel_class->handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark; +} + +InputsChannel* inputs_channel_new(RedsState *reds) +{ + return g_object_new(TYPE_INPUTS_CHANNEL, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", (int)SPICE_CHANNEL_INPUTS, + "id", 0, + "handle-acks", FALSE, + "migration-flags", (guint)(SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + NULL); - if (!(key_modifiers_timer = reds_core_timer_add(reds, key_modifiers_sender, inputs))) { - spice_error("key modifiers timer create failed"); - } - return inputs; } SpiceKbdInstance* inputs_channel_get_keyboard(InputsChannel *inputs) { - return inputs->keyboard; + return inputs->priv->keyboard; } int inputs_channel_set_keyboard(InputsChannel *inputs, SpiceKbdInstance *keyboard) { - if (inputs->keyboard) { + if (inputs->priv->keyboard) { spice_printerr("already have keyboard"); return -1; } - inputs->keyboard = keyboard; - inputs->keyboard->st = spice_kbd_state_new(red_channel_get_server(&inputs->base)); + inputs->priv->keyboard = keyboard; + inputs->priv->keyboard->st = spice_kbd_state_new(red_channel_get_server(RED_CHANNEL(inputs))); return 0; } SpiceMouseInstance* inputs_channel_get_mouse(InputsChannel *inputs) { - return inputs->mouse; + return inputs->priv->mouse; } int inputs_channel_set_mouse(InputsChannel *inputs, SpiceMouseInstance *mouse) { - if (inputs->mouse) { + if (inputs->priv->mouse) { spice_printerr("already have mouse"); return -1; } - inputs->mouse = mouse; - inputs->mouse->st = spice_mouse_state_new(); + inputs->priv->mouse = mouse; + inputs->priv->mouse->st = spice_mouse_state_new(); return 0; } SpiceTabletInstance* inputs_channel_get_tablet(InputsChannel *inputs) { - return inputs->tablet; + return inputs->priv->tablet; } int inputs_channel_set_tablet(InputsChannel *inputs, SpiceTabletInstance *tablet, RedsState *reds) { - if (inputs->tablet) { + if (inputs->priv->tablet) { spice_printerr("already have tablet"); return -1; } - inputs->tablet = tablet; - inputs->tablet->st = spice_tablet_state_new(); - inputs->tablet->st->reds = reds; + inputs->priv->tablet = tablet; + inputs->priv->tablet->st = spice_tablet_state_new(); + inputs->priv->tablet->st->reds = reds; return 0; } int inputs_channel_has_tablet(InputsChannel *inputs) { - return inputs != NULL && inputs->tablet != NULL; + return inputs != NULL && inputs->priv->tablet != NULL; } void inputs_channel_detach_tablet(InputsChannel *inputs, SpiceTabletInstance *tablet) { spice_printerr(""); - inputs->tablet = NULL; + inputs->priv->tablet = NULL; } gboolean inputs_channel_is_src_during_migrate(InputsChannel *inputs) { - return inputs->src_during_migrate; + return inputs->priv->src_during_migrate; } void inputs_channel_set_src_during_migrate(InputsChannel *inputs, gboolean value) { - inputs->src_during_migrate = value; + inputs->priv->src_during_migrate = value; } diff --git a/server/inputs-channel.h b/server/inputs-channel.h index 5317578..1dbb3a2 100644 --- a/server/inputs-channel.h +++ b/server/inputs-channel.h @@ -21,14 +21,42 @@ // Inputs channel, dealing with keyboard, mouse, tablet. // This include should only be used by reds.c and inputs-channel.c +#include <glib-object.h> #include <stdint.h> #include <spice/vd_agent.h> #include "red-channel.h" +G_BEGIN_DECLS + +#define TYPE_INPUTS_CHANNEL inputs_channel_get_type() + +#define INPUTS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_INPUTS_CHANNEL, InputsChannel)) +#define INPUTS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_INPUTS_CHANNEL, InputsChannelClass)) +#define INPUTS_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_INPUTS_CHANNEL)) +#define INPUTS_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_INPUTS_CHANNEL)) +#define INPUTS_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_INPUTS_CHANNEL, InputsChannelClass)) + typedef struct InputsChannel InputsChannel; +typedef struct InputsChannelClass InputsChannelClass; +typedef struct InputsChannelPrivate InputsChannelPrivate; + +struct InputsChannel +{ + RedChannel parent; + + InputsChannelPrivate *priv; +}; + +struct InputsChannelClass +{ + RedChannelClass parent_class; +}; + +GType inputs_channel_get_type(void) G_GNUC_CONST; InputsChannel* inputs_channel_new(RedsState *reds); + VDAgentMouseState *inputs_channel_get_mouse_state(InputsChannel *inputs); void inputs_channel_on_keyboard_leds_change(InputsChannel *inputs, uint8_t leds); void inputs_channel_set_tablet_logical_size(InputsChannel *inputs, int x_res, int y_res); @@ -53,4 +81,6 @@ enum { RED_PIPE_ITEM_MIGRATE_DATA, }; +G_END_DECLS + #endif diff --git a/server/main-channel-client.c b/server/main-channel-client.c index 82cb5c3..f18be1c 100644 --- a/server/main-channel-client.c +++ b/server/main-channel-client.c @@ -101,16 +101,6 @@ static void main_channel_client_set_property(GObject *object, } } -static void main_channel_client_dispose(GObject *object) -{ - G_OBJECT_CLASS(main_channel_client_parent_class)->dispose(object); -} - -static void main_channel_client_finalize(GObject *object) -{ - G_OBJECT_CLASS(main_channel_client_parent_class)->finalize(object); -} - static void ping_timer_cb(void *opaque); static void main_channel_client_constructed(GObject *object) { @@ -135,8 +125,6 @@ static void main_channel_client_class_init(MainChannelClientClass *klass) object_class->get_property = main_channel_client_get_property; object_class->set_property = main_channel_client_set_property; - object_class->dispose = main_channel_client_dispose; - object_class->finalize = main_channel_client_finalize; object_class->constructed = main_channel_client_constructed; g_object_class_install_property(object_class, @@ -341,15 +329,11 @@ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, spice_printerr("client %p connected: %d seamless %d", client, success, seamless); if (mcc->priv->mig_wait_connect) { - MainChannel *main_channel = SPICE_CONTAINEROF(channel, MainChannel, base); + MainChannel *main_channel = MAIN_CHANNEL(channel); mcc->priv->mig_wait_connect = FALSE; mcc->priv->mig_connect_ok = success; - spice_assert(main_channel->num_clients_mig_wait); - spice_assert(!seamless || main_channel->num_clients_mig_wait == 1); - if (!--main_channel->num_clients_mig_wait) { - reds_on_main_migrate_connected(channel->reds, seamless && success); - } + main_channel_on_migrate_connected(main_channel, seamless && success); } else { if (success) { spice_printerr("client %p MIGRATE_CANCEL", client); @@ -362,7 +346,7 @@ void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc, uint32_t src_version) { RedChannel *channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc)); - if (reds_on_migrate_dst_set_seamless(channel->reds, mcc, src_version)) { + if (reds_on_migrate_dst_set_seamless(red_channel_get_server(channel), mcc, src_version)) { mcc->priv->seamless_mig_dst = TRUE; red_channel_client_pipe_add_empty_msg(RED_CHANNEL_CLIENT(mcc), SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK); @@ -429,7 +413,10 @@ void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing *ping, red_channel_client_handle_message(rcc, size, SPICE_MSGC_PONG, ping); } #ifdef RED_STATISTICS - stat_update_value(red_channel_client_get_channel(rcc)->reds, roundtrip); + { + RedChannel *channel = red_channel_client_get_channel(rcc); + stat_update_value(red_channel_get_server(channel), roundtrip); + } #endif } @@ -470,7 +457,7 @@ void main_channel_client_migrate_dst_complete(MainChannelClient *mcc) RedChannel *channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc)); if (mcc->priv->mig_wait_prev_complete) { if (mcc->priv->mig_wait_prev_try_seamless) { - spice_assert(channel->clients_num == 1); + spice_assert(red_channel_get_n_clients(channel) == 1); red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(mcc), RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS); } else { @@ -528,9 +515,11 @@ static void do_ping_client(MainChannelClient *mcc, if (has_interval && interval > 0) { mcc->priv->ping_interval = interval * MSEC_PER_SEC; } - reds_core_timer_start(channel->reds, mcc->priv->ping_timer, mcc->priv->ping_interval); + reds_core_timer_start(red_channel_get_server(channel), + mcc->priv->ping_timer, mcc->priv->ping_interval); } else if (!strcmp(opt, "off")) { - reds_core_timer_cancel(channel->reds, mcc->priv->ping_timer); + reds_core_timer_cancel(red_channel_get_server(channel), + mcc->priv->ping_timer); } else { return; } @@ -543,11 +532,13 @@ static void ping_timer_cb(void *opaque) if (!red_channel_client_is_connected(RED_CHANNEL_CLIENT(mcc))) { spice_printerr("not connected to peer, ping off"); - reds_core_timer_cancel(channel->reds, mcc->priv->ping_timer); + reds_core_timer_cancel(red_channel_get_server(channel), + mcc->priv->ping_timer); return; } do_ping_client(mcc, NULL, 0, 0); - reds_core_timer_start(channel->reds, mcc->priv->ping_timer, mcc->priv->ping_interval); + reds_core_timer_start(red_channel_get_server(channel), + mcc->priv->ping_timer, mcc->priv->ping_interval); } #endif /* RED_STATISTICS */ @@ -611,15 +602,14 @@ uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc) void main_channel_client_migrate(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); - reds_on_main_channel_migrate(channel->reds, MAIN_CHANNEL_CLIENT(rcc)); + reds_on_main_channel_migrate(red_channel_get_server(channel), + MAIN_CHANNEL_CLIENT(rcc)); red_channel_client_default_migrate(rcc); } gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc) { RedChannelClient *rcc = RED_CHANNEL_CLIENT(mcc); - MainChannel* main_channel = SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), - MainChannel, base); if (red_channel_client_test_remote_cap(rcc, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) { RedClient *client = red_channel_client_get_client(rcc); @@ -633,7 +623,6 @@ gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc) mcc->priv->mig_wait_connect = TRUE; } mcc->priv->mig_connect_ok = FALSE; - main_channel->num_clients_mig_wait++; return TRUE; } return FALSE; diff --git a/server/main-channel-client.h b/server/main-channel-client.h index 5d284ad..50713ea 100644 --- a/server/main-channel-client.h +++ b/server/main-channel-client.h @@ -23,8 +23,6 @@ G_BEGIN_DECLS -typedef struct MainChannel MainChannel; - #define TYPE_MAIN_CHANNEL_CLIENT main_channel_client_get_type() #define MAIN_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_MAIN_CHANNEL_CLIENT, MainChannelClient)) @@ -49,6 +47,8 @@ struct MainChannelClientClass RedChannelClientClass parent_class; }; +typedef struct MainChannel MainChannel; + GType main_channel_client_get_type(void) G_GNUC_CONST; MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client, diff --git a/server/main-channel.c b/server/main-channel.c index e3d6c57..554c20d 100644 --- a/server/main-channel.c +++ b/server/main-channel.c @@ -50,15 +50,51 @@ #include "utils.h" #define ZERO_BUF_SIZE 4096 +// approximate max receive message size for main channel +#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \ + (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE) static const uint8_t zero_page[ZERO_BUF_SIZE] = {0}; + +G_DEFINE_TYPE(MainChannel, main_channel, RED_TYPE_CHANNEL) + +#define MAIN_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_MAIN_CHANNEL, MainChannelPrivate)) + +struct MainChannelPrivate +{ + uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE]; + RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?) + int num_clients_mig_wait; +}; + +static void +main_channel_constructed(GObject *object) +{ + MainChannel *self = MAIN_CHANNEL(object); + ClientCbs client_cbs = { NULL, }; + + G_OBJECT_CLASS(main_channel_parent_class)->constructed(object); + + red_channel_set_cap(RED_CHANNEL(self), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); + red_channel_set_cap(RED_CHANNEL(self), SPICE_MAIN_CAP_SEAMLESS_MIGRATE); + + client_cbs.migrate = main_channel_client_migrate; + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); +} + +static void +main_channel_init(MainChannel *self) +{ + self->priv = MAIN_CHANNEL_PRIVATE(self); +} + static void main_channel_release_pipe_item(RedChannelClient *rcc, RedPipeItem *base, int item_pushed); int main_channel_is_connected(MainChannel *main_chan) { - return red_channel_is_connected(&main_chan->base); + return red_channel_is_connected(RED_CHANNEL(main_chan)); } /* @@ -78,7 +114,7 @@ RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t c MainChannelClient *mcc; RedChannelClient *rcc; - for (link = main_chan->base.clients; link != NULL; link = link->next) { + for (link = red_channel_get_clients(RED_CHANNEL(main_chan)); link != NULL; link = link->next) { rcc = link->data; mcc = MAIN_CHANNEL_CLIENT(rcc); if (main_channel_client_get_connection_id(mcc) == connection_id) { @@ -134,7 +170,7 @@ static void main_channel_marshall_channels(RedChannelClient *rcc, RedChannel *channel = red_channel_client_get_channel(rcc); red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST, item); - channels_info = reds_msg_channels_new(channel->reds); + channels_info = reds_msg_channels_new(red_channel_get_server(channel)); spice_marshall_msg_main_channels_list(m, channels_info); free(channels_info); } @@ -167,7 +203,7 @@ void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, .is_client_mouse_allowed=is_client_mouse_allowed, }; - red_channel_pipes_new_add_push(&main_chan->base, + red_channel_pipes_new_add_push(RED_CHANNEL(main_chan), main_mouse_mode_item_new, &info); } @@ -188,10 +224,10 @@ static void main_channel_marshall_mouse_mode(RedChannelClient *rcc, void main_channel_push_agent_connected(MainChannel *main_chan) { - if (red_channel_test_remote_cap(&main_chan->base, SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) { - red_channel_pipes_add_type(&main_chan->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS); + if (red_channel_test_remote_cap(RED_CHANNEL(main_chan), SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) { + red_channel_pipes_add_type(RED_CHANNEL(main_chan), RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS); } else { - red_channel_pipes_add_empty_msg(&main_chan->base, SPICE_MSG_MAIN_AGENT_CONNECTED); + red_channel_pipes_add_empty_msg(RED_CHANNEL(main_chan), SPICE_MSG_MAIN_AGENT_CONNECTED); } } @@ -208,7 +244,7 @@ static void main_channel_marshall_agent_connected(SpiceMarshaller *m, void main_channel_push_agent_disconnected(MainChannel *main_chan) { - red_channel_pipes_add_type(&main_chan->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED); + red_channel_pipes_add_type(RED_CHANNEL(main_chan), RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED); } static void main_channel_marshall_agent_disconnected(RedChannelClient *rcc, @@ -242,7 +278,7 @@ static void main_channel_marshall_agent_data(RedChannelClient *rcc, static void main_channel_push_migrate_data_item(MainChannel *main_chan) { - red_channel_pipes_add_type(&main_chan->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA); + red_channel_pipes_add_type(RED_CHANNEL(main_chan), RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA); } static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc, @@ -251,7 +287,7 @@ static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc, { RedChannel *channel = red_channel_client_get_channel(rcc); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item); - reds_marshall_migrate_data(channel->reds, m); // TODO: from reds split. ugly separation. + reds_marshall_migrate_data(red_channel_get_server(channel), m); // TODO: from reds split. ugly separation. } static int main_channel_handle_migrate_data(RedChannelClient *rcc, @@ -262,7 +298,7 @@ static int main_channel_handle_migrate_data(RedChannelClient *rcc, SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message; /* not supported with multi-clients */ - spice_assert(channel->clients_num == 1); + spice_assert(red_channel_get_n_clients(channel) == 1); if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataMain)) { spice_printerr("bad message size %u", size); @@ -274,7 +310,9 @@ static int main_channel_handle_migrate_data(RedChannelClient *rcc, spice_error("bad header"); return FALSE; } - return reds_handle_migrate_data(channel->reds, mcc, (SpiceMigrateDataMain *)(header + 1), size); + return reds_handle_migrate_data(red_channel_get_server(channel), mcc, + (SpiceMigrateDataMain *)(header + 1), + size); } static void main_channel_marshall_init(RedChannelClient *rcc, @@ -292,7 +330,7 @@ static void main_channel_marshall_init(RedChannelClient *rcc, if (item->is_client_mouse_allowed) { init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT; } - init.agent_connected = reds_has_vdagent(channel->reds); + init.agent_connected = reds_has_vdagent(red_channel_get_server(channel)); init.agent_tokens = REDS_AGENT_WINDOW_SIZE; init.multi_media_time = item->multi_media_time; init.ram_hint = item->ram_hint; @@ -317,7 +355,7 @@ static void main_channel_marshall_notify(RedChannelClient *rcc, static void main_channel_fill_migrate_dst_info(MainChannel *main_channel, SpiceMigrationDstInfo *dst_info) { - RedsMigSpice *mig_dst = &main_channel->mig_target; + RedsMigSpice *mig_dst = &main_channel->priv->mig_target; dst_info->port = mig_dst->port; dst_info->sport = mig_dst->sport; dst_info->host_size = strlen(mig_dst->host) + 1; @@ -336,11 +374,9 @@ static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelCl { RedChannel *channel = red_channel_client_get_channel(rcc); SpiceMsgMainMigrationBegin migrate; - MainChannel *main_ch; red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN, item); - main_ch = SPICE_CONTAINEROF(channel, MainChannel, base); - main_channel_fill_migrate_dst_info(main_ch, &migrate.dst_info); + main_channel_fill_migrate_dst_info(MAIN_CHANNEL(channel), &migrate.dst_info); spice_marshall_msg_main_migrate_begin(m, &migrate); } @@ -350,11 +386,9 @@ static void main_channel_marshall_migrate_begin_seamless(SpiceMarshaller *m, { RedChannel *channel = red_channel_client_get_channel(rcc); SpiceMsgMainMigrateBeginSeamless migrate_seamless; - MainChannel *main_ch; red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS, item); - main_ch = SPICE_CONTAINEROF(channel, MainChannel, base); - main_channel_fill_migrate_dst_info(main_ch, &migrate_seamless.dst_info); + main_channel_fill_migrate_dst_info(MAIN_CHANNEL(channel), &migrate_seamless.dst_info); migrate_seamless.src_mig_version = SPICE_MIGRATION_PROTOCOL_VERSION; spice_marshall_msg_main_migrate_begin_seamless(m, &migrate_seamless); } @@ -365,29 +399,29 @@ void main_channel_push_multi_media_time(MainChannel *main_chan, int time) .time = time, }; - red_channel_pipes_new_add_push(&main_chan->base, + red_channel_pipes_new_add_push(RED_CHANNEL(main_chan), main_multi_media_time_item_new, &info); } static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target) { spice_assert(mig_target); - free(main_channel->mig_target.host); - main_channel->mig_target.host = spice_strdup(mig_target->host); - free(main_channel->mig_target.cert_subject); + free(main_channel->priv->mig_target.host); + main_channel->priv->mig_target.host = spice_strdup(mig_target->host); + free(main_channel->priv->mig_target.cert_subject); if (mig_target->cert_subject) { - main_channel->mig_target.cert_subject = spice_strdup(mig_target->cert_subject); + main_channel->priv->mig_target.cert_subject = spice_strdup(mig_target->cert_subject); } else { - main_channel->mig_target.cert_subject = NULL; + main_channel->priv->mig_target.cert_subject = NULL; } - main_channel->mig_target.port = mig_target->port; - main_channel->mig_target.sport = mig_target->sport; + main_channel->priv->mig_target.port = mig_target->port; + main_channel->priv->mig_target.sport = mig_target->sport; } void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target) { main_channel_fill_mig_target(main_chan, mig_target); - red_channel_pipes_add_type(&main_chan->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); + red_channel_pipes_add_type(RED_CHANNEL(main_chan), RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); } static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelClient *rcc, @@ -395,18 +429,18 @@ static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelC { RedChannel *channel = red_channel_client_get_channel(rcc); SpiceMsgMainMigrationSwitchHost migrate; - MainChannel *main_ch; + MainChannel *main_chan; spice_printerr(""); red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST, item); - main_ch = SPICE_CONTAINEROF(channel, MainChannel, base); - migrate.port = main_ch->mig_target.port; - migrate.sport = main_ch->mig_target.sport; - migrate.host_size = strlen(main_ch->mig_target.host) + 1; - migrate.host_data = (uint8_t *)main_ch->mig_target.host; - if (main_ch->mig_target.cert_subject) { - migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1; - migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject; + main_chan = MAIN_CHANNEL(channel); + migrate.port = main_chan->priv->mig_target.port; + migrate.sport = main_chan->priv->mig_target.sport; + migrate.host_size = strlen(main_chan->priv->mig_target.host) + 1; + migrate.host_data = (uint8_t *)main_chan->priv->mig_target.host; + if (main_chan->priv->mig_target.cert_subject) { + migrate.cert_subject_size = strlen(main_chan->priv->mig_target.cert_subject) + 1; + migrate.cert_subject_data = (uint8_t *)main_chan->priv->mig_target.cert_subject; } else { migrate.cert_subject_size = 0; migrate.cert_subject_data = NULL; @@ -536,8 +570,9 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint void *message) { RedChannel *channel = red_channel_client_get_channel(rcc); - MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base); + MainChannel *main_chan = MAIN_CHANNEL(channel); MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); + RedsState *reds = red_channel_get_server(channel); switch (type) { case SPICE_MSGC_MAIN_AGENT_START: { @@ -548,18 +583,18 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint return FALSE; } tokens = (SpiceMsgcMainAgentStart *)message; - reds_on_main_agent_start(channel->reds, mcc, tokens->num_tokens); + reds_on_main_agent_start(reds, mcc, tokens->num_tokens); break; } case SPICE_MSGC_MAIN_AGENT_DATA: { - reds_on_main_agent_data(channel->reds, mcc, message, size); + reds_on_main_agent_data(reds, mcc, message, size); break; } case SPICE_MSGC_MAIN_AGENT_TOKEN: { SpiceMsgcMainAgentTokens *tokens; tokens = (SpiceMsgcMainAgentTokens *)message; - reds_on_main_agent_tokens(channel->reds, mcc, tokens->num_tokens); + reds_on_main_agent_tokens(reds, mcc, tokens->num_tokens); break; } case SPICE_MSGC_MAIN_ATTACH_CHANNELS: @@ -583,7 +618,7 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint ((SpiceMsgcMainMigrateDstDoSeamless *)message)->src_version); break; case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST: - reds_on_main_mouse_mode_request(channel->reds, message, size); + reds_on_main_mouse_mode_request(reds, message, size); break; case SPICE_MSGC_PONG: { main_channel_client_handle_pong(mcc, (SpiceMsgPing *)message, size); @@ -605,13 +640,13 @@ static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, uint32_t size) { RedChannel *channel = red_channel_client_get_channel(rcc); - MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base); + MainChannel *main_chan = MAIN_CHANNEL(channel); MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); if (type == SPICE_MSGC_MAIN_AGENT_DATA) { - return reds_get_agent_data_buffer(channel->reds, mcc, size); + return reds_get_agent_data_buffer(red_channel_get_server(channel), mcc, size); } else { - return main_chan->recv_buf; + return main_chan->priv->recv_buf; } } @@ -622,7 +657,7 @@ static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc, { RedChannel *channel = red_channel_client_get_channel(rcc); if (type == SPICE_MSGC_MAIN_AGENT_DATA) { - reds_release_agent_data_buffer(channel->reds, msg); + reds_release_agent_data_buffer(red_channel_get_server(channel), msg); } } @@ -639,8 +674,7 @@ static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); spice_debug(NULL); - main_channel_push_migrate_data_item(SPICE_CONTAINEROF(channel, - MainChannel, base)); + main_channel_push_migrate_data_item(MAIN_CHANNEL(channel)); return TRUE; } @@ -665,12 +699,12 @@ MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client, int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen) { - return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1; + return main_chan ? getsockname(red_channel_get_first_socket(RED_CHANNEL(main_chan)), sa, salen) : -1; } int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen) { - return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1; + return main_chan ? getpeername(red_channel_get_first_socket(RED_CHANNEL(main_chan)), sa, salen) : -1; } // TODO: ? shouldn't it disonnect all clients? or shutdown all main_channels? @@ -678,79 +712,57 @@ void main_channel_close(MainChannel *main_chan) { int socketfd; - if (main_chan && (socketfd = red_channel_get_first_socket(&main_chan->base)) != -1) { + if (main_chan && (socketfd = red_channel_get_first_socket(RED_CHANNEL(main_chan))) != -1) { close(socketfd); } } MainChannel* main_channel_new(RedsState *reds) { - RedChannel *channel; - ChannelCbs channel_cbs = { NULL, }; - ClientCbs client_cbs = {NULL, }; - - channel_cbs.config_socket = main_channel_config_socket; - channel_cbs.on_disconnect = main_channel_client_on_disconnect; - channel_cbs.send_item = main_channel_send_item; - channel_cbs.hold_item = main_channel_hold_pipe_item; - channel_cbs.release_item = main_channel_release_pipe_item; - channel_cbs.alloc_recv_buf = main_channel_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = main_channel_release_msg_rcv_buf; - channel_cbs.handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark; - channel_cbs.handle_migrate_data = main_channel_handle_migrate_data; - // TODO: set the migration flag of the channel - channel = red_channel_create_parser(sizeof(MainChannel), reds, - reds_get_core_interface(reds), - SPICE_CHANNEL_MAIN, 0, - FALSE, /* handle_acks */ - spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL), - main_channel_handle_parsed, - &channel_cbs, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER); - spice_assert(channel); - red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); - red_channel_set_cap(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE); - - client_cbs.migrate = main_channel_client_migrate; - red_channel_register_client_cbs(channel, &client_cbs, NULL); - - return (MainChannel *)channel; + return g_object_new(TYPE_MAIN_CHANNEL, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", (gint)SPICE_CHANNEL_MAIN, + "id", 0, + "handle-acks", FALSE, /* handle_acks */ + "migration-flags", (SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + NULL); } static int main_channel_connect_semi_seamless(MainChannel *main_channel) { GList *link; - for (link = main_channel->base.clients; link != NULL; link = link->next) { + for (link = red_channel_get_clients(RED_CHANNEL(main_channel)); link != NULL; link = link->next) { RedChannelClient *rcc = link->data; MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); if (main_channel_client_connect_semi_seamless(mcc)) - main_channel->num_clients_mig_wait++; + main_channel->priv->num_clients_mig_wait++; } - return main_channel->num_clients_mig_wait; + return main_channel->priv->num_clients_mig_wait; } static int main_channel_connect_seamless(MainChannel *main_channel) { GList *link; - spice_assert(g_list_length(main_channel->base.clients) == 1); + spice_assert(red_channel_get_n_clients(RED_CHANNEL(main_channel)) == 1); - for (link = main_channel->base.clients; link != NULL; link = link->next) { + for (link = red_channel_get_clients(RED_CHANNEL(main_channel)); link != NULL; link = link->next) { RedChannelClient *rcc = link->data; MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); main_channel_client_connect_seamless(mcc); - main_channel->num_clients_mig_wait++; + main_channel->priv->num_clients_mig_wait++; } - return main_channel->num_clients_mig_wait; + return main_channel->priv->num_clients_mig_wait; } int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target, int try_seamless) { main_channel_fill_mig_target(main_channel, mig_target); - main_channel->num_clients_mig_wait = 0; + main_channel->priv->num_clients_mig_wait = 0; if (!main_channel_is_connected(main_channel)) { return 0; @@ -760,8 +772,10 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta return main_channel_connect_semi_seamless(main_channel); } else { RedChannelClient *rcc; + GList *clients = red_channel_get_clients(RED_CHANNEL(main_channel)); - rcc = main_channel->base.clients->data; + /* just test the first one */ + rcc = clients->data; if (!red_channel_client_test_remote_cap(rcc, SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) { @@ -777,27 +791,28 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan) { GList *link; - for (link = main_chan->base.clients; link != NULL; link = link->next) { + for (link = red_channel_get_clients(RED_CHANNEL(main_chan)); link != NULL; link = link->next) { RedChannelClient *rcc = link->data; MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); main_channel_client_migrate_cancel_wait(mcc); } - main_chan->num_clients_mig_wait = 0; + main_chan->priv->num_clients_mig_wait = 0; } int main_channel_migrate_src_complete(MainChannel *main_chan, int success) { GList *link; + GList *clients = red_channel_get_clients(RED_CHANNEL(main_chan)); int semi_seamless_count = 0; spice_printerr(""); - if (!main_chan->base.clients) { + if (!clients) { spice_printerr("no peer connected"); return 0; } - for (link = main_chan->base.clients; link != NULL; link = link->next) { + for (link = clients; link != NULL; link = link->next) { RedChannelClient *rcc = link->data; MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); if (main_channel_client_migrate_src_complete(mcc, success)) @@ -805,3 +820,39 @@ int main_channel_migrate_src_complete(MainChannel *main_chan, int success) } return semi_seamless_count; } + +void main_channel_on_migrate_connected(MainChannel *main_channel, gboolean seamless) +{ + g_return_if_fail(main_channel->priv->num_clients_mig_wait); + g_warn_if_fail(!seamless || main_channel->priv->num_clients_mig_wait == 1); + if (!--main_channel->priv->num_clients_mig_wait) { + reds_on_main_migrate_connected(red_channel_get_server(RED_CHANNEL(main_channel)), + seamless); + } +} + +static void +main_channel_class_init(MainChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + g_type_class_add_private(klass, sizeof(MainChannelPrivate)); + + object_class->constructed = main_channel_constructed; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL); + channel_class->handle_parsed = main_channel_handle_parsed; + + /* channel callbacks */ + channel_class->config_socket = main_channel_config_socket; + channel_class->on_disconnect = main_channel_client_on_disconnect; + channel_class->send_item = main_channel_send_item; + channel_class->hold_item = main_channel_hold_pipe_item; + channel_class->release_item = main_channel_release_pipe_item; + channel_class->alloc_recv_buf = main_channel_alloc_msg_rcv_buf; + channel_class->release_recv_buf = main_channel_release_msg_rcv_buf; + channel_class->handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark; + channel_class->handle_migrate_data = main_channel_handle_migrate_data; +} + diff --git a/server/main-channel.h b/server/main-channel.h index 43a2679..ce67241 100644 --- a/server/main-channel.h +++ b/server/main-channel.h @@ -18,21 +18,47 @@ #ifndef __MAIN_CHANNEL_H__ #define __MAIN_CHANNEL_H__ +#include <glib-object.h> #include <stdint.h> #include <spice/vd_agent.h> + #include "common/marshaller.h" #include "red-channel.h" #include "main-channel-client.h" +G_BEGIN_DECLS + +#define TYPE_MAIN_CHANNEL main_channel_get_type() + +#define MAIN_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_MAIN_CHANNEL, MainChannel)) +#define MAIN_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_MAIN_CHANNEL, MainChannelClass)) +#define IS_MAIN_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_MAIN_CHANNEL)) +#define IS_MAIN_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_MAIN_CHANNEL)) +#define MAIN_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_MAIN_CHANNEL, MainChannelClass)) + +typedef struct MainChannel MainChannel; +typedef struct MainChannelClass MainChannelClass; +typedef struct MainChannelPrivate MainChannelPrivate; + +struct MainChannel +{ + RedChannel parent; + + MainChannelPrivate *priv; +}; + +struct MainChannelClass +{ + RedChannelClass parent_class; +}; + +GType main_channel_get_type(void) G_GNUC_CONST; + // TODO: Defines used to calculate receive buffer size, and also by reds.c // other options: is to make a reds_main_consts.h, to duplicate defines. #define REDS_AGENT_WINDOW_SIZE 10 #define REDS_NUM_INTERNAL_AGENT_MESSAGES 1 -// approximate max receive message size for main channel -#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \ - (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE) - struct RedsMigSpice { char *host; char *cert_subject; @@ -41,13 +67,6 @@ struct RedsMigSpice { }; typedef struct RedsMigSpice RedsMigSpice; -typedef struct MainChannel { - RedChannel base; - uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE]; - RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?) - int num_clients_mig_wait; -} MainChannel; - MainChannel *main_channel_new(RedsState *reds); RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id); @@ -77,5 +96,8 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta void main_channel_migrate_cancel_wait(MainChannel *main_chan); /* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/ int main_channel_migrate_src_complete(MainChannel *main_chan, int success); +void main_channel_on_migrate_connected(MainChannel *main_channel, gboolean seamless); + +G_END_DECLS #endif diff --git a/server/red-channel-client-private.h b/server/red-channel-client-private.h index c7009fe..7fe54d0 100644 --- a/server/red-channel-client-private.h +++ b/server/red-channel-client-private.h @@ -21,6 +21,25 @@ #include "red-channel-client.h" #include "red-channel.h" +typedef struct RedChannelClientLatencyMonitor { + int state; + uint64_t last_pong_time; + SpiceTimer *timer; + uint32_t id; + int tcp_nodelay; + int warmup_was_sent; + + int64_t roundtrip; +} RedChannelClientLatencyMonitor; + +typedef struct RedChannelClientConnectivityMonitor { + int state; + uint32_t out_bytes; + uint32_t in_bytes; + uint32_t timeout; + SpiceTimer *timer; +} RedChannelClientConnectivityMonitor; + struct RedChannelClientPrivate { RedChannel *channel; RedClient *client; diff --git a/server/red-channel-client.c b/server/red-channel-client.c index 1c0339b..5e937b6 100644 --- a/server/red-channel-client.c +++ b/server/red-channel-client.c @@ -158,6 +158,8 @@ static const SpiceDataHeaderOpaque mini_header_wrapper = {NULL, sizeof(SpiceMini static void red_channel_client_start_ping_timer(RedChannelClient *rcc, uint32_t timeout) { + SpiceCoreInterfaceInternal *core; + if (!rcc->priv->latency_monitor.timer) { return; } @@ -165,11 +167,15 @@ static void red_channel_client_start_ping_timer(RedChannelClient *rcc, uint32_t return; } rcc->priv->latency_monitor.state = PING_STATE_TIMER; - rcc->priv->channel->core->timer_start(rcc->priv->latency_monitor.timer, timeout); + + core = red_channel_get_core_interface(rcc->priv->channel); + core->timer_start(rcc->priv->latency_monitor.timer, timeout); } static void red_channel_client_cancel_ping_timer(RedChannelClient *rcc) { + SpiceCoreInterfaceInternal *core; + if (!rcc->priv->latency_monitor.timer) { return; } @@ -177,7 +183,8 @@ static void red_channel_client_cancel_ping_timer(RedChannelClient *rcc) return; } - rcc->priv->channel->core->timer_cancel(rcc->priv->latency_monitor.timer); + core = red_channel_get_core_interface(rcc->priv->channel); + core->timer_cancel(rcc->priv->latency_monitor.timer); rcc->priv->latency_monitor.state = PING_STATE_NONE; } @@ -252,10 +259,10 @@ red_channel_client_set_property(GObject *object, break; case PROP_CHANNEL: if (self->priv->channel) - red_channel_unref(self->priv->channel); + g_object_unref(self->priv->channel); self->priv->channel = g_value_get_pointer(value); if (self->priv->channel) - red_channel_ref(self->priv->channel); + g_object_ref(self->priv->channel); break; case PROP_CLIENT: self->priv->client = g_value_get_pointer(value); @@ -312,7 +319,7 @@ red_channel_client_finalize(GObject *object) red_channel_client_destroy_remote_caps(self); if (self->priv->channel) { - red_channel_unref(self->priv->channel); + g_object_unref(self->priv->channel); } G_OBJECT_CLASS(red_channel_client_parent_class)->finalize(object); @@ -438,7 +445,6 @@ void red_channel_client_on_output(void *opaque, int n) if (rcc->priv->connectivity_monitor.timer) { rcc->priv->connectivity_monitor.out_bytes += n; } - stat_inc_counter(reds, rcc->priv->channel->out_bytes_counter, n); } void red_channel_client_on_input(void *opaque, int n) @@ -468,12 +474,14 @@ void red_channel_client_prepare_out_msg(void *opaque, struct iovec *vec, void red_channel_client_on_out_block(void *opaque) { + SpiceCoreInterfaceInternal *core; RedChannelClient *rcc = (RedChannelClient *)opaque; rcc->priv->send_data.blocked = TRUE; - rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch, - SPICE_WATCH_EVENT_READ | - SPICE_WATCH_EVENT_WRITE); + core = red_channel_get_core_interface(rcc->priv->channel); + core->watch_update_mask(rcc->priv->stream->watch, + SPICE_WATCH_EVENT_READ | + SPICE_WATCH_EVENT_WRITE); } static inline int red_channel_client_urgent_marshaller_is_active(RedChannelClient *rcc) @@ -533,9 +541,9 @@ static void red_channel_client_send_migrate(RedChannelClient *rcc) SpiceMsgMigrate migrate; red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE, NULL); - migrate.flags = rcc->priv->channel->migration_flags; + g_object_get(rcc->priv->channel, "migration-flags", &migrate.flags, NULL); spice_marshall_msg_migrate(rcc->priv->send_data.marshaller, &migrate); - if (rcc->priv->channel->migration_flags & SPICE_MIGRATE_NEED_FLUSH) { + if (migrate.flags & SPICE_MIGRATE_NEED_FLUSH) { rcc->priv->wait_migrate_flush_mark = TRUE; } @@ -607,7 +615,7 @@ static void red_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *ite red_channel_client_send_ping(rcc); break; default: - rcc->priv->channel->channel_cbs.send_item(rcc, item); + red_channel_send_item(rcc->priv->channel, rcc, item); return; } free(item); @@ -625,7 +633,7 @@ static void red_channel_client_release_item(RedChannelClient *rcc, free(item); break; default: - rcc->priv->channel->channel_cbs.release_item(rcc, item, item_pushed); + red_channel_release_item(rcc->priv->channel, rcc, item, item_pushed); } } @@ -670,9 +678,10 @@ void red_channel_client_on_out_msg_done(void *opaque) red_channel_client_release_sent_item(rcc); if (rcc->priv->send_data.blocked) { + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); rcc->priv->send_data.blocked = FALSE; - rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch, - SPICE_WATCH_EVENT_READ); + core->watch_update_mask(rcc->priv->stream->watch, + SPICE_WATCH_EVENT_READ); } if (red_channel_client_urgent_marshaller_is_active(rcc)) { @@ -750,8 +759,13 @@ static void red_channel_client_ping_timer(void *opaque) static inline int red_channel_client_waiting_for_ack(RedChannelClient *rcc) { - return (rcc->priv->channel->handle_acks && - (rcc->priv->ack_data.messages_window > rcc->priv->ack_data.client_window * 2)); + gboolean handle_acks; + g_object_get(rcc->priv->channel, + "handle-acks", &handle_acks, + NULL); + + return (handle_acks && (rcc->priv->ack_data.messages_window > + rcc->priv->ack_data.client_window * 2)); } /* @@ -792,6 +806,7 @@ static void red_channel_client_connectivity_timer(void *opaque) } if (is_alive) { + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); monitor->in_bytes = 0; monitor->out_bytes = 0; if (rcc->priv->send_data.blocked || red_channel_client_waiting_for_ack(rcc)) { @@ -802,18 +817,24 @@ static void red_channel_client_connectivity_timer(void *opaque) } else { monitor->state = CONNECTIVITY_STATE_CONNECTED; } - rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer, + core->timer_start(rcc->priv->connectivity_monitor.timer, rcc->priv->connectivity_monitor.timeout); } else { + uint32_t type, id; + g_object_get(rcc->priv->channel, + "channel-type", &type, + "id", &id, + NULL); monitor->state = CONNECTIVITY_STATE_DISCONNECTED; spice_warning("rcc %p on channel %d:%d has been unresponsive for more than %u ms, disconnecting", - rcc, rcc->priv->channel->type, rcc->priv->channel->id, monitor->timeout); + rcc, type, id, monitor->timeout); red_channel_client_disconnect(rcc); } } void red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc, uint32_t timeout_ms) { + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); if (!red_channel_client_is_connected(rcc)) { return; } @@ -826,8 +847,8 @@ void red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc, uin * on this channel. */ if (rcc->priv->latency_monitor.timer == NULL) { - rcc->priv->latency_monitor.timer = rcc->priv->channel->core->timer_add( - rcc->priv->channel->core, red_channel_client_ping_timer, rcc); + rcc->priv->latency_monitor.timer = core->timer_add( + core, red_channel_client_ping_timer, rcc); if (!red_client_during_migrate_at_target(rcc->priv->client)) { red_channel_client_start_ping_timer(rcc, PING_TEST_IDLE_NET_TIMEOUT_MS); } @@ -835,12 +856,12 @@ void red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc, uin } if (rcc->priv->connectivity_monitor.timer == NULL) { rcc->priv->connectivity_monitor.state = CONNECTIVITY_STATE_CONNECTED; - rcc->priv->connectivity_monitor.timer = rcc->priv->channel->core->timer_add( - rcc->priv->channel->core, red_channel_client_connectivity_timer, rcc); + rcc->priv->connectivity_monitor.timer = core->timer_add( + core, red_channel_client_connectivity_timer, rcc); rcc->priv->connectivity_monitor.timeout = timeout_ms; if (!red_client_during_migrate_at_target(rcc->priv->client)) { - rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer, - rcc->priv->connectivity_monitor.timeout); + core->timer_start(rcc->priv->connectivity_monitor.timer, + rcc->priv->connectivity_monitor.timeout); } } } @@ -861,9 +882,11 @@ static void red_channel_client_event(int fd, int event, void *data) static int red_channel_client_pre_create_validate(RedChannel *channel, RedClient *client) { - if (red_client_get_channel(client, channel->type, channel->id)) { + uint32_t type, id; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + if (red_client_get_channel(client, type, id)) { spice_printerr("Error client %p: duplicate channel type %d id %d", - client, channel->type, channel->id); + client, type, id); return FALSE; } return TRUE; @@ -874,24 +897,28 @@ static gboolean red_channel_client_initable_init(GInitable *initable, GError **error) { GError *local_error = NULL; + SpiceCoreInterfaceInternal *core; RedChannelClient *self = RED_CHANNEL_CLIENT(initable); pthread_mutex_lock(&self->priv->client->lock); if (!red_channel_client_pre_create_validate(self->priv->channel, self->priv->client)) { + uint32_t id, type; + g_object_get(self->priv->channel, + "channel-type", &type, + "id", &id, + NULL); g_set_error(&local_error, SPICE_SERVER_ERROR, SPICE_SERVER_ERROR_FAILED, "Client %p: duplicate channel type %d id %d", - self->priv->client, self->priv->channel->type, - self->priv->channel->id); + self->priv->client, type, id); goto cleanup; } + core = red_channel_get_core_interface(self->priv->channel); if (self->priv->monitor_latency && reds_stream_get_family(self->priv->stream) != AF_UNIX) { self->priv->latency_monitor.timer = - self->priv->channel->core->timer_add(self->priv->channel->core, - red_channel_client_ping_timer, - self); + core->timer_add(core, red_channel_client_ping_timer, self); if (!self->priv->client->during_target_migrate) { red_channel_client_start_ping_timer(self, @@ -901,27 +928,26 @@ static gboolean red_channel_client_initable_init(GInitable *initable, } self->incoming.opaque = self; - self->incoming.cb = &self->priv->channel->incoming_cb; + self->incoming.cb = red_channel_get_incoming_handler(self->priv->channel); self->incoming.header.data = self->incoming.header_buf; self->incoming.serial = 1; self->outgoing.opaque = self; - self->outgoing.cb = &self->priv->channel->outgoing_cb; + self->outgoing.cb = red_channel_get_outgoing_handler(self->priv->channel); self->outgoing.pos = 0; self->outgoing.size = 0; if (self->priv->stream) self->priv->stream->watch = - self->priv->channel->core->watch_add(self->priv->channel->core, - self->priv->stream->socket, - SPICE_WATCH_EVENT_READ, - red_channel_client_event, - self); - self->priv->id = self->priv->channel->clients_num; + core->watch_add(core, self->priv->stream->socket, + SPICE_WATCH_EVENT_READ, + red_channel_client_event, + self); + self->priv->id = red_channel_get_n_clients(self->priv->channel); red_channel_add_client(self->priv->channel, self); red_client_add_channel(self->priv->client, self); - if (!self->priv->channel->channel_cbs.config_socket(self)) { + if (!red_channel_config_socket(self->priv->channel, self)) { g_set_error_literal(&local_error, SPICE_SERVER_ERROR, SPICE_SERVER_ERROR_FAILED, @@ -981,8 +1007,9 @@ void red_channel_client_seamless_migration_done(RedChannelClient *rcc) red_channel_client_start_ping_timer(rcc, PING_TEST_IDLE_NET_TIMEOUT_MS); } if (rcc->priv->connectivity_monitor.timer) { - rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer, - rcc->priv->connectivity_monitor.timeout); + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); + core->timer_start(rcc->priv->connectivity_monitor.timer, + rcc->priv->connectivity_monitor.timeout); } } } @@ -1001,13 +1028,14 @@ int red_channel_client_is_waiting_for_migrate_data(RedChannelClient *rcc) void red_channel_client_default_migrate(RedChannelClient *rcc) { + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); if (rcc->priv->latency_monitor.timer) { red_channel_client_cancel_ping_timer(rcc); - rcc->priv->channel->core->timer_remove(rcc->priv->latency_monitor.timer); + core->timer_remove(rcc->priv->latency_monitor.timer); rcc->priv->latency_monitor.timer = NULL; } if (rcc->priv->connectivity_monitor.timer) { - rcc->priv->channel->core->timer_remove(rcc->priv->connectivity_monitor.timer); + core->timer_remove(rcc->priv->connectivity_monitor.timer); rcc->priv->connectivity_monitor.timer = NULL; } red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_MIGRATE); @@ -1024,7 +1052,8 @@ void red_channel_client_destroy(RedChannelClient *rcc) void red_channel_client_shutdown(RedChannelClient *rcc) { if (rcc->priv->stream && !rcc->priv->stream->shutdown) { - rcc->priv->channel->core->watch_remove(rcc->priv->stream->watch); + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); + core->watch_remove(rcc->priv->stream->watch); rcc->priv->stream->watch = NULL; shutdown(rcc->priv->stream->socket, SHUT_RDWR); rcc->priv->stream->shutdown = TRUE; @@ -1259,8 +1288,10 @@ void red_channel_client_push(RedChannelClient *rcc) } if (red_channel_client_no_item_being_sent(rcc) && ring_is_empty(&rcc->priv->pipe) && rcc->priv->stream->watch) { - rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch, - SPICE_WATCH_EVENT_READ); + SpiceCoreInterfaceInternal *core; + core = red_channel_get_core_interface(rcc->priv->channel); + core->watch_update_mask(rcc->priv->stream->watch, + SPICE_WATCH_EVENT_READ); } rcc->priv->during_send = FALSE; g_object_unref(rcc); @@ -1331,11 +1362,12 @@ static void red_channel_client_handle_pong(RedChannelClient *rcc, SpiceMsgPing * red_channel_client_start_ping_timer(rcc, PING_TEST_TIMEOUT_MS); } -static void red_channel_handle_migrate_flush_mark(RedChannelClient *rcc) +static void red_channel_client_handle_migrate_flush_mark(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); - if (channel->channel_cbs.handle_migrate_flush_mark) { - channel->channel_cbs.handle_migrate_flush_mark(rcc); + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel); + if (klass->handle_migrate_flush_mark) { + klass->handle_migrate_flush_mark(rcc); } } @@ -1346,23 +1378,30 @@ static void red_channel_handle_migrate_flush_mark(RedChannelClient *rcc) // 3) source migrates to target // 4) target sends data to all // So need to make all the handlers work with per channel/client data (what data exactly?) -static void red_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *message) +static void red_channel_client_handle_migrate_data(RedChannelClient *rcc, + uint32_t size, + void + *message) { RedChannel *channel = red_channel_client_get_channel(rcc); + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel); + uint32_t type, id; + + g_object_get(channel, "channel-type", &type, "id", &id, NULL); spice_debug("channel type %d id %d rcc %p size %u", - channel->type, channel->id, rcc, size); - if (!channel->channel_cbs.handle_migrate_data) { + type, id, rcc, size); + if (!klass->handle_migrate_data) { return; } if (!red_channel_client_is_waiting_for_migrate_data(rcc)) { spice_channel_client_error(rcc, "unexpected"); return; } - if (channel->channel_cbs.handle_migrate_data_get_serial) { + if (klass->handle_migrate_data_get_serial) { red_channel_client_set_message_serial(rcc, - channel->channel_cbs.handle_migrate_data_get_serial(rcc, size, message)); + klass->handle_migrate_data_get_serial(rcc, size, message)); } - if (!channel->channel_cbs.handle_migrate_data(rcc, size, message)) { + if (!klass->handle_migrate_data(rcc, size, message)) { spice_channel_client_error(rcc, "handle_migrate_data failed"); return; } @@ -1394,11 +1433,11 @@ int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size, spice_error("unexpected flush mark"); return FALSE; } - red_channel_handle_migrate_flush_mark(rcc); + red_channel_client_handle_migrate_flush_mark(rcc); rcc->priv->wait_migrate_flush_mark = FALSE; break; case SPICE_MSGC_MIGRATE_DATA: - red_channel_handle_migrate_data(rcc, size, message); + red_channel_client_handle_migrate_data(rcc, size, message); break; case SPICE_MSGC_PONG: red_channel_client_handle_pong(rcc, message); @@ -1417,7 +1456,7 @@ void red_channel_client_init_send_data(RedChannelClient *rcc, uint16_t msg_type, rcc->priv->send_data.header.set_msg_type(&rcc->priv->send_data.header, msg_type); rcc->priv->send_data.item = item; if (item) { - rcc->priv->channel->channel_cbs.hold_item(rcc, item); + red_channel_hold_item(rcc->priv->channel, rcc, item); } } @@ -1477,9 +1516,10 @@ static inline gboolean client_pipe_add(RedChannelClient *rcc, RedPipeItem *item, return FALSE; } if (ring_is_empty(&rcc->priv->pipe) && rcc->priv->stream->watch) { - rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch, - SPICE_WATCH_EVENT_READ | - SPICE_WATCH_EVENT_WRITE); + SpiceCoreInterfaceInternal *core; + core = red_channel_get_core_interface(rcc->priv->channel); + core->watch_update_mask(rcc->priv->stream->watch, + SPICE_WATCH_EVENT_READ | SPICE_WATCH_EVENT_WRITE); } rcc->priv->pipe_size++; ring_add(pos, &item->link); @@ -1558,7 +1598,7 @@ uint32_t red_channel_client_get_pipe_size(RedChannelClient *rcc) static gboolean red_channel_client_default_is_connected(RedChannelClient *rcc) { return rcc->priv->channel - && (g_list_find(rcc->priv->channel->clients, rcc) != NULL); + && (g_list_find(red_channel_get_clients(rcc->priv->channel), rcc) != NULL); } gboolean red_channel_client_is_connected(RedChannelClient *rcc) @@ -1611,27 +1651,30 @@ void red_channel_client_push_set_ack(RedChannelClient *rcc) static void red_channel_client_default_disconnect(RedChannelClient *rcc) { RedChannel *channel = rcc->priv->channel; + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(channel); + uint32_t type, id; if (!red_channel_client_is_connected(rcc)) { return; } + g_object_get(channel, "channel-type", &type, "id", &id, NULL); spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel, - channel->type, channel->id); + type, id); red_channel_client_pipe_clear(rcc); if (rcc->priv->stream->watch) { - channel->core->watch_remove(rcc->priv->stream->watch); + core->watch_remove(rcc->priv->stream->watch); rcc->priv->stream->watch = NULL; } if (rcc->priv->latency_monitor.timer) { - channel->core->timer_remove(rcc->priv->latency_monitor.timer); + core->timer_remove(rcc->priv->latency_monitor.timer); rcc->priv->latency_monitor.timer = NULL; } if (rcc->priv->connectivity_monitor.timer) { - channel->core->timer_remove(rcc->priv->connectivity_monitor.timer); + core->timer_remove(rcc->priv->connectivity_monitor.timer); rcc->priv->connectivity_monitor.timer = NULL; } red_channel_remove_client(channel, rcc); - channel->channel_cbs.on_disconnect(rcc); + red_channel_on_disconnect(channel, rcc); } void red_channel_client_disconnect(RedChannelClient *rcc) @@ -1688,7 +1731,7 @@ int red_channel_client_wait_pipe_item_sent(RedChannelClient *rcc, end_time = UINT64_MAX; } - rcc->priv->channel->channel_cbs.hold_item(rcc, item); + red_channel_hold_item(rcc->priv->channel, rcc, item); if (red_channel_client_is_blocked(rcc)) { red_channel_client_receive(rcc); @@ -1771,13 +1814,19 @@ void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc, gboolean red_channel_client_set_migration_seamless(RedChannelClient *rcc) { gboolean ret = FALSE; - - if (rcc->priv->channel->migration_flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) { + uint32_t type, id, flags; + + g_object_get(rcc->priv->channel, + "channel-type", &type, + "id", &id, + "migration-flags", &flags, + NULL); + if (flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) { rcc->priv->wait_migrate_data = TRUE; ret = TRUE; } - spice_debug("channel type %d id %d rcc %p wait data %d", rcc->priv->channel->type, rcc->priv->channel->id, rcc, - rcc->priv->wait_migrate_data); + spice_debug("channel type %d id %d rcc %p wait data %d", type, id, rcc, + rcc->priv->wait_migrate_data); return ret; } diff --git a/server/red-channel-client.h b/server/red-channel-client.h index d8cc317..6649485 100644 --- a/server/red-channel-client.h +++ b/server/red-channel-client.h @@ -46,10 +46,10 @@ G_BEGIN_DECLS #define RED_IS_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_CHANNEL_CLIENT)) #define RED_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHANNEL_CLIENT, RedChannelClientClass)) -typedef struct RedChannel RedChannel; typedef struct RedClient RedClient; typedef struct IncomingHandler IncomingHandler; +typedef struct RedChannel RedChannel; typedef struct RedChannelClient RedChannelClient; typedef struct RedChannelClientClass RedChannelClientClass; typedef struct RedChannelClientPrivate RedChannelClientPrivate; @@ -161,8 +161,10 @@ GType red_channel_client_get_type(void) G_GNUC_CONST; #define spice_channel_client_error(rcc, format, ...) \ do { \ RedChannel *_ch = red_channel_client_get_channel(rcc); \ + uint32_t _type, _id; \ + g_object_get(_ch, "channel-type", &_type, "id", &_id, NULL); \ spice_warning("rcc %p type %u id %u: " format, rcc, \ - _ch->type, _ch->id, ## __VA_ARGS__); \ + type, id, ## __VA_ARGS__); \ red_channel_client_shutdown(rcc); \ } while (0) diff --git a/server/red-channel.c b/server/red-channel.c index c714d90..df865f0 100644 --- a/server/red-channel.c +++ b/server/red-channel.c @@ -65,9 +65,135 @@ * from the channel's thread. */ -void red_channel_receive(RedChannel *channel) +G_DEFINE_ABSTRACT_TYPE(RedChannel, red_channel, G_TYPE_OBJECT) + +#define CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), RED_TYPE_CHANNEL, RedChannelPrivate)) + +struct RedChannelPrivate +{ + uint32_t type; + uint32_t id; + + SpiceCoreInterfaceInternal *core; + gboolean handle_acks; + + // RedChannel will hold only connected channel clients (logic - when pushing pipe item to all channel clients, there + // is no need to go over disconnect clients) + // . While client will hold the channel clients till it is destroyed + // and then it will destroy them as well. + // However RCC still holds a reference to the Channel. + // Maybe replace these logic with ref count? + // TODO: rename to 'connected_clients'? + GList *clients; + + RedChannelCapabilities local_caps; + uint32_t migration_flags; + + void *data; + + OutgoingHandlerInterface outgoing_cb; + IncomingHandlerInterface incoming_cb; + + ClientCbs client_cbs; + // TODO: when different channel_clients are in different threads from Channel -> need to protect! + pthread_t thread_id; + RedsState *reds; +#ifdef RED_STATISTICS + StatNodeRef stat; + uint64_t *out_bytes_counter; +#endif +}; + +enum { + PROP0, + PROP_SPICE_SERVER, + PROP_CORE_INTERFACE, + PROP_TYPE, + PROP_ID, + PROP_HANDLE_ACKS, + PROP_MIGRATION_FLAGS +}; + +static void +red_channel_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_receive, NULL); + RedChannel *self = RED_CHANNEL(object); + + switch (property_id) + { + case PROP_SPICE_SERVER: + g_value_set_pointer(value, self->priv->reds); + break; + case PROP_CORE_INTERFACE: + g_value_set_pointer(value, self->priv->core); + break; + case PROP_TYPE: + g_value_set_int(value, self->priv->type); + break; + case PROP_ID: + g_value_set_uint(value, self->priv->id); + break; + case PROP_HANDLE_ACKS: + g_value_set_boolean(value, self->priv->handle_acks); + break; + case PROP_MIGRATION_FLAGS: + g_value_set_uint(value, self->priv->migration_flags); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +red_channel_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + RedChannel *self = RED_CHANNEL(object); + + switch (property_id) + { + case PROP_SPICE_SERVER: + self->priv->reds = g_value_get_pointer(value); + break; + case PROP_CORE_INTERFACE: + self->priv->core = g_value_get_pointer(value); + break; + case PROP_TYPE: + self->priv->type = g_value_get_int(value); + break; + case PROP_ID: + self->priv->id = g_value_get_uint(value); + break; + case PROP_HANDLE_ACKS: + self->priv->handle_acks = g_value_get_boolean(value); + break; + case PROP_MIGRATION_FLAGS: + self->priv->migration_flags = g_value_get_uint(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +red_channel_finalize(GObject *object) +{ + RedChannel *self = RED_CHANNEL(object); + + if (self->priv->local_caps.num_common_caps) { + free(self->priv->local_caps.common_caps); + } + + if (self->priv->local_caps.num_caps) { + free(self->priv->local_caps.caps); + } + + G_OBJECT_CLASS(red_channel_parent_class)->finalize(object); } static void red_channel_client_default_peer_on_error(RedChannelClient *rcc) @@ -75,17 +201,170 @@ static void red_channel_client_default_peer_on_error(RedChannelClient *rcc) red_channel_client_disconnect(rcc); } +static void red_channel_on_output(void *opaque, int n) +{ + RedChannelClient *rcc = opaque; + RedChannel *self = red_channel_client_get_channel(rcc);; + + red_channel_client_on_output(opaque, n); + + stat_inc_counter(self->priv->reds, self->priv->out_bytes_counter, n); +} + +static void +red_channel_constructed(GObject *object) +{ + RedChannel *self = RED_CHANNEL(object); + spice_debug("%p: channel type %d id %d thread_id 0x%lx", self, + self->priv->type, self->priv->id, self->priv->thread_id); + + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + self->priv->incoming_cb.alloc_msg_buf = + (alloc_msg_recv_buf_proc)klass->alloc_recv_buf; + self->priv->incoming_cb.release_msg_buf = + (release_msg_recv_buf_proc)klass->release_recv_buf; + self->priv->incoming_cb.handle_message = (handle_message_proc)klass->handle_message; + self->priv->incoming_cb.handle_parsed = (handle_parsed_proc)klass->handle_parsed; + self->priv->incoming_cb.parser = klass->parser; + + G_OBJECT_CLASS(red_channel_parent_class)->constructed(object); +} + +static void red_channel_client_default_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, + int migration, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps) +{ + spice_error("not implemented"); +} + +static void red_channel_client_default_disconnect(RedChannelClient *base) +{ + red_channel_client_disconnect(base); +} + +static void +red_channel_class_init(RedChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof (RedChannelPrivate)); + + object_class->get_property = red_channel_get_property; + object_class->set_property = red_channel_set_property; + object_class->finalize = red_channel_finalize; + object_class->constructed = red_channel_constructed; + + g_object_class_install_property(object_class, + PROP_SPICE_SERVER, + g_param_spec_pointer("spice-server", + "spice-server", + "The spice server associated with this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_CORE_INTERFACE, + g_param_spec_pointer("core-interface", + "core-interface", + "The SpiceCoreInterface server associated with this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /* FIXME: generate enums for this in spice-common? */ + g_object_class_install_property(object_class, + PROP_TYPE, + g_param_spec_int("channel-type", + "channel type", + "Type of this channel", + 0, + SPICE_END_CHANNEL, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_ID, + g_param_spec_uint("id", + "id", + "ID of this channel", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_HANDLE_ACKS, + g_param_spec_boolean("handle-acks", + "Handle ACKs", + "Whether this channel handles ACKs", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_MIGRATION_FLAGS, + g_param_spec_uint("migration-flags", + "migration flags", + "Migration flags for this channel", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +red_channel_init(RedChannel *self) +{ + self->priv = CHANNEL_PRIVATE(self); + + red_channel_set_common_cap(self, SPICE_COMMON_CAP_MINI_HEADER); + self->priv->thread_id = pthread_self(); + self->priv->out_bytes_counter = 0; + + // TODO: send incoming_cb as parameters instead of duplicating? + self->priv->incoming_cb.on_error = + (on_incoming_error_proc)red_channel_client_default_peer_on_error; + self->priv->incoming_cb.on_input = red_channel_client_on_input; + self->priv->outgoing_cb.get_msg_size = red_channel_client_get_out_msg_size; + self->priv->outgoing_cb.prepare = red_channel_client_prepare_out_msg; + self->priv->outgoing_cb.on_block = red_channel_client_on_out_block; + self->priv->outgoing_cb.on_error = + (on_outgoing_error_proc)red_channel_client_default_peer_on_error; + self->priv->outgoing_cb.on_msg_done = red_channel_client_on_out_msg_done; + self->priv->outgoing_cb.on_output = red_channel_on_output; + + self->priv->client_cbs.connect = red_channel_client_default_connect; + self->priv->client_cbs.disconnect = red_channel_client_default_disconnect; + self->priv->client_cbs.migrate = red_channel_client_default_migrate; +} + + +void red_channel_receive(RedChannel *channel) +{ + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_receive, NULL); +} + void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc) { spice_assert(rcc); - channel->clients = g_list_append(channel->clients, rcc); + channel->priv->clients = g_list_append(channel->priv->clients, rcc); } int red_channel_test_remote_common_cap(RedChannel *channel, uint32_t cap) { GList *link; - for (link = channel->clients; link != NULL; link = link->next) { + for (link = channel->priv->clients; link != NULL; link = link->next) { RedChannelClient *rcc = link->data; if (!red_channel_client_test_remote_common_cap(rcc, cap)) { @@ -99,7 +378,7 @@ int red_channel_test_remote_cap(RedChannel *channel, uint32_t cap) { GList *link; - for (link = channel->clients; link != NULL; link = link->next) { + for (link = channel->priv->clients; link != NULL; link = link->next) { RedChannelClient *rcc = link->data; if (!red_channel_client_test_remote_cap(rcc, cap)) { @@ -136,7 +415,7 @@ gboolean red_client_seamless_migration_done_for_channel(RedClient *client, int red_channel_is_waiting_for_migrate_data(RedChannel *channel) { RedChannelClient *rcc; - guint n_clients = g_list_length(channel->clients); + guint n_clients = g_list_length(channel->priv->clients); if (!red_channel_is_connected(channel)) { return FALSE; @@ -146,190 +425,42 @@ int red_channel_is_waiting_for_migrate_data(RedChannel *channel) return FALSE; } spice_assert(n_clients == 1); - rcc = channel->clients->data; + rcc = channel->priv->clients->data; return red_channel_client_is_waiting_for_migrate_data(rcc); } -static void red_channel_client_default_connect(RedChannel *channel, RedClient *client, - RedsStream *stream, - int migration, - int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps) -{ - spice_error("not implemented"); -} - -static void red_channel_client_default_disconnect(RedChannelClient *base) -{ - red_channel_client_disconnect(base); -} - -RedChannel *red_channel_create(int size, - RedsState *reds, - const SpiceCoreInterfaceInternal *core, - uint32_t type, uint32_t id, - int handle_acks, - channel_handle_message_proc handle_message, - const ChannelCbs *channel_cbs, - uint32_t migration_flags) -{ - RedChannel *channel; - ClientCbs client_cbs = { NULL, }; - - spice_assert(size >= sizeof(*channel)); - spice_assert(channel_cbs->config_socket && channel_cbs->on_disconnect && handle_message && - channel_cbs->alloc_recv_buf && channel_cbs->release_item); - spice_assert(channel_cbs->handle_migrate_data || - !(migration_flags & SPICE_MIGRATE_NEED_DATA_TRANSFER)); - channel = spice_malloc0(size); - channel->type = type; - channel->id = id; - channel->refs = 1; - channel->handle_acks = handle_acks; - channel->migration_flags = migration_flags; - memcpy(&channel->channel_cbs, channel_cbs, sizeof(ChannelCbs)); - - channel->reds = reds; - channel->core = core; - - // TODO: send incoming_cb as parameters instead of duplicating? - channel->incoming_cb.alloc_msg_buf = (alloc_msg_recv_buf_proc)channel_cbs->alloc_recv_buf; - channel->incoming_cb.release_msg_buf = (release_msg_recv_buf_proc)channel_cbs->release_recv_buf; - channel->incoming_cb.handle_message = (handle_message_proc)handle_message; - channel->incoming_cb.on_error = - (on_incoming_error_proc)red_channel_client_default_peer_on_error; - channel->incoming_cb.on_input = red_channel_client_on_input; - channel->outgoing_cb.get_msg_size = red_channel_client_get_out_msg_size; - channel->outgoing_cb.prepare = red_channel_client_prepare_out_msg; - channel->outgoing_cb.on_block = red_channel_client_on_out_block; - channel->outgoing_cb.on_error = - (on_outgoing_error_proc)red_channel_client_default_peer_on_error; - channel->outgoing_cb.on_msg_done = red_channel_client_on_out_msg_done; - channel->outgoing_cb.on_output = red_channel_client_on_output; - - client_cbs.connect = red_channel_client_default_connect; - client_cbs.disconnect = red_channel_client_default_disconnect; - client_cbs.migrate = red_channel_client_default_migrate; - - red_channel_register_client_cbs(channel, &client_cbs, NULL); - red_channel_set_common_cap(channel, SPICE_COMMON_CAP_MINI_HEADER); - - channel->thread_id = pthread_self(); - - channel->out_bytes_counter = 0; - - spice_debug("channel type %d id %d thread_id 0x%lx", - channel->type, channel->id, channel->thread_id); - return channel; -} - -// TODO: red_worker can use this one -static void dummy_watch_update_mask(SpiceWatch *watch, int event_mask) -{ -} - -static SpiceWatch *dummy_watch_add(const SpiceCoreInterfaceInternal *iface, - int fd, int event_mask, SpiceWatchFunc func, void *opaque) -{ - return NULL; // apparently allowed? -} - -static void dummy_watch_remove(SpiceWatch *watch) -{ -} - -// TODO: actually, since I also use channel_client_dummy, no need for core. Can be NULL -static const SpiceCoreInterfaceInternal dummy_core = { - .watch_update_mask = dummy_watch_update_mask, - .watch_add = dummy_watch_add, - .watch_remove = dummy_watch_remove, -}; - -RedChannel *red_channel_create_dummy(int size, RedsState *reds, uint32_t type, uint32_t id) -{ - RedChannel *channel; - ClientCbs client_cbs = { NULL, }; - - spice_assert(size >= sizeof(*channel)); - channel = spice_malloc0(size); - channel->type = type; - channel->id = id; - channel->refs = 1; - channel->reds = reds; - channel->core = &dummy_core; - client_cbs.connect = red_channel_client_default_connect; - client_cbs.disconnect = red_channel_client_default_disconnect; - client_cbs.migrate = red_channel_client_default_migrate; - - red_channel_register_client_cbs(channel, &client_cbs, NULL); - red_channel_set_common_cap(channel, SPICE_COMMON_CAP_MINI_HEADER); - - channel->thread_id = pthread_self(); - spice_debug("channel type %d id %d thread_id 0x%lx", - channel->type, channel->id, channel->thread_id); - - channel->out_bytes_counter = 0; - - return channel; -} - -static int do_nothing_handle_message(RedChannelClient *rcc, - uint16_t type, - uint32_t size, - uint8_t *msg) -{ - return TRUE; -} - -RedChannel *red_channel_create_parser(int size, - RedsState *reds, - const SpiceCoreInterfaceInternal *core, - uint32_t type, uint32_t id, - int handle_acks, - spice_parse_channel_func_t parser, - channel_handle_parsed_proc handle_parsed, - const ChannelCbs *channel_cbs, - uint32_t migration_flags) +void red_channel_set_stat_node(RedChannel *channel, StatNodeRef stat) { - RedChannel *channel = red_channel_create(size, reds, core, type, id, - handle_acks, - do_nothing_handle_message, - channel_cbs, - migration_flags); - - if (channel == NULL) { - return NULL; - } - channel->incoming_cb.handle_parsed = (handle_parsed_proc)handle_parsed; - channel->incoming_cb.parser = parser; + spice_return_if_fail(channel != NULL); + spice_return_if_fail(channel->priv->stat == 0); - return channel; +#ifdef RED_STATISTICS + channel->priv->stat = stat; + channel->priv->out_bytes_counter = stat_add_counter(channel->priv->reds, stat, "out_bytes", TRUE); +#endif } -void red_channel_set_stat_node(RedChannel *channel, StatNodeRef stat) +StatNodeRef red_channel_get_stat_node(RedChannel *channel) { - spice_return_if_fail(channel != NULL); - spice_return_if_fail(channel->stat == 0); - #ifdef RED_STATISTICS - channel->stat = stat; - channel->out_bytes_counter = stat_add_counter(channel->reds, stat, "out_bytes", TRUE); + return channel->priv->stat; #endif + return 0; } void red_channel_register_client_cbs(RedChannel *channel, const ClientCbs *client_cbs, gpointer cbs_data) { - spice_assert(client_cbs->connect || channel->type == SPICE_CHANNEL_MAIN); - channel->client_cbs.connect = client_cbs->connect; + spice_assert(client_cbs->connect || channel->priv->type == SPICE_CHANNEL_MAIN); + channel->priv->client_cbs.connect = client_cbs->connect; if (client_cbs->disconnect) { - channel->client_cbs.disconnect = client_cbs->disconnect; + channel->priv->client_cbs.disconnect = client_cbs->disconnect; } if (client_cbs->migrate) { - channel->client_cbs.migrate = client_cbs->migrate; + channel->priv->client_cbs.migrate = client_cbs->migrate; } - channel->data = cbs_data; + channel->priv->data = cbs_data; } int test_capability(const uint32_t *caps, int num_caps, uint32_t cap) @@ -356,32 +487,12 @@ static void add_capability(uint32_t **caps, int *num_caps, uint32_t cap) void red_channel_set_common_cap(RedChannel *channel, uint32_t cap) { - add_capability(&channel->local_caps.common_caps, &channel->local_caps.num_common_caps, cap); + add_capability(&channel->priv->local_caps.common_caps, &channel->priv->local_caps.num_common_caps, cap); } void red_channel_set_cap(RedChannel *channel, uint32_t cap) { - add_capability(&channel->local_caps.caps, &channel->local_caps.num_caps, cap); -} - -void red_channel_ref(RedChannel *channel) -{ - channel->refs++; -} - -void red_channel_unref(RedChannel *channel) -{ - if (--channel->refs == 0) { - if (channel->local_caps.num_common_caps) { - free(channel->local_caps.common_caps); - } - - if (channel->local_caps.num_caps) { - free(channel->local_caps.caps); - } - - free(channel); - } + add_capability(&channel->priv->local_caps.caps, &channel->priv->local_caps.num_caps, cap); } void red_channel_destroy(RedChannel *channel) @@ -390,13 +501,13 @@ void red_channel_destroy(RedChannel *channel) return; } - g_list_foreach(channel->clients, (GFunc)red_channel_client_destroy, NULL); - red_channel_unref(channel); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_destroy, NULL); + g_object_unref(channel); } void red_channel_send(RedChannel *channel) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_send, NULL); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_send, NULL); } void red_channel_push(RedChannel *channel) @@ -405,14 +516,14 @@ void red_channel_push(RedChannel *channel) return; } - g_list_foreach(channel->clients, (GFunc)red_channel_client_push, NULL); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_push, NULL); } // TODO: this function doesn't make sense because the window should be client (WAN/LAN) // specific void red_channel_init_outgoing_messages_window(RedChannel *channel) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_init_outgoing_messages_window, NULL); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_init_outgoing_messages_window, NULL); } static void red_channel_client_pipe_add_type_proxy(gpointer data, gpointer user_data) @@ -423,7 +534,7 @@ static void red_channel_client_pipe_add_type_proxy(gpointer data, gpointer user_ void red_channel_pipes_add_type(RedChannel *channel, int pipe_item_type) { - g_list_foreach(channel->clients, red_channel_client_pipe_add_type_proxy, + g_list_foreach(channel->priv->clients, red_channel_client_pipe_add_type_proxy, GINT_TO_POINTER(pipe_item_type)); } @@ -435,12 +546,12 @@ static void red_channel_client_pipe_add_empty_msg_proxy(gpointer data, gpointer void red_channel_pipes_add_empty_msg(RedChannel *channel, int msg_type) { - g_list_foreach(channel->clients, red_channel_client_pipe_add_empty_msg_proxy, GINT_TO_POINTER(msg_type)); + g_list_foreach(channel->priv->clients, red_channel_client_pipe_add_empty_msg_proxy, GINT_TO_POINTER(msg_type)); } int red_channel_is_connected(RedChannel *channel) { - return channel && channel->clients; + return channel && channel->priv->clients; } void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc) @@ -448,19 +559,19 @@ void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc) GList *link; g_return_if_fail(channel == red_channel_client_get_channel(rcc)); - if (!pthread_equal(pthread_self(), channel->thread_id)) { + if (!pthread_equal(pthread_self(), channel->priv->thread_id)) { spice_warning("channel type %d id %d - " "channel->thread_id (0x%lx) != pthread_self (0x%lx)." "If one of the threads is != io-thread && != vcpu-thread, " "this might be a BUG", - channel->type, channel->id, - channel->thread_id, pthread_self()); + channel->priv->type, channel->priv->id, + channel->priv->thread_id, pthread_self()); } spice_return_if_fail(channel); - link = g_list_find(channel->clients, rcc); + link = g_list_find(channel->priv->clients, rcc); spice_return_if_fail(link != NULL); - channel->clients = g_list_remove_link(channel->clients, link); + channel->priv->clients = g_list_remove_link(channel->priv->clients, link); // TODO: should we set rcc->channel to NULL??? } @@ -474,17 +585,35 @@ void red_client_remove_channel(RedChannelClient *rcc) void red_channel_disconnect(RedChannel *channel) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_disconnect, NULL); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_disconnect, NULL); +} + +void red_channel_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, int migration, int num_common_caps, + uint32_t *common_caps, int num_caps, uint32_t *caps) +{ + channel->priv->client_cbs.connect(channel, client, stream, migration, + num_common_caps, common_caps, num_caps, + caps); } void red_channel_apply_clients(RedChannel *channel, channel_client_callback cb) { - g_list_foreach(channel->clients, (GFunc)cb, NULL); + g_list_foreach(channel->priv->clients, (GFunc)cb, NULL); } void red_channel_apply_clients_data(RedChannel *channel, channel_client_callback_data cb, void *data) { - g_list_foreach(channel->clients, (GFunc)cb, data); + g_list_foreach(channel->priv->clients, (GFunc)cb, data); +} + +GList *red_channel_get_clients(RedChannel *channel) +{ + return channel->priv->clients; +} +guint red_channel_get_n_clients(RedChannel *channel) +{ + return g_list_length(channel->priv->clients); } int red_channel_all_blocked(RedChannel *channel) @@ -492,10 +621,10 @@ int red_channel_all_blocked(RedChannel *channel) GList *link; RedChannelClient *rcc; - if (!channel || !channel->clients) { + if (!channel || !channel->priv->clients) { return FALSE; } - for (link = channel->clients; link != NULL; link = link->next) { + for (link = channel->priv->clients; link != NULL; link = link->next) { rcc = link->data; if (!red_channel_client_is_blocked(rcc)) { return FALSE; @@ -509,7 +638,7 @@ int red_channel_any_blocked(RedChannel *channel) GList *link; RedChannelClient *rcc; - for (link = channel->clients; link != NULL; link = link->next) { + for (link = channel->priv->clients; link != NULL; link = link->next) { rcc = link->data; if (red_channel_client_is_blocked(rcc)) { return TRUE; @@ -523,10 +652,10 @@ int red_channel_get_first_socket(RedChannel *channel) RedChannelClient *rcc; RedsStream *stream; - if (!channel || !channel->clients) { + if (!channel || !channel->priv->clients) { return -1; } - rcc = channel->clients->data; + rcc = channel->priv->clients->data; stream = red_channel_client_get_stream(rcc); return stream->socket; @@ -537,7 +666,7 @@ int red_channel_no_item_being_sent(RedChannel *channel) GList *link; RedChannelClient *rcc; - for (link = channel->clients; link != NULL; link = link->next) { + for (link = channel->priv->clients; link != NULL; link = link->next) { rcc = link->data; if (!red_channel_client_no_item_being_sent(rcc)) { return FALSE; @@ -617,7 +746,7 @@ void red_client_migrate(RedClient *client) rcc = link->data; channel = red_channel_client_get_channel(rcc); if (red_channel_client_is_connected(rcc)) { - channel->client_cbs.migrate(rcc); + channel->priv->client_cbs.migrate(rcc); } link = next; } @@ -650,7 +779,7 @@ void red_client_destroy(RedClient *client) // to wait for disconnection) // TODO: should we go back to async. For this we need to use // ref count for channel clients. - channel->client_cbs.disconnect(rcc); + channel->priv->client_cbs.disconnect(rcc); spice_assert(red_channel_client_pipe_is_empty(rcc)); spice_assert(red_channel_client_no_item_being_sent(rcc)); red_channel_client_destroy(rcc); @@ -670,7 +799,7 @@ RedChannelClient *red_client_get_channel(RedClient *client, int type, int id) RedChannel *channel; rcc = link->data; channel = red_channel_client_get_channel(rcc); - if (channel->type == type && channel->id == id) { + if (channel->priv->type == type && channel->priv->id == id) { ret = rcc; break; } @@ -763,7 +892,7 @@ static int red_channel_pipes_create_batch(RedChannel *channel, spice_assert(creator != NULL); spice_assert(pipe_add != NULL); - link = channel->clients; + link = channel->priv->clients; while (link != NULL) { next = link->next; rcc = link->data; @@ -806,7 +935,7 @@ uint32_t red_channel_max_pipe_size(RedChannel *channel) RedChannelClient *rcc; uint32_t pipe_size = 0; - for (link = channel->clients; link != NULL; link = link->next) { + for (link = channel->priv->clients; link != NULL; link = link->next) { uint32_t new_size; rcc = link->data; new_size = red_channel_client_get_pipe_size(rcc); @@ -821,7 +950,7 @@ uint32_t red_channel_min_pipe_size(RedChannel *channel) RedChannelClient *rcc; uint32_t pipe_size = ~0; - for (link = channel->clients; link != NULL; link = link->next) { + for (link = channel->priv->clients; link != NULL; link = link->next) { uint32_t new_size; rcc = link->data; new_size = red_channel_client_get_pipe_size(rcc); @@ -836,7 +965,7 @@ uint32_t red_channel_sum_pipes_size(RedChannel *channel) RedChannelClient *rcc; uint32_t sum = 0; - for (link = channel->clients; link != NULL; link = link->next) { + for (link = channel->priv->clients; link != NULL; link = link->next) { rcc = link->data; sum += red_channel_client_get_pipe_size(rcc); } @@ -879,5 +1008,116 @@ int red_channel_wait_all_sent(RedChannel *channel, RedsState* red_channel_get_server(RedChannel *channel) { - return channel->reds; + return channel->priv->reds; +} + +SpiceCoreInterfaceInternal* red_channel_get_core_interface(RedChannel *channel) +{ + return channel->priv->core; +} + +int red_channel_config_socket(RedChannel *self, RedChannelClient *rcc) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->config_socket, FALSE); + + return klass->config_socket(rcc); +} + +void red_channel_on_disconnect(RedChannel *self, RedChannelClient *rcc) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_if_fail(klass->on_disconnect); + + klass->on_disconnect(rcc); +} + +void red_channel_send_item(RedChannel *self, RedChannelClient *rcc, RedPipeItem *item) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_if_fail(klass->send_item); + + klass->send_item(rcc, item); +} + +void red_channel_hold_item(RedChannel *self, RedChannelClient *rcc, RedPipeItem *item) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_if_fail(klass->hold_item); + + klass->hold_item(rcc, item); +} + +void red_channel_release_item(RedChannel *self, RedChannelClient *rcc, + RedPipeItem *item, int item_pushed) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_if_fail(klass->release_item); + + klass->release_item(rcc, item, item_pushed); +} + +uint8_t* red_channel_alloc_recv_buf(RedChannel *self, RedChannelClient *rcc, + uint16_t type, uint32_t size) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->alloc_recv_buf, NULL); + + return klass->alloc_recv_buf(rcc, type, size); +} + +void red_channel_release_recv_buf(RedChannel *self, RedChannelClient *rcc, + uint16_t type, uint32_t size, uint8_t *msg) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_if_fail(klass->release_recv_buf); + + klass->release_recv_buf(rcc, type, size, msg); +} + +int red_channel_handle_migrate_flush_mark(RedChannel *self, RedChannelClient *rcc) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->handle_migrate_flush_mark, FALSE); + + return klass->handle_migrate_flush_mark(rcc); +} + +int red_channel_handle_migrate_data(RedChannel *self, RedChannelClient *rcc, + uint32_t size, void *message) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->handle_migrate_data, FALSE); + + return klass->handle_migrate_data(rcc, size, message); +} + +uint64_t red_channel_handle_migrate_data_get_serial(RedChannel *self, + RedChannelClient *rcc, + uint32_t size, void *message) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->handle_migrate_data_get_serial, 0); + + return klass->handle_migrate_data_get_serial(rcc, size, message); +} + +IncomingHandlerInterface* red_channel_get_incoming_handler(RedChannel *self) +{ + return &self->priv->incoming_cb; +} + +OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel *self) +{ + return &self->priv->outgoing_cb; +} + +void red_channel_reset_thread_id(RedChannel *self) +{ + self->priv->thread_id = pthread_self(); +} + +RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self) +{ + return &self->priv->local_caps; } diff --git a/server/red-channel.h b/server/red-channel.h index 5c1d555..1b8abce 100644 --- a/server/red-channel.h +++ b/server/red-channel.h @@ -36,9 +36,10 @@ #include "red-pipe-item.h" #include "red-channel-client.h" -/* Red Channel interface */ +#include <glib-object.h> + +G_BEGIN_DECLS -typedef struct RedChannel RedChannel; typedef struct RedClient RedClient; typedef uint8_t *(*channel_alloc_msg_recv_buf_proc)(RedChannelClient *channel, @@ -71,13 +72,53 @@ typedef void (*channel_client_connect_proc)(RedChannel *channel, RedClient *clie typedef void (*channel_client_disconnect_proc)(RedChannelClient *base); typedef void (*channel_client_migrate_proc)(RedChannelClient *base); -// TODO: add ASSERTS for thread_id in client and channel calls -// + /* - * callbacks that are triggered from channel client stream events. - * They are called from the thread that listen to the stream events. + * callbacks that are triggered from client events. + * They should be called from the thread that handles the RedClient */ typedef struct { + channel_client_connect_proc connect; + channel_client_disconnect_proc disconnect; + channel_client_migrate_proc migrate; +} ClientCbs; + +int test_capability(const uint32_t *caps, int num_caps, uint32_t cap); + +#define RED_TYPE_CHANNEL red_channel_get_type() + +#define RED_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHANNEL, RedChannel)) +#define RED_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_CHANNEL, RedChannelClass)) +#define RED_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_CHANNEL)) +#define RED_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_CHANNEL)) +#define RED_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHANNEL, RedChannelClass)) + +typedef struct RedChannel RedChannel; +typedef struct RedChannelClass RedChannelClass; +typedef struct RedChannelPrivate RedChannelPrivate; + +struct RedChannel +{ + GObject parent; + + RedChannelPrivate *priv; +}; + +struct RedChannelClass +{ + GObjectClass parent_class; + + /* subclasses must implement either handle_message(), or both parser() and + * handle_parsed() */ + channel_handle_message_proc handle_message; + spice_parse_channel_func_t parser; + channel_handle_parsed_proc handle_parsed; + + // TODO: add ASSERTS for thread_id in client and channel calls + /* + * callbacks that are triggered from channel client stream events. + * They are called from the thread that listen to the stream events. + */ channel_configure_socket_proc config_socket; channel_disconnect_proc on_disconnect; channel_send_pipe_item_proc send_item; @@ -88,18 +129,9 @@ typedef struct { channel_handle_migrate_flush_mark_proc handle_migrate_flush_mark; channel_handle_migrate_data_proc handle_migrate_data; channel_handle_migrate_data_get_serial_proc handle_migrate_data_get_serial; -} ChannelCbs; - +}; -/* - * callbacks that are triggered from client events. - * They should be called from the thread that handles the RedClient - */ -typedef struct { - channel_client_connect_proc connect; - channel_client_disconnect_proc disconnect; - channel_client_migrate_proc migrate; -} ClientCbs; +/* Red Channel interface */ typedef struct RedChannelCapabilities { int num_common_caps; @@ -108,94 +140,20 @@ typedef struct RedChannelCapabilities { uint32_t *caps; } RedChannelCapabilities; -int test_capability(const uint32_t *caps, int num_caps, uint32_t cap); - -typedef struct RedChannelClientLatencyMonitor { - int state; - uint64_t last_pong_time; - SpiceTimer *timer; - uint32_t id; - int tcp_nodelay; - int warmup_was_sent; - - int64_t roundtrip; -} RedChannelClientLatencyMonitor; - -typedef struct RedChannelClientConnectivityMonitor { - int state; - uint32_t out_bytes; - uint32_t in_bytes; - uint32_t timeout; - SpiceTimer *timer; -} RedChannelClientConnectivityMonitor; - -struct RedChannel { - uint32_t type; - uint32_t id; - - uint32_t refs; - - RingItem link; // channels link for reds - - const SpiceCoreInterfaceInternal *core; - int handle_acks; - - // RedChannel will hold only connected channel clients (logic - when pushing pipe item to all channel clients, there - // is no need to go over disconnect clients) - // . While client will hold the channel clients till it is destroyed - // and then it will destroy them as well. - // However RCC still holds a reference to the Channel. - // Maybe replace these logic with ref count? - // TODO: rename to 'connected_clients'? - GList *clients; - uint32_t clients_num; - - OutgoingHandlerInterface outgoing_cb; - IncomingHandlerInterface incoming_cb; - - ChannelCbs channel_cbs; - ClientCbs client_cbs; - - RedChannelCapabilities local_caps; - uint32_t migration_flags; - - void *data; - - // TODO: when different channel_clients are in different threads from Channel -> need to protect! - pthread_t thread_id; - RedsState *reds; -#ifdef RED_STATISTICS - StatNodeRef stat; - uint64_t *out_bytes_counter; -#endif -}; - -#define RED_CHANNEL(Channel) ((RedChannel *)(Channel)) - -/* if one of the callbacks should cause disconnect, use red_channel_shutdown and don't - * explicitly destroy the channel */ -RedChannel *red_channel_create(int size, - RedsState *reds, - const SpiceCoreInterfaceInternal *core, - uint32_t type, uint32_t id, - int handle_acks, - channel_handle_message_proc handle_message, - const ChannelCbs *channel_cbs, - uint32_t migration_flags); +GType red_channel_get_type(void) G_GNUC_CONST; /* alternative constructor, meant for marshaller based (inputs,main) channels, * will become default eventually */ +/* RedChannel *red_channel_create_parser(int size, RedsState *reds, const SpiceCoreInterfaceInternal *core, uint32_t type, uint32_t id, - int handle_acks, + gboolean handle_acks, spice_parse_channel_func_t parser, channel_handle_parsed_proc handle_parsed, - const ChannelCbs *channel_cbs, uint32_t migration_flags); -void red_channel_ref(RedChannel *channel); -void red_channel_unref(RedChannel *channel); + */ void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc); void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc); @@ -206,11 +164,6 @@ void red_channel_register_client_cbs(RedChannel *channel, const ClientCbs *clien void red_channel_set_common_cap(RedChannel *channel, uint32_t cap); void red_channel_set_cap(RedChannel *channel, uint32_t cap); -// TODO: tmp, for channels that don't use RedChannel yet (e.g., snd channel), but -// do use the client callbacks. So the channel clients are not connected (the channel doesn't -// have list of them, but they do have a link to the channel, and the client has a list of them) -RedChannel *red_channel_create_dummy(int size, RedsState *reds, uint32_t type, uint32_t id); - int red_channel_is_connected(RedChannel *channel); /* seamless migration is supported for only one client. This routine @@ -274,6 +227,9 @@ void red_channel_receive(RedChannel *channel); void red_channel_send(RedChannel *channel); // For red_worker void red_channel_disconnect(RedChannel *channel); +void red_channel_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, int migration, int num_common_caps, + uint32_t *common_caps, int num_caps, uint32_t *caps); /* return the sum of all the rcc pipe size */ uint32_t red_channel_max_pipe_size(RedChannel *channel); @@ -286,8 +242,38 @@ uint32_t red_channel_sum_pipes_size(RedChannel *channel); typedef void (*channel_client_callback)(RedChannelClient *rcc); typedef void (*channel_client_callback_data)(RedChannelClient *rcc, void *data); void red_channel_apply_clients(RedChannel *channel, channel_client_callback v); -void red_channel_apply_clients_data(RedChannel *channel, channel_client_callback_data v, void * data); +void red_channel_apply_clients_data(RedChannel *channel, channel_client_callback_data v, void *data); +GList *red_channel_get_clients(RedChannel *channel); +guint red_channel_get_n_clients(RedChannel *channel); struct RedsState* red_channel_get_server(RedChannel *channel); +SpiceCoreInterfaceInternal* red_channel_get_core_interface(RedChannel *channel); + +/* channel callback function */ +int red_channel_config_socket(RedChannel *self, RedChannelClient *rcc); +void red_channel_on_disconnect(RedChannel *self, RedChannelClient *rcc); +void red_channel_send_item(RedChannel *self, RedChannelClient *rcc, RedPipeItem *item); +void red_channel_hold_item(RedChannel *self, RedChannelClient *rcc, RedPipeItem *item); +void red_channel_release_item(RedChannel *channel, RedChannelClient *rcc, + RedPipeItem *item, int item_pushed); +uint8_t* red_channel_alloc_recv_buf(RedChannel *self, RedChannelClient *rcc, + uint16_t type, uint32_t size); +void red_channel_release_recv_buf(RedChannel *self, RedChannelClient *rcc, + uint16_t type, uint32_t size, uint8_t *msg); +int red_channel_handle_migrate_flush_mark(RedChannel *self, RedChannelClient *rcc); +int red_channel_handle_migrate_data(RedChannel *self, RedChannelClient *rcc, + uint32_t size, void *message); +uint64_t red_channel_handle_migrate_data_get_serial(RedChannel *self, + RedChannelClient *rcc, + uint32_t size, void *message); +void red_channel_reset_thread_id(RedChannel *self); +StatNodeRef red_channel_get_stat_node(RedChannel *channel); + +/* FIXME: do these even need to be in RedChannel? It's really only used in + * RedChannelClient. Needs refactoring */ +IncomingHandlerInterface* red_channel_get_incoming_handler(RedChannel *self); +OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel *self); + +RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self); struct RedClient { RedsState *reds; @@ -362,4 +348,6 @@ int red_channel_wait_all_sent(RedChannel *channel, #define CHANNEL_BLOCKED_SLEEP_DURATION 10000 //micro +G_END_DECLS + #endif diff --git a/server/red-parse-qxl.h b/server/red-parse-qxl.h index 9c30572..5da6916 100644 --- a/server/red-parse-qxl.h +++ b/server/red-parse-qxl.h @@ -64,6 +64,8 @@ static inline RedDrawable *red_drawable_ref(RedDrawable *drawable) return drawable; } +void red_drawable_unref(RedDrawable *red_drawable); + typedef struct RedUpdateCmd { QXLReleaseInfoExt release_info_ext; SpiceRect area; diff --git a/server/red-qxl.c b/server/red-qxl.c index ef39f0e..d255eb6 100644 --- a/server/red-qxl.c +++ b/server/red-qxl.c @@ -81,7 +81,7 @@ static void red_qxl_set_display_peer(RedChannel *channel, RedClient *client, Dispatcher *dispatcher; spice_debug("%s", ""); - dispatcher = (Dispatcher *)channel->data; + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); payload.client = client; payload.stream = stream; payload.migration = migration; @@ -108,7 +108,7 @@ static void red_qxl_disconnect_display_peer(RedChannelClient *rcc) return; } - dispatcher = (Dispatcher *)channel->data; + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); spice_printerr(""); payload.rcc = rcc; @@ -125,11 +125,14 @@ static void red_qxl_display_migrate(RedChannelClient *rcc) RedWorkerMessageDisplayMigrate payload; Dispatcher *dispatcher; RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type, id; + if (!channel) { return; } - dispatcher = (Dispatcher *)channel->data; - spice_printerr("channel type %u id %u", channel->type, channel->id); + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); + spice_printerr("channel type %u id %u", type, id); payload.rcc = rcc; dispatcher_send_message(dispatcher, RED_WORKER_MESSAGE_DISPLAY_MIGRATE, @@ -142,7 +145,7 @@ static void red_qxl_set_cursor_peer(RedChannel *channel, RedClient *client, Reds uint32_t *caps) { RedWorkerMessageCursorConnect payload = {0,}; - Dispatcher *dispatcher = (Dispatcher *)channel->data; + Dispatcher *dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); spice_printerr(""); payload.client = client; payload.stream = stream; @@ -170,7 +173,7 @@ static void red_qxl_disconnect_cursor_peer(RedChannelClient *rcc) return; } - dispatcher = (Dispatcher *)channel->data; + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); spice_printerr(""); payload.rcc = rcc; @@ -184,12 +187,14 @@ static void red_qxl_cursor_migrate(RedChannelClient *rcc) RedWorkerMessageCursorMigrate payload; Dispatcher *dispatcher; RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type, id; if (!channel) { return; } - dispatcher = (Dispatcher *)channel->data; - spice_printerr("channel type %u id %u", channel->type, channel->id); + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); + spice_printerr("channel type %u id %u", type, id); payload.rcc = rcc; dispatcher_send_message(dispatcher, RED_WORKER_MESSAGE_CURSOR_MIGRATE, diff --git a/server/red-replay-qxl.c b/server/red-replay-qxl.c index 281bc7a..a64d438 100644 --- a/server/red-replay-qxl.c +++ b/server/red-replay-qxl.c @@ -24,7 +24,7 @@ #include <zlib.h> #include <pthread.h> #include "reds.h" -#include "red-worker.h" +#include "red-qxl.h" #include "red-common.h" #include "memslot.h" #include "red-parse-qxl.h" diff --git a/server/red-worker.c b/server/red-worker.c index 5be29aa..0d462b5 100644 --- a/server/red-worker.c +++ b/server/red-worker.c @@ -23,16 +23,12 @@ #include <stdio.h> #include <stdarg.h> -#include <fcntl.h> -#include <sys/socket.h> -#include <netinet/in.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <poll.h> #include <pthread.h> -#include <netinet/tcp.h> #include <openssl/ssl.h> #include <inttypes.h> #include <glib.h> @@ -97,33 +93,8 @@ static RedsState* red_worker_get_server(RedWorker *worker); static int display_is_connected(RedWorker *worker) { - return (worker->display_channel && red_channel_is_connected( - &worker->display_channel->common.base)); -} - -static uint8_t *common_alloc_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size) -{ - RedChannel *channel = red_channel_client_get_channel(rcc); - CommonGraphicsChannel *common = SPICE_CONTAINEROF(channel, CommonGraphicsChannel, base); - - /* SPICE_MSGC_MIGRATE_DATA is the only client message whose size is dynamic */ - if (type == SPICE_MSGC_MIGRATE_DATA) { - return spice_malloc(size); - } - - if (size > CHANNEL_RECEIVE_BUF_SIZE) { - spice_critical("unexpected message size %u (max is %d)", size, CHANNEL_RECEIVE_BUF_SIZE); - return NULL; - } - return common->recv_buf; -} - -static void common_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size, - uint8_t* msg) -{ - if (type == SPICE_MSGC_MIGRATE_DATA) { - free(msg); - } + return worker->display_channel && + red_channel_is_connected(RED_CHANNEL(worker->display_channel)); } void red_drawable_unref(RedDrawable *red_drawable) @@ -244,7 +215,7 @@ static int red_process_display(RedWorker *worker, int *ring_is_empty) &update, ext_cmd.cmd.data)) { break; } - if (!validate_surface(worker->display_channel, update.surface_id)) { + if (!display_channel_validate_surface(worker->display_channel, update.surface_id)) { spice_warning("Invalid surface in QXL_CMD_UPDATE"); } else { display_channel_draw(worker->display_channel, &update.area, update.surface_id); @@ -285,7 +256,7 @@ static int red_process_display(RedWorker *worker, int *ring_is_empty) spice_error("bad command type"); } n++; - if (red_channel_all_blocked(&worker->display_channel->common.base) + if (red_channel_all_blocked(RED_CHANNEL(worker->display_channel)) || spice_get_monotonic_time_ns() - start > NSEC_PER_SEC / 100) { worker->event_timeout = 0; return n; @@ -398,80 +369,6 @@ static void flush_all_qxl_commands(RedWorker *worker) flush_cursor_commands(worker); } -static int common_channel_config_socket(RedChannelClient *rcc) -{ - RedClient *client = red_channel_client_get_client(rcc); - MainChannelClient *mcc = red_client_get_main(client); - RedsStream *stream = red_channel_client_get_stream(rcc); - CommonGraphicsChannelClient *ccc = COMMON_GRAPHICS_CHANNEL_CLIENT(rcc); - int flags; - int delay_val; - gboolean low_bw; - - if ((flags = fcntl(stream->socket, F_GETFL)) == -1) { - spice_warning("accept failed, %s", strerror(errno)); - return FALSE; - } - - if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) { - spice_warning("accept failed, %s", strerror(errno)); - return FALSE; - } - - // TODO - this should be dynamic, not one time at channel creation - low_bw = main_channel_client_is_low_bandwidth(mcc); - common_graphics_channel_client_set_low_bandwidth(ccc, low_bw); - delay_val = low_bw ? 0 : 1; - /* FIXME: Using Nagle's Algorithm can lead to apparent delays, depending - * on the delayed ack timeout on the other side. - * Instead of using Nagle's, we need to implement message buffering on - * the application level. - * see: http://www.stuartcheshire.org/papers/NagleDelayedAck/ - */ - if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, - sizeof(delay_val)) == -1) { - if (errno != ENOTSUP) { - spice_warning("setsockopt failed, %s", strerror(errno)); - } - } - return TRUE; -} - -CommonGraphicsChannel *red_worker_new_channel(RedWorker *worker, int size, - const char *name, - uint32_t channel_type, int migration_flags, - ChannelCbs *channel_cbs, - channel_handle_parsed_proc handle_parsed) -{ - RedChannel *channel = NULL; - CommonGraphicsChannel *common; - - 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, red_worker_get_server(worker), - &worker->core, channel_type, - worker->qxl->id, TRUE /* handle_acks */, - spice_get_client_channel_parser(channel_type, NULL), - handle_parsed, - channel_cbs, - migration_flags); - spice_return_val_if_fail(channel, NULL); - red_channel_set_stat_node(channel, stat_add_node(red_worker_get_server(worker), - worker->stat, name, TRUE)); - - common = (CommonGraphicsChannel *)channel; - common->qxl = worker->qxl; - return common; -} - static void guest_set_client_capabilities(RedWorker *worker) { int i; @@ -503,7 +400,7 @@ static void guest_set_client_capabilities(RedWorker *worker) return; } if ((worker->display_channel == NULL) || - (RED_CHANNEL(worker->display_channel)->clients_num == 0)) { + (red_channel_get_n_clients(RED_CHANNEL(worker->display_channel)) == 0)) { red_qxl_set_client_capabilities(worker->qxl, FALSE, caps); } else { // Take least common denominator @@ -542,7 +439,7 @@ static void cursor_connect(RedWorker *worker, RedClient *client, RedsStream *str red_channel_client_ack_zero_messages_window(rcc); red_channel_client_push_set_ack(rcc); - cursor_channel_init(channel, ccc); + cursor_channel_do_init(channel, ccc); } static void handle_dev_update_async(void *opaque, void *payload) @@ -608,18 +505,6 @@ static void handle_dev_destroy_surfaces(void *opaque, void *payload) cursor_channel_reset(worker->cursor_channel); } -static void display_update_monitors_config(DisplayChannel *display, - QXLMonitorsConfig *config, - uint16_t count, uint16_t max_allowed) -{ - - if (display->monitors_config) - monitors_config_unref(display->monitors_config); - - display->monitors_config = - monitors_config_new(config->heads, count, max_allowed); -} - static void red_worker_push_monitors_config(RedWorker *worker) { DisplayChannelClient *dcc; @@ -630,21 +515,6 @@ static void red_worker_push_monitors_config(RedWorker *worker) } } -static void set_monitors_config_to_primary(DisplayChannel *display) -{ - DrawContext *context = &display->surfaces[0].context; - QXLHead head = { 0, }; - - spice_return_if_fail(display->surfaces[0].context.canvas); - - if (display->monitors_config) - monitors_config_unref(display->monitors_config); - - head.width = context->width; - head.height = context->height; - display->monitors_config = monitors_config_new(&head, 1, 1); -} - static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id, QXLDevSurfaceCreate surface) { @@ -675,20 +545,22 @@ static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id, display_channel_create_surface(display, 0, surface.width, surface.height, surface.stride, surface.format, line_0, surface.flags & QXL_SURF_FLAG_KEEP_DATA, TRUE); - set_monitors_config_to_primary(display); + display_channel_set_monitors_config_to_primary(display); - if (display_is_connected(worker) && !worker->display_channel->common.during_target_migrate) { + if (display_is_connected(worker) && + !common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(worker->display_channel))) + { /* guest created primary, so it will (hopefully) send a monitors_config * now, don't send our own temporary one */ if (!worker->driver_cap_monitors_config) { red_worker_push_monitors_config(worker); } - red_pipes_add_verb(&worker->display_channel->common.base, + red_pipes_add_verb(RED_CHANNEL(worker->display_channel), SPICE_MSG_DISPLAY_MARK); - red_channel_push(&worker->display_channel->common.base); + red_channel_push(RED_CHANNEL(worker->display_channel)); } - cursor_channel_init(worker->cursor_channel, NULL); + cursor_channel_do_init(worker->cursor_channel, NULL); } static void handle_dev_create_primary_surface(void *opaque, void *payload) @@ -703,12 +575,12 @@ static void destroy_primary_surface(RedWorker *worker, uint32_t surface_id) { DisplayChannel *display = worker->display_channel; - if (!validate_surface(display, surface_id)) + if (!display_channel_validate_surface(display, surface_id)) return; spice_warn_if_fail(surface_id == 0); spice_debug(NULL); - if (!display->surfaces[surface_id].context.canvas) { + if (!display_channel_surface_has_canvas(display, surface_id)) { spice_warning("double destroy of primary surface"); return; } @@ -717,8 +589,10 @@ static void destroy_primary_surface(RedWorker *worker, uint32_t surface_id) display_channel_destroy_surface_wait(display, 0); display_channel_surface_unref(display, 0); + /* FIXME: accessing private date only for warning purposes... spice_warn_if_fail(ring_is_empty(&display->streams)); - spice_warn_if_fail(!display->surfaces[surface_id].context.canvas); + */ + spice_warn_if_fail(!display_channel_surface_has_canvas(display, surface_id)); cursor_channel_reset(worker->cursor_channel); } @@ -784,10 +658,10 @@ static void handle_dev_start(void *opaque, void *payload) spice_assert(!worker->running); if (worker->cursor_channel) { - COMMON_GRAPHICS_CHANNEL(worker->cursor_channel)->during_target_migrate = FALSE; + common_graphics_channel_set_during_target_migrate(COMMON_GRAPHICS_CHANNEL(worker->cursor_channel), FALSE); } if (worker->display_channel) { - worker->display_channel->common.during_target_migrate = FALSE; + common_graphics_channel_set_during_target_migrate(COMMON_GRAPHICS_CHANNEL(worker->display_channel), FALSE); display_channel_wait_for_migrate_data(worker->display_channel); } worker->running = TRUE; @@ -807,16 +681,18 @@ static void handle_dev_oom(void *opaque, void *payload) RedWorker *worker = opaque; DisplayChannel *display = worker->display_channel; - RedChannel *display_red_channel = &display->common.base; + RedChannel *display_red_channel = RED_CHANNEL(display); int ring_is_empty; spice_return_if_fail(worker->running); // streams? but without streams also leak +#if FIXME spice_debug("OOM1 #draw=%u, #glz_draw=%u current %u pipes %u", display->drawable_count, display->glz_drawable_count, display->current_size, red_channel_sum_pipes_size(display_red_channel)); +#endif while (red_process_display(worker, &ring_is_empty)) { red_channel_push(display_red_channel); } @@ -824,12 +700,14 @@ static void handle_dev_oom(void *opaque, void *payload) display_channel_free_some(worker->display_channel); red_qxl_flush_resources(worker->qxl); } +#if FIXME spice_debug("OOM2 #draw=%u, #glz_draw=%u current %u pipes %u", display->drawable_count, display->glz_drawable_count, display->current_size, red_channel_sum_pipes_size(display_red_channel)); red_qxl_clear_pending(worker->qxl->st, RED_DISPATCHER_PENDING_OOM); +#endif } static void handle_dev_reset_cursor(void *opaque, void *payload) @@ -842,9 +720,7 @@ static void handle_dev_reset_cursor(void *opaque, void *payload) static void handle_dev_reset_image_cache(void *opaque, void *payload) { RedWorker *worker = opaque; - DisplayChannel *display = worker->display_channel; - - image_cache_reset(&display->image_cache); + display_channel_reset_image_cache(worker->display_channel); } static void handle_dev_destroy_surface_wait_async(void *opaque, void *payload) @@ -964,7 +840,7 @@ static void handle_dev_monitors_config_async(void *opaque, void *payload) /* TODO: raise guest bug (requires added QXL interface) */ return; } - display_update_monitors_config(worker->display_channel, dev_monitors_config, + display_channel_update_monitors_config(worker->display_channel, dev_monitors_config, MIN(count, msg->max_monitors), MIN(max_allowed, msg->max_monitors)); red_worker_push_monitors_config(worker); @@ -1509,17 +1385,28 @@ RedWorker* red_worker_new(QXLInstance *qxl, worker->event_timeout = INF_EVENT_WAIT; - worker->cursor_channel = cursor_channel_new(worker); + worker->cursor_channel = cursor_channel_new(reds, worker->qxl, &worker->core); + red_channel_set_stat_node(RED_CHANNEL(worker->cursor_channel), + stat_add_node(red_worker_get_server(worker), + worker->stat, + "cursor_channel", TRUE)); channel = RED_CHANNEL(worker->cursor_channel); red_channel_register_client_cbs(channel, client_cursor_cbs, dispatcher); + g_object_set_data(G_OBJECT(channel), "dispatcher", dispatcher); reds_register_channel(reds, channel); // TODO: handle seemless migration. Temp, setting migrate to FALSE - worker->display_channel = display_channel_new(reds, worker, FALSE, reds_get_streaming_video(reds), + worker->display_channel = display_channel_new(reds, worker->qxl, &worker->core, + FALSE, reds_get_streaming_video(reds), init_info.n_surfaces); + red_channel_set_stat_node(RED_CHANNEL(worker->display_channel), + stat_add_node(red_worker_get_server(worker), + worker->stat, + "display_channel", TRUE)); channel = RED_CHANNEL(worker->display_channel); red_channel_register_client_cbs(channel, client_display_cbs, dispatcher); + g_object_set_data(G_OBJECT(channel), "dispatcher", dispatcher); red_channel_set_cap(channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG); red_channel_set_cap(channel, SPICE_DISPLAY_CAP_PREF_COMPRESSION); red_channel_set_cap(channel, SPICE_DISPLAY_CAP_STREAM_REPORT); @@ -1536,8 +1423,8 @@ SPICE_GNUC_NORETURN static void *red_worker_main(void *arg) spice_assert(MAX_PIPE_SIZE > WIDE_CLIENT_ACK_WINDOW && MAX_PIPE_SIZE > NARROW_CLIENT_ACK_WINDOW); //ensure wakeup by ack message - RED_CHANNEL(worker->cursor_channel)->thread_id = pthread_self(); - RED_CHANNEL(worker->display_channel)->thread_id = pthread_self(); + red_channel_reset_thread_id(RED_CHANNEL(worker->cursor_channel)); + red_channel_reset_thread_id(RED_CHANNEL(worker->display_channel)); GMainLoop *loop = g_main_loop_new(worker->core.main_context, FALSE); g_main_loop_run(loop); diff --git a/server/red-worker.h b/server/red-worker.h index 501128b..ebc5a97 100644 --- a/server/red-worker.h +++ b/server/red-worker.h @@ -25,73 +25,9 @@ typedef struct RedWorker RedWorker; -#define COMMON_CLIENT_TIMEOUT (NSEC_PER_SEC * 30) - -#define CHANNEL_RECEIVE_BUF_SIZE 1024 -typedef struct CommonGraphicsChannel { - RedChannel base; // Must be the first thing - - QXLInstance *qxl; - uint8_t recv_buf[CHANNEL_RECEIVE_BUF_SIZE]; - uint32_t id_alloc; // bitfield. TODO - use this instead of shift scheme. - int during_target_migrate; /* TRUE when the client that is associated with the channel - is during migration. Turned off when the vm is started. - The flag is used to avoid sending messages that are artifacts - of the transition from stopped vm to loaded vm (e.g., recreation - of the primary surface) */ -} CommonGraphicsChannel; - -#define COMMON_GRAPHICS_CHANNEL(Channel) ((CommonGraphicsChannel*)(Channel)) - -enum { - RED_PIPE_ITEM_TYPE_VERB = RED_PIPE_ITEM_TYPE_CHANNEL_BASE, - RED_PIPE_ITEM_TYPE_INVAL_ONE, - - RED_PIPE_ITEM_TYPE_COMMON_LAST -}; - -typedef struct RedVerbItem { - RedPipeItem base; - uint16_t verb; -} RedVerbItem; - -static inline void red_marshall_verb(RedChannelClient *rcc, RedVerbItem *item) -{ - red_channel_client_init_send_data(rcc, item->verb, NULL); -} - -static inline void red_pipe_add_verb(RedChannelClient* rcc, uint16_t verb) -{ - RedVerbItem *item = spice_new(RedVerbItem, 1); - - red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_VERB); - item->verb = verb; - red_channel_client_pipe_add(rcc, &item->base); -} - -static inline void red_pipe_add_verb_proxy(RedChannelClient *rcc, gpointer data) -{ - uint16_t verb = GPOINTER_TO_UINT(data); - red_pipe_add_verb(rcc, verb); -} - - -static inline void red_pipes_add_verb(RedChannel *channel, uint16_t verb) -{ - red_channel_apply_clients_data(channel, red_pipe_add_verb_proxy, GUINT_TO_POINTER(verb)); -} - RedWorker* red_worker_new(QXLInstance *qxl, const ClientCbs *client_cursor_cbs, const ClientCbs *client_display_cbs); bool red_worker_run(RedWorker *worker); -void red_drawable_unref(RedDrawable *red_drawable); - -CommonGraphicsChannel *red_worker_new_channel(RedWorker *worker, int size, - const char *name, - uint32_t channel_type, int migration_flags, - ChannelCbs *channel_cbs, - channel_handle_parsed_proc handle_parsed); - #endif diff --git a/server/reds-private.h b/server/reds-private.h index 74a251b..4b86d2d 100644 --- a/server/reds-private.h +++ b/server/reds-private.h @@ -120,8 +120,7 @@ struct RedsState { Ring mig_target_clients; int num_mig_target_clients; - int num_of_channels; - Ring channels; + GList *channels; int mouse_mode; int is_client_mouse_allowed; int dispatcher_allows_client_mouse; diff --git a/server/reds.c b/server/reds.c index 9f5551e..27fc5da 100644 --- a/server/reds.c +++ b/server/reds.c @@ -463,15 +463,13 @@ void stat_update_value(RedsState *reds, uint32_t value) void reds_register_channel(RedsState *reds, RedChannel *channel) { spice_assert(reds); - ring_add(&reds->channels, &channel->link); - reds->num_of_channels++; + reds->channels = g_list_append(reds->channels, channel); } void reds_unregister_channel(RedsState *reds, RedChannel *channel) { - if (ring_item_is_linked(&channel->link)) { - ring_remove(&channel->link); - reds->num_of_channels--; + if (g_list_find(reds->channels, channel)) { + reds->channels = g_list_remove(reds->channels, channel); } else { spice_warning("not found"); } @@ -479,11 +477,13 @@ void reds_unregister_channel(RedsState *reds, RedChannel *channel) static RedChannel *reds_find_channel(RedsState *reds, uint32_t type, uint32_t id) { - RingItem *now; + GList *l; - RING_FOREACH(now, &reds->channels) { - RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link); - if (channel->type == type && channel->id == id) { + for (l = reds->channels; l != NULL; l = l->next) { + RedChannel *channel = l->data; + uint32_t this_type, this_id; + g_object_get(channel, "channel-type", &this_type, "id", &this_id, NULL); + if (this_type == type && this_id == id) { return channel; } } @@ -552,7 +552,7 @@ static void reds_reset_vdp(RedsState *reds) * In addition, there used to be a misshandling of AGENT_TOKENS message in spice-gtk: it * overrides the amount of tokens, instead of adding the given amount. */ - if (red_channel_test_remote_cap(&reds->main_channel->base, + if (red_channel_test_remote_cap(RED_CHANNEL(reds->main_channel), SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) { dev->priv->agent_attached = FALSE; } else { @@ -743,7 +743,7 @@ static void reds_agent_remove(RedsState *reds) reds->vdagent = NULL; reds_update_mouse_mode(reds); if (reds_main_channel_connected(reds) && - !red_channel_is_waiting_for_migrate_data(&reds->main_channel->base)) { + !red_channel_is_waiting_for_migrate_data(RED_CHANNEL(reds->main_channel))) { main_channel_push_agent_disconnected(reds->main_channel); } } @@ -995,7 +995,9 @@ SPICE_GNUC_VISIBLE int spice_server_get_num_clients(SpiceServer *reds) static int channel_supports_multiple_clients(RedChannel *channel) { - switch (channel->type) { + uint32_t type; + g_object_get(channel, "channel-type", &type, NULL); + switch (type) { case SPICE_CHANNEL_MAIN: case SPICE_CHANNEL_DISPLAY: case SPICE_CHANNEL_CURSOR: @@ -1007,23 +1009,25 @@ static int channel_supports_multiple_clients(RedChannel *channel) static void reds_fill_channels(RedsState *reds, SpiceMsgChannels *channels_info) { - RingItem *now; + GList *l; int used_channels = 0; - RING_FOREACH(now, &reds->channels) { - RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link); + for (l = reds->channels; l != NULL; l = l->next) { + uint32_t type, id; + RedChannel *channel = l->data; if (reds->num_clients > 1 && !channel_supports_multiple_clients(channel)) { continue; } - channels_info->channels[used_channels].type = channel->type; - channels_info->channels[used_channels].id = channel->id; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + channels_info->channels[used_channels].type = type; + channels_info->channels[used_channels].id = id; used_channels++; } channels_info->num_of_channels = used_channels; - if (used_channels != reds->num_of_channels) { - spice_warning("sent %d out of %d", used_channels, reds->num_of_channels); + if (used_channels != g_list_length(reds->channels)) { + spice_warning("sent %d out of %d", used_channels, g_list_length(reds->channels)); } } @@ -1034,7 +1038,7 @@ SpiceMsgChannels *reds_msg_channels_new(RedsState *reds) spice_assert(reds != NULL); channels_info = (SpiceMsgChannels *)spice_malloc(sizeof(SpiceMsgChannels) - + reds->num_of_channels * sizeof(SpiceChannelId)); + + g_list_length(reds->channels) * sizeof(SpiceChannelId)); reds_fill_channels(reds, channels_info); @@ -1546,12 +1550,12 @@ static int reds_send_link_ack(RedsState *reds, RedLinkInfo *link) return FALSE; } spice_assert(reds->main_channel); - channel = &reds->main_channel->base; + channel = RED_CHANNEL(reds->main_channel); } reds_channel_init_auth_caps(link, channel); /* make sure common caps are set */ - channel_caps = &channel->local_caps; + channel_caps = red_channel_get_local_capabilities(channel); ack.num_common_caps = GUINT32_TO_LE(channel_caps->num_common_caps); ack.num_channel_caps = GUINT32_TO_LE(channel_caps->num_caps); hdr_size += channel_caps->num_common_caps * sizeof(uint32_t); @@ -1888,13 +1892,13 @@ static void reds_channel_do_link(RedChannel *channel, RedClient *client, spice_assert(stream); caps = (uint32_t *)((uint8_t *)link_msg + link_msg->caps_offset); - channel->client_cbs.connect(channel, client, stream, - red_client_during_migrate_at_target(client), - link_msg->num_common_caps, - link_msg->num_common_caps ? caps : NULL, - link_msg->num_channel_caps, - link_msg->num_channel_caps ? - caps + link_msg->num_common_caps : NULL); + red_channel_connect(channel, client, stream, + red_client_during_migrate_at_target(client), + link_msg->num_common_caps, + link_msg->num_common_caps ? caps : NULL, + link_msg->num_channel_caps, + link_msg->num_channel_caps ? + caps + link_msg->num_common_caps : NULL); } /* @@ -3123,7 +3127,7 @@ static RedCharDevice *attach_to_red_agent(RedsState *reds, SpiceCharDeviceInstan dev->priv->plug_generation++; if (dev->priv->mig_data || - red_channel_is_waiting_for_migrate_data(&reds->main_channel->base)) { + red_channel_is_waiting_for_migrate_data(RED_CHANNEL(reds->main_channel))) { /* Migration in progress (code is running on the destination host): * 1. Add the client to spice char device, if it was not already added. * 2.a If this (qemu-kvm state load side of migration) happens first @@ -3442,7 +3446,6 @@ static int do_spice_init(RedsState *reds, SpiceCoreInterface *core_interface) ring_init(&reds->clients); reds->num_clients = 0; reds->main_dispatcher = main_dispatcher_new(reds, reds->core); - ring_init(&reds->channels); ring_init(&reds->mig_target_clients); reds->char_devices = NULL; reds->mig_wait_disconnect_clients = NULL; @@ -3986,7 +3989,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *reds, const cha * be valid (see reds_reset_vdp for more details). */ try_seamless = reds->seamless_migration_enabled && - red_channel_test_remote_cap(&reds->main_channel->base, + red_channel_test_remote_cap(RED_CHANNEL(reds->main_channel), SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS); /* main channel will take care of clients that are still during migration (at target)*/ if (main_channel_migrate_connect(reds->main_channel, reds->config->mig_spice, diff --git a/server/smartcard.c b/server/smartcard.c index ec42960..c681d39 100644 --- a/server/smartcard.c +++ b/server/smartcard.c @@ -49,6 +49,61 @@ // Maximal length of APDU #define APDUBufSize 270 +#define RED_TYPE_SMARTCARD_CHANNEL red_smartcard_channel_get_type() + +#define RED_SMARTCARD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannel)) +#define RED_SMARTCARD_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelClass)) +#define RED_IS_SMARTCARD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_SMARTCARD_CHANNEL)) +#define RED_IS_SMARTCARD_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_SMARTCARD_CHANNEL)) +#define RED_SMARTCARD_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelClass)) + +typedef struct RedSmartcardChannel RedSmartcardChannel; +typedef struct RedSmartcardChannelClass RedSmartcardChannelClass; +typedef struct RedSmartcardChannelPrivate RedSmartcardChannelPrivate; + +struct RedSmartcardChannel +{ + RedChannel parent; + + RedSmartcardChannelPrivate *priv; +}; + +struct RedSmartcardChannelClass +{ + RedChannelClass parent_class; +}; + +GType red_smartcard_channel_get_type(void) G_GNUC_CONST; + +G_DEFINE_TYPE(RedSmartcardChannel, red_smartcard_channel, RED_TYPE_CHANNEL) + +#define SMARTCARD_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelPrivate)) + +struct RedSmartcardChannelPrivate +{ + gpointer padding; +}; + +static void +red_smartcard_channel_init(RedSmartcardChannel *self) +{ + self->priv = SMARTCARD_CHANNEL_PRIVATE(self); +} + +RedSmartcardChannel * +red_smartcard_channel_new(RedsState *reds) +{ + return g_object_new(RED_TYPE_SMARTCARD_CHANNEL, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", SPICE_CHANNEL_SMARTCARD, + "id", 0, + "handle-acks", FALSE /* handle_acks */, + "migration-flags", (SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + NULL); +} + + G_DEFINE_TYPE(RedCharDeviceSmartcard, red_char_device_smartcard, RED_TYPE_CHAR_DEVICE) #define RED_CHAR_DEVICE_SMARTCARD_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RED_TYPE_CHAR_DEVICE_SMARTCARD, RedCharDeviceSmartcardPrivate)) @@ -74,10 +129,6 @@ typedef struct RedMsgItem { static RedMsgItem *smartcard_get_vsc_msg_item(RedChannelClient *rcc, VSCMsgHeader *vheader); static void smartcard_channel_client_pipe_add_push(RedChannelClient *rcc, RedPipeItem *item); -typedef struct SmartCardChannel { - RedChannel base; -} SmartCardChannel; - static struct Readers { uint32_t num; SpiceCharDeviceInstance* sin[SMARTCARD_MAX_READERS]; @@ -531,43 +582,53 @@ static void smartcard_connect_client(RedChannel *channel, RedClient *client, } } -SmartCardChannel *g_smartcard_channel; - -static void smartcard_init(RedsState *reds) +static void +red_smartcard_channel_constructed(GObject *object) { - ChannelCbs channel_cbs = { NULL, }; + RedSmartcardChannel *self = RED_SMARTCARD_CHANNEL(object); + RedsState *reds = red_channel_get_server(RED_CHANNEL(self)); ClientCbs client_cbs = { NULL, }; - uint32_t migration_flags = SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER; - - spice_assert(!g_smartcard_channel); - channel_cbs.config_socket = smartcard_channel_client_config_socket; - channel_cbs.on_disconnect = smartcard_channel_client_on_disconnect; - channel_cbs.send_item = smartcard_channel_send_item; - channel_cbs.hold_item = smartcard_channel_hold_pipe_item; - channel_cbs.release_item = smartcard_channel_release_pipe_item; - channel_cbs.alloc_recv_buf = smartcard_channel_client_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = smartcard_channel_client_release_msg_rcv_buf; - channel_cbs.handle_migrate_flush_mark = smartcard_channel_client_handle_migrate_flush_mark; - channel_cbs.handle_migrate_data = smartcard_channel_client_handle_migrate_data; - - g_smartcard_channel = (SmartCardChannel*)red_channel_create(sizeof(SmartCardChannel), - reds, - reds_get_core_interface(reds), - SPICE_CHANNEL_SMARTCARD, 0, - FALSE /* handle_acks */, - smartcard_channel_client_handle_message, - &channel_cbs, - migration_flags); - - if (!g_smartcard_channel) { - spice_error("failed to allocate Smartcard Channel"); - } + G_OBJECT_CLASS(red_smartcard_channel_parent_class)->constructed(object); client_cbs.connect = smartcard_connect_client; - red_channel_register_client_cbs(&g_smartcard_channel->base, &client_cbs, NULL); + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); + + reds_register_channel(reds, RED_CHANNEL(self)); +} + +static void +red_smartcard_channel_class_init(RedSmartcardChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + g_type_class_add_private(klass, sizeof(RedSmartcardChannelPrivate)); + + object_class->constructed = red_smartcard_channel_constructed; + + channel_class->handle_message = smartcard_channel_client_handle_message, + + channel_class->config_socket = smartcard_channel_client_config_socket; + channel_class->on_disconnect = smartcard_channel_client_on_disconnect; + channel_class->send_item = smartcard_channel_send_item; + channel_class->hold_item = smartcard_channel_hold_pipe_item; + channel_class->release_item = smartcard_channel_release_pipe_item; + channel_class->alloc_recv_buf = smartcard_channel_client_alloc_msg_rcv_buf; + channel_class->release_recv_buf = smartcard_channel_client_release_msg_rcv_buf; + channel_class->handle_migrate_flush_mark = smartcard_channel_client_handle_migrate_flush_mark; + channel_class->handle_migrate_data = smartcard_channel_client_handle_migrate_data; + +} + +/* FIXME: remove global */ +RedSmartcardChannel *g_smartcard_channel; + +static void smartcard_init(RedsState *reds) +{ + spice_assert(!g_smartcard_channel); - reds_register_channel(reds, &g_smartcard_channel->base); + g_smartcard_channel = red_smartcard_channel_new(reds); } diff --git a/server/sound.c b/server/sound.c index e87428f..6982be3 100644 --- a/server/sound.c +++ b/server/sound.c @@ -31,6 +31,7 @@ #include "spice.h" #include "red-common.h" +#include "dummy-channel.h" #include "dummy-channel-client.h" #include "main-channel.h" #include "main-channel-client.h" @@ -218,6 +219,7 @@ static void snd_disconnect_channel(SndChannel *channel) SndWorker *worker; RedsState *reds; RedChannel *red_channel; + uint32_t type; if (!channel || !channel->stream) { spice_debug("not connected"); @@ -225,8 +227,9 @@ static void snd_disconnect_channel(SndChannel *channel) } red_channel = red_channel_client_get_channel(channel->channel_client); reds = snd_channel_get_server(channel); + g_object_get(red_channel, "channel-type", &type, NULL); spice_debug("SndChannel=%p rcc=%p type=%d", - channel, channel->channel_client, red_channel->type); + channel, channel->channel_client, type); worker = channel->worker; channel->cleanup(channel); red_channel_client_disconnect(worker->connection->channel_client); @@ -999,12 +1002,14 @@ static void snd_disconnect_channel_client(RedChannelClient *rcc) { SndWorker *worker; RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type; spice_assert(channel); - spice_assert(channel->data); - worker = (SndWorker *)channel->data; + worker = (SndWorker *)g_object_get_data(G_OBJECT(channel), "sound-worker"); + spice_assert(worker); + g_object_get(channel, "channel-type", &type, NULL); - spice_debug("channel-type=%d", channel->type); + spice_debug("channel-type=%d", type); if (worker->connection) { spice_assert(worker->connection->channel_client == rcc); snd_disconnect_channel(worker->connection); @@ -1059,6 +1064,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_start(SpicePlaybackInstance *sin) sin->st->worker.active = 1; if (!channel) return; + spice_assert(!playback_channel->base.active); reds_disable_mm_time(snd_channel_get_server(channel)); playback_channel->base.active = TRUE; @@ -1078,6 +1084,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin) sin->st->worker.active = 0; if (!channel) return; + spice_assert(playback_channel->base.active); reds_enable_mm_time(snd_channel_get_server(channel)); playback_channel->base.active = FALSE; @@ -1146,7 +1153,9 @@ void snd_set_playback_latency(RedClient *client, uint32_t latency) SndWorker *now = workers; for (; now; now = now->next) { - if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection && + uint32_t type; + g_object_get(now->base_channel, "channel-type", &type, NULL); + if (type == SPICE_CHANNEL_PLAYBACK && now->connection && red_channel_client_get_client(now->connection->channel_client) == client) { if (red_channel_client_test_remote_cap(now->connection->channel_client, @@ -1214,7 +1223,7 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt int migration, int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps) { - SndWorker *worker = channel->data; + SndWorker *worker = g_object_get_data(G_OBJECT(channel), "sound-worker"); PlaybackChannel *playback_channel; SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker); @@ -1243,7 +1252,7 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt SPICE_PLAYBACK_CAP_CELT_0_5_1); int client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client, SPICE_PLAYBACK_CAP_OPUS); - int playback_compression = reds_config_get_playback_compression(channel->reds); + int playback_compression = reds_config_get_playback_compression(red_channel_get_server(channel)); int desired_mode = snd_desired_audio_mode(playback_compression, st->frequency, client_can_celt, client_can_opus); playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW; @@ -1272,8 +1281,8 @@ static void snd_record_migrate_channel_client(RedChannelClient *rcc) spice_debug(NULL); spice_assert(channel); - spice_assert(channel->data); - worker = (SndWorker *)channel->data; + worker = (SndWorker *)g_object_get_data(G_OBJECT(channel), "sound-worker"); + spice_assert(worker); if (worker->connection) { spice_assert(worker->connection->channel_client == rcc); @@ -1463,7 +1472,7 @@ static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStre int migration, int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps) { - SndWorker *worker = channel->data; + SndWorker *worker = g_object_get_data(G_OBJECT(channel), "sound-worker"); RecordChannel *record_channel; SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker); @@ -1501,8 +1510,8 @@ static void snd_playback_migrate_channel_client(RedChannelClient *rcc) RedChannel *channel = red_channel_client_get_channel(rcc); spice_assert(channel); - spice_assert(channel->data); - worker = (SndWorker *)channel->data; + worker = (SndWorker *)g_object_get_data(G_OBJECT(channel), "sound-worker"); + spice_assert(worker); spice_debug(NULL); if (worker->connection) { @@ -1543,8 +1552,9 @@ void snd_attach_playback(RedsState *reds, SpicePlaybackInstance *sin) sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */ // TODO: Make RedChannel base of worker? instead of assigning it to channel->data - channel = red_channel_create_dummy(sizeof(RedChannel), reds, SPICE_CHANNEL_PLAYBACK, 0); + channel = dummy_channel_new(reds, SPICE_CHANNEL_PLAYBACK, 0); + g_object_set_data(G_OBJECT(channel), "sound-worker", playback_worker); client_cbs.connect = snd_set_playback_peer; client_cbs.disconnect = snd_disconnect_channel_client; client_cbs.migrate = snd_playback_migrate_channel_client; @@ -1572,8 +1582,9 @@ void snd_attach_record(RedsState *reds, SpiceRecordInstance *sin) sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */ // TODO: Make RedChannel base of worker? instead of assigning it to channel->data - channel = red_channel_create_dummy(sizeof(RedChannel), reds, SPICE_CHANNEL_RECORD, 0); + channel = dummy_channel_new(reds, SPICE_CHANNEL_RECORD, 0); + g_object_set_data(G_OBJECT(channel), "sound-worker", record_worker); client_cbs.connect = snd_set_record_peer; client_cbs.disconnect = snd_disconnect_channel_client; client_cbs.migrate = snd_record_migrate_channel_client; @@ -1629,7 +1640,9 @@ void snd_set_playback_compression(int on) SndWorker *now = workers; for (; now; now = now->next) { - if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) { + uint32_t type; + g_object_get(now->base_channel, "channel-type", &type, NULL); + if (type == SPICE_CHANNEL_PLAYBACK && now->connection) { PlaybackChannel* playback = (PlaybackChannel*)now->connection; SpicePlaybackState *st = SPICE_CONTAINEROF(now, SpicePlaybackState, worker); int client_can_celt = red_channel_client_test_remote_cap(playback->base.channel_client, diff --git a/server/spicevmc.c b/server/spicevmc.c index 0b37b4a..6441542 100644 --- a/server/spicevmc.c +++ b/server/spicevmc.c @@ -52,16 +52,6 @@ typedef struct RedVmcPipeItem { uint32_t buf_used; } RedVmcPipeItem; -typedef struct SpiceVmcState { - RedChannel channel; /* Must be the first item */ - RedChannelClient *rcc; - RedCharDevice *chardev; - SpiceCharDeviceInstance *chardev_sin; - RedVmcPipeItem *pipe_item; - RedCharDeviceWriteBuffer *recv_from_client_buf; - uint8_t port_opened; -} SpiceVmcState; - #define RED_TYPE_CHAR_DEVICE_SPICEVMC red_char_device_spicevmc_get_type() #define RED_CHAR_DEVICE_SPICEVMC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmc)) @@ -89,6 +79,133 @@ static RedCharDevice *red_char_device_spicevmc_new(SpiceCharDeviceInstance *sin, G_DEFINE_TYPE(RedCharDeviceSpiceVmc, red_char_device_spicevmc, RED_TYPE_CHAR_DEVICE) +#define RED_CHAR_DEVICE_SPICEVMC_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmcPrivate)) + +#define SPICE_TYPE_VMC_STATE spice_vmc_state_get_type() + +#define SPICE_VMC_STATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SPICE_TYPE_VMC_STATE, SpiceVmcState)) +#define SPICE_VMC_STATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SPICE_TYPE_VMC_STATE, SpiceVmcStateClass)) +#define SPICE_IS_VMC_STATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SPICE_TYPE_VMC_STATE)) +#define SPICE_IS_VMC_STATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SPICE_TYPE_VMC_STATE)) +#define SPICE_VMC_STATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SPICE_TYPE_VMC_STATE, SpiceVmcStateClass)) + +typedef struct SpiceVmcState SpiceVmcState; +typedef struct SpiceVmcStateClass SpiceVmcStateClass; +typedef struct SpiceVmcStatePrivate SpiceVmcStatePrivate; + +struct SpiceVmcState +{ + RedChannel parent; + + SpiceVmcStatePrivate *priv; +}; + +struct SpiceVmcStateClass +{ + RedChannelClass parent_class; +}; + +GType spice_vmc_state_get_type(void) G_GNUC_CONST; + +G_DEFINE_TYPE(SpiceVmcState, spice_vmc_state, RED_TYPE_CHANNEL) + +#define VMC_STATE_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), SPICE_TYPE_VMC_STATE, SpiceVmcStatePrivate)) + +struct SpiceVmcStatePrivate +{ + RedChannelClient *rcc; + RedCharDevice *chardev; + SpiceCharDeviceInstance *chardev_sin; + RedVmcPipeItem *pipe_item; + RedCharDeviceWriteBuffer *recv_from_client_buf; + uint8_t port_opened; +}; + +enum { + PROP0, + PROP_DEVICE_INSTANCE +}; + +static void +spice_vmc_state_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + SpiceVmcState *self = SPICE_VMC_STATE(object); + + switch (property_id) + { + case PROP_DEVICE_INSTANCE: + g_value_set_pointer(value, self->priv->chardev_sin); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +spice_vmc_state_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + SpiceVmcState *self = SPICE_VMC_STATE(object); + + switch (property_id) + { + case PROP_DEVICE_INSTANCE: + self->priv->chardev_sin = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void spicevmc_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, int migration, int num_common_caps, + uint32_t *common_caps, int num_caps, uint32_t *caps); + +static void +spice_vmc_state_constructed(GObject *object) +{ + SpiceVmcState *self = SPICE_VMC_STATE(object); + ClientCbs client_cbs = { NULL, }; + RedsState *reds = red_channel_get_server(RED_CHANNEL(self)); + + G_OBJECT_CLASS(spice_vmc_state_parent_class)->constructed(object); + + client_cbs.connect = spicevmc_connect; + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); + + red_channel_init_outgoing_messages_window(RED_CHANNEL(self)); + + self->priv->chardev = red_char_device_spicevmc_new(self->priv->chardev_sin, + reds, self); + + reds_register_channel(reds, RED_CHANNEL(self)); +} + +static void +spice_vmc_state_init(SpiceVmcState *self) +{ + self->priv = VMC_STATE_PRIVATE(self); +} + +SpiceVmcState *spice_vmc_state_new(RedsState *reds, uint8_t channel_type, SpiceCharDeviceInstance *sin) +{ + static uint8_t id[256] = { 0, }; + return g_object_new(SPICE_TYPE_VMC_STATE, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", channel_type, + "id", id[channel_type]++, + "handle-acks", FALSE, + "migration-flags", (SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + "device-instance", sin, + NULL); +} + typedef struct RedPortInitPipeItem { RedPipeItem base; char* name; @@ -117,17 +234,17 @@ static RedPipeItem *spicevmc_chardev_read_msg_from_dev(SpiceCharDeviceInstance * sif = spice_char_device_get_interface(sin); - if (!state->rcc) { + if (!state->priv->rcc) { return NULL; } - if (!state->pipe_item) { + if (!state->priv->pipe_item) { msg_item = spice_new0(RedVmcPipeItem, 1); - red_pipe_item_init(&msg_item->base, RED_PIPE_ITEM_TYPE_SPICEVMC_DATA); + red_pipe_item_init_full(&msg_item->base, RED_PIPE_ITEM_TYPE_SPICEVMC_DATA, free); } else { - spice_assert(state->pipe_item->buf_used == 0); - msg_item = state->pipe_item; - state->pipe_item = NULL; + spice_assert(state->priv->pipe_item->buf_used == 0); + msg_item = state->priv->pipe_item; + state->priv->pipe_item = NULL; } n = sif->read(sin, msg_item->buf, @@ -137,7 +254,7 @@ static RedPipeItem *spicevmc_chardev_read_msg_from_dev(SpiceCharDeviceInstance * msg_item->buf_used = n; return (RedPipeItem *)msg_item; } else { - state->pipe_item = msg_item; + state->priv->pipe_item = msg_item; return NULL; } } @@ -149,21 +266,21 @@ static void spicevmc_chardev_send_msg_to_client(RedPipeItem *msg, SpiceVmcState *state = opaque; RedVmcPipeItem *vmc_msg = (RedVmcPipeItem *)msg; - spice_assert(red_channel_client_get_client(state->rcc) == client); + spice_assert(red_channel_client_get_client(state->priv->rcc) == client); red_pipe_item_ref(vmc_msg); - red_channel_client_pipe_add_push(state->rcc, (RedPipeItem *)vmc_msg); + red_channel_client_pipe_add_push(state->priv->rcc, (RedPipeItem *)vmc_msg); } static void spicevmc_port_send_init(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); - SpiceVmcState *state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel); - SpiceCharDeviceInstance *sin = state->chardev_sin; + SpiceVmcState *state = SPICE_VMC_STATE(channel); + SpiceCharDeviceInstance *sin = state->priv->chardev_sin; RedPortInitPipeItem *item = spice_malloc(sizeof(RedPortInitPipeItem)); red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_PORT_INIT); item->name = strdup(sin->portname); - item->opened = state->port_opened; + item->opened = state->priv->port_opened; red_channel_client_pipe_add_push(rcc, &item->base); } @@ -188,10 +305,10 @@ static void spicevmc_char_dev_remove_client(RedClient *client, void *opaque) SpiceVmcState *state = opaque; spice_printerr("vmc state %p, client %p", state, client); - spice_assert(state->rcc && - red_channel_client_get_client(state->rcc) == client); + spice_assert(state->priv->rcc && + red_channel_client_get_client(state->priv->rcc) == client); - red_channel_client_shutdown(state->rcc); + red_channel_client_shutdown(state->priv->rcc); } static int spicevmc_red_channel_client_config_socket(RedChannelClient *rcc) @@ -199,8 +316,10 @@ static int spicevmc_red_channel_client_config_socket(RedChannelClient *rcc) int delay_val = 1; RedsStream *stream = red_channel_client_get_stream(rcc); RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type; - if (channel->type == SPICE_CHANNEL_USBREDIR) { + g_object_get(channel, "channel-type", &type, NULL); + if (type == SPICE_CHANNEL_USBREDIR) { if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) != 0) { if (errno != ENOTSUP && errno != ENOPROTOOPT) { @@ -224,19 +343,19 @@ static void spicevmc_red_channel_client_on_disconnect(RedChannelClient *rcc) return; } - state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel); + state = SPICE_VMC_STATE(channel); - if (state->recv_from_client_buf) { /* partial message which wasn't pushed to device */ - red_char_device_write_buffer_release(state->chardev, state->recv_from_client_buf); - state->recv_from_client_buf = NULL; + if (state->priv->recv_from_client_buf) { /* partial message which wasn't pushed to device */ + red_char_device_write_buffer_release(state->priv->chardev, state->priv->recv_from_client_buf); + state->priv->recv_from_client_buf = NULL; } - if (state->chardev) { - if (red_char_device_client_exists(state->chardev, client)) { - red_char_device_client_remove(state->chardev, client); + if (state->priv->chardev) { + if (red_char_device_client_exists(state->priv->chardev, client)) { + red_char_device_client_remove(state->priv->chardev, client); } else { spice_printerr("client %p have already been removed from char dev %p", - client, state->chardev); + client, state->priv->chardev); } } @@ -245,17 +364,17 @@ static void spicevmc_red_channel_client_on_disconnect(RedChannelClient *rcc) if (!red_channel_client_is_destroying(rcc)) red_channel_client_destroy(rcc); - state->rcc = NULL; - sif = spice_char_device_get_interface(state->chardev_sin); + state->priv->rcc = NULL; + sif = spice_char_device_get_interface(state->priv->chardev_sin); if (sif->state) { - sif->state(state->chardev_sin, 0); + sif->state(state->priv->chardev_sin, 0); } } static SpiceVmcState *spicevmc_red_channel_client_get_state(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); - return SPICE_CONTAINEROF(channel, SpiceVmcState, channel); + return SPICE_VMC_STATE(channel); } static int spicevmc_channel_client_handle_migrate_flush_mark(RedChannelClient *rcc) @@ -283,7 +402,7 @@ static int spicevmc_channel_client_handle_migrate_data(RedChannelClient *rcc, spice_error("bad header"); return FALSE; } - return red_char_device_restore(state->chardev, &mig_data->base); + return red_char_device_restore(state->priv->chardev, &mig_data->base); } static int spicevmc_red_channel_client_handle_message(RedChannelClient *rcc, @@ -295,14 +414,14 @@ static int spicevmc_red_channel_client_handle_message(RedChannelClient *rcc, SpiceCharDeviceInterface *sif; state = spicevmc_red_channel_client_get_state(rcc); - sif = spice_char_device_get_interface(state->chardev_sin); + sif = spice_char_device_get_interface(state->priv->chardev_sin); switch (type) { case SPICE_MSGC_SPICEVMC_DATA: - spice_assert(state->recv_from_client_buf->buf == msg); - state->recv_from_client_buf->buf_used = size; - red_char_device_write_buffer_add(state->chardev, state->recv_from_client_buf); - state->recv_from_client_buf = NULL; + spice_assert(state->priv->recv_from_client_buf->buf == msg); + state->priv->recv_from_client_buf->buf_used = size; + red_char_device_write_buffer_add(state->priv->chardev, state->priv->recv_from_client_buf); + state->priv->recv_from_client_buf = NULL; break; case SPICE_MSGC_PORT_EVENT: if (size != sizeof(uint8_t)) { @@ -310,7 +429,7 @@ static int spicevmc_red_channel_client_handle_message(RedChannelClient *rcc, return FALSE; } if (sif->base.minor_version >= 2 && sif->event != NULL) - sif->event(state->chardev_sin, *msg); + sif->event(state->priv->chardev_sin, *msg); break; default: return red_channel_client_handle_message(rcc, size, type, msg); @@ -327,20 +446,20 @@ static uint8_t *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, RedChannel *channel = red_channel_client_get_channel(rcc); RedClient *client = red_channel_client_get_client(rcc); - state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel); + state = SPICE_VMC_STATE(channel); switch (type) { case SPICE_MSGC_SPICEVMC_DATA: - assert(!state->recv_from_client_buf); + assert(!state->priv->recv_from_client_buf); - state->recv_from_client_buf = red_char_device_write_buffer_get(state->chardev, - client, - size); - if (!state->recv_from_client_buf) { + state->priv->recv_from_client_buf = red_char_device_write_buffer_get(state->priv->chardev, + client, + size); + if (!state->priv->recv_from_client_buf) { spice_error("failed to allocate write buffer"); return NULL; } - return state->recv_from_client_buf->buf; + return state->priv->recv_from_client_buf->buf; default: return spice_malloc(size); @@ -356,13 +475,14 @@ static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc, SpiceVmcState *state; RedChannel *channel = red_channel_client_get_channel(rcc); - state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel); + state = SPICE_VMC_STATE(channel); switch (type) { case SPICE_MSGC_SPICEVMC_DATA: - if (state->recv_from_client_buf) { /* buffer wasn't pushed to device */ - red_char_device_write_buffer_release(state->chardev, state->recv_from_client_buf); - state->recv_from_client_buf = NULL; + if (state->priv->recv_from_client_buf) { /* buffer wasn't pushed to device */ + red_char_device_write_buffer_release(state->priv->chardev, + state->priv->recv_from_client_buf); + state->priv->recv_from_client_buf = NULL; } break; default: @@ -393,12 +513,12 @@ static void spicevmc_red_channel_send_migrate_data(RedChannelClient *rcc, SpiceVmcState *state; RedChannel *channel = red_channel_client_get_channel(rcc); - state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel); + state = SPICE_VMC_STATE(channel); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item); spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SPICEVMC_MAGIC); spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SPICEVMC_VERSION); - red_char_device_migrate_data_marshall(state->chardev, m); + red_char_device_migrate_data_marshall(state->priv->chardev, m); } static void spicevmc_red_channel_send_port_init(RedChannelClient *rcc, @@ -464,6 +584,40 @@ static void spicevmc_red_channel_release_pipe_item(RedChannelClient *rcc, } } +static void +spice_vmc_state_class_init(SpiceVmcStateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + g_type_class_add_private(klass, sizeof(SpiceVmcStatePrivate)); + + object_class->get_property = spice_vmc_state_get_property; + object_class->set_property = spice_vmc_state_set_property; + object_class->constructed = spice_vmc_state_constructed; + + channel_class->handle_message = spicevmc_red_channel_client_handle_message; + + channel_class->config_socket = spicevmc_red_channel_client_config_socket; + channel_class->on_disconnect = spicevmc_red_channel_client_on_disconnect; + channel_class->send_item = spicevmc_red_channel_send_item; + channel_class->hold_item = spicevmc_red_channel_hold_pipe_item; + channel_class->release_item = spicevmc_red_channel_release_pipe_item; + channel_class->alloc_recv_buf = spicevmc_red_channel_alloc_msg_rcv_buf; + channel_class->release_recv_buf = spicevmc_red_channel_release_msg_rcv_buf; + channel_class->handle_migrate_flush_mark = spicevmc_channel_client_handle_migrate_flush_mark; + channel_class->handle_migrate_data = spicevmc_channel_client_handle_migrate_data; + + g_object_class_install_property(object_class, + PROP_DEVICE_INSTANCE, + g_param_spec_pointer("device-instance", + "device instance", + "Device instance for this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + static void spicevmc_connect(RedChannel *channel, RedClient *client, RedsStream *stream, int migration, int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps) @@ -472,13 +626,15 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client, SpiceVmcState *state; SpiceCharDeviceInstance *sin; SpiceCharDeviceInterface *sif; + uint32_t type, id; - state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel); - sin = state->chardev_sin; + state = SPICE_VMC_STATE(channel); + sin = state->priv->chardev_sin; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); - if (state->rcc) { + if (state->priv->rcc) { spice_printerr("channel client %d:%d (%p) already connected, refusing second connection", - channel->type, channel->id, state->rcc); + type, id, state->priv->rcc); // TODO: notify client in advance about the in use channel using // SPICE_MSG_MAIN_CHANNEL_IN_USE (for example) reds_stream_free(stream); @@ -490,21 +646,21 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client, if (!rcc) { return; } - state->rcc = rcc; + state->priv->rcc = rcc; red_channel_client_ack_zero_messages_window(rcc); if (strcmp(sin->subtype, "port") == 0) { spicevmc_port_send_init(rcc); } - if (!red_char_device_client_add(state->chardev, client, FALSE, 0, ~0, ~0, + if (!red_char_device_client_add(state->priv->chardev, client, FALSE, 0, ~0, ~0, red_channel_client_is_waiting_for_migrate_data(rcc))) { spice_warning("failed to add client to spicevmc"); red_channel_client_disconnect(rcc); return; } - sif = spice_char_device_get_interface(state->chardev_sin); + sif = spice_char_device_get_interface(state->priv->chardev_sin); if (sif->state) { sif->state(sin, 1); } @@ -514,37 +670,9 @@ RedCharDevice *spicevmc_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin, uint8_t channel_type) { - static uint8_t id[256] = { 0, }; - SpiceVmcState *state; - ChannelCbs channel_cbs = { NULL, }; - ClientCbs client_cbs = { NULL, }; - - channel_cbs.config_socket = spicevmc_red_channel_client_config_socket; - channel_cbs.on_disconnect = spicevmc_red_channel_client_on_disconnect; - channel_cbs.send_item = spicevmc_red_channel_send_item; - channel_cbs.hold_item = spicevmc_red_channel_hold_pipe_item; - channel_cbs.release_item = spicevmc_red_channel_release_pipe_item; - channel_cbs.alloc_recv_buf = spicevmc_red_channel_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = spicevmc_red_channel_release_msg_rcv_buf; - channel_cbs.handle_migrate_flush_mark = spicevmc_channel_client_handle_migrate_flush_mark; - channel_cbs.handle_migrate_data = spicevmc_channel_client_handle_migrate_data; - - state = (SpiceVmcState*)red_channel_create(sizeof(SpiceVmcState), reds, - reds_get_core_interface(reds), channel_type, id[channel_type]++, - FALSE /* handle_acks */, - spicevmc_red_channel_client_handle_message, - &channel_cbs, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER); - red_channel_init_outgoing_messages_window(&state->channel); - - client_cbs.connect = spicevmc_connect; - red_channel_register_client_cbs(&state->channel, &client_cbs, NULL); - - state->chardev = red_char_device_spicevmc_new(sin, reds, state); - state->chardev_sin = sin; + SpiceVmcState *state = spice_vmc_state_new(reds, channel_type, sin); - reds_register_channel(reds, &state->channel); - return state->chardev; + return state->priv->chardev; } /* Must be called from RedClient handling thread. */ @@ -555,17 +683,18 @@ void spicevmc_device_disconnect(RedsState *reds, SpiceCharDeviceInstance *sin) /* FIXME */ state = (SpiceVmcState *)red_char_device_opaque_get((RedCharDevice*)sin->st); - if (state->recv_from_client_buf) { - red_char_device_write_buffer_release(state->chardev, state->recv_from_client_buf); + if (state->priv->recv_from_client_buf) { + red_char_device_write_buffer_release(state->priv->chardev, + state->priv->recv_from_client_buf); } /* FIXME */ red_char_device_destroy((RedCharDevice*)sin->st); - state->chardev = NULL; + state->priv->chardev = NULL; sin->st = NULL; - reds_unregister_channel(reds, &state->channel); - free(state->pipe_item); - red_channel_destroy(&state->channel); + reds_unregister_channel(reds, RED_CHANNEL(state)); + free(state->priv->pipe_item); + red_channel_destroy(RED_CHANNEL(state)); } SPICE_GNUC_VISIBLE void spice_server_port_event(SpiceCharDeviceInstance *sin, uint8_t event) @@ -580,16 +709,16 @@ SPICE_GNUC_VISIBLE void spice_server_port_event(SpiceCharDeviceInstance *sin, ui /* FIXME */ state = (SpiceVmcState *)red_char_device_opaque_get((RedCharDevice*)sin->st); if (event == SPICE_PORT_EVENT_OPENED) { - state->port_opened = TRUE; + state->priv->port_opened = TRUE; } else if (event == SPICE_PORT_EVENT_CLOSED) { - state->port_opened = FALSE; + state->priv->port_opened = FALSE; } - if (state->rcc == NULL) { + if (state->priv->rcc == NULL) { return; } - spicevmc_port_send_event(state->rcc, event); + spicevmc_port_send_event(state->priv->rcc, event); } static void diff --git a/server/stream.c b/server/stream.c index 47a6924..004e886 100644 --- a/server/stream.c +++ b/server/stream.c @@ -20,13 +20,14 @@ #include "stream.h" #include "display-channel.h" +#include "display-channel-private.h" #include "main-channel-client.h" #define FPS_TEST_INTERVAL 1 #define FOREACH_STREAMS(display, item) \ - for (item = ring_get_head(&(display)->streams); \ + for (item = ring_get_head(&(display)->priv->streams); \ item != NULL; \ - item = ring_next(&(display)->streams, item)) + item = ring_next(&(display)->priv->streams, item)) void stream_agent_stats_print(StreamAgent *agent) { @@ -72,11 +73,11 @@ void stream_stop(DisplayChannel *display, Stream *stream) spice_return_if_fail(ring_item_is_linked(&stream->link)); spice_return_if_fail(!stream->current); - spice_debug("stream %d", get_stream_id(display, stream)); + spice_debug("stream %d", display_channel_get_stream_id(display, stream)); FOREACH_DCC(display, link, next, dcc) { StreamAgent *stream_agent; - stream_agent = dcc_get_stream_agent(dcc, get_stream_id(display, stream)); + stream_agent = dcc_get_stream_agent(dcc, display_channel_get_stream_id(display, stream)); region_clear(&stream_agent->vis_region); region_clear(&stream_agent->clip); spice_assert(!red_pipe_item_is_linked(&stream_agent->destroy_item)); @@ -94,25 +95,25 @@ void stream_stop(DisplayChannel *display, Stream *stream) red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &stream_agent->destroy_item); stream_agent_stats_print(stream_agent); } - display->streams_size_total -= stream->width * stream->height; + display->priv->streams_size_total -= stream->width * stream->height; ring_remove(&stream->link); stream_unref(display, stream); } static void stream_free(DisplayChannel *display, Stream *stream) { - stream->next = display->free_streams; - display->free_streams = stream; + stream->next = display->priv->free_streams; + display->priv->free_streams = stream; } void display_channel_init_streams(DisplayChannel *display) { int i; - ring_init(&display->streams); - display->free_streams = NULL; + ring_init(&display->priv->streams); + display->priv->free_streams = NULL; for (i = 0; i < NUM_STREAMS; i++) { - Stream *stream = &display->streams_buf[i]; + Stream *stream = &display->priv->streams_buf[i]; ring_item_init(&stream->link); stream_free(display, stream); } @@ -126,7 +127,7 @@ void stream_unref(DisplayChannel *display, Stream *stream) spice_warn_if_fail(!ring_item_is_linked(&stream->link)); stream_free(display, stream); - display->stream_count--; + display->priv->stream_count--; } void stream_agent_unref(DisplayChannel *display, StreamAgent *agent) @@ -169,7 +170,7 @@ static void update_copy_graduality(DisplayChannel *display, Drawable *drawable) SpiceBitmap *bitmap; spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY); - if (display->stream_video != SPICE_STREAM_VIDEO_FILTER) { + if (display_channel_get_stream_video(display) != SPICE_STREAM_VIDEO_FILTER) { drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID; return; } @@ -284,7 +285,7 @@ static void attach_stream(DisplayChannel *display, Drawable *drawable, Stream *s StreamAgent *agent; QRegion clip_in_draw_dest; - agent = dcc_get_stream_agent(dcc, get_stream_id(display, stream)); + agent = dcc_get_stream_agent(dcc, display_channel_get_stream_id(display, stream)); region_or(&agent->vis_region, &drawable->tree_item.base.rgn); region_init(&clip_in_draw_dest); @@ -335,7 +336,7 @@ static void before_reattach_stream(DisplayChannel *display, return; } - index = get_stream_id(display, stream); + index = display_channel_get_stream_id(display, stream); DRAWABLE_FOREACH_DPI_SAFE(stream->current, ring_item, next, dpi) { dcc = dpi->dcc; agent = dcc_get_stream_agent(dcc, index); @@ -392,11 +393,11 @@ static void before_reattach_stream(DisplayChannel *display, static Stream *display_channel_stream_try_new(DisplayChannel *display) { Stream *stream; - if (!display->free_streams) { + if (!display->priv->free_streams) { return NULL; } - stream = display->free_streams; - display->free_streams = display->free_streams->next; + stream = display->priv->free_streams; + display->priv->free_streams = display->priv->free_streams->next; return stream; } @@ -416,7 +417,7 @@ static void display_channel_create_stream(DisplayChannel *display, Drawable *dra spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY); src_rect = &drawable->red_drawable->u.copy.src_area; - ring_add(&display->streams, &stream->link); + ring_add(&display->priv->streams, &stream->link); stream->current = drawable; stream->last_time = drawable->creation_time; stream->width = src_rect->right - src_rect->left; @@ -438,13 +439,13 @@ static void display_channel_create_stream(DisplayChannel *display, Drawable *dra } stream->num_input_frames = 0; stream->input_fps_start_time = drawable->creation_time; - display->streams_size_total += stream->width * stream->height; - display->stream_count++; + display->priv->streams_size_total += stream->width * stream->height; + display->priv->stream_count++; FOREACH_DCC(display, link, next, dcc) { dcc_create_stream(dcc, stream); } spice_debug("stream %d %dx%d (%d, %d) (%d, %d) %u fps", - (int)(stream - display->streams_buf), stream->width, + (int)(stream - display->priv->streams_buf), stream->width, stream->height, stream->dest_area.left, stream->dest_area.top, stream->dest_area.right, stream->dest_area.bottom, stream->input_fps); @@ -520,7 +521,7 @@ void stream_trace_update(DisplayChannel *display, Drawable *drawable) } } - trace = display->items_trace; + trace = display->priv->items_trace; trace_end = trace + NUM_TRACE_ITEMS; for (; trace < trace_end; trace++) { if (is_next_stream_frame(display, drawable, trace->width, trace->height, @@ -591,7 +592,7 @@ static void dcc_update_streams_max_latency(DisplayChannelClient *dcc, StreamAgen } dcc_set_max_stream_latency(dcc, 0); - if (DCC_TO_DC(dcc)->stream_count == 1) { + if (DCC_TO_DC(dcc)->priv->stream_count == 1) { return; } for (i = 0; i < NUM_STREAMS; i++) { @@ -650,7 +651,7 @@ static uint64_t get_initial_bit_rate(DisplayChannelClient *dcc, Stream *stream) /* dividing the available bandwidth among the active streams, and saving * (1-RED_STREAM_CHANNEL_CAPACITY) of it for other messages */ return (RED_STREAM_CHANNEL_CAPACITY * bit_rate * - stream->width * stream->height) / DCC_TO_DC(dcc)->streams_size_total; + stream->width * stream->height) / DCC_TO_DC(dcc)->priv->streams_size_total; } static uint32_t get_roundtrip_ms(void *opaque) @@ -703,7 +704,7 @@ static void update_client_playback_delay(void *opaque, uint32_t delay_ms) void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream) { - StreamAgent *agent = dcc_get_stream_agent(dcc, get_stream_id(DCC_TO_DC(dcc), stream)); + StreamAgent *agent = dcc_get_stream_agent(dcc, display_channel_get_stream_id(DCC_TO_DC(dcc), stream)); spice_return_if_fail(region_is_empty(&agent->vis_region)); @@ -741,7 +742,7 @@ void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream) agent->report_id = rand(); red_pipe_item_init(&report_pipe_item->pipe_item, RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT); - report_pipe_item->stream_id = get_stream_id(DCC_TO_DC(dcc), stream); + report_pipe_item->stream_id = display_channel_get_stream_id(DCC_TO_DC(dcc), stream); red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &report_pipe_item->pipe_item); } #ifdef STREAM_STATS @@ -782,7 +783,7 @@ static void dcc_detach_stream_gracefully(DisplayChannelClient *dcc, Drawable *update_area_limit) { DisplayChannel *display = DCC_TO_DC(dcc); - int stream_id = get_stream_id(display, stream); + int stream_id = display_channel_get_stream_id(display, stream); StreamAgent *agent = dcc_get_stream_agent(dcc, stream_id); /* stopping the client from playing older frames at once*/ @@ -868,7 +869,7 @@ static void detach_stream_gracefully(DisplayChannel *display, Stream *stream, */ void stream_detach_behind(DisplayChannel *display, QRegion *region, Drawable *drawable) { - Ring *ring = &display->streams; + Ring *ring = &display->priv->streams; RingItem *item = ring_get_head(ring); GList *link, *next; DisplayChannelClient *dcc; @@ -880,12 +881,12 @@ void stream_detach_behind(DisplayChannel *display, QRegion *region, Drawable *dr item = ring_next(ring, item); FOREACH_DCC(display, link, next, dcc) { - StreamAgent *agent = dcc_get_stream_agent(dcc, get_stream_id(display, stream)); + StreamAgent *agent = dcc_get_stream_agent(dcc, display_channel_get_stream_id(display, stream)); if (region_intersects(&agent->vis_region, region)) { dcc_detach_stream_gracefully(dcc, stream, drawable); detach = 1; - spice_debug("stream %d", get_stream_id(display, stream)); + spice_debug("stream %d", display_channel_get_stream_id(display, stream)); } } if (detach && stream->current) { @@ -904,7 +905,7 @@ void stream_detach_and_stop(DisplayChannel *display) RingItem *stream_item; spice_debug(NULL); - while ((stream_item = ring_get_head(&display->streams))) { + while ((stream_item = ring_get_head(&display->priv->streams))) { Stream *stream = SPICE_CONTAINEROF(stream_item, Stream, link); detach_stream_gracefully(display, stream, NULL); @@ -914,7 +915,7 @@ void stream_detach_and_stop(DisplayChannel *display) void stream_timeout(DisplayChannel *display) { - Ring *ring = &display->streams; + Ring *ring = &display->priv->streams; RingItem *item; red_time_t now = spice_get_monotonic_time_ns(); @@ -937,7 +938,7 @@ void stream_trace_add_drawable(DisplayChannel *display, Drawable *item) return; } - trace = &display->items_trace[display->next_item_trace++ & ITEMS_TRACE_MASK]; + trace = &display->priv->items_trace[display->priv->next_item_trace++ & ITEMS_TRACE_MASK]; trace->time = item->creation_time; trace->first_frame_time = item->first_frame_time; trace->frames_count = item->frames_count; diff --git a/server/stream.h b/server/stream.h index 29071f5..f337059 100644 --- a/server/stream.h +++ b/server/stream.h @@ -41,9 +41,6 @@ #define RED_STREAM_DEFAULT_LOW_START_BIT_RATE (2.5 * 1024 * 1024) // 2.5Mbps #define MAX_FPS 30 -/* move back to display_channel once struct private */ -typedef struct DisplayChannel DisplayChannel; - typedef struct Stream Stream; typedef struct RedStreamActivateReportItem { -- 2.4.11 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel