Convert the RedChannelClient heirarchy into GObjects. Since the existing constructors could fail and return NULL, I inherited the base channel client from GInitable, which introduces a dependency on gio. --- configure.ac | 4 +- server/Makefile.am | 4 + server/cache-item.tmpl.c | 40 +- server/common-graphics-channel-client-private.h | 12 +- server/common-graphics-channel-client.c | 129 ++- server/common-graphics-channel-client.h | 48 +- server/cursor-channel-client.c | 82 +- server/cursor-channel-client.h | 34 +- server/cursor-channel.c | 27 +- server/dcc-encoders.c | 158 +-- server/dcc-encoders.h | 2 - server/dcc-private.h | 9 +- server/dcc-send.c | 237 ++--- server/dcc.c | 437 ++++++--- server/dcc.h | 34 +- server/display-channel.c | 14 +- server/dummy-channel-client.c | 157 +++ server/dummy-channel-client.h | 58 ++ server/inputs-channel-client.c | 90 +- server/inputs-channel-client.h | 47 +- server/inputs-channel.c | 6 +- server/inputs-channel.h | 2 + server/main-channel-client.c | 376 +++++--- server/main-channel-client.h | 41 +- server/main-channel.c | 22 +- server/red-channel-client-private.h | 7 +- server/red-channel-client.c | 1178 +++++++++++++---------- server/red-channel-client.h | 163 +++- server/red-channel.h | 109 +-- server/red-worker.h | 1 - server/reds.c | 15 +- server/reds.h | 1 + server/smartcard-channel-client.c | 417 ++++++++ server/smartcard-channel-client.h | 113 +++ server/smartcard.c | 377 ++------ server/smartcard.h | 21 + server/sound.c | 42 +- server/spice-server.h | 16 + server/spicevmc.c | 6 +- server/stream.c | 4 +- server/tests/test_display_base.c | 1 + 41 files changed, 2926 insertions(+), 1615 deletions(-) create mode 100644 server/dummy-channel-client.c create mode 100644 server/dummy-channel-client.h create mode 100644 server/smartcard-channel-client.c create mode 100644 server/smartcard-channel-client.h diff --git a/configure.ac b/configure.ac index 8419508..c07a315 100644 --- a/configure.ac +++ b/configure.ac @@ -103,8 +103,8 @@ SPICE_PROTOCOL_MIN_VER=0.12.11 PKG_CHECK_MODULES([SPICE_PROTOCOL], [spice-protocol >= $SPICE_PROTOCOL_MIN_VER]) AC_SUBST([SPICE_PROTOCOL_MIN_VER]) -PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.22]) -AS_VAR_APPEND([SPICE_REQUIRES], [" glib-2.0 >= 2.22"]) +PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.22 gio-2.0 >= 2.22]) +AS_VAR_APPEND([SPICE_REQUIRES], [" glib-2.0 >= 2.22 gio-2.0 >= 2.22"]) PKG_CHECK_MODULES([GOBJECT2], [gobject-2.0 >= 2.22]) AS_VAR_APPEND([SPICE_REQUIRES], [" gobject-2.0 >= 2.22"]) diff --git a/server/Makefile.am b/server/Makefile.am index 4337dbf..b1de055 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -102,6 +102,8 @@ libserver_la_SOURCES = \ red-channel-client.c \ red-channel-client.h \ red-channel-client-private.h \ + dummy-channel-client.c \ + dummy-channel-client.h \ red-common.h \ dispatcher.c \ dispatcher.h \ @@ -167,6 +169,8 @@ if HAVE_SMARTCARD libserver_la_SOURCES += \ smartcard.c \ smartcard.h \ + smartcard-channel-client.c \ + smartcard-channel-client.h \ $(NULL) endif diff --git a/server/cache-item.tmpl.c b/server/cache-item.tmpl.c index 0617bea..8636367 100644 --- a/server/cache-item.tmpl.c +++ b/server/cache-item.tmpl.c @@ -50,12 +50,12 @@ static RedCacheItem *FUNC_NAME(find)(CHANNELCLIENT *channel_client, uint64_t id) { - RedCacheItem *item = channel_client->CACHE_NAME[CACHE_HASH_KEY(id)]; + RedCacheItem *item = channel_client->priv->CACHE_NAME[CACHE_HASH_KEY(id)]; while (item) { if (item->id == id) { ring_remove(&item->u.cache_data.lru_link); - ring_add(&channel_client->VAR_NAME(lru), &item->u.cache_data.lru_link); + ring_add(&channel_client->priv->VAR_NAME(lru), &item->u.cache_data.lru_link); break; } item = item->u.cache_data.next; @@ -68,7 +68,7 @@ static void FUNC_NAME(remove)(CHANNELCLIENT *channel_client, RedCacheItem *item) RedCacheItem **now; spice_assert(item); - now = &channel_client->CACHE_NAME[CACHE_HASH_KEY(item->id)]; + now = &channel_client->priv->CACHE_NAME[CACHE_HASH_KEY(item->id)]; for (;;) { spice_assert(*now); if (*now == item) { @@ -78,11 +78,11 @@ static void FUNC_NAME(remove)(CHANNELCLIENT *channel_client, RedCacheItem *item) now = &(*now)->u.cache_data.next; } ring_remove(&item->u.cache_data.lru_link); - channel_client->VAR_NAME(items)--; - channel_client->VAR_NAME(available) += item->size; + channel_client->priv->VAR_NAME(items)--; + channel_client->priv->VAR_NAME(available) += item->size; red_pipe_item_init(&item->u.pipe_data, RED_PIPE_ITEM_TYPE_INVAL_ONE); - red_channel_client_pipe_add_tail_and_push(&channel_client->common.base, &item->u.pipe_data); // for now + red_channel_client_pipe_add_tail_and_push(RED_CHANNEL_CLIENT(channel_client), &item->u.pipe_data); // for now } static int FUNC_NAME(add)(CHANNELCLIENT *channel_client, uint64_t id, size_t size) @@ -92,21 +92,21 @@ static int FUNC_NAME(add)(CHANNELCLIENT *channel_client, uint64_t id, size_t siz item = spice_new(RedCacheItem, 1); - channel_client->VAR_NAME(available) -= size; - while (channel_client->VAR_NAME(available) < 0) { - RedCacheItem *tail = (RedCacheItem *)ring_get_tail(&channel_client->VAR_NAME(lru)); + channel_client->priv->VAR_NAME(available) -= size; + while (channel_client->priv->VAR_NAME(available) < 0) { + RedCacheItem *tail = (RedCacheItem *)ring_get_tail(&channel_client->priv->VAR_NAME(lru)); if (!tail) { - channel_client->VAR_NAME(available) += size; + channel_client->priv->VAR_NAME(available) += size; free(item); return FALSE; } FUNC_NAME(remove)(channel_client, tail); } - ++channel_client->VAR_NAME(items); - item->u.cache_data.next = channel_client->CACHE_NAME[(key = CACHE_HASH_KEY(id))]; - channel_client->CACHE_NAME[key] = item; + ++channel_client->priv->VAR_NAME(items); + item->u.cache_data.next = channel_client->priv->CACHE_NAME[(key = CACHE_HASH_KEY(id))]; + channel_client->priv->CACHE_NAME[key] = item; ring_item_init(&item->u.cache_data.lru_link); - ring_add(&channel_client->VAR_NAME(lru), &item->u.cache_data.lru_link); + ring_add(&channel_client->priv->VAR_NAME(lru), &item->u.cache_data.lru_link); item->id = id; item->size = size; item->inval_type = CACHE_INVAL_TYPE; @@ -118,15 +118,15 @@ static void FUNC_NAME(reset)(CHANNELCLIENT *channel_client, long size) int i; for (i = 0; i < CACHE_HASH_SIZE; i++) { - while (channel_client->CACHE_NAME[i]) { - RedCacheItem *item = channel_client->CACHE_NAME[i]; - channel_client->CACHE_NAME[i] = item->u.cache_data.next; + while (channel_client->priv->CACHE_NAME[i]) { + RedCacheItem *item = channel_client->priv->CACHE_NAME[i]; + channel_client->priv->CACHE_NAME[i] = item->u.cache_data.next; free(item); } } - ring_init(&channel_client->VAR_NAME(lru)); - channel_client->VAR_NAME(available) = size; - channel_client->VAR_NAME(items) = 0; + ring_init(&channel_client->priv->VAR_NAME(lru)); + channel_client->priv->VAR_NAME(available) = size; + channel_client->priv->VAR_NAME(items) = 0; } diff --git a/server/common-graphics-channel-client-private.h b/server/common-graphics-channel-client-private.h index 92442ff..b702239 100644 --- a/server/common-graphics-channel-client-private.h +++ b/server/common-graphics-channel-client-private.h @@ -14,16 +14,16 @@ 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_CLIENT_PRIVATE_H -#define COMMON_GRAPHICS_CHANNEL_CLIENT_PRIVATE_H +#ifndef _COMMON_GRAPHICS_CHANNEL_CLIENT_PRIVATE_H +#define _COMMON_GRAPHICS_CHANNEL_CLIENT_PRIVATE_H #include "common-graphics-channel-client.h" #include "red-channel-client-private.h" -struct CommonGraphicsChannelClient { - RedChannelClient base; - +struct CommonGraphicsChannelClientPrivate +{ + gboolean migration_target; int is_low_bandwidth; }; -#endif /* COMMON_GRAPHICS_CHANNEL_CLIENT_PRIVATE_H */ +#endif /* _COMMON_GRAPHICS_CHANNEL_CLIENT_PRIVATE_H */ diff --git a/server/common-graphics-channel-client.c b/server/common-graphics-channel-client.c index f25d737..e83855b 100644 --- a/server/common-graphics-channel-client.c +++ b/server/common-graphics-channel-client.c @@ -22,40 +22,111 @@ #include "dcc.h" #include "red-channel-client.h" -void common_graphics_channel_client_set_low_bandwidth(CommonGraphicsChannelClient *self, - gboolean low_bandwidth) +G_DEFINE_TYPE (CommonGraphicsChannelClient, common_graphics_channel_client, RED_TYPE_CHANNEL_CLIENT) + +#define GRAPHICS_CHANNEL_CLIENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT, CommonGraphicsChannelClientPrivate)) + +enum +{ + PROP0, + PROP_MIGRATION_TARGET +}; + +static void common_graphics_channel_client_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - self->is_low_bandwidth = low_bandwidth; + CommonGraphicsChannelClient *self = COMMON_GRAPHICS_CHANNEL_CLIENT(object); + + switch (property_id) + { + case PROP_MIGRATION_TARGET: + g_value_set_boolean(value, self->priv->migration_target); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } } -gboolean common_graphics_channel_client_is_low_bandwidth(CommonGraphicsChannelClient *self) +static void common_graphics_channel_client_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { - return self->is_low_bandwidth; -} - -CommonGraphicsChannelClient *common_graphics_channel_client_new(CommonGraphicsChannel *common, - int size, - RedClient *client, - RedsStream *stream, - int mig_target, - int monitor_latency, - uint32_t *common_caps, - int num_common_caps, - uint32_t *caps, - int num_caps) -{ - RedChannelClient *rcc = - red_channel_client_create(size, RED_CHANNEL(common), client, stream, monitor_latency, - num_common_caps, common_caps, num_caps, caps); - if (!rcc) { - return NULL; + CommonGraphicsChannelClient *self = COMMON_GRAPHICS_CHANNEL_CLIENT(object); + + switch (property_id) + { + case PROP_MIGRATION_TARGET: + self->priv->migration_target = g_value_get_boolean(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } - CommonGraphicsChannelClient *common_cc = (CommonGraphicsChannelClient*)rcc; - common->during_target_migrate = mig_target; +} + +static void common_graphics_channel_client_dispose(GObject *object) +{ + G_OBJECT_CLASS(common_graphics_channel_client_parent_class)->dispose(object); +} + +static void common_graphics_channel_client_finalize(GObject *object) +{ + G_OBJECT_CLASS(common_graphics_channel_client_parent_class)->finalize(object); +} + +static void common_graphics_channel_client_constructed(GObject *object) +{ + CommonGraphicsChannelClient *self = COMMON_GRAPHICS_CHANNEL_CLIENT(object); + CommonGraphicsChannel *channel = COMMON_GRAPHICS_CHANNEL(red_channel_client_get_channel(RED_CHANNEL_CLIENT(self))); + + G_OBJECT_CLASS(common_graphics_channel_client_parent_class)->constructed(object); // TODO: move wide/narrow ack setting to red_channel. - red_channel_client_ack_set_client_window(rcc, - common_cc->is_low_bandwidth ? - WIDE_CLIENT_ACK_WINDOW : NARROW_CLIENT_ACK_WINDOW); - return common_cc; + // FIXME: nothing has set is_low_bandwidth yet... + red_channel_client_ack_set_client_window(RED_CHANNEL_CLIENT(self), + self->priv->is_low_bandwidth ? + WIDE_CLIENT_ACK_WINDOW : NARROW_CLIENT_ACK_WINDOW); + + channel->during_target_migrate = self->priv->migration_target; +} + +static void common_graphics_channel_client_class_init(CommonGraphicsChannelClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof (CommonGraphicsChannelClientPrivate)); + + object_class->get_property = common_graphics_channel_client_get_property; + object_class->set_property = common_graphics_channel_client_set_property; + object_class->dispose = common_graphics_channel_client_dispose; + object_class->finalize = common_graphics_channel_client_finalize; + object_class->constructed = common_graphics_channel_client_constructed; + + g_object_class_install_property(object_class, + PROP_MIGRATION_TARGET, + g_param_spec_boolean("migration-target", + "migration target", + "Whether this is a migration target", + FALSE, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +static void common_graphics_channel_client_init(CommonGraphicsChannelClient *self) +{ + self->priv = GRAPHICS_CHANNEL_CLIENT_PRIVATE(self); +} + +void common_graphics_channel_client_set_low_bandwidth(CommonGraphicsChannelClient *self, + gboolean low_bandwidth) +{ + self->priv->is_low_bandwidth = low_bandwidth; +} + +gboolean common_graphics_channel_client_is_low_bandwidth(CommonGraphicsChannelClient *self) +{ + return self->priv->is_low_bandwidth; } diff --git a/server/common-graphics-channel-client.h b/server/common-graphics-channel-client.h index fedd1a1..dc1173a 100644 --- a/server/common-graphics-channel-client.h +++ b/server/common-graphics-channel-client.h @@ -14,12 +14,41 @@ 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_CLIENT_H -#define COMMON_GRAPHICS_CHANNEL_CLIENT_H +#ifndef _COMMON_GRAPHICS_CHANNEL_CLIENT_H +#define _COMMON_GRAPHICS_CHANNEL_CLIENT_H +#include <glib-object.h> #include "red-common.h" +#include "red-channel-client.h" + +G_BEGIN_DECLS + +#define TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT common_graphics_channel_client_get_type() + +#define COMMON_GRAPHICS_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT, CommonGraphicsChannelClient)) +#define COMMON_GRAPHICS_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT, CommonGraphicsChannelClientClass)) +#define IS_COMMON_GRAPHICS_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT)) +#define IS_COMMON_GRAPHICS_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT)) +#define COMMON_GRAPHICS_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT, CommonGraphicsChannelClientClass)) typedef struct CommonGraphicsChannelClient CommonGraphicsChannelClient; +typedef struct CommonGraphicsChannelClientClass CommonGraphicsChannelClientClass; +typedef struct CommonGraphicsChannelClientPrivate CommonGraphicsChannelClientPrivate; + +struct CommonGraphicsChannelClient +{ + RedChannelClient parent; + + CommonGraphicsChannelClientPrivate *priv; +}; + +struct CommonGraphicsChannelClientClass +{ + RedChannelClientClass parent_class; +}; + +GType common_graphics_channel_client_get_type(void) G_GNUC_CONST; + typedef struct CommonGraphicsChannel CommonGraphicsChannel; typedef struct RedClient RedClient; typedef struct RedsStream RedsStream; @@ -28,15 +57,6 @@ void common_graphics_channel_client_set_low_bandwidth(CommonGraphicsChannelClien gboolean low_bandwidth); gboolean common_graphics_channel_client_is_low_bandwidth(CommonGraphicsChannelClient *self); -CommonGraphicsChannelClient *common_graphics_channel_client_new(CommonGraphicsChannel *common, - int size, - RedClient *client, - RedsStream *stream, - int mig_target, - int monitor_latency, - uint32_t *common_caps, - int num_common_caps, - uint32_t *caps, - int num_caps); - -#endif /* COMMON_GRAPHICS_CHANNEL_CLIENT_H */ +G_END_DECLS + +#endif /* _COMMON_GRAPHICS_CHANNEL_CLIENT_H */ diff --git a/server/cursor-channel-client.c b/server/cursor-channel-client.c index 8cddd6c..424c83a 100644 --- a/server/cursor-channel-client.c +++ b/server/cursor-channel-client.c @@ -38,17 +38,40 @@ enum { RED_PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE, }; -struct CursorChannelClient { - CommonGraphicsChannelClient common; +G_DEFINE_TYPE(CursorChannelClient, cursor_channel_client, TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT) +#define CURSOR_CHANNEL_CLIENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_CURSOR_CHANNEL_CLIENT, CursorChannelClientPrivate)) + +struct CursorChannelClientPrivate +{ RedCacheItem *cursor_cache[CURSOR_CACHE_HASH_SIZE]; Ring cursor_cache_lru; long cursor_cache_available; uint32_t cursor_cache_items; }; +static void cursor_channel_client_constructed(GObject *object) +{ + G_OBJECT_CLASS(cursor_channel_client_parent_class)->constructed(object); +} + +static void +cursor_channel_client_class_init(CursorChannelClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(CursorChannelClientPrivate)); -#define RCC_TO_CCC(rcc) ((CursorChannelClient*)rcc) + object_class->constructed = cursor_channel_client_constructed; +} + +static void +cursor_channel_client_init(CursorChannelClient *self) +{ + self->priv = CURSOR_CHANNEL_CLIENT_PRIVATE(self); + ring_init(&self->priv->cursor_cache_lru); + self->priv->cursor_cache_available = CLIENT_CURSOR_CACHE_SIZE; +} #define CLIENT_CURSOR_CACHE #include "cache-item.tmpl.c" @@ -60,7 +83,7 @@ static int _cursor_count = 0; void cursor_channel_client_reset_cursor_cache(RedChannelClient *rcc) { - red_cursor_cache_reset(RCC_TO_CCC(rcc), CLIENT_CURSOR_CACHE_SIZE); + red_cursor_cache_reset(CURSOR_CHANNEL_CLIENT(rcc), CLIENT_CURSOR_CACHE_SIZE); } void cursor_channel_client_on_disconnect(RedChannelClient *rcc) @@ -87,28 +110,35 @@ CursorChannelClient* cursor_channel_client_new(CursorChannel *cursor, RedClient uint32_t *common_caps, int num_common_caps, uint32_t *caps, int num_caps) { - spice_return_val_if_fail(cursor, NULL); - spice_return_val_if_fail(client, NULL); - spice_return_val_if_fail(stream, NULL); - spice_return_val_if_fail(!num_common_caps || common_caps, NULL); - spice_return_val_if_fail(!num_caps || caps, NULL); - - CursorChannelClient *ccc = - (CursorChannelClient*)common_graphics_channel_client_new((CommonGraphicsChannel*)cursor, - sizeof(CursorChannelClient), - client, stream, - mig_target, - FALSE, - common_caps, - num_common_caps, - caps, - num_caps); - spice_return_val_if_fail(ccc != NULL, NULL); - - ring_init(&ccc->cursor_cache_lru); - ccc->cursor_cache_available = CLIENT_CURSOR_CACHE_SIZE; - - return ccc; + CursorChannelClient *rcc; + GArray *common_caps_array = NULL, *caps_array = NULL; + + if (common_caps) { + common_caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*common_caps), num_common_caps); + g_array_append_vals(common_caps_array, common_caps, num_common_caps); + } + if (caps) { + caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*caps), num_caps); + g_array_append_vals(caps_array, caps, num_caps); + } + + rcc = g_initable_new(TYPE_CURSOR_CHANNEL_CLIENT, + NULL, NULL, + "channel", cursor, + "client", client, + "stream", stream, + "migration-target", mig_target, + "monitor-latency", FALSE, + "common-caps", common_caps_array, + "caps", caps_array, + NULL); + + if (caps_array) + g_array_unref(caps_array); + if (common_caps_array) + g_array_unref(common_caps_array); + + return rcc; } RedCacheItem* cursor_channel_client_cache_find(CursorChannelClient *ccc, uint64_t id) diff --git a/server/cursor-channel-client.h b/server/cursor-channel-client.h index 3e18d65..22e2c8a 100644 --- a/server/cursor-channel-client.h +++ b/server/cursor-channel-client.h @@ -18,15 +18,40 @@ #ifndef CURSOR_CHANNEL_CLIENT_H_ # define CURSOR_CHANNEL_CLIENT_H_ +#include <glib-object.h> #include "cache-item.h" #include "red-common.h" #include "red-channel.h" #include "reds-stream.h" -typedef struct CursorChannel CursorChannel; +G_BEGIN_DECLS + +#define TYPE_CURSOR_CHANNEL_CLIENT cursor_channel_client_get_type() + +#define CURSOR_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_CURSOR_CHANNEL_CLIENT, CursorChannelClient)) +#define CURSOR_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_CURSOR_CHANNEL_CLIENT, CursorChannelClientClass)) +#define IS_CURSOR_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_CURSOR_CHANNEL_CLIENT)) +#define IS_CURSOR_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_CURSOR_CHANNEL_CLIENT)) +#define CURSOR_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_CURSOR_CHANNEL_CLIENT, CursorChannelClientClass)) + typedef struct CursorChannelClient CursorChannelClient; +typedef struct CursorChannelClientClass CursorChannelClientClass; +typedef struct CursorChannelClientPrivate CursorChannelClientPrivate; +typedef struct CursorChannel CursorChannel; -#define CURSOR_CHANNEL_CLIENT(Client) ((CursorChannelClient*)(Client)) +struct CursorChannelClient +{ + CommonGraphicsChannelClient parent; + + CursorChannelClientPrivate *priv; +}; + +struct CursorChannelClientClass +{ + CommonGraphicsChannelClientClass parent_class; +}; + +GType cursor_channel_client_get_type(void) G_GNUC_CONST; CursorChannelClient* cursor_channel_client_new(CursorChannel *cursor, RedClient *client, @@ -34,11 +59,14 @@ CursorChannelClient* cursor_channel_client_new(CursorChannel *cursor, int mig_target, uint32_t *common_caps, int num_common_caps, - uint32_t *caps, int num_caps); + uint32_t *caps, + int num_caps); void cursor_channel_client_migrate(CursorChannelClient* client); void cursor_channel_client_reset_cursor_cache(RedChannelClient *rcc); void cursor_channel_client_on_disconnect(RedChannelClient *rcc); RedCacheItem* cursor_channel_client_cache_find(CursorChannelClient *ccc, uint64_t id); int cursor_channel_client_cache_add(CursorChannelClient *ccc, uint64_t id, size_t size); +G_END_DECLS + #endif /* CURSOR_CHANNEL_CLIENT_H_ */ diff --git a/server/cursor-channel.c b/server/cursor-channel.c index c230081..032a1e1 100644 --- a/server/cursor-channel.c +++ b/server/cursor-channel.c @@ -191,8 +191,8 @@ static void put_cursor_pipe_item(CursorChannelClient *ccc, // TODO: share code between before/after_push since most of the items need the same // release -static void cursor_channel_client_release_item_before_push(CursorChannelClient *ccc, - RedPipeItem *item) +static void cursor_channel_release_item_before_push(CursorChannelClient *ccc, + RedPipeItem *item) { switch (item->type) { case RED_PIPE_ITEM_TYPE_CURSOR: { @@ -211,8 +211,8 @@ static void cursor_channel_client_release_item_before_push(CursorChannelClient * } } -static void cursor_channel_client_release_item_after_push(CursorChannelClient *ccc, - RedPipeItem *item) +static void cursor_channel_release_item_after_push(CursorChannelClient *ccc, + RedPipeItem *item) { switch (item->type) { case RED_PIPE_ITEM_TYPE_CURSOR: { @@ -229,12 +229,12 @@ static void red_marshall_cursor_init(RedChannelClient *rcc, SpiceMarshaller *bas RedPipeItem *pipe_item) { CursorChannel *cursor_channel; - CursorChannelClient *ccc = (CursorChannelClient*)rcc; + CursorChannelClient *ccc = CURSOR_CHANNEL_CLIENT(rcc); SpiceMsgCursorInit msg; AddBufInfo info; spice_assert(rcc); - cursor_channel = (CursorChannel*)rcc->channel; + 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; @@ -251,8 +251,9 @@ static void cursor_marshall(RedChannelClient *rcc, SpiceMarshaller *m, RedCursorPipeItem *cursor_pipe_item) { - CursorChannel *cursor_channel = SPICE_CONTAINEROF(rcc->channel, CursorChannel, common.base); - CursorChannelClient *ccc = (CursorChannelClient*)rcc; + CursorChannel *cursor_channel = SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), + CursorChannel, common.base); + CursorChannelClient *ccc = CURSOR_CHANNEL_CLIENT(rcc); CursorItem *item = cursor_pipe_item->cursor_item; RedPipeItem *pipe_item = &cursor_pipe_item->base; RedCursorCmd *cmd; @@ -316,7 +317,7 @@ static inline void red_marshall_inval(RedChannelClient *rcc, static void cursor_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) { SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); - CursorChannelClient *ccc = (CursorChannelClient*)rcc; + CursorChannelClient *ccc = CURSOR_CHANNEL_CLIENT(rcc); switch (pipe_item->type) { case RED_PIPE_ITEM_TYPE_CURSOR: @@ -340,7 +341,7 @@ static void cursor_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_it spice_error("invalid pipe item type"); } - cursor_channel_client_release_item_before_push(ccc, pipe_item); + cursor_channel_release_item_before_push(ccc, pipe_item); red_channel_client_begin_send_message(rcc); } @@ -365,15 +366,15 @@ static void cursor_channel_hold_pipe_item(RedChannelClient *rcc, RedPipeItem *it static void cursor_channel_release_item(RedChannelClient *rcc, RedPipeItem *item, int item_pushed) { - CursorChannelClient *ccc = (CursorChannelClient*)rcc; + CursorChannelClient *ccc = CURSOR_CHANNEL_CLIENT(rcc); spice_assert(item); if (item_pushed) { - cursor_channel_client_release_item_after_push(ccc, item); + cursor_channel_release_item_after_push(ccc, item); } else { spice_debug("not pushed (%d)", item->type); - cursor_channel_client_release_item_before_push(ccc, item); + cursor_channel_release_item_before_push(ccc, item); } } diff --git a/server/dcc-encoders.c b/server/dcc-encoders.c index 0433ebf..7cf4e03 100644 --- a/server/dcc-encoders.c +++ b/server/dcc-encoders.c @@ -292,34 +292,34 @@ static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input) static void dcc_init_quic(DisplayChannelClient *dcc) { - dcc->quic_data.usr.error = quic_usr_error; - dcc->quic_data.usr.warn = quic_usr_warn; - dcc->quic_data.usr.info = quic_usr_warn; - dcc->quic_data.usr.malloc = quic_usr_malloc; - dcc->quic_data.usr.free = quic_usr_free; - dcc->quic_data.usr.more_space = quic_usr_more_space; - dcc->quic_data.usr.more_lines = quic_usr_more_lines; + dcc->priv->quic_data.usr.error = quic_usr_error; + dcc->priv->quic_data.usr.warn = quic_usr_warn; + dcc->priv->quic_data.usr.info = quic_usr_warn; + dcc->priv->quic_data.usr.malloc = quic_usr_malloc; + dcc->priv->quic_data.usr.free = quic_usr_free; + dcc->priv->quic_data.usr.more_space = quic_usr_more_space; + dcc->priv->quic_data.usr.more_lines = quic_usr_more_lines; - dcc->quic = quic_create(&dcc->quic_data.usr); + dcc->priv->quic = quic_create(&dcc->priv->quic_data.usr); - if (!dcc->quic) { + if (!dcc->priv->quic) { spice_critical("create quic failed"); } } static void dcc_init_lz(DisplayChannelClient *dcc) { - dcc->lz_data.usr.error = lz_usr_error; - dcc->lz_data.usr.warn = lz_usr_warn; - dcc->lz_data.usr.info = lz_usr_warn; - dcc->lz_data.usr.malloc = lz_usr_malloc; - dcc->lz_data.usr.free = lz_usr_free; - dcc->lz_data.usr.more_space = lz_usr_more_space; - dcc->lz_data.usr.more_lines = lz_usr_more_lines; + dcc->priv->lz_data.usr.error = lz_usr_error; + dcc->priv->lz_data.usr.warn = lz_usr_warn; + dcc->priv->lz_data.usr.info = lz_usr_warn; + dcc->priv->lz_data.usr.malloc = lz_usr_malloc; + dcc->priv->lz_data.usr.free = lz_usr_free; + dcc->priv->lz_data.usr.more_space = lz_usr_more_space; + dcc->priv->lz_data.usr.more_lines = lz_usr_more_lines; - dcc->lz = lz_create(&dcc->lz_data.usr); + dcc->priv->lz = lz_create(&dcc->priv->lz_data.usr); - if (!dcc->lz) { + if (!dcc->priv->lz) { spice_critical("create lz failed"); } } @@ -329,7 +329,7 @@ static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *im GlzData *lz_data = (GlzData *)usr; GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem *)image; DisplayChannelClient *drawable_cc = glz_drawable_instance->glz_drawable->dcc; - DisplayChannelClient *this_cc = SPICE_CONTAINEROF(lz_data, DisplayChannelClient, glz_data); + DisplayChannelClient *this_cc = lz_data->data.dcc; if (this_cc == drawable_cc) { dcc_free_glz_drawable_instance(drawable_cc, glz_drawable_instance); } else { @@ -340,33 +340,33 @@ static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *im * called from any DisplayChannelClient thread, hence the need for * this check. */ - pthread_mutex_lock(&drawable_cc->glz_drawables_inst_to_free_lock); + pthread_mutex_lock(&drawable_cc->priv->glz_drawables_inst_to_free_lock); ring_add_before(&glz_drawable_instance->free_link, - &drawable_cc->glz_drawables_inst_to_free); - pthread_mutex_unlock(&drawable_cc->glz_drawables_inst_to_free_lock); + &drawable_cc->priv->glz_drawables_inst_to_free); + pthread_mutex_unlock(&drawable_cc->priv->glz_drawables_inst_to_free_lock); } } static void dcc_init_glz_data(DisplayChannelClient *dcc) { - dcc->glz_data.usr.error = glz_usr_error; - dcc->glz_data.usr.warn = glz_usr_warn; - dcc->glz_data.usr.info = glz_usr_warn; - dcc->glz_data.usr.malloc = glz_usr_malloc; - dcc->glz_data.usr.free = glz_usr_free; - dcc->glz_data.usr.more_space = glz_usr_more_space; - dcc->glz_data.usr.more_lines = glz_usr_more_lines; - dcc->glz_data.usr.free_image = glz_usr_free_image; + dcc->priv->glz_data.usr.error = glz_usr_error; + dcc->priv->glz_data.usr.warn = glz_usr_warn; + dcc->priv->glz_data.usr.info = glz_usr_warn; + dcc->priv->glz_data.usr.malloc = glz_usr_malloc; + dcc->priv->glz_data.usr.free = glz_usr_free; + dcc->priv->glz_data.usr.more_space = glz_usr_more_space; + dcc->priv->glz_data.usr.more_lines = glz_usr_more_lines; + dcc->priv->glz_data.usr.free_image = glz_usr_free_image; } static void dcc_init_jpeg(DisplayChannelClient *dcc) { - dcc->jpeg_data.usr.more_space = jpeg_usr_more_space; - dcc->jpeg_data.usr.more_lines = jpeg_usr_more_lines; + dcc->priv->jpeg_data.usr.more_space = jpeg_usr_more_space; + dcc->priv->jpeg_data.usr.more_lines = jpeg_usr_more_lines; - dcc->jpeg = jpeg_encoder_create(&dcc->jpeg_data.usr); + dcc->priv->jpeg = jpeg_encoder_create(&dcc->priv->jpeg_data.usr); - if (!dcc->jpeg) { + if (!dcc->priv->jpeg) { spice_critical("create jpeg encoder failed"); } } @@ -374,12 +374,12 @@ static void dcc_init_jpeg(DisplayChannelClient *dcc) #ifdef USE_LZ4 static inline void dcc_init_lz4(DisplayChannelClient *dcc) { - dcc->lz4_data.usr.more_space = lz4_usr_more_space; - dcc->lz4_data.usr.more_lines = lz4_usr_more_lines; + dcc->priv->lz4_data.usr.more_space = lz4_usr_more_space; + dcc->priv->lz4_data.usr.more_lines = lz4_usr_more_lines; - dcc->lz4 = lz4_encoder_create(&dcc->lz4_data.usr); + dcc->priv->lz4 = lz4_encoder_create(&dcc->priv->lz4_data.usr); - if (!dcc->lz4) { + if (!dcc->priv->lz4) { spice_critical("create lz4 encoder failed"); } } @@ -387,12 +387,12 @@ static inline void dcc_init_lz4(DisplayChannelClient *dcc) static void dcc_init_zlib(DisplayChannelClient *dcc) { - dcc->zlib_data.usr.more_space = zlib_usr_more_space; - dcc->zlib_data.usr.more_input = zlib_usr_more_input; + dcc->priv->zlib_data.usr.more_space = zlib_usr_more_space; + dcc->priv->zlib_data.usr.more_input = zlib_usr_more_input; - dcc->zlib = zlib_encoder_create(&dcc->zlib_data.usr, ZLIB_DEFAULT_COMPRESSION_LEVEL); + dcc->priv->zlib = zlib_encoder_create(&dcc->priv->zlib_data.usr, ZLIB_DEFAULT_COMPRESSION_LEVEL); - if (!dcc->zlib) { + if (!dcc->priv->zlib) { spice_critical("create zlib encoder failed"); } } @@ -409,23 +409,23 @@ void dcc_encoders_init(DisplayChannelClient *dcc) dcc_init_zlib(dcc); // todo: tune level according to bandwidth - dcc->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL; + dcc->priv->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL; } void dcc_encoders_free(DisplayChannelClient *dcc) { - quic_destroy(dcc->quic); - dcc->quic = NULL; - lz_destroy(dcc->lz); - dcc->lz = NULL; - jpeg_encoder_destroy(dcc->jpeg); - dcc->jpeg = NULL; + quic_destroy(dcc->priv->quic); + dcc->priv->quic = NULL; + lz_destroy(dcc->priv->lz); + dcc->priv->lz = NULL; + jpeg_encoder_destroy(dcc->priv->jpeg); + dcc->priv->jpeg = NULL; #ifdef USE_LZ4 - lz4_encoder_destroy(dcc->lz4); - dcc->lz4 = NULL; + lz4_encoder_destroy(dcc->priv->lz4); + dcc->priv->lz4 = NULL; #endif - zlib_encoder_destroy(dcc->zlib); - dcc->zlib = NULL; + zlib_encoder_destroy(dcc->priv->zlib); + dcc->priv->zlib = NULL; } static void marshaller_compress_buf_free(uint8_t *data, void *opaque) @@ -514,9 +514,9 @@ void dcc_free_glz_drawable(DisplayChannelClient *dcc, RedGlzDrawable *drawable) glz_link); if (!ring_item_is_linked(&instance->free_link)) { // the instance didn't get out from window yet - glz_enc_dictionary_remove_image(dcc->glz_dict->dict, + glz_enc_dictionary_remove_image(dcc->priv->glz_dict->dict, instance->context, - &dcc->glz_data.usr); + &dcc->priv->glz_data.usr); } dcc_free_glz_drawable_instance(dcc, instance); @@ -539,10 +539,10 @@ int dcc_free_some_independent_glz_drawables(DisplayChannelClient *dcc) if (!dcc) { return 0; } - ring_link = ring_get_head(&dcc->glz_drawables); + ring_link = ring_get_head(&dcc->priv->glz_drawables); while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) { RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link); - ring_link = ring_next(&dcc->glz_drawables, ring_link); + ring_link = ring_next(&dcc->priv->glz_drawables, ring_link); if (!glz_drawable->drawable) { dcc_free_glz_drawable(dcc, glz_drawable); n++; @@ -555,17 +555,17 @@ void dcc_free_glz_drawables_to_free(DisplayChannelClient* dcc) { RingItem *ring_link; - if (!dcc->glz_dict) { + if (!dcc->priv->glz_dict) { return; } - pthread_mutex_lock(&dcc->glz_drawables_inst_to_free_lock); - while ((ring_link = ring_get_head(&dcc->glz_drawables_inst_to_free))) { + pthread_mutex_lock(&dcc->priv->glz_drawables_inst_to_free_lock); + while ((ring_link = ring_get_head(&dcc->priv->glz_drawables_inst_to_free))) { GlzDrawableInstanceItem *drawable_instance = SPICE_CONTAINEROF(ring_link, GlzDrawableInstanceItem, free_link); dcc_free_glz_drawable_instance(dcc, drawable_instance); } - pthread_mutex_unlock(&dcc->glz_drawables_inst_to_free_lock); + pthread_mutex_unlock(&dcc->priv->glz_drawables_inst_to_free_lock); } /* Clear all lz drawables - enforce their removal from the global dictionary. @@ -573,7 +573,7 @@ void dcc_free_glz_drawables_to_free(DisplayChannelClient* dcc) void dcc_free_glz_drawables(DisplayChannelClient *dcc) { RingItem *ring_link; - GlzSharedDictionary *glz_dict = dcc ? dcc->glz_dict : NULL; + GlzSharedDictionary *glz_dict = dcc ? dcc->priv->glz_dict : NULL; if (!glz_dict) { return; @@ -581,7 +581,7 @@ void dcc_free_glz_drawables(DisplayChannelClient *dcc) // assure no display channel is during global lz encoding pthread_rwlock_wrlock(&glz_dict->encode_lock); - while ((ring_link = ring_get_head(&dcc->glz_drawables))) { + while ((ring_link = ring_get_head(&dcc->priv->glz_drawables))) { RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link); // no need to lock the to_free list, since we assured no other thread is encoding and // thus not other thread access the to_free list of the channel @@ -592,9 +592,9 @@ void dcc_free_glz_drawables(DisplayChannelClient *dcc) void dcc_freeze_glz(DisplayChannelClient *dcc) { - pthread_rwlock_wrlock(&dcc->glz_dict->encode_lock); - dcc->glz_dict->migrate_freeze = TRUE; - pthread_rwlock_unlock(&dcc->glz_dict->encode_lock); + pthread_rwlock_wrlock(&dcc->priv->glz_dict->encode_lock); + dcc->priv->glz_dict->migrate_freeze = TRUE; + pthread_rwlock_unlock(&dcc->priv->glz_dict->encode_lock); } static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client, uint8_t id, @@ -641,21 +641,23 @@ static GlzSharedDictionary *create_glz_dictionary(DisplayChannelClient *dcc, uint8_t id, int window_size) { spice_info("Lz Window %d Size=%d", id, window_size); + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dcc)); GlzEncDictContext *glz_dict = - glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &dcc->glz_data.usr); + glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &dcc->priv->glz_data.usr); - return glz_shared_dictionary_new(RED_CHANNEL_CLIENT(dcc)->client, id, glz_dict); + return glz_shared_dictionary_new(client, id, glz_dict); } GlzSharedDictionary *get_glz_dictionary_for_dcc(DisplayChannelClient *dcc, uint8_t id, int window_size) { GlzSharedDictionary *shared_dict; + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dcc)); pthread_mutex_lock(&glz_dictionary_list_lock); - shared_dict = find_glz_dictionary(RED_CHANNEL_CLIENT(dcc)->client, id); + shared_dict = find_glz_dictionary(client, id); if (shared_dict) { shared_dict->refs++; } else { @@ -671,10 +673,11 @@ static GlzSharedDictionary *restore_glz_dictionary(DisplayChannelClient *dcc, uint8_t id, GlzEncDictRestoreData *restore_data) { + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dcc)); GlzEncDictContext *glz_dict = - glz_enc_dictionary_restore(restore_data, &dcc->glz_data.usr); + glz_enc_dictionary_restore(restore_data, &dcc->priv->glz_data.usr); - return glz_shared_dictionary_new(RED_CHANNEL_CLIENT(dcc)->client, id, glz_dict); + return glz_shared_dictionary_new(client, id, glz_dict); } GlzSharedDictionary *restore_glz_dictionary_for_dcc(DisplayChannelClient *dcc, @@ -682,10 +685,11 @@ GlzSharedDictionary *restore_glz_dictionary_for_dcc(DisplayChannelClient *dcc, GlzEncDictRestoreData *restore_data) { GlzSharedDictionary *shared_dict = NULL; + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dcc)); pthread_mutex_lock(&glz_dictionary_list_lock); - shared_dict = find_glz_dictionary(RED_CHANNEL_CLIENT(dcc)->client, id); + shared_dict = find_glz_dictionary(client, id); if (shared_dict) { shared_dict->refs++; @@ -705,14 +709,14 @@ void dcc_release_glz(DisplayChannelClient *dcc) dcc_free_glz_drawables(dcc); - glz_encoder_destroy(dcc->glz); - dcc->glz = NULL; + glz_encoder_destroy(dcc->priv->glz); + dcc->priv->glz = NULL; - if (!(shared_dict = dcc->glz_dict)) { + if (!(shared_dict = dcc->priv->glz_dict)) { return; } - dcc->glz_dict = NULL; + dcc->priv->glz_dict = NULL; pthread_mutex_lock(&glz_dictionary_list_lock); if (--shared_dict->refs != 0) { pthread_mutex_unlock(&glz_dictionary_list_lock); @@ -720,6 +724,6 @@ void dcc_release_glz(DisplayChannelClient *dcc) } ring_remove(&shared_dict->base); pthread_mutex_unlock(&glz_dictionary_list_lock); - glz_enc_dictionary_destroy(shared_dict->dict, &dcc->glz_data.usr); + glz_enc_dictionary_destroy(shared_dict->dict, &dcc->priv->glz_data.usr); free(shared_dict); } diff --git a/server/dcc-encoders.h b/server/dcc-encoders.h index 2c133ea..92aeca9 100644 --- a/server/dcc-encoders.h +++ b/server/dcc-encoders.h @@ -34,8 +34,6 @@ typedef struct RedCompressBuf RedCompressBuf; typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem; typedef struct RedGlzDrawable RedGlzDrawable; -/* FIXME: remove */ -typedef struct DisplayChannelClient DisplayChannelClient; void dcc_encoders_init (DisplayChannelClient *dcc); void dcc_encoders_free (DisplayChannelClient *dcc); diff --git a/server/dcc-private.h b/server/dcc-private.h index 85c5a33..db54b16 100644 --- a/server/dcc-private.h +++ b/server/dcc-private.h @@ -24,8 +24,7 @@ #include "dcc-encoders.h" #include "stream.h" -struct DisplayChannelClient { - CommonGraphicsChannelClient common; +struct DisplayChannelClientPrivate { uint32_t id; SpiceImageCompression image_compression; spice_wan_compression_t jpeg_state; @@ -75,10 +74,10 @@ struct DisplayChannelClient { Ring glz_drawables_inst_to_free; // list of instances to be freed pthread_mutex_t glz_drawables_inst_to_free_lock; - uint8_t surface_client_created[NUM_SURFACES]; - QRegion surface_client_lossy_region[NUM_SURFACES]; + uint8_t *surface_client_created; + QRegion *surface_client_lossy_region; - StreamAgent stream_agents[NUM_STREAMS]; + StreamAgent *stream_agents; int use_mjpeg_encoder_rate_control; uint32_t streams_max_latency; uint64_t streams_max_bit_rate; diff --git a/server/dcc-send.c b/server/dcc-send.c index d17413a..2525753 100644 --- a/server/dcc-send.c +++ b/server/dcc-send.c @@ -50,7 +50,7 @@ typedef struct BitmapData { static int dcc_pixmap_cache_unlocked_hit(DisplayChannelClient *dcc, uint64_t id, int *lossy) { - PixmapCache *cache = dcc->pixmap_cache; + PixmapCache *cache = dcc->priv->pixmap_cache; NewCacheItem *item; uint64_t serial; @@ -59,11 +59,12 @@ static int dcc_pixmap_cache_unlocked_hit(DisplayChannelClient *dcc, uint64_t id, while (item) { if (item->id == id) { + uint32_t client_id = dcc->priv->id; ring_remove(&item->lru_link); ring_add(&cache->lru, &item->lru_link); - spice_assert(dcc->id < MAX_CACHE_CLIENTS); - item->sync[dcc->id] = serial; - cache->sync[dcc->id] = serial; + spice_assert(client_id < MAX_CACHE_CLIENTS); + item->sync[client_id] = serial; + cache->sync[client_id] = serial; *lossy = item->lossy; break; } @@ -76,7 +77,7 @@ static int dcc_pixmap_cache_unlocked_hit(DisplayChannelClient *dcc, uint64_t id, static int dcc_pixmap_cache_hit(DisplayChannelClient *dcc, uint64_t id, int *lossy) { int hit; - PixmapCache *cache = dcc->pixmap_cache; + PixmapCache *cache = dcc->priv->pixmap_cache; pthread_mutex_lock(&cache->lock); hit = dcc_pixmap_cache_unlocked_hit(dcc, id, lossy); @@ -96,7 +97,7 @@ static int is_surface_area_lossy(DisplayChannelClient *dcc, uint32_t surface_id, spice_return_val_if_fail(validate_surface(display, surface_id), FALSE); surface = &display->surfaces[surface_id]; - surface_lossy_region = &dcc->surface_client_lossy_region[surface_id]; + surface_lossy_region = &dcc->priv->surface_client_lossy_region[surface_id]; if (!area) { if (region_is_empty(surface_lossy_region)) { @@ -132,7 +133,7 @@ static int is_surface_area_lossy(DisplayChannelClient *dcc, uint32_t surface_id, static int is_bitmap_lossy(RedChannelClient *rcc, SpiceImage *image, SpiceRect *area, Drawable *drawable, BitmapData *out_data) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); if (image == NULL) { // self bitmap @@ -188,15 +189,17 @@ static int is_brush_lossy(RedChannelClient *rcc, SpiceBrush *brush, static RedPipeItem *dcc_get_tail(DisplayChannelClient *dcc) { - return (RedPipeItem*)ring_get_tail(&RED_CHANNEL_CLIENT(dcc)->pipe); + return (RedPipeItem*)ring_get_tail(&RED_CHANNEL_CLIENT(dcc)->priv->pipe); } static void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc, SpiceImage *image, SpiceImage *io_image, int is_lossy) { - DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannel *display_channel = + SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), DisplayChannel, + common.base); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { spice_assert(image->descriptor.width * image->descriptor.height > 0); @@ -205,8 +208,8 @@ static void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc, image->descriptor.width * image->descriptor.height, is_lossy)) { io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME; - dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] = - image->descriptor.id; + 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); } } @@ -240,8 +243,8 @@ static void marshal_sub_msg_inval_list_wait(SpiceMarshaller *m, /* use legacy SpiceDataHeader (with sub_list) */ static void send_free_list_legacy(RedChannelClient *rcc) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - FreeList *free_list = &dcc->send_data.free_list; + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); + FreeList *free_list = &dcc->priv->send_data.free_list; SpiceMarshaller *marshaller; int sub_list_len = 1; SpiceMarshaller *wait_m = NULL; @@ -271,8 +274,8 @@ static void send_free_list_legacy(RedChannelClient *rcc) /* use mini header and SPICE_MSG_LIST */ static void send_free_list(RedChannelClient *rcc) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - FreeList *free_list = &dcc->send_data.free_list; + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); + FreeList *free_list = &dcc->priv->send_data.free_list; int sub_list_len = 1; SpiceMarshaller *urgent_marshaller; SpiceMarshaller *wait_m = NULL; @@ -283,14 +286,14 @@ static void send_free_list(RedChannelClient *rcc) int i; urgent_marshaller = red_channel_client_switch_to_urgent_sender(rcc); - for (i = 0; i < dcc->send_data.num_pixmap_cache_items; i++) { + for (i = 0; i < dcc->priv->send_data.num_pixmap_cache_items; i++) { int dummy; /* When using the urgent marshaller, the serial number of the message that is * going to be sent right after the SPICE_MSG_LIST, is increased by one. * But all this message pixmaps cache references used its old serial. * we use pixmap_cache_items to collect these pixmaps, and we update their serial * by calling pixmap_cache_hit. */ - dcc_pixmap_cache_hit(dcc, dcc->send_data.pixmap_cache_items[i], &dummy); + dcc_pixmap_cache_hit(dcc, dcc->priv->send_data.pixmap_cache_items[i], &dummy); } if (free_list->wait.header.wait_count) { @@ -356,12 +359,12 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, if (simage->descriptor.flags & SPICE_IMAGE_FLAGS_HIGH_BITS_SET) { image.descriptor.flags = SPICE_IMAGE_FLAGS_HIGH_BITS_SET; } - pthread_mutex_lock(&dcc->pixmap_cache->lock); + pthread_mutex_lock(&dcc->priv->pixmap_cache->lock); if ((simage->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { int lossy_cache_item; if (dcc_pixmap_cache_unlocked_hit(dcc, image.descriptor.id, &lossy_cache_item)) { - dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] = + 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) { @@ -377,10 +380,10 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, spice_assert(bitmap_palette_out == NULL); spice_assert(lzplt_palette_out == NULL); stat_inc_counter(reds, display->cache_hits_counter, 1); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); + pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); return FILL_BITS_TYPE_CACHE; } else { - pixmap_cache_unlocked_set_lossy(dcc->pixmap_cache, simage->descriptor.id, + pixmap_cache_unlocked_set_lossy(dcc->priv->pixmap_cache, simage->descriptor.id, FALSE); image.descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME; } @@ -395,7 +398,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, surface_id = simage->u.surface.surface_id; if (!validate_surface(display, surface_id)) { spice_warning("Invalid surface in SPICE_IMAGE_TYPE_SURFACE"); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); + pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); return FILL_BITS_TYPE_SURFACE; } @@ -410,7 +413,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); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); + pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); return FILL_BITS_TYPE_SURFACE; } case SPICE_IMAGE_TYPE_BITMAP: { @@ -421,7 +424,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, /* Images must be added to the cache only after they are compressed in order to prevent starvation in the client between pixmap_cache and global dictionary (in cases of multiple monitors) */ - if (reds_stream_get_family(rcc->stream) == AF_UNIX || + if (reds_stream_get_family(rcc->priv->stream) == AF_UNIX || !dcc_compress_image(dcc, &image, &simage->u.bitmap, drawable, can_lossy, &comp_send_data)) { SpicePalette *palette; @@ -442,7 +445,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, } spice_marshaller_add_ref_chunks(m, bitmap->data); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); + pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); return FILL_BITS_TYPE_BITMAP; } else { red_display_add_image_to_pixmap_cache(rcc, simage, &image, @@ -460,7 +463,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, } spice_assert(!comp_send_data.is_lossy || can_lossy); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); + pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); return (comp_send_data.is_lossy ? FILL_BITS_TYPE_COMPRESS_LOSSY : FILL_BITS_TYPE_COMPRESS_LOSSLESS); } @@ -474,27 +477,27 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, spice_assert(bitmap_palette_out == NULL); spice_assert(lzplt_palette_out == NULL); spice_marshaller_add_ref_chunks(m, image.u.quic.data); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); + pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); return FILL_BITS_TYPE_COMPRESS_LOSSLESS; default: spice_error("invalid image type %u", image.descriptor.type); } - pthread_mutex_unlock(&dcc->pixmap_cache->lock); + pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); return FILL_BITS_TYPE_INVALID; } static void fill_mask(RedChannelClient *rcc, SpiceMarshaller *m, SpiceImage *mask_bitmap, Drawable *drawable) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); if (mask_bitmap && m) { - if (dcc->image_compression != SPICE_IMAGE_COMPRESSION_OFF) { + if (dcc->priv->image_compression != SPICE_IMAGE_COMPRESSION_OFF) { /* todo: pass compression argument */ - SpiceImageCompression save_img_comp = dcc->image_compression; - dcc->image_compression = SPICE_IMAGE_COMPRESSION_OFF; + SpiceImageCompression save_img_comp = dcc->priv->image_compression; + dcc->priv->image_compression = SPICE_IMAGE_COMPRESSION_OFF; fill_bits(dcc, m, mask_bitmap, drawable, FALSE); - dcc->image_compression = save_img_comp; + dcc->priv->image_compression = save_img_comp; } else { fill_bits(dcc, m, mask_bitmap, drawable, FALSE); } @@ -518,7 +521,7 @@ static void marshall_qxl_draw_fill(RedChannelClient *rcc, { Drawable *item = dpi->drawable; RedDrawable *drawable = item->red_drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); SpiceMarshaller *brush_pat_out; SpiceMarshaller *mask_bitmap_out; SpiceFill fill; @@ -548,7 +551,7 @@ static void surface_lossy_region_update(DisplayChannelClient *dcc, return; } - surface_lossy_region = &dcc->surface_client_lossy_region[item->surface_id]; + surface_lossy_region = &dcc->priv->surface_client_lossy_region[item->surface_id]; drawable = item->red_drawable; if (drawable->clip.type == SPICE_CLIP_TYPE_RECTS ) { @@ -600,7 +603,7 @@ static int pipe_rendered_drawables_intersect_with_areas(DisplayChannelClient *dc Ring *pipe; spice_assert(num_surfaces); - pipe = &RED_CHANNEL_CLIENT(dcc)->pipe; + pipe = &RED_CHANNEL_CLIENT(dcc)->priv->pipe; for (pipe_item = (RedPipeItem *)ring_get_head(pipe); pipe_item; @@ -692,7 +695,7 @@ static void red_pipe_replace_rendered_drawables_with_images(DisplayChannelClient resent_areas[0] = *first_area; num_resent = 1; - pipe = &RED_CHANNEL_CLIENT(dcc)->pipe; + pipe = &RED_CHANNEL_CLIENT(dcc)->priv->pipe; // going from the oldest to the newest for (pipe_item = (RedPipeItem *)ring_get_tail(pipe); @@ -738,7 +741,7 @@ static void red_add_lossless_drawable_dependencies(RedChannelClient *rcc, SpiceRect *deps_areas[], int num_deps) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); DisplayChannel *display = DCC_TO_DC(dcc); RedDrawable *drawable = item->red_drawable; int sync_rendered = FALSE; @@ -805,7 +808,7 @@ static void red_lossy_marshall_qxl_draw_fill(RedChannelClient *rcc, SpiceMarshaller *m, RedDrawablePipeItem *dpi) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); Drawable *item = dpi->drawable; RedDrawable *drawable = item->red_drawable; @@ -863,7 +866,7 @@ static FillBitsType red_marshall_qxl_draw_opaque(RedChannelClient *rcc, RedDrawablePipeItem *dpi, int src_allowed_lossy) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); Drawable *item = dpi->drawable; RedDrawable *drawable = item->red_drawable; SpiceMarshaller *brush_pat_out; @@ -896,7 +899,7 @@ static void red_lossy_marshall_qxl_draw_opaque(RedChannelClient *rcc, SpiceMarshaller *m, RedDrawablePipeItem *dpi) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); Drawable *item = dpi->drawable; RedDrawable *drawable = item->red_drawable; @@ -962,7 +965,7 @@ static FillBitsType red_marshall_qxl_draw_copy(RedChannelClient *rcc, RedDrawablePipeItem *dpi, int src_allowed_lossy) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); Drawable *item = dpi->drawable; RedDrawable *drawable = item->red_drawable; SpiceMarshaller *src_bitmap_out; @@ -988,7 +991,7 @@ static void red_lossy_marshall_qxl_draw_copy(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, RedDrawablePipeItem *dpi) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); Drawable *item = dpi->drawable; RedDrawable *drawable = item->red_drawable; int has_mask = !!drawable->u.copy.mask.bitmap; @@ -1013,7 +1016,7 @@ static void red_marshall_qxl_draw_transparent(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, RedDrawablePipeItem *dpi) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); Drawable *item = dpi->drawable; RedDrawable *drawable = item->red_drawable; SpiceMarshaller *src_bitmap_out; @@ -1062,7 +1065,7 @@ static FillBitsType red_marshall_qxl_draw_alpha_blend(RedChannelClient *rcc, int src_allowed_lossy) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; SpiceMarshaller *src_bitmap_out; SpiceAlphaBlend alpha_blend; @@ -1086,7 +1089,7 @@ static void red_lossy_marshall_qxl_draw_alpha_blend(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; int src_is_lossy; BitmapData src_bitmap_data; @@ -1128,7 +1131,7 @@ static void red_lossy_marshall_qxl_copy_bits(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; SpiceRect src_rect; int horz_offset; @@ -1157,7 +1160,7 @@ static void red_marshall_qxl_draw_blend(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; SpiceMarshaller *src_bitmap_out; SpiceMarshaller *mask_bitmap_out; @@ -1181,7 +1184,7 @@ static void red_lossy_marshall_qxl_draw_blend(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; int src_is_lossy; BitmapData src_bitmap_data; @@ -1243,7 +1246,7 @@ static void red_lossy_marshall_qxl_draw_blackness(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; int has_mask = !!drawable->u.blackness.mask.bitmap; @@ -1277,7 +1280,7 @@ static void red_lossy_marshall_qxl_draw_whiteness(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; int has_mask = !!drawable->u.whiteness.mask.bitmap; @@ -1317,7 +1320,7 @@ static void red_marshall_qxl_draw_rop3(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; SpiceRop3 rop3; SpiceMarshaller *src_bitmap_out; @@ -1346,7 +1349,7 @@ static void red_lossy_marshall_qxl_draw_rop3(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; int src_is_lossy; BitmapData src_bitmap_data; @@ -1401,7 +1404,7 @@ static void red_marshall_qxl_draw_composite(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; SpiceMarshaller *src_bitmap_out; SpiceMarshaller *mask_bitmap_out; @@ -1426,7 +1429,7 @@ static void red_lossy_marshall_qxl_draw_composite(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; int src_is_lossy; BitmapData src_bitmap_data; @@ -1482,7 +1485,7 @@ static void red_marshall_qxl_draw_stroke(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; SpiceStroke stroke; SpiceMarshaller *brush_pat_out; @@ -1507,7 +1510,7 @@ static void red_lossy_marshall_qxl_draw_stroke(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; int brush_is_lossy; BitmapData brush_bitmap_data; @@ -1562,7 +1565,7 @@ static void red_marshall_qxl_draw_text(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; SpiceText text; SpiceMarshaller *brush_pat_out; @@ -1589,7 +1592,7 @@ static void red_lossy_marshall_qxl_draw_text(RedChannelClient *rcc, RedDrawablePipeItem *dpi) { Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *drawable = item->red_drawable; int fg_is_lossy; BitmapData fg_bitmap_data; @@ -1654,7 +1657,7 @@ static void red_lossy_marshall_qxl_draw_text(RedChannelClient *rcc, static int red_marshall_stream_data(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, Drawable *drawable) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); DisplayChannel *display = DCC_TO_DC(dcc); Stream *stream = drawable->stream; SpiceImage *image; @@ -1689,11 +1692,11 @@ static int red_marshall_stream_data(RedChannelClient *rcc, height = stream->height; } - StreamAgent *agent = &dcc->stream_agents[get_stream_id(display, stream)]; + StreamAgent *agent = &dcc->priv->stream_agents[get_stream_id(display, stream)]; uint64_t time_now = spice_get_monotonic_time_ns(); size_t outbuf_size; - if (!dcc->use_mjpeg_encoder_rate_control) { + if (!dcc->priv->use_mjpeg_encoder_rate_control) { if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) { agent->frames--; #ifdef STREAM_STATS @@ -1707,16 +1710,16 @@ static int red_marshall_stream_data(RedChannelClient *rcc, frame_mm_time = drawable->red_drawable->mm_time ? drawable->red_drawable->mm_time : reds_get_mm_time(); - outbuf_size = dcc->send_data.stream_outbuf_size; + outbuf_size = dcc->priv->send_data.stream_outbuf_size; ret = mjpeg_encoder_encode_frame(agent->mjpeg_encoder, &image->u.bitmap, width, height, &drawable->red_drawable->u.copy.src_area, stream->top_down, frame_mm_time, - &dcc->send_data.stream_outbuf, + &dcc->priv->send_data.stream_outbuf, &outbuf_size, &n); switch (ret) { case MJPEG_ENCODER_FRAME_DROP: - spice_assert(dcc->use_mjpeg_encoder_rate_control); + spice_assert(dcc->priv->use_mjpeg_encoder_rate_control); #ifdef STREAM_STATS agent->stats.num_drops_fps++; #endif @@ -1729,7 +1732,7 @@ static int red_marshall_stream_data(RedChannelClient *rcc, spice_error("bad return value (%d) from mjpeg_encoder_encode_frame", ret); return FALSE; } - dcc->send_data.stream_outbuf_size = outbuf_size; + dcc->priv->send_data.stream_outbuf_size = outbuf_size; if (!drawable->sized_stream) { SpiceMsgDisplayStreamData stream_data; @@ -1758,7 +1761,7 @@ static int red_marshall_stream_data(RedChannelClient *rcc, spice_marshall_msg_display_stream_data_sized(base_marshaller, &stream_data); } spice_marshaller_add_ref(base_marshaller, - dcc->send_data.stream_outbuf, n); + dcc->priv->send_data.stream_outbuf, n); agent->last_send_time = time_now; #ifdef STREAM_STATS agent->stats.num_frames_sent++; @@ -1795,7 +1798,7 @@ static void display_channel_marshall_migrate_data_surfaces(DisplayChannelClient for (i = 0; i < NUM_SURFACES; i++) { SpiceRect lossy_rect; - if (!dcc->surface_client_created[i]) { + if (!dcc->priv->surface_client_created[i]) { continue; } spice_marshaller_add_uint32(m2, i); @@ -1804,7 +1807,7 @@ static void display_channel_marshall_migrate_data_surfaces(DisplayChannelClient if (!lossy) { continue; } - region_extents(&dcc->surface_client_lossy_region[i], &lossy_rect); + region_extents(&dcc->priv->surface_client_lossy_region[i], &lossy_rect); spice_marshaller_add_int32(m2, lossy_rect.left); spice_marshaller_add_int32(m2, lossy_rect.top); spice_marshaller_add_int32(m2, lossy_rect.right); @@ -1816,34 +1819,35 @@ static void display_channel_marshall_migrate_data(RedChannelClient *rcc, SpiceMarshaller *base_marshaller) { DisplayChannel *display_channel; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); SpiceMigrateDataDisplay display_data = {0,}; - display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); + display_channel = SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), + DisplayChannel, common.base); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, NULL); spice_marshaller_add_uint32(base_marshaller, SPICE_MIGRATE_DATA_DISPLAY_MAGIC); spice_marshaller_add_uint32(base_marshaller, SPICE_MIGRATE_DATA_DISPLAY_VERSION); - spice_assert(dcc->pixmap_cache); + spice_assert(dcc->priv->pixmap_cache); spice_assert(MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS == 4 && MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS == MAX_CACHE_CLIENTS); display_data.message_serial = red_channel_client_get_message_serial(rcc); - display_data.low_bandwidth_setting = dcc->common.is_low_bandwidth; + display_data.low_bandwidth_setting = common_graphics_channel_client_is_low_bandwidth(COMMON_GRAPHICS_CHANNEL_CLIENT(dcc)); - display_data.pixmap_cache_freezer = pixmap_cache_freeze(dcc->pixmap_cache); - display_data.pixmap_cache_id = dcc->pixmap_cache->id; - display_data.pixmap_cache_size = dcc->pixmap_cache->size; - memcpy(display_data.pixmap_cache_clients, dcc->pixmap_cache->sync, + display_data.pixmap_cache_freezer = pixmap_cache_freeze(dcc->priv->pixmap_cache); + display_data.pixmap_cache_id = dcc->priv->pixmap_cache->id; + display_data.pixmap_cache_size = dcc->priv->pixmap_cache->size; + memcpy(display_data.pixmap_cache_clients, dcc->priv->pixmap_cache->sync, sizeof(display_data.pixmap_cache_clients)); - spice_assert(dcc->glz_dict); + spice_assert(dcc->priv->glz_dict); dcc_freeze_glz(dcc); - display_data.glz_dict_id = dcc->glz_dict->id; - glz_enc_dictionary_get_restore_data(dcc->glz_dict->dict, + display_data.glz_dict_id = dcc->priv->glz_dict->id; + glz_enc_dictionary_get_restore_data(dcc->priv->glz_dict->dict, &display_data.glz_dict_data, - &dcc->glz_data.usr); + &dcc->priv->glz_data.usr); /* all data besided the surfaces ref */ spice_marshaller_add(base_marshaller, @@ -1855,12 +1859,12 @@ static void display_channel_marshall_migrate_data(RedChannelClient *rcc, static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc, SpiceMarshaller *base_marshaller) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); SpiceMsgWaitForChannels wait; PixmapCache *pixmap_cache; red_channel_client_init_send_data(rcc, SPICE_MSG_WAIT_FOR_CHANNELS, NULL); - pixmap_cache = dcc->pixmap_cache; + pixmap_cache = dcc->priv->pixmap_cache; pthread_mutex_lock(&pixmap_cache->lock); @@ -1868,8 +1872,8 @@ static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc, wait.wait_list[0].channel_type = SPICE_CHANNEL_DISPLAY; wait.wait_list[0].channel_id = pixmap_cache->generation_initiator.client; wait.wait_list[0].message_serial = pixmap_cache->generation_initiator.message; - dcc->pixmap_cache_generation = pixmap_cache->generation; - dcc->pending_pixmaps_sync = FALSE; + dcc->priv->pixmap_cache_generation = pixmap_cache->generation; + dcc->priv->pending_pixmaps_sync = FALSE; pthread_mutex_unlock(&pixmap_cache->lock); @@ -1878,23 +1882,24 @@ static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc, static void dcc_pixmap_cache_reset(DisplayChannelClient *dcc, SpiceMsgWaitForChannels* sync_data) { - PixmapCache *cache = dcc->pixmap_cache; + PixmapCache *cache = dcc->priv->pixmap_cache; uint8_t wait_count; uint64_t serial; uint32_t i; + uint32_t client_id = dcc->priv->id; serial = red_channel_client_get_message_serial(RED_CHANNEL_CLIENT(dcc)); pthread_mutex_lock(&cache->lock); pixmap_cache_clear(cache); - dcc->pixmap_cache_generation = ++cache->generation; - cache->generation_initiator.client = dcc->id; + dcc->priv->pixmap_cache_generation = ++cache->generation; + cache->generation_initiator.client = client_id; cache->generation_initiator.message = serial; - cache->sync[dcc->id] = serial; + cache->sync[client_id] = serial; wait_count = 0; for (i = 0; i < MAX_CACHE_CLIENTS; i++) { - if (cache->sync[i] && i != dcc->id) { + if (cache->sync[i] && i != client_id) { sync_data->wait_list[wait_count].channel_type = SPICE_CHANNEL_DISPLAY; sync_data->wait_list[wait_count].channel_id = i; sync_data->wait_list[wait_count++].message_serial = cache->sync[i]; @@ -1907,7 +1912,7 @@ static void dcc_pixmap_cache_reset(DisplayChannelClient *dcc, SpiceMsgWaitForCha static void display_channel_marshall_reset_cache(RedChannelClient *rcc, SpiceMarshaller *base_marshaller) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); SpiceMsgWaitForChannels wait; red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS, NULL); @@ -1921,7 +1926,7 @@ static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, RedImageItem *item) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); DisplayChannel *display = DCC_TO_DC(dcc); SpiceImage red_image; SpiceBitmap bitmap; @@ -1981,7 +1986,7 @@ static void red_marshall_image(RedChannelClient *rcc, int comp_succeeded = dcc_compress_image(dcc, &red_image, &bitmap, NULL, item->can_lossy, &comp_send_data); - surface_lossy_region = &dcc->surface_client_lossy_region[item->surface_id]; + surface_lossy_region = &dcc->priv->surface_client_lossy_region[item->surface_id]; if (comp_succeeded) { spice_marshall_Image(src_bitmap_out, &red_image, &bitmap_palette_out, &lzplt_palette_out); @@ -2126,7 +2131,9 @@ static void marshall_qxl_drawable(RedChannelClient *rcc, spice_return_if_fail(rcc); Drawable *item = dpi->drawable; - DisplayChannel *display = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); + DisplayChannel *display = + SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), DisplayChannel, + common.base); spice_return_if_fail(display); /* allow sized frames to be streamed, even if they where replaced by another frame, since @@ -2143,7 +2150,7 @@ static void marshall_qxl_drawable(RedChannelClient *rcc, static void marshall_stream_start(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, StreamAgent *agent) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); Stream *stream = agent->stream; agent->last_send_time = 0; @@ -2181,7 +2188,7 @@ static void marshall_stream_clip(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, RedStreamClipItem *item) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); StreamAgent *agent = item->stream_agent; spice_return_if_fail(agent->stream); @@ -2199,7 +2206,7 @@ static void marshall_stream_clip(RedChannelClient *rcc, static void marshall_stream_end(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, StreamAgent* agent) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); SpiceMsgDisplayStreamDestroy destroy; red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL); @@ -2211,12 +2218,13 @@ static void marshall_stream_end(RedChannelClient *rcc, static void marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller *m, RedUpgradeItem *item) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); RedDrawable *red_drawable; SpiceMsgDisplayDrawCopy copy; SpiceMarshaller *src_bitmap_out, *mask_bitmap_out; + RedChannel *channel = red_channel_client_get_channel(rcc); - spice_assert(rcc && rcc->channel && item && item->drawable); + spice_assert(rcc && channel && item && item->drawable); red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->base); red_drawable = item->drawable->red_drawable; @@ -2240,9 +2248,9 @@ static void marshall_surface_create(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, SpiceMsgSurfaceCreate *surface_create) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); - region_init(&dcc->surface_client_lossy_region[surface_create->surface_id]); + region_init(&dcc->priv->surface_client_lossy_region[surface_create->surface_id]); red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE, NULL); spice_marshall_msg_display_surface_create(base_marshaller, surface_create); @@ -2251,10 +2259,10 @@ static void marshall_surface_create(RedChannelClient *rcc, static void marshall_surface_destroy(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, uint32_t surface_id) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); SpiceMsgSurfaceDestroy surface_destroy; - region_destroy(&dcc->surface_client_lossy_region[surface_id]); + region_destroy(&dcc->priv->surface_client_lossy_region[surface_id]); red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_DESTROY, NULL); surface_destroy.surface_id = surface_id; @@ -2293,8 +2301,8 @@ static void marshall_stream_activate_report(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, uint32_t stream_id) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - StreamAgent *agent = &dcc->stream_agents[stream_id]; + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); + StreamAgent *agent = &dcc->priv->stream_agents[stream_id]; SpiceMsgDisplayStreamActivateReport msg; red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT, NULL); @@ -2309,7 +2317,7 @@ static void marshall_gl_scanout(RedChannelClient *rcc, SpiceMarshaller *m, RedPipeItem *item) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); DisplayChannel *display_channel = DCC_TO_DC(dcc); QXLInstance* qxl = display_channel->common.qxl; @@ -2334,15 +2342,16 @@ static void marshall_gl_draw(RedChannelClient *rcc, static void begin_send_message(RedChannelClient *rcc) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - FreeList *free_list = &dcc->send_data.free_list; + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); + FreeList *free_list = &dcc->priv->send_data.free_list; if (free_list->res->count) { int sync_count = 0; int i; + uint32_t client_id = dcc->priv->id; for (i = 0; i < MAX_CACHE_CLIENTS; i++) { - if (i != dcc->id && free_list->sync[i] != 0) { + if (i != client_id && free_list->sync[i] != 0) { free_list->wait.header.wait_list[sync_count].channel_type = SPICE_CHANNEL_DISPLAY; free_list->wait.header.wait_list[sync_count].channel_id = i; free_list->wait.header.wait_list[sync_count++].message_serial = free_list->sync[i]; @@ -2350,7 +2359,7 @@ static void begin_send_message(RedChannelClient *rcc) } free_list->wait.header.wait_count = sync_count; - if (rcc->is_mini_header) { + if (rcc->priv->is_mini_header) { send_free_list(rcc); } else { send_free_list_legacy(rcc); @@ -2361,9 +2370,9 @@ static void begin_send_message(RedChannelClient *rcc) static void reset_send_data(DisplayChannelClient *dcc) { - dcc->send_data.free_list.res->count = 0; - dcc->send_data.num_pixmap_cache_items = 0; - memset(dcc->send_data.free_list.sync, 0, sizeof(dcc->send_data.free_list.sync)); + dcc->priv->send_data.free_list.res->count = 0; + dcc->priv->send_data.num_pixmap_cache_items = 0; + memset(dcc->priv->send_data.free_list.sync, 0, sizeof(dcc->priv->send_data.free_list.sync)); } void dcc_send_item(DisplayChannelClient *dcc, RedPipeItem *pipe_item) diff --git a/server/dcc.c b/server/dcc.c index 26a2fc9..6b5517e 100644 --- a/server/dcc.c +++ b/server/dcc.c @@ -20,9 +20,161 @@ #endif #include "dcc-private.h" +#include "dcc.h" #include "display-channel.h" +#include "red-channel-client-private.h" +#include "spice-server-enums.h" + +G_DEFINE_TYPE(DisplayChannelClient, display_channel_client, TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT) + +#define DISPLAY_CHANNEL_CLIENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_DISPLAY_CHANNEL_CLIENT, DisplayChannelClientPrivate)) #define DISPLAY_CLIENT_SHORT_TIMEOUT 15000000000ULL //nano +#define DISPLAY_FREE_LIST_DEFAULT_SIZE 128 + + enum +{ + PROP0, + PROP_IMAGE_COMPRESSION, + PROP_JPEG_STATE, + PROP_ZLIB_GLZ_STATE +}; +static void +display_channel_client_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + DisplayChannelClient *self = DISPLAY_CHANNEL_CLIENT(object); + + switch (property_id) + { + case PROP_IMAGE_COMPRESSION: + g_value_set_enum(value, self->priv->image_compression); + break; + case PROP_JPEG_STATE: + g_value_set_enum(value, self->priv->jpeg_state); + break; + case PROP_ZLIB_GLZ_STATE: + g_value_set_enum(value, self->priv->zlib_glz_state); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +display_channel_client_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + DisplayChannelClient *self = DISPLAY_CHANNEL_CLIENT(object); + + switch (property_id) + { + case PROP_IMAGE_COMPRESSION: + self->priv->image_compression = g_value_get_enum(value); + break; + case PROP_JPEG_STATE: + self->priv->jpeg_state = g_value_get_enum(value); + break; + case PROP_ZLIB_GLZ_STATE: + self->priv->zlib_glz_state = g_value_get_enum(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void dcc_init_stream_agents(DisplayChannelClient *dcc); + +static void +display_channel_client_constructed(GObject *object) +{ + DisplayChannelClient *self = DISPLAY_CHANNEL_CLIENT(object); + + G_OBJECT_CLASS(display_channel_client_parent_class)->constructed(object); + + dcc_init_stream_agents(self); + dcc_encoders_init(self); + + self->priv->surface_client_created = g_new0(uint8_t, NUM_SURFACES); + self->priv->surface_client_lossy_region = g_new0(QRegion, NUM_SURFACES); +} + +static void +display_channel_client_finalize(GObject *object) +{ + DisplayChannelClient *self = DISPLAY_CHANNEL_CLIENT(object); + g_free(self->priv->stream_agents); + g_free(self->priv->surface_client_created); + g_free(self->priv->surface_client_lossy_region); + + G_OBJECT_CLASS(display_channel_client_parent_class)->finalize(object); +} + +static void +display_channel_client_class_init(DisplayChannelClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(DisplayChannelClientPrivate)); + + object_class->get_property = display_channel_client_get_property; + object_class->set_property = display_channel_client_set_property; + object_class->constructed = display_channel_client_constructed; + object_class->finalize = display_channel_client_finalize; + + g_object_class_install_property(object_class, + PROP_IMAGE_COMPRESSION, + g_param_spec_enum("image-compression", + "image compression", + "Image compression type", + SPICE_TYPE_SPICE_IMAGE_COMPRESSION_T, + SPICE_IMAGE_COMPRESSION_INVALID, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_JPEG_STATE, + g_param_spec_enum("jpeg-state", + "jpeg state", + "JPEG compression state", + SPICE_TYPE_SPICE_WAN_COMPRESSION_T, + SPICE_WAN_COMPRESSION_INVALID, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_ZLIB_GLZ_STATE, + g_param_spec_enum("zlib-glz-state", + "zlib glz state", + "zlib glz state", + SPICE_TYPE_SPICE_WAN_COMPRESSION_T, + SPICE_WAN_COMPRESSION_INVALID, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +static void display_channel_client_init(DisplayChannelClient *self) +{ + self->priv = DISPLAY_CHANNEL_CLIENT_PRIVATE(self); + + ring_init(&self->priv->palette_cache_lru); + self->priv->palette_cache_available = CLIENT_PALETTE_CACHE_SIZE; + // todo: tune quality according to bandwidth + self->priv->jpeg_quality = 85; + + size_t stream_buf_size; + stream_buf_size = 32*1024; + self->priv->send_data.stream_outbuf = spice_malloc(stream_buf_size); + self->priv->send_data.stream_outbuf_size = stream_buf_size; + self->priv->send_data.free_list.res = + spice_malloc(sizeof(SpiceResourceList) + + DISPLAY_FREE_LIST_DEFAULT_SIZE * sizeof(SpiceResourceID)); + self->priv->send_data.free_list.res_size = DISPLAY_FREE_LIST_DEFAULT_SIZE; +} static RedSurfaceCreateItem *red_surface_create_item_new(RedChannel* channel, uint32_t surface_id, @@ -76,7 +228,7 @@ int dcc_clear_surface_drawables_from_pipe(DisplayChannelClient *dcc, int surface no other drawable depends on them */ rcc = RED_CHANNEL_CLIENT(dcc); - ring = &rcc->pipe; + ring = &rcc->priv->pipe; item = (RedPipeItem *) ring; while ((item = (RedPipeItem *)ring_next(ring, (RingItem *)item))) { Drawable *drawable; @@ -141,6 +293,7 @@ void dcc_create_surface(DisplayChannelClient *dcc, int surface_id) { DisplayChannel *display; RedSurface *surface; + RedChannel *channel; RedSurfaceCreateItem *create; uint32_t flags; @@ -153,15 +306,16 @@ void dcc_create_surface(DisplayChannelClient *dcc, int surface_id) /* don't send redundant create surface commands to client */ if (!dcc || display->common.during_target_migrate || - dcc->surface_client_created[surface_id]) { + dcc->priv->surface_client_created[surface_id]) { return; } + channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(dcc)); surface = &display->surfaces[surface_id]; - create = red_surface_create_item_new(RED_CHANNEL_CLIENT(dcc)->channel, + create = red_surface_create_item_new(channel, surface_id, surface->context.width, surface->context.height, surface->context.format, flags); - dcc->surface_client_created[surface_id] = TRUE; + dcc->priv->surface_client_created[surface_id] = TRUE; red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &create->pipe_item); } @@ -263,7 +417,7 @@ static void add_drawable_surface_images(DisplayChannelClient *dcc, Drawable *dra surface_id = drawable->surface_deps[x]; if (surface_id != -1) { - if (dcc->surface_client_created[surface_id] == TRUE) { + if (dcc->priv->surface_client_created[surface_id] == TRUE) { continue; } dcc_create_surface(dcc, surface_id); @@ -272,7 +426,7 @@ static void add_drawable_surface_images(DisplayChannelClient *dcc, Drawable *dra } } - if (dcc->surface_client_created[drawable->surface_id] == TRUE) { + if (dcc->priv->surface_client_created[drawable->surface_id] == TRUE) { return; } @@ -338,20 +492,19 @@ static void dcc_init_stream_agents(DisplayChannelClient *dcc) int i; DisplayChannel *display = DCC_TO_DC(dcc); + dcc->priv->stream_agents = g_new0(StreamAgent, NUM_STREAMS); for (i = 0; i < NUM_STREAMS; i++) { - StreamAgent *agent = &dcc->stream_agents[i]; + StreamAgent *agent = &dcc->priv->stream_agents[i]; agent->stream = &display->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); red_pipe_item_init(&agent->destroy_item, RED_PIPE_ITEM_TYPE_STREAM_DESTROY); } - dcc->use_mjpeg_encoder_rate_control = + dcc->priv->use_mjpeg_encoder_rate_control = red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(dcc), SPICE_DISPLAY_CAP_STREAM_REPORT); } -#define DISPLAY_FREE_LIST_DEFAULT_SIZE 128 - DisplayChannelClient *dcc_new(DisplayChannel *display, RedClient *client, RedsStream *stream, int mig_target, @@ -363,36 +516,36 @@ DisplayChannelClient *dcc_new(DisplayChannel *display, { DisplayChannelClient *dcc; - - dcc = (DisplayChannelClient*)common_graphics_channel_client_new( - COMMON_GRAPHICS_CHANNEL(display), sizeof(DisplayChannelClient), - client, stream, mig_target, TRUE, - common_caps, num_common_caps, - caps, num_caps); - dcc->id = display->common.qxl->id; - spice_return_val_if_fail(dcc, NULL); + GArray *common_caps_array = NULL, *caps_array = NULL; + + if (common_caps) { + common_caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*common_caps), num_common_caps); + g_array_append_vals(common_caps_array, common_caps, num_common_caps); + } + if (caps) { + caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*caps), num_caps); + g_array_append_vals(caps_array, caps, num_caps); + } + + dcc = g_initable_new(TYPE_DISPLAY_CHANNEL_CLIENT, + NULL, NULL, + "channel", display, + "client", client, + "stream", stream, + "migration-target", mig_target, + "monitor-latency", TRUE, + "common-caps", common_caps_array, + "caps", caps_array, + "image-compression", image_compression, + "jpeg-state", jpeg_state, + "zlib-glz-state", zlib_glz_state, + NULL); spice_info("New display (client %p) dcc %p stream %p", client, dcc, stream); - ring_init(&dcc->palette_cache_lru); - dcc->palette_cache_available = CLIENT_PALETTE_CACHE_SIZE; - dcc->image_compression = image_compression; - dcc->jpeg_state = jpeg_state; - dcc->zlib_glz_state = zlib_glz_state; - // TODO: tune quality according to bandwidth - dcc->jpeg_quality = 85; - - size_t stream_buf_size; - stream_buf_size = 32*1024; - dcc->send_data.stream_outbuf = spice_malloc(stream_buf_size); - dcc->send_data.stream_outbuf_size = stream_buf_size; - dcc->send_data.free_list.res = - spice_malloc(sizeof(SpiceResourceList) + - DISPLAY_FREE_LIST_DEFAULT_SIZE * sizeof(SpiceResourceID)); - dcc->send_data.free_list.res_size = DISPLAY_FREE_LIST_DEFAULT_SIZE; - - dcc_init_stream_agents(dcc); - - dcc_encoders_init(dcc); + if (common_caps_array) + g_array_unref(common_caps_array); + if (caps_array) + g_array_unref(caps_array); return dcc; } @@ -411,19 +564,20 @@ static void dcc_create_all_streams(DisplayChannelClient *dcc) /* TODO: this function is evil^Wsynchronous, fix */ static int display_channel_client_wait_for_init(DisplayChannelClient *dcc) { - dcc->expect_init = TRUE; + dcc->priv->expect_init = TRUE; uint64_t end_time = spice_get_monotonic_time_ns() + COMMON_CLIENT_TIMEOUT; for (;;) { red_channel_client_receive(RED_CHANNEL_CLIENT(dcc)); if (!red_channel_client_is_connected(RED_CHANNEL_CLIENT(dcc))) { break; } - if (dcc->pixmap_cache && dcc->glz_dict) { - dcc->pixmap_cache_generation = dcc->pixmap_cache->generation; + if (dcc->priv->pixmap_cache && dcc->priv->glz_dict) { + uint32_t id = dcc->priv->id; + dcc->priv->pixmap_cache_generation = dcc->priv->pixmap_cache->generation; /* TODO: move common.id? if it's used for a per client structure.. */ - spice_info("creating encoder with id == %d", dcc->id); - dcc->glz = glz_encoder_create(dcc->id, dcc->glz_dict->dict, &dcc->glz_data.usr); - if (!dcc->glz) { + spice_info("creating encoder with id == %d", id); + dcc->priv->glz = glz_encoder_create(id, dcc->priv->glz_dict->dict, &dcc->priv->glz_data.usr); + if (!dcc->priv->glz) { spice_critical("create global lz failed"); } return TRUE; @@ -462,7 +616,7 @@ void dcc_start(DisplayChannelClient *dcc) dcc_create_all_streams(dcc); } - if (reds_stream_is_plain_unix(rcc->stream) && + if (reds_stream_is_plain_unix(red_channel_client_get_stream(rcc)) && red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_GL_SCANOUT)) { red_channel_client_pipe_add(rcc, dcc_gl_scanout_item_new(rcc, NULL, 0)); dcc_push_monitors_config(dcc); @@ -474,7 +628,7 @@ static void dcc_destroy_stream_agents(DisplayChannelClient *dcc) int i; for (i = 0; i < NUM_STREAMS; i++) { - StreamAgent *agent = &dcc->stream_agents[i]; + StreamAgent *agent = &dcc->priv->stream_agents[i]; region_destroy(&agent->vis_region); region_destroy(&agent->clip); if (agent->mjpeg_encoder) { @@ -488,16 +642,16 @@ void dcc_stop(DisplayChannelClient *dcc) { DisplayChannel *dc = DCC_TO_DC(dcc); - pixmap_cache_unref(dcc->pixmap_cache); - dcc->pixmap_cache = NULL; + pixmap_cache_unref(dcc->priv->pixmap_cache); + dcc->priv->pixmap_cache = NULL; dcc_release_glz(dcc); dcc_palette_cache_reset(dcc); - free(dcc->send_data.stream_outbuf); - free(dcc->send_data.free_list.res); + free(dcc->priv->send_data.stream_outbuf); + free(dcc->priv->send_data.free_list.res); dcc_destroy_stream_agents(dcc); dcc_encoders_free(dcc); - if (dcc->gl_draw_ongoing) { + if (dcc->priv->gl_draw_ongoing) { display_channel_gl_draw_done(dc); } } @@ -541,21 +695,23 @@ void dcc_push_monitors_config(DisplayChannelClient *dcc) DisplayChannel *dc = DCC_TO_DC(dcc); MonitorsConfig *monitors_config = dc->monitors_config; RedMonitorsConfigItem *mci; + RedChannel *channel; if (monitors_config == NULL) { spice_warning("monitors_config is NULL"); return; } - if (!red_channel_client_test_remote_cap(&dcc->common.base, + if (!red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(dcc), SPICE_DISPLAY_CAP_MONITORS_CONFIG)) { return; } - mci = red_monitors_config_item_new(dcc->common.base.channel, + channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(dcc)); + mci = red_monitors_config_item_new(channel, monitors_config_ref(dc->monitors_config)); - red_channel_client_pipe_add(&dcc->common.base, &mci->pipe_item); - red_channel_client_push(&dcc->common.base); + red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &mci->pipe_item); + red_channel_client_push(RED_CHANNEL_CLIENT(dcc)); } static RedSurfaceDestroyItem *red_surface_destroy_item_new(RedChannel *channel, @@ -576,7 +732,7 @@ RedPipeItem *dcc_gl_scanout_item_new(RedChannelClient *rcc, void *data, int num) spice_return_val_if_fail(item != NULL, NULL); /* FIXME: on !unix peer, start streaming with a video codec */ - if (!reds_stream_is_plain_unix(rcc->stream) || + if (!reds_stream_is_plain_unix(rcc->priv->stream) || !red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_GL_SCANOUT)) { spice_printerr("FIXME: client does not support GL scanout"); red_channel_client_disconnect(rcc); @@ -590,7 +746,7 @@ RedPipeItem *dcc_gl_scanout_item_new(RedChannelClient *rcc, void *data, int num) RedPipeItem *dcc_gl_draw_item_new(RedChannelClient *rcc, void *data, int num) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); const SpiceMsgDisplayGlDraw *draw = data; RedGlDrawItem *item = spice_new(RedGlDrawItem, 1); spice_return_val_if_fail(item != NULL, NULL); @@ -601,7 +757,7 @@ RedPipeItem *dcc_gl_draw_item_new(RedChannelClient *rcc, void *data, int num) return NULL; } - dcc->gl_draw_ongoing = TRUE; + dcc->priv->gl_draw_ongoing = TRUE; item->draw = *draw; red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_GL_DRAW); @@ -622,11 +778,11 @@ void dcc_destroy_surface(DisplayChannelClient *dcc, uint32_t surface_id) channel = RED_CHANNEL(display); if (COMMON_GRAPHICS_CHANNEL(display)->during_target_migrate || - !dcc->surface_client_created[surface_id]) { + !dcc->priv->surface_client_created[surface_id]) { return; } - dcc->surface_client_created[surface_id] = FALSE; + dcc->priv->surface_client_created[surface_id] = FALSE; destroy = red_surface_destroy_item_new(channel, surface_id); red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &destroy->pipe_item); } @@ -657,7 +813,7 @@ static RedGlzDrawable *get_glz_drawable(DisplayChannelClient *dcc, Drawable *dra ring_item_init(&ret->link); ring_item_init(&ret->drawable_link); - ring_add_before(&ret->link, &dcc->glz_drawables); + ring_add_before(&ret->link, &dcc->priv->glz_drawables); ring_add(&drawable->glz_ring, &ret->drawable_link); DCC_TO_DC(dcc)->glz_drawable_count++; return ret; @@ -705,7 +861,7 @@ static int dcc_compress_image_glz(DisplayChannelClient *dcc, stat_start_time_t start_time; stat_start_time_init(&start_time, &display_channel->zlib_glz_stat); spice_assert(bitmap_fmt_is_rgb(src->format)); - GlzData *glz_data = &dcc->glz_data; + GlzData *glz_data = &dcc->priv->glz_data; ZlibData *zlib_data; LzImageType type = bitmap_fmt_to_lz_image_type[src->format]; RedGlzDrawable *glz_drawable; @@ -727,7 +883,7 @@ static int dcc_compress_image_glz(DisplayChannelClient *dcc, glz_data->data.u.lines_data.next = 0; glz_data->data.u.lines_data.reverse = 0; - glz_size = glz_encode(dcc->glz, type, src->x, src->y, + glz_size = glz_encode(dcc->priv->glz, type, src->x, src->y, (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL, 0, src->stride, glz_data->data.bufs_head->buf.bytes, sizeof(glz_data->data.bufs_head->buf), @@ -740,14 +896,14 @@ static int dcc_compress_image_glz(DisplayChannelClient *dcc, goto glz; } stat_start_time_init(&start_time, &display_channel->zlib_glz_stat); - zlib_data = &dcc->zlib_data; + zlib_data = &dcc->priv->zlib_data; encoder_data_init(&zlib_data->data, dcc); zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head; zlib_data->data.u.compressed_data.size_left = glz_size; - zlib_size = zlib_encode(dcc->zlib, dcc->zlib_level, + zlib_size = zlib_encode(dcc->priv->zlib, dcc->priv->zlib_level, glz_size, zlib_data->data.bufs_head->buf.bytes, sizeof(zlib_data->data.bufs_head->buf)); @@ -782,8 +938,8 @@ static int dcc_compress_image_lz(DisplayChannelClient *dcc, SpiceImage *dest, SpiceBitmap *src, compress_send_data_t* o_comp_data) { - LzData *lz_data = &dcc->lz_data; - LzContext *lz = dcc->lz; + LzData *lz_data = &dcc->priv->lz_data; + LzContext *lz = dcc->priv->lz; LzImageType type = bitmap_fmt_to_lz_image_type[src->format]; int size; // size of the compressed data @@ -847,10 +1003,10 @@ static int dcc_compress_image_lz(DisplayChannelClient *dcc, static int dcc_compress_image_jpeg(DisplayChannelClient *dcc, SpiceImage *dest, SpiceBitmap *src, compress_send_data_t* o_comp_data) { - JpegData *jpeg_data = &dcc->jpeg_data; - LzData *lz_data = &dcc->lz_data; - JpegEncoderContext *jpeg = dcc->jpeg; - LzContext *lz = dcc->lz; + JpegData *jpeg_data = &dcc->priv->jpeg_data; + LzData *lz_data = &dcc->priv->lz_data; + JpegEncoderContext *jpeg = dcc->priv->jpeg; + LzContext *lz = dcc->priv->lz; volatile JpegEncoderImageType jpeg_in_type; int jpeg_size = 0; volatile int has_alpha = FALSE; @@ -906,7 +1062,7 @@ static int dcc_compress_image_jpeg(DisplayChannelClient *dcc, SpiceImage *dest, jpeg_data->data.u.lines_data.reverse = 1; stride = -src->stride; } - jpeg_size = jpeg_encode(jpeg, dcc->jpeg_quality, jpeg_in_type, + jpeg_size = jpeg_encode(jpeg, dcc->priv->jpeg_quality, jpeg_in_type, src->x, src->y, NULL, 0, stride, jpeg_data->data.bufs_head->buf.bytes, sizeof(jpeg_data->data.bufs_head->buf)); @@ -975,8 +1131,8 @@ static int dcc_compress_image_jpeg(DisplayChannelClient *dcc, SpiceImage *dest, static int dcc_compress_image_lz4(DisplayChannelClient *dcc, SpiceImage *dest, SpiceBitmap *src, compress_send_data_t* o_comp_data) { - Lz4Data *lz4_data = &dcc->lz4_data; - Lz4EncoderContext *lz4 = dcc->lz4; + Lz4Data *lz4_data = &dcc->priv->lz4_data; + 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); @@ -1025,8 +1181,8 @@ static int dcc_compress_image_lz4(DisplayChannelClient *dcc, SpiceImage *dest, static int dcc_compress_image_quic(DisplayChannelClient *dcc, SpiceImage *dest, SpiceBitmap *src, compress_send_data_t* o_comp_data) { - QuicData *quic_data = &dcc->quic_data; - QuicContext *quic = dcc->quic; + QuicData *quic_data = &dcc->priv->quic_data; + QuicContext *quic = dcc->priv->quic; volatile QuicImageType type; int size, stride; stat_start_time_t start_time; @@ -1191,7 +1347,7 @@ int dcc_compress_image(DisplayChannelClient *dcc, stat_start_time_init(&start_time, &display_channel->off_stat); - image_compression = get_compression_for_bitmap(src, dcc->image_compression, drawable); + image_compression = get_compression_for_bitmap(src, dcc->priv->image_compression, drawable); switch (image_compression) { case SPICE_IMAGE_COMPRESSION_OFF: break; @@ -1204,15 +1360,15 @@ int dcc_compress_image(DisplayChannelClient *dcc, success = dcc_compress_image_quic(dcc, dest, src, o_comp_data); break; case SPICE_IMAGE_COMPRESSION_GLZ: - if ((src->x * src->y) < glz_enc_dictionary_get_size(dcc->glz_dict->dict)) { + if ((src->x * src->y) < glz_enc_dictionary_get_size(dcc->priv->glz_dict->dict)) { int frozen; /* using the global dictionary only if it is not frozen */ - pthread_rwlock_rdlock(&dcc->glz_dict->encode_lock); - frozen = dcc->glz_dict->migrate_freeze; + pthread_rwlock_rdlock(&dcc->priv->glz_dict->encode_lock); + frozen = dcc->priv->glz_dict->migrate_freeze; if (!frozen) { success = dcc_compress_image_glz(dcc, dest, src, drawable, o_comp_data); } - pthread_rwlock_unlock(&dcc->glz_dict->encode_lock); + pthread_rwlock_unlock(&dcc->priv->glz_dict->encode_lock); if (!frozen) { break; } @@ -1220,7 +1376,7 @@ int dcc_compress_image(DisplayChannelClient *dcc, goto lz_compress; #ifdef USE_LZ4 case SPICE_IMAGE_COMPRESSION_LZ4: - if (red_channel_client_test_remote_cap(&dcc->common.base, + if (red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(dcc), SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) { success = dcc_compress_image_lz4(dcc, dest, src, o_comp_data); break; @@ -1271,7 +1427,7 @@ void dcc_palette_cache_reset(DisplayChannelClient *dcc) static void dcc_push_release(DisplayChannelClient *dcc, uint8_t type, uint64_t id, uint64_t* sync_data) { - FreeList *free_list = &dcc->send_data.free_list; + FreeList *free_list = &dcc->priv->send_data.free_list; int i; for (i = 0; i < MAX_CACHE_CLIENTS; i++) { @@ -1296,21 +1452,22 @@ static void dcc_push_release(DisplayChannelClient *dcc, uint8_t type, uint64_t i int dcc_pixmap_cache_unlocked_add(DisplayChannelClient *dcc, uint64_t id, uint32_t size, int lossy) { - PixmapCache *cache = dcc->pixmap_cache; + PixmapCache *cache = dcc->priv->pixmap_cache; NewCacheItem *item; uint64_t serial; int key; + uint32_t client_id = dcc->priv->id; spice_assert(size > 0); item = spice_new(NewCacheItem, 1); serial = red_channel_client_get_message_serial(RED_CHANNEL_CLIENT(dcc)); - if (cache->generation != dcc->pixmap_cache_generation) { - if (!dcc->pending_pixmaps_sync) { + if (cache->generation != dcc->priv->pixmap_cache_generation) { + if (!dcc->priv->pending_pixmaps_sync) { red_channel_client_pipe_add_type( RED_CHANNEL_CLIENT(dcc), RED_PIPE_ITEM_TYPE_PIXMAP_SYNC); - dcc->pending_pixmaps_sync = TRUE; + dcc->priv->pending_pixmaps_sync = TRUE; } free(item); return FALSE; @@ -1322,7 +1479,7 @@ int dcc_pixmap_cache_unlocked_add(DisplayChannelClient *dcc, uint64_t id, NewCacheItem **now; if (!(tail = (NewCacheItem *)ring_get_tail(&cache->lru)) || - tail->sync[dcc->id] == serial) { + tail->sync[client_id] == serial) { cache->available += size; free(item); return FALSE; @@ -1340,7 +1497,7 @@ int dcc_pixmap_cache_unlocked_add(DisplayChannelClient *dcc, uint64_t id, ring_remove(&tail->lru_link); cache->items--; cache->available += tail->size; - cache->sync[dcc->id] = serial; + cache->sync[client_id] = serial; dcc_push_release(dcc, SPICE_RES_TYPE_PIXMAP, tail->id, tail->sync); free(tail); } @@ -1353,30 +1510,31 @@ int dcc_pixmap_cache_unlocked_add(DisplayChannelClient *dcc, uint64_t id, item->size = size; item->lossy = lossy; memset(item->sync, 0, sizeof(item->sync)); - item->sync[dcc->id] = serial; - cache->sync[dcc->id] = serial; + item->sync[client_id] = serial; + cache->sync[client_id] = serial; return TRUE; } static int dcc_handle_init(DisplayChannelClient *dcc, SpiceMsgcDisplayInit *init) { - spice_return_val_if_fail(dcc->expect_init, FALSE); - dcc->expect_init = FALSE; + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dcc)); + spice_return_val_if_fail(dcc->priv->expect_init, FALSE); + dcc->priv->expect_init = FALSE; - spice_return_val_if_fail(!dcc->pixmap_cache, FALSE); - dcc->pixmap_cache = pixmap_cache_get(RED_CHANNEL_CLIENT(dcc)->client, + spice_return_val_if_fail(!dcc->priv->pixmap_cache, FALSE); + dcc->priv->pixmap_cache = pixmap_cache_get(client, init->pixmap_cache_id, init->pixmap_cache_size); - spice_return_val_if_fail(dcc->pixmap_cache, FALSE); + spice_return_val_if_fail(dcc->priv->pixmap_cache, FALSE); - spice_return_val_if_fail(!dcc->glz_dict, FALSE); - ring_init(&dcc->glz_drawables); - ring_init(&dcc->glz_drawables_inst_to_free); - pthread_mutex_init(&dcc->glz_drawables_inst_to_free_lock, NULL); - dcc->glz_dict = get_glz_dictionary_for_dcc(dcc, + spice_return_val_if_fail(!dcc->priv->glz_dict, FALSE); + ring_init(&dcc->priv->glz_drawables); + ring_init(&dcc->priv->glz_drawables_inst_to_free); + pthread_mutex_init(&dcc->priv->glz_drawables_inst_to_free_lock, NULL); + dcc->priv->glz_dict = get_glz_dictionary_for_dcc(dcc, init->glz_dictionary_id, init->glz_dictionary_window_size); - spice_return_val_if_fail(dcc->glz_dict, FALSE); + spice_return_val_if_fail(dcc->priv->glz_dict, FALSE); return TRUE; } @@ -1390,7 +1548,7 @@ static int dcc_handle_stream_report(DisplayChannelClient *dcc, return FALSE; } - agent = &dcc->stream_agents[report->stream_id]; + agent = &dcc->priv->stream_agents[report->stream_id]; if (!agent->mjpeg_encoder) { return TRUE; } @@ -1420,7 +1578,7 @@ static int dcc_handle_preferred_compression(DisplayChannelClient *dcc, case SPICE_IMAGE_COMPRESSION_LZ: case SPICE_IMAGE_COMPRESSION_GLZ: case SPICE_IMAGE_COMPRESSION_OFF: - dcc->image_compression = pc->image_compression; + dcc->priv->image_compression = pc->image_compression; break; default: spice_warning("preferred-compression: unsupported image compression setting"); @@ -1432,13 +1590,13 @@ static int dcc_handle_gl_draw_done(DisplayChannelClient *dcc) { DisplayChannel *display = DCC_TO_DC(dcc); - if (G_UNLIKELY(!dcc->gl_draw_ongoing)) { + if (G_UNLIKELY(!dcc->priv->gl_draw_ongoing)) { g_warning("unexpected DRAW_DONE received\n"); /* close client connection */ return FALSE; } - dcc->gl_draw_ongoing = FALSE; + dcc->priv->gl_draw_ongoing = FALSE; display_channel_gl_draw_done(display); return TRUE; @@ -1446,7 +1604,7 @@ static int dcc_handle_gl_draw_done(DisplayChannelClient *dcc) int dcc_handle_message(RedChannelClient *rcc, uint32_t size, uint16_t type, void *msg) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); switch (type) { case SPICE_MSGC_DISPLAY_INIT: @@ -1466,26 +1624,26 @@ int dcc_handle_message(RedChannelClient *rcc, uint32_t size, uint16_t type, void static int dcc_handle_migrate_glz_dictionary(DisplayChannelClient *dcc, SpiceMigrateDataDisplay *migrate) { - spice_return_val_if_fail(!dcc->glz_dict, FALSE); + spice_return_val_if_fail(!dcc->priv->glz_dict, FALSE); - ring_init(&dcc->glz_drawables); - ring_init(&dcc->glz_drawables_inst_to_free); - pthread_mutex_init(&dcc->glz_drawables_inst_to_free_lock, NULL); - dcc->glz_dict = restore_glz_dictionary_for_dcc(dcc, + ring_init(&dcc->priv->glz_drawables); + ring_init(&dcc->priv->glz_drawables_inst_to_free); + pthread_mutex_init(&dcc->priv->glz_drawables_inst_to_free_lock, NULL); + dcc->priv->glz_dict = restore_glz_dictionary_for_dcc(dcc, migrate->glz_dict_id, &migrate->glz_dict_data); - return dcc->glz_dict != NULL; + return dcc->priv->glz_dict != NULL; } static int restore_surface(DisplayChannelClient *dcc, uint32_t surface_id) { /* we don't process commands till we receive the migration data, thus, * we should have not sent any surface to the client. */ - if (dcc->surface_client_created[surface_id]) { + if (dcc->priv->surface_client_created[surface_id]) { spice_warning("surface %u is already marked as client_created", surface_id); return FALSE; } - dcc->surface_client_created[surface_id] = TRUE; + dcc->priv->surface_client_created[surface_id] = TRUE; return TRUE; } @@ -1523,14 +1681,15 @@ static int restore_surfaces_lossy(DisplayChannelClient *dcc, lossy_rect.top = mig_lossy_rect->top; lossy_rect.right = mig_lossy_rect->right; lossy_rect.bottom = mig_lossy_rect->bottom; - region_init(&dcc->surface_client_lossy_region[surface_id]); - region_add(&dcc->surface_client_lossy_region[surface_id], &lossy_rect); + region_init(&dcc->priv->surface_client_lossy_region[surface_id]); + region_add(&dcc->priv->surface_client_lossy_region[surface_id], &lossy_rect); } return TRUE; } int dcc_handle_migrate_data(DisplayChannelClient *dcc, uint32_t size, void *message) { + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dcc)); DisplayChannel *display = DCC_TO_DC(dcc); int surfaces_restored = FALSE; SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message; @@ -1548,39 +1707,41 @@ int dcc_handle_migrate_data(DisplayChannelClient *dcc, uint32_t size, void *mess * channel client that froze the cache on the src size receives the migrate * data and unfreezes the cache by setting its size > 0 and by triggering * pixmap_cache_reset */ - dcc->pixmap_cache = pixmap_cache_get(RED_CHANNEL_CLIENT(dcc)->client, - migrate_data->pixmap_cache_id, -1); - spice_return_val_if_fail(dcc->pixmap_cache, FALSE); + dcc->priv->pixmap_cache = pixmap_cache_get(client, + migrate_data->pixmap_cache_id, -1); + spice_return_val_if_fail(dcc->priv->pixmap_cache, FALSE); - pthread_mutex_lock(&dcc->pixmap_cache->lock); + pthread_mutex_lock(&dcc->priv->pixmap_cache->lock); for (i = 0; i < MAX_CACHE_CLIENTS; i++) { - dcc->pixmap_cache->sync[i] = MAX(dcc->pixmap_cache->sync[i], + dcc->priv->pixmap_cache->sync[i] = MAX(dcc->priv->pixmap_cache->sync[i], migrate_data->pixmap_cache_clients[i]); } - pthread_mutex_unlock(&dcc->pixmap_cache->lock); + pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock); if (migrate_data->pixmap_cache_freezer) { /* activating the cache. The cache will start to be active after * pixmap_cache_reset is called, when handling RED_PIPE_ITEM_TYPE_PIXMAP_RESET */ - dcc->pixmap_cache->size = migrate_data->pixmap_cache_size; + dcc->priv->pixmap_cache->size = migrate_data->pixmap_cache_size; red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(dcc), RED_PIPE_ITEM_TYPE_PIXMAP_RESET); } if (dcc_handle_migrate_glz_dictionary(dcc, migrate_data)) { - dcc->glz = - glz_encoder_create(dcc->id, dcc->glz_dict->dict, &dcc->glz_data.usr); + uint32_t id = dcc->priv->id; + dcc->priv->glz = + glz_encoder_create(id, dcc->priv->glz_dict->dict, &dcc->priv->glz_data.usr); } else { spice_critical("restoring global lz dictionary failed"); } - dcc->common.is_low_bandwidth = migrate_data->low_bandwidth_setting; + common_graphics_channel_client_set_low_bandwidth(COMMON_GRAPHICS_CHANNEL_CLIENT(dcc), + migrate_data->low_bandwidth_setting); if (migrate_data->low_bandwidth_setting) { red_channel_client_ack_set_client_window(RED_CHANNEL_CLIENT(dcc), WIDE_CLIENT_ACK_WINDOW); - if (dcc->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) { + if (dcc->priv->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) { display->enable_jpeg = TRUE; } - if (dcc->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) { + if (dcc->priv->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) { display->enable_zlib_glz_wrap = TRUE; } } @@ -1686,46 +1847,46 @@ void dcc_release_item(DisplayChannelClient *dcc, RedPipeItem *item, int item_pus StreamAgent* dcc_get_stream_agent(DisplayChannelClient *dcc, int stream_id) { - return &dcc->stream_agents[stream_id]; + return &dcc->priv->stream_agents[stream_id]; } GlzSharedDictionary* dcc_get_glz_dictionary(DisplayChannelClient *dcc) { - return dcc->glz_dict; + return dcc->priv->glz_dict; } spice_wan_compression_t dcc_get_jpeg_state(DisplayChannelClient *dcc) { - return dcc->jpeg_state; + return dcc->priv->jpeg_state; } spice_wan_compression_t dcc_get_zlib_glz_state(DisplayChannelClient *dcc) { - return dcc->zlib_glz_state; + return dcc->priv->zlib_glz_state; } gboolean dcc_use_mjpeg_encoder_rate_control(DisplayChannelClient *dcc) { - return dcc->use_mjpeg_encoder_rate_control; + return dcc->priv->use_mjpeg_encoder_rate_control; } uint32_t dcc_get_max_stream_latency(DisplayChannelClient *dcc) { - return dcc->streams_max_latency; + return dcc->priv->streams_max_latency; } void dcc_set_max_stream_latency(DisplayChannelClient *dcc, uint32_t latency) { - dcc->streams_max_latency = latency; + dcc->priv->streams_max_latency = latency; } uint64_t dcc_get_max_stream_bit_rate(DisplayChannelClient *dcc) { - return dcc->streams_max_bit_rate; + return dcc->priv->streams_max_bit_rate; } void dcc_set_max_stream_bit_rate(DisplayChannelClient *dcc, uint64_t rate) { - dcc->streams_max_bit_rate = rate; + dcc->priv->streams_max_bit_rate = rate; } diff --git a/server/dcc.h b/server/dcc.h index 76ac401..9567afd 100644 --- a/server/dcc.h +++ b/server/dcc.h @@ -18,11 +18,40 @@ #ifndef DCC_H_ # define DCC_H_ +#include <glib-object.h> #include "image-cache.h" #include "pixmap-cache.h" #include "red-worker.h" #include "display-limits.h" +G_BEGIN_DECLS + +#define TYPE_DISPLAY_CHANNEL_CLIENT display_channel_client_get_type() + +#define DISPLAY_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DISPLAY_CHANNEL_CLIENT, DisplayChannelClient)) +#define DISPLAY_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DISPLAY_CHANNEL_CLIENT, DisplayChannelClientClass)) +#define IS_DISPLAY_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DISPLAY_CHANNEL_CLIENT)) +#define IS_DISPLAY_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DISPLAY_CHANNEL_CLIENT)) +#define DISPLAY_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DISPLAY_CHANNEL_CLIENT, DisplayChannelClientClass)) + +typedef struct DisplayChannelClient DisplayChannelClient; +typedef struct DisplayChannelClientClass DisplayChannelClientClass; +typedef struct DisplayChannelClientPrivate DisplayChannelClientPrivate; + +struct DisplayChannelClient +{ + CommonGraphicsChannelClient parent; + + DisplayChannelClientPrivate *priv; +}; + +struct DisplayChannelClientClass +{ + CommonGraphicsChannelClientClass parent_class; +}; + +GType display_channel_client_get_type(void) G_GNUC_CONST; + #define PALETTE_CACHE_HASH_SHIFT 8 #define PALETTE_CACHE_HASH_SIZE (1 << PALETTE_CACHE_HASH_SHIFT) #define PALETTE_CACHE_HASH_MASK (PALETTE_CACHE_HASH_SIZE - 1) @@ -58,11 +87,8 @@ typedef struct FreeList { WaitForChannels wait; } FreeList; -typedef struct DisplayChannelClient DisplayChannelClient; - #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)) -#define RCC_TO_DCC(rcc) ((DisplayChannelClient*)rcc) typedef struct RedSurfaceCreateItem { SpiceMsgSurfaceCreate surface_create; @@ -184,4 +210,6 @@ void dcc_set_max_stream_latency(DisplayChannelClient *dcc, uint32_t latency); uint64_t dcc_get_max_stream_bit_rate(DisplayChannelClient *dcc); void dcc_set_max_stream_bit_rate(DisplayChannelClient *dcc, uint64_t rate); +G_END_DECLS + #endif /* DCC_H_ */ diff --git a/server/display-channel.c b/server/display-channel.c index e6112b4..352058a 100644 --- a/server/display-channel.c +++ b/server/display-channel.c @@ -1202,7 +1202,7 @@ int display_channel_wait_for_migrate_data(DisplayChannel *display) rcc = channel->clients->data; - red_channel_client_ref(rcc); + g_object_ref(rcc); for (;;) { red_channel_client_receive(rcc); if (!red_channel_client_is_connected(rcc)) { @@ -1220,7 +1220,7 @@ int display_channel_wait_for_migrate_data(DisplayChannel *display) } usleep(DISPLAY_CLIENT_RETRY_INTERVAL); } - red_channel_client_unref(rcc); + g_object_unref(rcc); return ret; } @@ -1944,7 +1944,7 @@ static void on_disconnect(RedChannelClient *rcc) spice_info(NULL); spice_return_if_fail(rcc != NULL); - dcc = RCC_TO_DCC(rcc); + dcc = DISPLAY_CHANNEL_CLIENT(rcc); display = DCC_TO_DC(dcc); dcc_stop(dcc); // TODO: start/stop -> connect/disconnect? @@ -1958,7 +1958,7 @@ static void on_disconnect(RedChannelClient *rcc) static void send_item(RedChannelClient *rcc, RedPipeItem *item) { - dcc_send_item(RCC_TO_DCC(rcc), item); + dcc_send_item(DISPLAY_CHANNEL_CLIENT(rcc), item); } static void hold_item(RedChannelClient *rcc, RedPipeItem *item) @@ -1979,7 +1979,7 @@ static void hold_item(RedChannelClient *rcc, RedPipeItem *item) static void release_item(RedChannelClient *rcc, RedPipeItem *item, int item_pushed) { - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); spice_return_if_fail(item != NULL); dcc_release_item(dcc, item, item_pushed); @@ -2004,7 +2004,7 @@ static uint64_t handle_migrate_data_get_serial(RedChannelClient *rcc, uint32_t s static int handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *message) { - return dcc_handle_migrate_data(RCC_TO_DCC(rcc), size, message); + return dcc_handle_migrate_data(DISPLAY_CHANNEL_CLIENT(rcc), size, message); } static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces, uint32_t surface_id) @@ -2127,7 +2127,7 @@ void display_channel_process_surface_cmd(DisplayChannel *display, RedSurfaceCmd void display_channel_update_compression(DisplayChannel *display, DisplayChannelClient *dcc) { - gboolean is_low_bw = common_graphics_channel_client_is_low_bandwidth((CommonGraphicsChannelClient*)dcc); + 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; } else { diff --git a/server/dummy-channel-client.c b/server/dummy-channel-client.c new file mode 100644 index 0000000..a0354fd --- /dev/null +++ b/server/dummy-channel-client.c @@ -0,0 +1,157 @@ +/* + 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/>. +*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "dummy-channel-client.h" +#include "red-channel.h" + +static void dummy_channel_client_initable_interface_init(GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE(DummyChannelClient, dummy_channel_client, RED_TYPE_CHANNEL_CLIENT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, + dummy_channel_client_initable_interface_init)) + +#define DUMMY_CHANNEL_CLIENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_DUMMY_CHANNEL_CLIENT, DummyChannelClientPrivate)) + +struct DummyChannelClientPrivate +{ + gboolean connected; +}; + +static int dummy_channel_client_pre_create_validate(RedChannel *channel, RedClient *client) +{ + if (red_client_get_channel(client, channel->type, channel->id)) { + spice_printerr("Error client %p: duplicate channel type %d id %d", + client, channel->type, channel->id); + return FALSE; + } + return TRUE; +} + +static gboolean dummy_channel_client_initable_init(GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GError *local_error = NULL; + DummyChannelClient *self = DUMMY_CHANNEL_CLIENT(initable); + RedChannelClient *rcc = RED_CHANNEL_CLIENT(self); + RedClient *client = red_channel_client_get_client(rcc); + RedChannel *channel = red_channel_client_get_channel(rcc); + pthread_mutex_lock(&client->lock); + if (!dummy_channel_client_pre_create_validate(channel, + client)) { + g_set_error(&local_error, + SPICE_SERVER_ERROR, + SPICE_SERVER_ERROR_FAILED, + "Client %p: duplicate channel type %d id %d", + client, channel->type, channel->id); + goto cleanup; + } + + rcc->incoming.header.data = rcc->incoming.header_buf; + rcc->incoming.serial = 1; + + red_channel_add_client(channel, rcc); + red_client_add_channel(client, rcc); + +cleanup: + pthread_mutex_unlock(&client->lock); + if (local_error) { + g_warning("Failed to create channel client: %s", local_error->message); + g_propagate_error(error, local_error); + } + return local_error == NULL; +} + +static void dummy_channel_client_initable_interface_init(GInitableIface *iface) +{ + iface->init = dummy_channel_client_initable_init; +} + +gboolean dummy_channel_client_is_connected(RedChannelClient *rcc) +{ + return DUMMY_CHANNEL_CLIENT(rcc)->priv->connected; +} + +void dummy_channel_client_disconnect(RedChannelClient *rcc) +{ + DummyChannelClient *self = DUMMY_CHANNEL_CLIENT(rcc); + RedChannel *channel = red_channel_client_get_channel(rcc); + GList *link; + + if (channel && (link = g_list_find(channel->clients, rcc))) { + spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel, + channel->type, channel->id); + red_channel_remove_client(channel, link->data); + } + self->priv->connected = FALSE; +} + +static void +dummy_channel_client_class_init(DummyChannelClientClass *klass) +{ + RedChannelClientClass *cc_class = RED_CHANNEL_CLIENT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(DummyChannelClientPrivate)); + + cc_class->is_connected = dummy_channel_client_is_connected; + cc_class->disconnect = dummy_channel_client_disconnect; +} + +static void +dummy_channel_client_init(DummyChannelClient *self) +{ + self->priv = DUMMY_CHANNEL_CLIENT_PRIVATE(self); + + self->priv->connected = TRUE; +} + +RedChannelClient* dummy_channel_client_create(RedChannel *channel, + RedClient *client, + int num_common_caps, + uint32_t *common_caps, + int num_caps, uint32_t *caps) +{ + RedChannelClient *rcc; + GArray *common_caps_array = NULL, *caps_array = NULL; + + if (common_caps) { + common_caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*common_caps), num_common_caps); + g_array_append_vals(common_caps_array, common_caps, num_common_caps); + } + if (caps) { + caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*caps), num_caps); + g_array_append_vals(caps_array, caps, num_caps); + } + + rcc = g_initable_new(TYPE_DUMMY_CHANNEL_CLIENT, + NULL, NULL, + "channel", channel, + "client", client, + "caps", caps_array, + "common-caps", common_caps_array, + NULL); + + if (caps_array) + g_array_unref(caps_array); + if (common_caps_array) + g_array_unref(common_caps_array); + + return rcc; +} diff --git a/server/dummy-channel-client.h b/server/dummy-channel-client.h new file mode 100644 index 0000000..f7a1d6e --- /dev/null +++ b/server/dummy-channel-client.h @@ -0,0 +1,58 @@ +/* + 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_CLIENT_H__ +#define __DUMMY_CHANNEL_CLIENT_H__ + +#include <glib-object.h> +#include "red-channel-client.h" + +G_BEGIN_DECLS + +#define TYPE_DUMMY_CHANNEL_CLIENT dummy_channel_client_get_type() + +#define DUMMY_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_DUMMY_CHANNEL_CLIENT, DummyChannelClient)) +#define DUMMY_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_DUMMY_CHANNEL_CLIENT, DummyChannelClientClass)) +#define IS_DUMMY_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_DUMMY_CHANNEL_CLIENT)) +#define IS_DUMMY_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_DUMMY_CHANNEL_CLIENT)) +#define DUMMY_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_DUMMY_CHANNEL_CLIENT, DummyChannelClientClass)) + +typedef struct DummyChannelClient DummyChannelClient; +typedef struct DummyChannelClientClass DummyChannelClientClass; +typedef struct DummyChannelClientPrivate DummyChannelClientPrivate; + +struct DummyChannelClient +{ + RedChannelClient parent; + + DummyChannelClientPrivate *priv; +}; + +struct DummyChannelClientClass +{ + RedChannelClientClass parent_class; +}; + +GType dummy_channel_client_get_type(void) G_GNUC_CONST; + +RedChannelClient *dummy_channel_client_create(RedChannel *channel, + RedClient *client, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps); + +G_END_DECLS + +#endif /* __DUMMY_CHANNEL_CLIENT_H__ */ diff --git a/server/inputs-channel-client.c b/server/inputs-channel-client.c index e02fbc9..dc2b9ee 100644 --- a/server/inputs-channel-client.c +++ b/server/inputs-channel-client.c @@ -19,15 +19,31 @@ #endif #include "inputs-channel-client.h" -#include "inputs-channel.h" #include "migration-protocol.h" #include "red-channel-client-private.h" -struct InputsChannelClient { - RedChannelClient base; +G_DEFINE_TYPE(InputsChannelClient, inputs_channel_client, RED_TYPE_CHANNEL_CLIENT) + +#define INPUTS_CHANNEL_CLIENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_INPUTS_CHANNEL_CLIENT, InputsChannelClientPrivate)) + +struct InputsChannelClientPrivate +{ uint16_t motion_count; }; +static void +inputs_channel_client_class_init(InputsChannelClientClass *klass) +{ + g_type_class_add_private(klass, sizeof(InputsChannelClientPrivate)); +} + +static void +inputs_channel_client_init(InputsChannelClient *self) +{ + self->priv = INPUTS_CHANNEL_CLIENT_PRIVATE(self); + self->priv->motion_count = 0; +} + InputsChannelClient* inputs_channel_client_create(RedChannel *channel, RedClient *client, RedsStream *stream, @@ -37,57 +53,71 @@ InputsChannelClient* inputs_channel_client_create(RedChannel *channel, int num_caps, uint32_t *caps) { - InputsChannelClient* icc = - (InputsChannelClient*)red_channel_client_create(sizeof(InputsChannelClient), - channel, client, - stream, - monitor_latency, - num_common_caps, - common_caps, num_caps, - caps); - if (icc) - icc->motion_count = 0; - return icc; + InputsChannelClient *rcc; + GArray *common_caps_array = NULL, *caps_array = NULL; + + if (common_caps) { + common_caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*common_caps), num_common_caps); + g_array_append_vals(common_caps_array, common_caps, num_common_caps); + } + if (caps) { + caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*caps), num_caps); + g_array_append_vals(caps_array, caps, num_caps); + } + rcc = g_initable_new(TYPE_INPUTS_CHANNEL_CLIENT, + NULL, NULL, + "channel", channel, + "client", client, + "stream", stream, + "monitor-latency", monitor_latency, + "caps", caps_array, + "common-caps", common_caps_array, + NULL); + + if (caps_array) + g_array_unref(caps_array); + if (common_caps_array) + g_array_unref(common_caps_array); + + return rcc; + } void inputs_channel_client_send_migrate_data(RedChannelClient *rcc, SpiceMarshaller *m, RedPipeItem *item) { - InputsChannelClient *icc = SPICE_CONTAINEROF(rcc, InputsChannelClient, base); - InputsChannel *inputs = (InputsChannel*)rcc->channel; + InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc); + InputsChannel *inputs = (InputsChannel*)red_channel_client_get_channel(rcc); inputs_channel_set_src_during_migrate(inputs, FALSE); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item); spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_MAGIC); spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_VERSION); - spice_marshaller_add_uint16(m, icc->motion_count); + spice_marshaller_add_uint16(m, icc->priv->motion_count); } void inputs_channel_client_handle_migrate_data(InputsChannelClient *icc, uint16_t motion_count) { - icc->motion_count = motion_count; + icc->priv->motion_count = motion_count; - for (; icc->motion_count >= SPICE_INPUT_MOTION_ACK_BUNCH; - icc->motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH) { - red_channel_client_pipe_add_type(&icc->base, RED_PIPE_ITEM_MOUSE_MOTION_ACK); + for (; icc->priv->motion_count >= SPICE_INPUT_MOTION_ACK_BUNCH; + icc->priv->motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH) { + red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(icc), + RED_PIPE_ITEM_MOUSE_MOTION_ACK); } } void inputs_channel_client_on_mouse_motion(InputsChannelClient *icc) { - InputsChannel *inputs_channel = (InputsChannel *)icc->base.channel; + InputsChannel *inputs_channel = (InputsChannel *)red_channel_client_get_channel(RED_CHANNEL_CLIENT(icc)); - if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 && + if (++icc->priv->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 && !inputs_channel_is_src_during_migrate(inputs_channel)) { - red_channel_client_pipe_add_type(&icc->base, RED_PIPE_ITEM_MOUSE_MOTION_ACK); - icc->motion_count = 0; + red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(icc), + RED_PIPE_ITEM_MOUSE_MOTION_ACK); + icc->priv->motion_count = 0; } } - -RedChannelClient* inputs_channel_client_get_base(InputsChannelClient* icc) -{ - return &icc->base; -} diff --git a/server/inputs-channel-client.h b/server/inputs-channel-client.h index e655d9f..c303660 100644 --- a/server/inputs-channel-client.h +++ b/server/inputs-channel-client.h @@ -13,14 +13,42 @@ 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 _INPUTS_CHANNEL_CLIENT_H_ #define _INPUTS_CHANNEL_CLIENT_H_ -#include "red-channel.h" +#include <glib-object.h> +#include "red-channel-client.h" +#include "inputs-channel.h" + +G_BEGIN_DECLS + +#define TYPE_INPUTS_CHANNEL_CLIENT inputs_channel_client_get_type() + +#define INPUTS_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_INPUTS_CHANNEL_CLIENT, InputsChannelClient)) +#define INPUTS_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_INPUTS_CHANNEL_CLIENT, InputsChannelClientClass)) +#define IS_INPUTS_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_INPUTS_CHANNEL_CLIENT)) +#define IS_INPUTS_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_INPUTS_CHANNEL_CLIENT)) +#define INPUTS_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_INPUTS_CHANNEL_CLIENT, InputsChannelClientClass)) typedef struct InputsChannelClient InputsChannelClient; +typedef struct InputsChannelClientClass InputsChannelClientClass; +typedef struct InputsChannelClientPrivate InputsChannelClientPrivate; + +struct InputsChannelClient +{ + RedChannelClient parent; + + InputsChannelClientPrivate *priv; +}; + +struct InputsChannelClientClass +{ + RedChannelClientClass parent_class; +}; + +GType inputs_channel_client_get_type(void) G_GNUC_CONST; InputsChannelClient* inputs_channel_client_create(RedChannel *channel, RedClient *client, @@ -31,14 +59,13 @@ InputsChannelClient* inputs_channel_client_create(RedChannel *channel, int num_caps, uint32_t *caps); -void inputs_channel_client_send_migrate_data(RedChannelClient *rcc, - SpiceMarshaller *m, - RedPipeItem *item); -void inputs_channel_client_handle_migrate_data(InputsChannelClient *icc, - uint16_t motion_count); -void inputs_channel_client_on_mouse_motion(InputsChannelClient *icc); +uint16_t inputs_channel_client_get_motion_count(InputsChannelClient* self); +/* only for migration */ +void inputs_channel_client_set_motion_count(InputsChannelClient* self, uint16_t count); +void inputs_channel_client_on_mouse_motion(InputsChannelClient* self); +void inputs_channel_client_send_migrate_data(RedChannelClient *rcc, SpiceMarshaller *m, RedPipeItem *item); +void inputs_channel_client_handle_migrate_data(InputsChannelClient *icc, uint16_t motion_count); -/* FIXME: temporary until GObjectification */ -RedChannelClient* inputs_channel_client_get_base(InputsChannelClient* icc); +G_END_DECLS #endif /* _INPUTS_CHANNEL_CLIENT_H_ */ diff --git a/server/inputs-channel.c b/server/inputs-channel.c index 237905e..c0fdc98 100644 --- a/server/inputs-channel.c +++ b/server/inputs-channel.c @@ -277,7 +277,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui void *message) { InputsChannel *inputs_channel = (InputsChannel *)red_channel_client_get_channel(rcc); - InputsChannelClient *icc = (InputsChannelClient *)rcc; + InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc); uint32_t i; RedsState *reds = red_channel_get_server(&inputs_channel->base); @@ -514,7 +514,7 @@ static void inputs_connect(RedChannel *channel, RedClient *client, if (!icc) { return; } - inputs_pipe_add_init(inputs_channel_client_get_base(icc)); + inputs_pipe_add_init(RED_CHANNEL_CLIENT(icc)); } static void inputs_migrate(RedChannelClient *rcc) @@ -555,7 +555,7 @@ static int inputs_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *message) { - InputsChannelClient *icc = (InputsChannelClient*)rcc; + InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc); InputsChannel *inputs = (InputsChannel*)red_channel_client_get_channel(rcc); SpiceMigrateDataHeader *header; SpiceMigrateDataInputs *mig_data; diff --git a/server/inputs-channel.h b/server/inputs-channel.h index ce25ac7..5317578 100644 --- a/server/inputs-channel.h +++ b/server/inputs-channel.h @@ -24,6 +24,8 @@ #include <stdint.h> #include <spice/vd_agent.h> +#include "red-channel.h" + typedef struct InputsChannel InputsChannel; InputsChannel* inputs_channel_new(RedsState *reds); diff --git a/server/main-channel-client.c b/server/main-channel-client.c index 077bcef..82cb5c3 100644 --- a/server/main-channel-client.c +++ b/server/main-channel-client.c @@ -19,6 +19,7 @@ #endif #include <inttypes.h> + #include "main-channel-client.h" #include "main-channel.h" #include "red-channel-client-private.h" @@ -38,8 +39,11 @@ enum NetTestStage { #define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30) #define PING_INTERVAL (MSEC_PER_SEC * 10) -struct MainChannelClient { - RedChannelClient base; +G_DEFINE_TYPE(MainChannelClient, main_channel_client, RED_TYPE_CHANNEL_CLIENT) + +#define MAIN_CHANNEL_CLIENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_MAIN_CHANNEL_CLIENT, MainChannelClientPrivate)) + +struct MainChannelClientPrivate { uint32_t connection_id; uint32_t ping_id; uint32_t net_test_id; @@ -58,6 +62,102 @@ struct MainChannelClient { int seamless_mig_dst; }; +enum { + PROP0, + PROP_CONNECTION_ID +}; + +static void main_channel_client_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + MainChannelClient *self = MAIN_CHANNEL_CLIENT(object); + + switch (property_id) + { + case PROP_CONNECTION_ID: + g_value_set_uint(value, self->priv->connection_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void main_channel_client_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + MainChannelClient *self = MAIN_CHANNEL_CLIENT(object); + + switch (property_id) + { + case PROP_CONNECTION_ID: + self->priv->connection_id = g_value_get_uint(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +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) +{ + G_OBJECT_CLASS(main_channel_client_parent_class)->constructed(object); +#ifdef RED_STATISTICS + MainChannelClient *self = MAIN_CHANNEL_CLIENT(object); + RedsState *reds = red_channel_get_server(red_channel_client_get_channel(RED_CHANNEL_CLIENT(object))); + + if (!(self->priv->ping_timer = reds_get_core_interface(reds)->timer_add(reds_get_core_interface(reds), + ping_timer_cb, self))) { + spice_error("ping timer create failed"); + } + self->priv->ping_interval = PING_INTERVAL; +#endif +} + +static void main_channel_client_class_init(MainChannelClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(MainChannelClientPrivate)); + + 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, + PROP_CONNECTION_ID, + g_param_spec_uint("connection-id", + "Connection ID", + "Connection ID", + 0, + G_MAXUINT, + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +static void main_channel_client_init(MainChannelClient *self) +{ + self->priv = MAIN_CHANNEL_CLIENT_PRIVATE(self); + self->priv->bitrate_per_sec = ~0; +} + RedPipeItem *main_notify_item_new(void *data, int num) { RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem)); @@ -70,18 +170,19 @@ RedPipeItem *main_notify_item_new(void *data, int num) void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate) { - if (!mcc || mcc->net_test_id) { + if (!mcc || mcc->priv->net_test_id) { return; } if (test_rate) { if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES) && main_channel_client_push_ping(mcc, 0) && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) { - mcc->net_test_id = mcc->ping_id - 2; - mcc->net_test_stage = NET_TEST_STAGE_WARMUP; + mcc->priv->net_test_id = mcc->priv->ping_id - 2; + mcc->priv->net_test_stage = NET_TEST_STAGE_WARMUP; } } else { - red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT); + red_channel_client_start_connectivity_monitoring(RED_CHANNEL_CLIENT(mcc), + CLIENT_CONNECTIVITY_TIMEOUT); } } @@ -102,7 +203,7 @@ int main_channel_client_push_ping(MainChannelClient *mcc, int size) return FALSE; } item = red_ping_item_new(size); - red_channel_client_pipe_add_push(&mcc->base, item); + red_channel_client_pipe_add_push(RED_CHANNEL_CLIENT(mcc), item); return TRUE; } @@ -120,7 +221,7 @@ void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_ { RedPipeItem *item = main_agent_tokens_item_new(num_tokens); - red_channel_client_pipe_add_push(&mcc->base, item); + red_channel_client_pipe_add_push(RED_CHANNEL_CLIENT(mcc), item); } static RedPipeItem *main_agent_data_item_new(uint8_t* data, size_t len, @@ -143,7 +244,7 @@ void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, RedPipeItem *item; item = main_agent_data_item_new(data, len, free_data, opaque); - red_channel_client_pipe_add_push(&mcc->base, item); + red_channel_client_pipe_add_push(RED_CHANNEL_CLIENT(mcc), item); } static RedPipeItem *main_init_item_new(int connection_id, @@ -174,10 +275,10 @@ void main_channel_client_push_init(MainChannelClient *mcc, { RedPipeItem *item; - item = main_init_item_new(mcc->connection_id, display_channels_hint, + item = main_init_item_new(mcc->priv->connection_id, display_channels_hint, current_mouse_mode, is_client_mouse_allowed, multi_media_time, ram_hint); - red_channel_client_pipe_add_push(&mcc->base, item); + red_channel_client_pipe_add_push(RED_CHANNEL_CLIENT(mcc), item); } static RedPipeItem *main_name_item_new(const char *name) @@ -195,12 +296,12 @@ void main_channel_client_push_name(MainChannelClient *mcc, const char *name) { RedPipeItem *item; - if (!red_channel_client_test_remote_cap(&mcc->base, + if (!red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(mcc), SPICE_MAIN_CAP_NAME_AND_UUID)) return; item = main_name_item_new(name); - red_channel_client_pipe_add_push(&mcc->base, item); + red_channel_client_pipe_add_push(RED_CHANNEL_CLIENT(mcc), item); } static RedPipeItem *main_uuid_item_new(const uint8_t uuid[16]) @@ -217,39 +318,42 @@ void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16 { RedPipeItem *item; - if (!red_channel_client_test_remote_cap(&mcc->base, + if (!red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(mcc), SPICE_MAIN_CAP_NAME_AND_UUID)) return; item = main_uuid_item_new(uuid); - red_channel_client_pipe_add_push(&mcc->base, item); + red_channel_client_pipe_add_push(RED_CHANNEL_CLIENT(mcc), item); } void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg) { RedPipeItem *item = main_notify_item_new((void *)msg, 1); - red_channel_client_pipe_add_push(&mcc->base, item); + red_channel_client_pipe_add_push(RED_CHANNEL_CLIENT(mcc), item); } void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, int success, int seamless) { - spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless); - if (mcc->mig_wait_connect) { - MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base); + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(mcc)); + RedChannel *channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(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); - mcc->mig_wait_connect = FALSE; - mcc->mig_connect_ok = success; + 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(mcc->base.channel->reds, seamless && success); + reds_on_main_migrate_connected(channel->reds, seamless && success); } } else { if (success) { - spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client); - red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL); + spice_printerr("client %p MIGRATE_CANCEL", client); + red_channel_client_pipe_add_empty_msg(RED_CHANNEL_CLIENT(mcc), SPICE_MSG_MAIN_MIGRATE_CANCEL); } } } @@ -257,12 +361,13 @@ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc, uint32_t src_version) { - if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc, src_version)) { - mcc->seamless_mig_dst = TRUE; - red_channel_client_pipe_add_empty_msg(&mcc->base, + RedChannel *channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc)); + if (reds_on_migrate_dst_set_seamless(channel->reds, 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); } else { - red_channel_client_pipe_add_empty_msg(&mcc->base, + red_channel_client_pipe_add_empty_msg(RED_CHANNEL_CLIENT(mcc), SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK); } } @@ -273,48 +378,48 @@ void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing *ping, roundtrip = g_get_monotonic_time() - ping->timestamp; - if (ping->id == mcc->net_test_id) { - switch (mcc->net_test_stage) { + if (ping->id == mcc->priv->net_test_id) { + switch (mcc->priv->net_test_stage) { case NET_TEST_STAGE_WARMUP: - mcc->net_test_id++; - mcc->net_test_stage = NET_TEST_STAGE_LATENCY; - mcc->latency = roundtrip; + mcc->priv->net_test_id++; + mcc->priv->net_test_stage = NET_TEST_STAGE_LATENCY; + mcc->priv->latency = roundtrip; break; case NET_TEST_STAGE_LATENCY: - mcc->net_test_id++; - mcc->net_test_stage = NET_TEST_STAGE_RATE; - mcc->latency = MIN(mcc->latency, roundtrip); + mcc->priv->net_test_id++; + mcc->priv->net_test_stage = NET_TEST_STAGE_RATE; + mcc->priv->latency = MIN(mcc->priv->latency, roundtrip); break; case NET_TEST_STAGE_RATE: - mcc->net_test_id = 0; - if (roundtrip <= mcc->latency) { + mcc->priv->net_test_id = 0; + if (roundtrip <= mcc->priv->latency) { // probably high load on client or server result with incorrect values spice_printerr("net test: invalid values, latency %" PRIu64 " roundtrip %" PRIu64 ". assuming high" - "bandwidth", mcc->latency, roundtrip); - mcc->latency = 0; - mcc->net_test_stage = NET_TEST_STAGE_INVALID; - red_channel_client_start_connectivity_monitoring(&mcc->base, + "bandwidth", mcc->priv->latency, roundtrip); + mcc->priv->latency = 0; + mcc->priv->net_test_stage = NET_TEST_STAGE_INVALID; + red_channel_client_start_connectivity_monitoring(RED_CHANNEL_CLIENT(mcc), CLIENT_CONNECTIVITY_TIMEOUT); break; } - mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000 - / (roundtrip - mcc->latency); - mcc->net_test_stage = NET_TEST_STAGE_COMPLETE; + mcc->priv->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000 + / (roundtrip - mcc->priv->latency); + mcc->priv->net_test_stage = NET_TEST_STAGE_COMPLETE; spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s", - (double)mcc->latency / 1000, - mcc->bitrate_per_sec, - (double)mcc->bitrate_per_sec / 1024 / 1024, + (double)mcc->priv->latency / 1000, + mcc->priv->bitrate_per_sec, + (double)mcc->priv->bitrate_per_sec / 1024 / 1024, main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : ""); - red_channel_client_start_connectivity_monitoring(&mcc->base, + red_channel_client_start_connectivity_monitoring(RED_CHANNEL_CLIENT(mcc), CLIENT_CONNECTIVITY_TIMEOUT); break; default: spice_printerr("invalid net test stage, ping id %d test id %d stage %d", ping->id, - mcc->net_test_id, - mcc->net_test_stage); - mcc->net_test_stage = NET_TEST_STAGE_INVALID; + mcc->priv->net_test_id, + mcc->priv->net_test_stage); + mcc->priv->net_test_stage = NET_TEST_STAGE_INVALID; } return; } else { @@ -324,101 +429,108 @@ 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(rcc->channel->reds, roundtrip); + stat_update_value(red_channel_client_get_channel(rcc)->reds, roundtrip); #endif } gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc) { - return mcc->seamless_mig_dst; + return mcc->priv->seamless_mig_dst; } void main_channel_client_handle_migrate_end(MainChannelClient *mcc) { - if (!red_client_during_migrate_at_target(mcc->base.client)) { + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(mcc)); + if (!red_client_during_migrate_at_target(client)) { spice_printerr("unexpected SPICE_MSGC_MIGRATE_END"); return; } - if (!red_channel_client_test_remote_cap(&mcc->base, + if (!red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(mcc), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) { spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, " "client does not support semi-seamless migration"); return; } - red_client_semi_seamless_migrate_complete(mcc->base.client); + red_client_semi_seamless_migrate_complete(client); } void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc) { - if (mcc->mig_wait_connect) { - spice_printerr("client %p cancel wait connect", mcc->base.client); - mcc->mig_wait_connect = FALSE; - mcc->mig_connect_ok = FALSE; + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(mcc)); + if (mcc->priv->mig_wait_connect) { + spice_printerr("client %p cancel wait connect", client); + mcc->priv->mig_wait_connect = FALSE; + mcc->priv->mig_connect_ok = FALSE; } - mcc->mig_wait_prev_complete = FALSE; + mcc->priv->mig_wait_prev_complete = FALSE; } void main_channel_client_migrate_dst_complete(MainChannelClient *mcc) { - if (mcc->mig_wait_prev_complete) { - if (mcc->mig_wait_prev_try_seamless) { - spice_assert(mcc->base.channel->clients_num == 1); - red_channel_client_pipe_add_type(&mcc->base, + 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); + red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(mcc), RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS); } else { - red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN); + red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(mcc), + RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN); } - mcc->mig_wait_connect = TRUE; - mcc->mig_wait_prev_complete = FALSE; + mcc->priv->mig_wait_connect = TRUE; + mcc->priv->mig_wait_prev_complete = FALSE; } } gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc, gboolean success) { + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(mcc)); gboolean ret = FALSE; - int semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base, + int semi_seamless_support = red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(mcc), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); - if (semi_seamless_support && mcc->mig_connect_ok) { + if (semi_seamless_support && mcc->priv->mig_connect_ok) { if (success) { - spice_printerr("client %p MIGRATE_END", mcc->base.client); - red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END); + spice_printerr("client %p MIGRATE_END", client); + red_channel_client_pipe_add_empty_msg(RED_CHANNEL_CLIENT(mcc), SPICE_MSG_MAIN_MIGRATE_END); ret = TRUE; } else { - spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client); - red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL); + spice_printerr("client %p MIGRATE_CANCEL", client); + red_channel_client_pipe_add_empty_msg(RED_CHANNEL_CLIENT(mcc), SPICE_MSG_MAIN_MIGRATE_CANCEL); } } else { if (success) { - spice_printerr("client %p SWITCH_HOST", mcc->base.client); - red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); + spice_printerr("client %p SWITCH_HOST", client); + red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(mcc), + RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); } } - mcc->mig_connect_ok = FALSE; - mcc->mig_wait_connect = FALSE; + mcc->priv->mig_connect_ok = FALSE; + mcc->priv->mig_wait_connect = FALSE; return ret; } uint32_t main_channel_client_get_link_id(MainChannelClient *mcc) { - return mcc->connection_id; + return mcc->priv->connection_id; } #ifdef RED_STATISTICS static void do_ping_client(MainChannelClient *mcc, const char *opt, int has_interval, int interval) { + RedChannel *channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc)); spice_printerr(""); if (!opt) { main_channel_client_push_ping(mcc, 0); } else if (!strcmp(opt, "on")) { if (has_interval && interval > 0) { - mcc->ping_interval = interval * MSEC_PER_SEC; + mcc->priv->ping_interval = interval * MSEC_PER_SEC; } - reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval); + reds_core_timer_start(channel->reds, mcc->priv->ping_timer, mcc->priv->ping_interval); } else if (!strcmp(opt, "off")) { - reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer); + reds_core_timer_cancel(channel->reds, mcc->priv->ping_timer); } else { return; } @@ -427,14 +539,15 @@ static void do_ping_client(MainChannelClient *mcc, static void ping_timer_cb(void *opaque) { MainChannelClient *mcc = opaque; + RedChannel *channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc)); - if (!red_channel_client_is_connected(&mcc->base)) { + if (!red_channel_client_is_connected(RED_CHANNEL_CLIENT(mcc))) { spice_printerr("not connected to peer, ping off"); - reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer); + reds_core_timer_cancel(channel->reds, mcc->priv->ping_timer); return; } do_ping_client(mcc, NULL, 0, 0); - reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval); + reds_core_timer_start(channel->reds, mcc->priv->ping_timer, mcc->priv->ping_interval); } #endif /* RED_STATISTICS */ @@ -443,66 +556,83 @@ MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps) { - MainChannelClient *mcc = (MainChannelClient*) - red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, - client, stream, FALSE, num_common_caps, - common_caps, num_caps, caps); - spice_assert(mcc != NULL); - mcc->connection_id = connection_id; - mcc->bitrate_per_sec = ~0; -#ifdef RED_STATISTICS - if (!(mcc->ping_timer = reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb, mcc))) { - spice_error("ping timer create failed"); + MainChannelClient *mcc; + GArray *common_caps_array = NULL, *caps_array = NULL; + + if (common_caps) { + common_caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*common_caps), num_common_caps); + g_array_append_vals(common_caps_array, common_caps, num_common_caps); } - mcc->ping_interval = PING_INTERVAL; -#endif + if (caps) { + caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*caps), num_caps); + g_array_append_vals(caps_array, caps, num_caps); + } + + mcc = g_initable_new(TYPE_MAIN_CHANNEL_CLIENT, + NULL, NULL, + "channel", RED_CHANNEL(main_chan), + "client", client, + "stream", stream, + "monitor-latency", FALSE, + "caps", caps_array, + "common-caps", common_caps_array, + "connection-id", connection_id, + NULL); + + if (caps_array) + g_array_unref(caps_array); + if (common_caps_array) + g_array_unref(common_caps_array); + return mcc; } int main_channel_client_is_network_info_initialized(MainChannelClient *mcc) { - return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE; + return mcc->priv->net_test_stage == NET_TEST_STAGE_COMPLETE; } int main_channel_client_is_low_bandwidth(MainChannelClient *mcc) { // TODO: configurable? - return mcc->bitrate_per_sec < 10 * 1024 * 1024; + return mcc->priv->bitrate_per_sec < 10 * 1024 * 1024; } uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc) { - return mcc->bitrate_per_sec; + return mcc->priv->bitrate_per_sec; } uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc) { - return mcc->latency / 1000; + return mcc->priv->latency / 1000; } void main_channel_client_migrate(RedChannelClient *rcc) { - reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc, MainChannelClient, base)); + RedChannel *channel = red_channel_client_get_channel(rcc); + reds_on_main_channel_migrate(channel->reds, MAIN_CHANNEL_CLIENT(rcc)); red_channel_client_default_migrate(rcc); } gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc) { - RedChannelClient *rcc = main_channel_client_get_base(mcc); - MainChannel* main_channel = SPICE_CONTAINEROF(rcc->channel, MainChannel, base); + 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); if (red_client_during_migrate_at_target(client)) { spice_printerr("client %p: wait till previous migration completes", client); - mcc->mig_wait_prev_complete = TRUE; - mcc->mig_wait_prev_try_seamless = FALSE; + mcc->priv->mig_wait_prev_complete = TRUE; + mcc->priv->mig_wait_prev_try_seamless = FALSE; } else { red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN); - mcc->mig_wait_connect = TRUE; + mcc->priv->mig_wait_connect = TRUE; } - mcc->mig_connect_ok = FALSE; + mcc->priv->mig_connect_ok = FALSE; main_channel->num_clients_mig_wait++; return TRUE; } @@ -511,42 +641,44 @@ gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc) void main_channel_client_connect_seamless(MainChannelClient *mcc) { - spice_assert(red_channel_client_test_remote_cap(&mcc->base, + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(mcc)); + spice_assert(red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(mcc), SPICE_MAIN_CAP_SEAMLESS_MIGRATE)); - if (red_client_during_migrate_at_target(mcc->base.client)) { - spice_printerr("client %p: wait till previous migration completes", mcc->base.client); - mcc->mig_wait_prev_complete = TRUE; - mcc->mig_wait_prev_try_seamless = TRUE; + if (red_client_during_migrate_at_target(client)) { + spice_printerr("client %p: wait till previous migration completes", client); + mcc->priv->mig_wait_prev_complete = TRUE; + mcc->priv->mig_wait_prev_try_seamless = TRUE; } else { - red_channel_client_pipe_add_type(&mcc->base, + red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(mcc), RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS); - mcc->mig_wait_connect = TRUE; + mcc->priv->mig_wait_connect = TRUE; } - mcc->mig_connect_ok = FALSE; + mcc->priv->mig_connect_ok = FALSE; } RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc) { spice_assert(mcc); - return &mcc->base; + return RED_CHANNEL_CLIENT(mcc); } uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc) { - return mcc->connection_id; + return mcc->priv->connection_id; } uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc) { - return ++mcc->ping_id; + return ++mcc->priv->ping_id; } void main_channel_client_on_send_init(MainChannelClient *mcc) { - mcc->init_sent = TRUE; + mcc->priv->init_sent = TRUE; } gboolean main_channel_client_get_init_sent(MainChannelClient *mcc) { - return mcc->init_sent; + return mcc->priv->init_sent; } + diff --git a/server/main-channel-client.h b/server/main-channel-client.h index e103edc..5d284ad 100644 --- a/server/main-channel-client.h +++ b/server/main-channel-client.h @@ -17,15 +17,44 @@ #ifndef __MAIN_CHANNEL_CLIENT_H__ #define __MAIN_CHANNEL_CLIENT_H__ -#include "red-channel.h" +#include <glib-object.h> +#include "common/messages.h" +#include "red-channel-client.h" + +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)) +#define MAIN_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_MAIN_CHANNEL_CLIENT, MainChannelClientClass)) +#define IS_MAIN_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_MAIN_CHANNEL_CLIENT)) +#define IS_MAIN_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_MAIN_CHANNEL_CLIENT)) +#define MAIN_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_MAIN_CHANNEL_CLIENT, MainChannelClientClass)) + typedef struct MainChannelClient MainChannelClient; +typedef struct MainChannelClientClass MainChannelClientClass; +typedef struct MainChannelClientPrivate MainChannelClientPrivate; + +struct MainChannelClient +{ + RedChannelClient parent; + + MainChannelClientPrivate *priv; +}; + +struct MainChannelClientClass +{ + RedChannelClientClass parent_class; +}; + +GType main_channel_client_get_type(void) G_GNUC_CONST; MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client, - RedsStream *stream, uint32_t connection_id, - int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps); + RedsStream *stream, uint32_t connection_id, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps); void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens); void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len, @@ -68,8 +97,6 @@ int main_channel_client_is_low_bandwidth(MainChannelClient *mcc); uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc); uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc); -RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc); - void main_channel_client_push_name(MainChannelClient *mcc, const char *name); void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]); @@ -160,4 +187,6 @@ typedef struct RedMultiMediaTimePipeItem { RedPipeItem *main_notify_item_new(void *data, int num); +G_END_DECLS + #endif /* __MAIN_CHANNEL_CLIENT_H__ */ diff --git a/server/main-channel.c b/server/main-channel.c index fbbb032..e3d6c57 100644 --- a/server/main-channel.c +++ b/server/main-channel.c @@ -80,7 +80,7 @@ RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t c for (link = main_chan->base.clients; link != NULL; link = link->next) { rcc = link->data; - mcc = (MainChannelClient*) rcc; + mcc = MAIN_CHANNEL_CLIENT(rcc); if (main_channel_client_get_connection_id(mcc) == connection_id) { return red_channel_client_get_client(rcc); } @@ -117,7 +117,7 @@ static RedPipeItem *main_multi_media_time_item_new(RedChannelClient *rcc, static void main_channel_push_channels(MainChannelClient *mcc) { - RedChannelClient *rcc = main_channel_client_get_base(mcc); + RedChannelClient *rcc = RED_CHANNEL_CLIENT(mcc); if (red_client_during_migrate_at_target(red_channel_client_get_client(rcc))) { spice_printerr("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS" "during migration"); @@ -143,7 +143,7 @@ static void main_channel_marshall_ping(RedChannelClient *rcc, SpiceMarshaller *m, RedPingPipeItem *item) { - MainChannelClient *mcc = (MainChannelClient*)rcc; + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); SpiceMsgPing ping; int size_left = item->size; @@ -258,7 +258,7 @@ static int main_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *message) { RedChannel *channel = red_channel_client_get_channel(rcc); - MainChannelClient *mcc = (MainChannelClient*)rcc; + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message; /* not supported with multi-clients */ @@ -427,7 +427,7 @@ static void main_channel_marshall_multi_media_time(RedChannelClient *rcc, static void main_channel_send_item(RedChannelClient *rcc, RedPipeItem *base) { - MainChannelClient *mcc = (MainChannelClient*)rcc; + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); /* In semi-seamless migration (dest side), the connection is started from scratch, and @@ -537,7 +537,7 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint { RedChannel *channel = red_channel_client_get_channel(rcc); MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base); - MainChannelClient *mcc = (MainChannelClient*)rcc; + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); switch (type) { case SPICE_MSGC_MAIN_AGENT_START: { @@ -606,7 +606,7 @@ static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, { RedChannel *channel = red_channel_client_get_channel(rcc); MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base); - MainChannelClient *mcc = (MainChannelClient*)rcc; + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); if (type == SPICE_MSGC_MAIN_AGENT_DATA) { return reds_get_agent_data_buffer(channel->reds, mcc, size); @@ -724,7 +724,7 @@ static int main_channel_connect_semi_seamless(MainChannel *main_channel) for (link = main_channel->base.clients; link != NULL; link = link->next) { RedChannelClient *rcc = link->data; - MainChannelClient *mcc = (MainChannelClient*)rcc; + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); if (main_channel_client_connect_semi_seamless(mcc)) main_channel->num_clients_mig_wait++; } @@ -739,7 +739,7 @@ static int main_channel_connect_seamless(MainChannel *main_channel) for (link = main_channel->base.clients; link != NULL; link = link->next) { RedChannelClient *rcc = link->data; - MainChannelClient *mcc = (MainChannelClient*)rcc; + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); main_channel_client_connect_seamless(mcc); main_channel->num_clients_mig_wait++; } @@ -779,7 +779,7 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan) for (link = main_chan->base.clients; link != NULL; link = link->next) { RedChannelClient *rcc = link->data; - MainChannelClient *mcc = (MainChannelClient*)rcc; + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); main_channel_client_migrate_cancel_wait(mcc); } main_chan->num_clients_mig_wait = 0; @@ -799,7 +799,7 @@ int main_channel_migrate_src_complete(MainChannel *main_chan, int success) for (link = main_chan->base.clients; link != NULL; link = link->next) { RedChannelClient *rcc = link->data; - MainChannelClient *mcc = (MainChannelClient*)rcc; + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); if (main_channel_client_migrate_src_complete(mcc, success)) semi_seamless_count++; } diff --git a/server/red-channel-client-private.h b/server/red-channel-client-private.h index 4ad4d90..c7009fe 100644 --- a/server/red-channel-client-private.h +++ b/server/red-channel-client-private.h @@ -21,12 +21,11 @@ #include "red-channel-client.h" #include "red-channel.h" -struct RedChannelClient { +struct RedChannelClientPrivate { RedChannel *channel; RedClient *client; RedsStream *stream; - int dummy; - int dummy_connected; + gboolean monitor_latency; uint32_t refs; @@ -57,8 +56,6 @@ struct RedChannelClient { } urgent; } send_data; - OutgoingHandler outgoing; - IncomingHandler incoming; int during_send; int id; // debugging purposes Ring pipe; diff --git a/server/red-channel-client.c b/server/red-channel-client.c index 02450df..1c0339b 100644 --- a/server/red-channel-client.c +++ b/server/red-channel-client.c @@ -36,6 +36,28 @@ #include "red-channel-client-private.h" #include "red-channel.h" +static void red_channel_client_initable_interface_init(GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE(RedChannelClient, red_channel_client, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, + red_channel_client_initable_interface_init)) + +#define CHANNEL_CLIENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), RED_TYPE_CHANNEL_CLIENT, RedChannelClientPrivate)) + +static gboolean red_channel_client_initable_init(GInitable *initable, + GCancellable *cancellable, + GError **error); + +enum { + PROP0, + PROP_STREAM, + PROP_CHANNEL, + PROP_CLIENT, + PROP_MONITOR_LATENCY, + PROP_COMMON_CAPS, + PROP_CAPS +}; + #define PING_TEST_TIMEOUT_MS (MSEC_PER_SEC * 15) #define PING_TEST_IDLE_NET_TIMEOUT_MS (MSEC_PER_SEC / 10) @@ -58,36 +80,112 @@ typedef struct RedEmptyMsgPipeItem { int msg; } RedEmptyMsgPipeItem; +static uint32_t full_header_get_msg_size(SpiceDataHeaderOpaque *header) +{ + return GUINT32_FROM_LE(((SpiceDataHeader *)header->data)->size); +} + +static uint32_t mini_header_get_msg_size(SpiceDataHeaderOpaque *header) +{ + return GUINT32_FROM_LE(((SpiceMiniDataHeader *)header->data)->size); +} + +static uint16_t full_header_get_msg_type(SpiceDataHeaderOpaque *header) +{ + return GUINT16_FROM_LE(((SpiceDataHeader *)header->data)->type); +} + +static uint16_t mini_header_get_msg_type(SpiceDataHeaderOpaque *header) +{ + return GUINT16_FROM_LE(((SpiceMiniDataHeader *)header->data)->type); +} + +static void full_header_set_msg_type(SpiceDataHeaderOpaque *header, uint16_t type) +{ + ((SpiceDataHeader *)header->data)->type = GUINT16_TO_LE(type); +} + +static void mini_header_set_msg_type(SpiceDataHeaderOpaque *header, uint16_t type) +{ + ((SpiceMiniDataHeader *)header->data)->type = GUINT16_TO_LE(type); +} + +static void full_header_set_msg_size(SpiceDataHeaderOpaque *header, uint32_t size) +{ + ((SpiceDataHeader *)header->data)->size = GUINT32_TO_LE(size); +} + +static void mini_header_set_msg_size(SpiceDataHeaderOpaque *header, uint32_t size) +{ + ((SpiceMiniDataHeader *)header->data)->size = GUINT32_TO_LE(size); +} + +static void full_header_set_msg_serial(SpiceDataHeaderOpaque *header, uint64_t serial) +{ + ((SpiceDataHeader *)header->data)->serial = GUINT64_TO_LE(serial); +} + +static void mini_header_set_msg_serial(SpiceDataHeaderOpaque *header, uint64_t serial) +{ + spice_error("attempt to set header serial on mini header"); +} + +static void full_header_set_msg_sub_list(SpiceDataHeaderOpaque *header, uint32_t sub_list) +{ + ((SpiceDataHeader *)header->data)->sub_list = GUINT32_TO_LE(sub_list); +} + +static void mini_header_set_msg_sub_list(SpiceDataHeaderOpaque *header, uint32_t sub_list) +{ + spice_error("attempt to set header sub list on mini header"); +} + +static const SpiceDataHeaderOpaque full_header_wrapper = {NULL, sizeof(SpiceDataHeader), + full_header_set_msg_type, + full_header_set_msg_size, + full_header_set_msg_serial, + full_header_set_msg_sub_list, + full_header_get_msg_type, + full_header_get_msg_size}; + +static const SpiceDataHeaderOpaque mini_header_wrapper = {NULL, sizeof(SpiceMiniDataHeader), + mini_header_set_msg_type, + mini_header_set_msg_size, + mini_header_set_msg_serial, + mini_header_set_msg_sub_list, + mini_header_get_msg_type, + mini_header_get_msg_size}; + static void red_channel_client_start_ping_timer(RedChannelClient *rcc, uint32_t timeout) { - if (!rcc->latency_monitor.timer) { + if (!rcc->priv->latency_monitor.timer) { return; } - if (rcc->latency_monitor.state != PING_STATE_NONE) { + if (rcc->priv->latency_monitor.state != PING_STATE_NONE) { return; } - rcc->latency_monitor.state = PING_STATE_TIMER; - rcc->channel->core->timer_start(rcc->latency_monitor.timer, timeout); + rcc->priv->latency_monitor.state = PING_STATE_TIMER; + rcc->priv->channel->core->timer_start(rcc->priv->latency_monitor.timer, timeout); } static void red_channel_client_cancel_ping_timer(RedChannelClient *rcc) { - if (!rcc->latency_monitor.timer) { + if (!rcc->priv->latency_monitor.timer) { return; } - if (rcc->latency_monitor.state != PING_STATE_TIMER) { + if (rcc->priv->latency_monitor.state != PING_STATE_TIMER) { return; } - rcc->channel->core->timer_cancel(rcc->latency_monitor.timer); - rcc->latency_monitor.state = PING_STATE_NONE; + rcc->priv->channel->core->timer_cancel(rcc->priv->latency_monitor.timer); + rcc->priv->latency_monitor.state = PING_STATE_NONE; } static void red_channel_client_restart_ping_timer(RedChannelClient *rcc) { uint64_t passed, timeout; - passed = (spice_get_monotonic_time_ns() - rcc->latency_monitor.last_pong_time) / NSEC_PER_MILLISEC; + passed = (spice_get_monotonic_time_ns() - rcc->priv->latency_monitor.last_pong_time) / NSEC_PER_MILLISEC; timeout = PING_TEST_IDLE_NET_TIMEOUT_MS; if (passed < PING_TEST_TIMEOUT_MS) { timeout += PING_TEST_TIMEOUT_MS - passed; @@ -96,9 +194,236 @@ static void red_channel_client_restart_ping_timer(RedChannelClient *rcc) red_channel_client_start_ping_timer(rcc, timeout); } +static void +red_channel_client_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + RedChannelClient *self = RED_CHANNEL_CLIENT(object); + + switch (property_id) + { + case PROP_STREAM: + g_value_set_pointer(value, self->priv->stream); + break; + case PROP_CHANNEL: + g_value_set_pointer(value, self->priv->channel); + break; + case PROP_CLIENT: + g_value_set_pointer(value, self->priv->client); + break; + case PROP_MONITOR_LATENCY: + g_value_set_boolean(value, self->priv->monitor_latency); + break; + case PROP_COMMON_CAPS: + { + GArray *arr = g_array_sized_new(FALSE, FALSE, + sizeof(*self->priv->remote_caps.common_caps), + self->priv->remote_caps.num_common_caps); + g_value_take_boxed(value, arr); + } + break; + case PROP_CAPS: + { + GArray *arr = g_array_sized_new(FALSE, FALSE, + sizeof(*self->priv->remote_caps.caps), + self->priv->remote_caps.num_caps); + g_value_take_boxed(value, arr); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +red_channel_client_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + RedChannelClient *self = RED_CHANNEL_CLIENT(object); + + switch (property_id) + { + case PROP_STREAM: + self->priv->stream = g_value_get_pointer(value); + break; + case PROP_CHANNEL: + if (self->priv->channel) + red_channel_unref(self->priv->channel); + self->priv->channel = g_value_get_pointer(value); + if (self->priv->channel) + red_channel_ref(self->priv->channel); + break; + case PROP_CLIENT: + self->priv->client = g_value_get_pointer(value); + break; + case PROP_MONITOR_LATENCY: + self->priv->monitor_latency = g_value_get_boolean(value); + break; + case PROP_COMMON_CAPS: + { + GArray *caps = g_value_get_boxed(value); + if (caps) { + self->priv->remote_caps.num_common_caps = caps->len; + self->priv->remote_caps.common_caps = spice_memdup(caps->data, caps->len * sizeof(uint32_t)); + } + } + break; + case PROP_CAPS: + { + GArray *caps = g_value_get_boxed(value); + if (caps) { + self->priv->remote_caps.num_caps = caps->len; + self->priv->remote_caps.caps = spice_memdup(caps->data, caps->len * sizeof(uint32_t)); + } + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void red_channel_client_destroy_remote_caps(RedChannelClient* rcc) +{ + rcc->priv->remote_caps.num_common_caps = 0; + free(rcc->priv->remote_caps.common_caps); + rcc->priv->remote_caps.num_caps = 0; + free(rcc->priv->remote_caps.caps); +} + +static void +red_channel_client_finalize(GObject *object) +{ + RedChannelClient *self = RED_CHANNEL_CLIENT(object); + + reds_stream_free(self->priv->stream); + self->priv->stream = NULL; + + if (self->priv->send_data.main.marshaller) { + spice_marshaller_destroy(self->priv->send_data.main.marshaller); + } + + if (self->priv->send_data.urgent.marshaller) { + spice_marshaller_destroy(self->priv->send_data.urgent.marshaller); + } + + red_channel_client_destroy_remote_caps(self); + if (self->priv->channel) { + red_channel_unref(self->priv->channel); + } + + G_OBJECT_CLASS(red_channel_client_parent_class)->finalize(object); +} + +static void red_channel_client_initable_interface_init(GInitableIface *iface) +{ + iface->init = red_channel_client_initable_init; +} + +static gboolean red_channel_client_default_is_connected(RedChannelClient *rcc); +static void red_channel_client_default_disconnect(RedChannelClient *rcc); + + +static void red_channel_client_constructed(GObject *object) +{ + RedChannelClient *self = RED_CHANNEL_CLIENT(object); + + if (red_channel_client_test_remote_common_cap(self, SPICE_COMMON_CAP_MINI_HEADER)) { + self->incoming.header = mini_header_wrapper; + self->priv->send_data.header = mini_header_wrapper; + self->priv->is_mini_header = TRUE; + } else { + self->incoming.header = full_header_wrapper; + self->priv->send_data.header = full_header_wrapper; + self->priv->is_mini_header = FALSE; + } +} + +static void red_channel_client_class_init(RedChannelClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_debug("%s", G_STRFUNC); + g_type_class_add_private(klass, sizeof(RedChannelClientPrivate)); + + object_class->get_property = red_channel_client_get_property; + object_class->set_property = red_channel_client_set_property; + object_class->finalize = red_channel_client_finalize; + object_class->constructed = red_channel_client_constructed; + + klass->is_connected = red_channel_client_default_is_connected; + klass->disconnect = red_channel_client_default_disconnect; + + g_object_class_install_property(object_class, + PROP_STREAM, + g_param_spec_pointer("stream", "stream", + "Associated RedStream", + G_PARAM_STATIC_STRINGS + | G_PARAM_READWRITE + | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property(object_class, + PROP_CHANNEL, + g_param_spec_pointer("channel", "channel", + "Associated RedChannel", + G_PARAM_STATIC_STRINGS + | G_PARAM_READWRITE + | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property(object_class, + PROP_CLIENT, + g_param_spec_pointer("client", "client", + "Associated RedClient", + G_PARAM_STATIC_STRINGS + | G_PARAM_READWRITE + | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property(object_class, + PROP_MONITOR_LATENCY, + g_param_spec_boolean("monitor-latency", "monitor-latency", + "Whether to monitor latency for this client", + FALSE, + G_PARAM_STATIC_STRINGS + | G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property(object_class, + PROP_COMMON_CAPS, + g_param_spec_boxed("common-caps", "common-caps", + "Common Capabilities", + G_TYPE_ARRAY, + G_PARAM_STATIC_STRINGS + | G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property(object_class, + PROP_CAPS, + g_param_spec_boxed("caps", "caps", + "Capabilities", + G_TYPE_ARRAY, + G_PARAM_STATIC_STRINGS + | G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +red_channel_client_init(RedChannelClient *self) +{ + self->priv = CHANNEL_CLIENT_PRIVATE(self); + self->priv->ack_data.messages_window = ~0; // blocks send message (maybe use send_data.blocked + + // block flags) + self->priv->ack_data.client_generation = ~0; + self->priv->ack_data.client_window = CLIENT_ACK_WINDOW; + self->priv->send_data.main.marshaller = spice_marshaller_new(); + self->priv->send_data.urgent.marshaller = spice_marshaller_new(); + + self->priv->send_data.marshaller = self->priv->send_data.main.marshaller; + + ring_init(&self->priv->pipe); + self->priv->pipe_size = 0; +} + RedChannel* red_channel_client_get_channel(RedChannelClient* rcc) { - return rcc->channel; + return rcc->priv->channel; } IncomingHandler* red_channel_client_get_incoming_handler(RedChannelClient *rcc) @@ -110,18 +435,18 @@ void red_channel_client_on_output(void *opaque, int n) { RedChannelClient *rcc = opaque; - if (rcc->connectivity_monitor.timer) { - rcc->connectivity_monitor.out_bytes += n; + if (rcc->priv->connectivity_monitor.timer) { + rcc->priv->connectivity_monitor.out_bytes += n; } - stat_inc_counter(reds, rcc->channel->out_bytes_counter, n); + stat_inc_counter(reds, rcc->priv->channel->out_bytes_counter, n); } void red_channel_client_on_input(void *opaque, int n) { RedChannelClient *rcc = opaque; - if (rcc->connectivity_monitor.timer) { - rcc->connectivity_monitor.in_bytes += n; + if (rcc->priv->connectivity_monitor.timer) { + rcc->priv->connectivity_monitor.in_bytes += n; } } @@ -129,7 +454,7 @@ int red_channel_client_get_out_msg_size(void *opaque) { RedChannelClient *rcc = (RedChannelClient *)opaque; - return rcc->send_data.size; + return rcc->priv->send_data.size; } void red_channel_client_prepare_out_msg(void *opaque, struct iovec *vec, @@ -137,7 +462,7 @@ void red_channel_client_prepare_out_msg(void *opaque, struct iovec *vec, { RedChannelClient *rcc = (RedChannelClient *)opaque; - *vec_size = spice_marshaller_fill_iovec(rcc->send_data.marshaller, + *vec_size = spice_marshaller_fill_iovec(rcc->priv->send_data.marshaller, vec, IOV_MAX, pos); } @@ -145,46 +470,46 @@ void red_channel_client_on_out_block(void *opaque) { RedChannelClient *rcc = (RedChannelClient *)opaque; - rcc->send_data.blocked = TRUE; - rcc->channel->core->watch_update_mask(rcc->stream->watch, - SPICE_WATCH_EVENT_READ | - SPICE_WATCH_EVENT_WRITE); + 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); } static inline int red_channel_client_urgent_marshaller_is_active(RedChannelClient *rcc) { - return (rcc->send_data.marshaller == rcc->send_data.urgent.marshaller); + return (rcc->priv->send_data.marshaller == rcc->priv->send_data.urgent.marshaller); } static void red_channel_client_reset_send_data(RedChannelClient *rcc) { - spice_marshaller_reset(rcc->send_data.marshaller); - rcc->send_data.header.data = spice_marshaller_reserve_space(rcc->send_data.marshaller, - rcc->send_data.header.header_size); - spice_marshaller_set_base(rcc->send_data.marshaller, rcc->send_data.header.header_size); - rcc->send_data.header.set_msg_type(&rcc->send_data.header, 0); - rcc->send_data.header.set_msg_size(&rcc->send_data.header, 0); + spice_marshaller_reset(rcc->priv->send_data.marshaller); + rcc->priv->send_data.header.data = spice_marshaller_reserve_space(rcc->priv->send_data.marshaller, + rcc->priv->send_data.header.header_size); + spice_marshaller_set_base(rcc->priv->send_data.marshaller, rcc->priv->send_data.header.header_size); + rcc->priv->send_data.header.set_msg_type(&rcc->priv->send_data.header, 0); + rcc->priv->send_data.header.set_msg_size(&rcc->priv->send_data.header, 0); /* Keeping the serial consecutive: resetting it if reset_send_data * has been called before, but no message has been sent since then. */ - if (rcc->send_data.last_sent_serial != rcc->send_data.serial) { - spice_assert(rcc->send_data.serial - rcc->send_data.last_sent_serial == 1); + if (rcc->priv->send_data.last_sent_serial != rcc->priv->send_data.serial) { + spice_assert(rcc->priv->send_data.serial - rcc->priv->send_data.last_sent_serial == 1); /* When the urgent marshaller is active, the serial was incremented by * the call to reset_send_data that was made for the main marshaller. * The urgent msg receives this serial, and the main msg serial is - * the following one. Thus, (rcc->send_data.serial - rcc->send_data.last_sent_serial) + * the following one. Thus, (rcc->priv->send_data.serial - rcc->priv->send_data.last_sent_serial) * should be 1 in this case*/ if (!red_channel_client_urgent_marshaller_is_active(rcc)) { - rcc->send_data.serial = rcc->send_data.last_sent_serial; + rcc->priv->send_data.serial = rcc->priv->send_data.last_sent_serial; } } - rcc->send_data.serial++; + rcc->priv->send_data.serial++; - if (!rcc->is_mini_header) { - spice_assert(rcc->send_data.marshaller != rcc->send_data.urgent.marshaller); - rcc->send_data.header.set_msg_sub_list(&rcc->send_data.header, 0); - rcc->send_data.header.set_msg_serial(&rcc->send_data.header, rcc->send_data.serial); + if (!rcc->priv->is_mini_header) { + spice_assert(rcc->priv->send_data.marshaller != rcc->priv->send_data.urgent.marshaller); + rcc->priv->send_data.header.set_msg_sub_list(&rcc->priv->send_data.header, 0); + rcc->priv->send_data.header.set_msg_serial(&rcc->priv->send_data.header, rcc->priv->send_data.serial); } } @@ -194,11 +519,11 @@ static void red_channel_client_send_set_ack(RedChannelClient *rcc) spice_assert(rcc); red_channel_client_init_send_data(rcc, SPICE_MSG_SET_ACK, NULL); - ack.generation = ++rcc->ack_data.generation; - ack.window = rcc->ack_data.client_window; - rcc->ack_data.messages_window = 0; + ack.generation = ++rcc->priv->ack_data.generation; + ack.window = rcc->priv->ack_data.client_window; + rcc->priv->ack_data.messages_window = 0; - spice_marshall_msg_set_ack(rcc->send_data.marshaller, &ack); + spice_marshall_msg_set_ack(rcc->priv->send_data.marshaller, &ack); red_channel_client_begin_send_message(rcc); } @@ -208,10 +533,10 @@ 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->channel->migration_flags; - spice_marshall_msg_migrate(rcc->send_data.marshaller, &migrate); - if (rcc->channel->migration_flags & SPICE_MIGRATE_NEED_FLUSH) { - rcc->wait_migrate_flush_mark = TRUE; + migrate.flags = rcc->priv->channel->migration_flags; + spice_marshall_msg_migrate(rcc->priv->send_data.marshaller, &migrate); + if (rcc->priv->channel->migration_flags & SPICE_MIGRATE_NEED_FLUSH) { + rcc->priv->wait_migrate_flush_mark = TRUE; } red_channel_client_begin_send_message(rcc); @@ -221,25 +546,25 @@ static void red_channel_client_send_ping(RedChannelClient *rcc) { SpiceMsgPing ping; - if (!rcc->latency_monitor.warmup_was_sent) { // latency test start + if (!rcc->priv->latency_monitor.warmup_was_sent) { // latency test start int delay_val; socklen_t opt_size = sizeof(delay_val); - rcc->latency_monitor.warmup_was_sent = TRUE; + rcc->priv->latency_monitor.warmup_was_sent = TRUE; /* * When testing latency, TCP_NODELAY must be switched on, otherwise, * sending the ping message is delayed by Nagle algorithm, and the * roundtrip measurment is less accurate (bigger). */ - rcc->latency_monitor.tcp_nodelay = 1; - if (getsockopt(rcc->stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, + rcc->priv->latency_monitor.tcp_nodelay = 1; + if (getsockopt(rcc->priv->stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, &opt_size) == -1) { spice_warning("getsockopt failed, %s", strerror(errno)); } else { - rcc->latency_monitor.tcp_nodelay = delay_val; + rcc->priv->latency_monitor.tcp_nodelay = delay_val; if (!delay_val) { delay_val = 1; - if (setsockopt(rcc->stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, + if (setsockopt(rcc->priv->stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) { if (errno != ENOTSUP) { spice_warning("setsockopt failed, %s", strerror(errno)); @@ -250,9 +575,9 @@ static void red_channel_client_send_ping(RedChannelClient *rcc) } red_channel_client_init_send_data(rcc, SPICE_MSG_PING, NULL); - ping.id = rcc->latency_monitor.id; + ping.id = rcc->priv->latency_monitor.id; ping.timestamp = spice_get_monotonic_time_ns(); - spice_marshall_msg_ping(rcc->send_data.marshaller, &ping); + spice_marshall_msg_ping(rcc->priv->send_data.marshaller, &ping); red_channel_client_begin_send_message(rcc); } @@ -282,7 +607,7 @@ static void red_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *ite red_channel_client_send_ping(rcc); break; default: - rcc->channel->channel_cbs.send_item(rcc, item); + rcc->priv->channel->channel_cbs.send_item(rcc, item); return; } free(item); @@ -300,28 +625,28 @@ static void red_channel_client_release_item(RedChannelClient *rcc, free(item); break; default: - rcc->channel->channel_cbs.release_item(rcc, item, item_pushed); + rcc->priv->channel->channel_cbs.release_item(rcc, item, item_pushed); } } static inline void red_channel_client_release_sent_item(RedChannelClient *rcc) { - if (rcc->send_data.item) { + if (rcc->priv->send_data.item) { red_channel_client_release_item(rcc, - rcc->send_data.item, TRUE); - rcc->send_data.item = NULL; + rcc->priv->send_data.item, TRUE); + rcc->priv->send_data.item = NULL; } } static void red_channel_client_restore_main_sender(RedChannelClient *rcc) { - spice_marshaller_reset(rcc->send_data.urgent.marshaller); - rcc->send_data.marshaller = rcc->send_data.main.marshaller; - rcc->send_data.header.data = rcc->send_data.main.header_data; - if (!rcc->is_mini_header) { - rcc->send_data.header.set_msg_serial(&rcc->send_data.header, rcc->send_data.serial); + spice_marshaller_reset(rcc->priv->send_data.urgent.marshaller); + rcc->priv->send_data.marshaller = rcc->priv->send_data.main.marshaller; + rcc->priv->send_data.header.data = rcc->priv->send_data.main.header_data; + if (!rcc->priv->is_mini_header) { + rcc->priv->send_data.header.set_msg_serial(&rcc->priv->send_data.header, rcc->priv->send_data.serial); } - rcc->send_data.item = rcc->send_data.main.item; + rcc->priv->send_data.item = rcc->priv->send_data.main.item; } void red_channel_client_on_out_msg_done(void *opaque) @@ -329,10 +654,10 @@ void red_channel_client_on_out_msg_done(void *opaque) RedChannelClient *rcc = (RedChannelClient *)opaque; int fd; - rcc->send_data.size = 0; + rcc->priv->send_data.size = 0; - if (spice_marshaller_get_fd(rcc->send_data.marshaller, &fd)) { - if (reds_stream_send_msgfd(rcc->stream, fd) < 0) { + if (spice_marshaller_get_fd(rcc->priv->send_data.marshaller, &fd)) { + if (reds_stream_send_msgfd(rcc->priv->stream, fd) < 0) { perror("sendfd"); red_channel_client_disconnect(rcc); if (fd != -1) @@ -344,18 +669,20 @@ void red_channel_client_on_out_msg_done(void *opaque) } red_channel_client_release_sent_item(rcc); - if (rcc->send_data.blocked) { - rcc->send_data.blocked = FALSE; - rcc->channel->core->watch_update_mask(rcc->stream->watch, - SPICE_WATCH_EVENT_READ); + if (rcc->priv->send_data.blocked) { + rcc->priv->send_data.blocked = FALSE; + rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch, + SPICE_WATCH_EVENT_READ); } if (red_channel_client_urgent_marshaller_is_active(rcc)) { red_channel_client_restore_main_sender(rcc); - spice_assert(rcc->send_data.header.data != NULL); + spice_assert(rcc->priv->send_data.header.data != NULL); red_channel_client_begin_send_message(rcc); } else { - if (rcc->latency_monitor.timer && !rcc->send_data.blocked && rcc->pipe_size == 0) { + if (rcc->priv->latency_monitor.timer + && !rcc->priv->send_data.blocked + && rcc->priv->pipe_size == 0) { /* It is possible that the socket will become idle, so we may be able to test latency */ red_channel_client_restart_ping_timer(rcc); } @@ -365,49 +692,30 @@ void red_channel_client_on_out_msg_done(void *opaque) static void red_channel_client_pipe_remove(RedChannelClient *rcc, RedPipeItem *item) { - rcc->pipe_size--; + rcc->priv->pipe_size--; ring_remove(&item->link); } -static void red_channel_client_set_remote_caps(RedChannelClient* rcc, - int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps) -{ - rcc->remote_caps.num_common_caps = num_common_caps; - rcc->remote_caps.common_caps = spice_memdup(common_caps, num_common_caps * sizeof(uint32_t)); - - rcc->remote_caps.num_caps = num_caps; - rcc->remote_caps.caps = spice_memdup(caps, num_caps * sizeof(uint32_t)); -} - -static void red_channel_client_destroy_remote_caps(RedChannelClient* rcc) -{ - rcc->remote_caps.num_common_caps = 0; - free(rcc->remote_caps.common_caps); - rcc->remote_caps.num_caps = 0; - free(rcc->remote_caps.caps); -} - int red_channel_client_test_remote_common_cap(RedChannelClient *rcc, uint32_t cap) { - return test_capability(rcc->remote_caps.common_caps, - rcc->remote_caps.num_common_caps, + return test_capability(rcc->priv->remote_caps.common_caps, + rcc->priv->remote_caps.num_common_caps, cap); } int red_channel_client_test_remote_cap(RedChannelClient *rcc, uint32_t cap) { - return test_capability(rcc->remote_caps.caps, - rcc->remote_caps.num_caps, + return test_capability(rcc->priv->remote_caps.caps, + rcc->priv->remote_caps.num_caps, cap); } static void red_channel_client_push_ping(RedChannelClient *rcc) { - spice_assert(rcc->latency_monitor.state == PING_STATE_NONE); - rcc->latency_monitor.state = PING_STATE_WARMUP; - rcc->latency_monitor.warmup_was_sent = FALSE; - rcc->latency_monitor.id = rand(); + spice_assert(rcc->priv->latency_monitor.state == PING_STATE_NONE); + rcc->priv->latency_monitor.state = PING_STATE_WARMUP; + rcc->priv->latency_monitor.warmup_was_sent = FALSE; + rcc->priv->latency_monitor.id = rand(); red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_PING); red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_PING); } @@ -416,7 +724,7 @@ static void red_channel_client_ping_timer(void *opaque) { RedChannelClient *rcc = opaque; - spice_assert(rcc->latency_monitor.state == PING_STATE_TIMER); + spice_assert(rcc->priv->latency_monitor.state == PING_STATE_TIMER); red_channel_client_cancel_ping_timer(rcc); #ifdef HAVE_LINUX_SOCKIOS_H /* SIOCOUTQ is a Linux only ioctl on sockets. */ @@ -424,7 +732,7 @@ static void red_channel_client_ping_timer(void *opaque) int so_unsent_size = 0; /* retrieving the occupied size of the socket's tcp snd buffer (unacked + unsent) */ - if (ioctl(rcc->stream->socket, SIOCOUTQ, &so_unsent_size) == -1) { + if (ioctl(rcc->priv->stream->socket, SIOCOUTQ, &so_unsent_size) == -1) { spice_printerr("ioctl(SIOCOUTQ) failed, %s", strerror(errno)); } if (so_unsent_size > 0) { @@ -442,8 +750,8 @@ static void red_channel_client_ping_timer(void *opaque) static inline int red_channel_client_waiting_for_ack(RedChannelClient *rcc) { - return (rcc->channel->handle_acks && - (rcc->ack_data.messages_window > rcc->ack_data.client_window * 2)); + return (rcc->priv->channel->handle_acks && + (rcc->priv->ack_data.messages_window > rcc->priv->ack_data.client_window * 2)); } /* @@ -461,12 +769,12 @@ static inline int red_channel_client_waiting_for_ack(RedChannelClient *rcc) static void red_channel_client_connectivity_timer(void *opaque) { RedChannelClient *rcc = opaque; - RedChannelClientConnectivityMonitor *monitor = &rcc->connectivity_monitor; + RedChannelClientConnectivityMonitor *monitor = &rcc->priv->connectivity_monitor; int is_alive = TRUE; if (monitor->state == CONNECTIVITY_STATE_BLOCKED) { if (monitor->in_bytes == 0 && monitor->out_bytes == 0) { - if (!rcc->send_data.blocked && !red_channel_client_waiting_for_ack(rcc)) { + if (!rcc->priv->send_data.blocked && !red_channel_client_waiting_for_ack(rcc)) { spice_error("mismatch between rcc-state and connectivity-state"); } spice_debug("rcc is blocked; connection is idle"); @@ -474,8 +782,8 @@ static void red_channel_client_connectivity_timer(void *opaque) } } else if (monitor->state == CONNECTIVITY_STATE_WAIT_PONG) { if (monitor->in_bytes == 0) { - if (rcc->latency_monitor.state != PING_STATE_WARMUP && - rcc->latency_monitor.state != PING_STATE_LATENCY) { + if (rcc->priv->latency_monitor.state != PING_STATE_WARMUP && + rcc->priv->latency_monitor.state != PING_STATE_LATENCY) { spice_error("mismatch between rcc-state and connectivity-state"); } spice_debug("rcc waits for pong; connection is idle"); @@ -486,20 +794,20 @@ static void red_channel_client_connectivity_timer(void *opaque) if (is_alive) { monitor->in_bytes = 0; monitor->out_bytes = 0; - if (rcc->send_data.blocked || red_channel_client_waiting_for_ack(rcc)) { + if (rcc->priv->send_data.blocked || red_channel_client_waiting_for_ack(rcc)) { monitor->state = CONNECTIVITY_STATE_BLOCKED; - } else if (rcc->latency_monitor.state == PING_STATE_WARMUP || - rcc->latency_monitor.state == PING_STATE_LATENCY) { + } else if (rcc->priv->latency_monitor.state == PING_STATE_WARMUP || + rcc->priv->latency_monitor.state == PING_STATE_LATENCY) { monitor->state = CONNECTIVITY_STATE_WAIT_PONG; } else { monitor->state = CONNECTIVITY_STATE_CONNECTED; } - rcc->channel->core->timer_start(rcc->connectivity_monitor.timer, - rcc->connectivity_monitor.timeout); + rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer, + rcc->priv->connectivity_monitor.timeout); } else { monitor->state = CONNECTIVITY_STATE_DISCONNECTED; spice_warning("rcc %p on channel %d:%d has been unresponsive for more than %u ms, disconnecting", - rcc, rcc->channel->type, rcc->channel->id, monitor->timeout); + rcc, rcc->priv->channel->type, rcc->priv->channel->id, monitor->timeout); red_channel_client_disconnect(rcc); } } @@ -517,22 +825,22 @@ void red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc, uin * channel-client even if there are no ongoing channel specific messages * on this channel. */ - if (rcc->latency_monitor.timer == NULL) { - rcc->latency_monitor.timer = rcc->channel->core->timer_add( - rcc->channel->core, red_channel_client_ping_timer, rcc); - if (!red_client_during_migrate_at_target(rcc->client)) { + 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); + if (!red_client_during_migrate_at_target(rcc->priv->client)) { red_channel_client_start_ping_timer(rcc, PING_TEST_IDLE_NET_TIMEOUT_MS); } - rcc->latency_monitor.roundtrip = -1; - } - if (rcc->connectivity_monitor.timer == NULL) { - rcc->connectivity_monitor.state = CONNECTIVITY_STATE_CONNECTED; - rcc->connectivity_monitor.timer = rcc->channel->core->timer_add( - rcc->channel->core, red_channel_client_connectivity_timer, rcc); - rcc->connectivity_monitor.timeout = timeout_ms; - if (!red_client_during_migrate_at_target(rcc->client)) { - rcc->channel->core->timer_start(rcc->connectivity_monitor.timer, - rcc->connectivity_monitor.timeout); + rcc->priv->latency_monitor.roundtrip = -1; + } + 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.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); } } } @@ -541,92 +849,16 @@ static void red_channel_client_event(int fd, int event, void *data) { RedChannelClient *rcc = (RedChannelClient *)data; - red_channel_client_ref(rcc); + g_object_ref(rcc); if (event & SPICE_WATCH_EVENT_READ) { red_channel_client_receive(rcc); } if (event & SPICE_WATCH_EVENT_WRITE) { red_channel_client_push(rcc); } - red_channel_client_unref(rcc); -} - -static uint32_t full_header_get_msg_size(SpiceDataHeaderOpaque *header) -{ - return GUINT32_FROM_LE(((SpiceDataHeader *)header->data)->size); -} - -static uint32_t mini_header_get_msg_size(SpiceDataHeaderOpaque *header) -{ - return GUINT32_FROM_LE(((SpiceMiniDataHeader *)header->data)->size); -} - -static uint16_t full_header_get_msg_type(SpiceDataHeaderOpaque *header) -{ - return GUINT16_FROM_LE(((SpiceDataHeader *)header->data)->type); -} - -static uint16_t mini_header_get_msg_type(SpiceDataHeaderOpaque *header) -{ - return GUINT16_FROM_LE(((SpiceMiniDataHeader *)header->data)->type); -} - -static void full_header_set_msg_type(SpiceDataHeaderOpaque *header, uint16_t type) -{ - ((SpiceDataHeader *)header->data)->type = GUINT16_TO_LE(type); + g_object_unref(rcc); } -static void mini_header_set_msg_type(SpiceDataHeaderOpaque *header, uint16_t type) -{ - ((SpiceMiniDataHeader *)header->data)->type = GUINT16_TO_LE(type); -} - -static void full_header_set_msg_size(SpiceDataHeaderOpaque *header, uint32_t size) -{ - ((SpiceDataHeader *)header->data)->size = GUINT32_TO_LE(size); -} - -static void mini_header_set_msg_size(SpiceDataHeaderOpaque *header, uint32_t size) -{ - ((SpiceMiniDataHeader *)header->data)->size = GUINT32_TO_LE(size); -} - -static void full_header_set_msg_serial(SpiceDataHeaderOpaque *header, uint64_t serial) -{ - ((SpiceDataHeader *)header->data)->serial = GUINT64_TO_LE(serial); -} - -static void mini_header_set_msg_serial(SpiceDataHeaderOpaque *header, uint64_t serial) -{ - spice_error("attempt to set header serial on mini header"); -} - -static void full_header_set_msg_sub_list(SpiceDataHeaderOpaque *header, uint32_t sub_list) -{ - ((SpiceDataHeader *)header->data)->sub_list = GUINT32_TO_LE(sub_list); -} - -static void mini_header_set_msg_sub_list(SpiceDataHeaderOpaque *header, uint32_t sub_list) -{ - spice_error("attempt to set header sub list on mini header"); -} - -static const SpiceDataHeaderOpaque full_header_wrapper = {NULL, sizeof(SpiceDataHeader), - full_header_set_msg_type, - full_header_set_msg_size, - full_header_set_msg_serial, - full_header_set_msg_sub_list, - full_header_get_msg_type, - full_header_get_msg_size}; - -static const SpiceDataHeaderOpaque mini_header_wrapper = {NULL, sizeof(SpiceMiniDataHeader), - mini_header_set_msg_type, - mini_header_set_msg_size, - mini_header_set_msg_serial, - mini_header_set_msg_sub_list, - mini_header_get_msg_type, - mini_header_get_msg_size}; - static int red_channel_client_pre_create_validate(RedChannel *channel, RedClient *client) { if (red_client_get_channel(client, channel->type, channel->id)) { @@ -637,221 +869,165 @@ static int red_channel_client_pre_create_validate(RedChannel *channel, RedClient return TRUE; } -RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client, - RedsStream *stream, - int monitor_latency, - int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps) -{ - RedChannelClient *rcc = NULL; - - pthread_mutex_lock(&client->lock); - if (!red_channel_client_pre_create_validate(channel, client)) { - goto error; - } - spice_assert(stream && channel && size >= sizeof(RedChannelClient)); - rcc = spice_malloc0(size); - rcc->stream = stream; - rcc->channel = channel; - rcc->client = client; - rcc->refs = 1; - rcc->ack_data.messages_window = ~0; // blocks send message (maybe use send_data.blocked + - // block flags) - rcc->ack_data.client_generation = ~0; - rcc->ack_data.client_window = CLIENT_ACK_WINDOW; - rcc->send_data.main.marshaller = spice_marshaller_new(); - rcc->send_data.urgent.marshaller = spice_marshaller_new(); - - rcc->send_data.marshaller = rcc->send_data.main.marshaller; - - rcc->incoming.opaque = rcc; - rcc->incoming.cb = &channel->incoming_cb; - - rcc->outgoing.opaque = rcc; - rcc->outgoing.cb = &channel->outgoing_cb; - rcc->outgoing.pos = 0; - rcc->outgoing.size = 0; - - red_channel_client_set_remote_caps(rcc, num_common_caps, common_caps, num_caps, caps); - if (red_channel_client_test_remote_common_cap(rcc, SPICE_COMMON_CAP_MINI_HEADER)) { - rcc->incoming.header = mini_header_wrapper; - rcc->send_data.header = mini_header_wrapper; - rcc->is_mini_header = TRUE; - } else { - rcc->incoming.header = full_header_wrapper; - rcc->send_data.header = full_header_wrapper; - rcc->is_mini_header = FALSE; +static gboolean red_channel_client_initable_init(GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GError *local_error = NULL; + 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)) { + 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); + goto cleanup; + } + + 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); + + if (!self->priv->client->during_target_migrate) { + red_channel_client_start_ping_timer(self, + PING_TEST_IDLE_NET_TIMEOUT_MS); + } + self->priv->latency_monitor.roundtrip = -1; } - rcc->incoming.header.data = rcc->incoming.header_buf; - rcc->incoming.serial = 1; + self->incoming.opaque = self; + self->incoming.cb = &self->priv->channel->incoming_cb; + self->incoming.header.data = self->incoming.header_buf; + self->incoming.serial = 1; - if (!channel->channel_cbs.config_socket(rcc)) { - goto error; - } + self->outgoing.opaque = self; + self->outgoing.cb = &self->priv->channel->outgoing_cb; + self->outgoing.pos = 0; + self->outgoing.size = 0; - ring_init(&rcc->pipe); - rcc->pipe_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; + red_channel_add_client(self->priv->channel, self); + red_client_add_channel(self->priv->client, self); - stream->watch = channel->core->watch_add(channel->core, - stream->socket, - SPICE_WATCH_EVENT_READ, - red_channel_client_event, rcc); - rcc->id = g_list_length(channel->clients); - red_channel_add_client(channel, rcc); - red_client_add_channel(client, rcc); - red_channel_ref(channel); - pthread_mutex_unlock(&client->lock); + if (!self->priv->channel->channel_cbs.config_socket(self)) { + g_set_error_literal(&local_error, + SPICE_SERVER_ERROR, + SPICE_SERVER_ERROR_FAILED, + "Unable to configure socket"); + } - if (monitor_latency && reds_stream_get_family(stream) != AF_UNIX) { - rcc->latency_monitor.timer = channel->core->timer_add( - channel->core, red_channel_client_ping_timer, rcc); - if (!client->during_target_migrate) { - red_channel_client_start_ping_timer(rcc, PING_TEST_IDLE_NET_TIMEOUT_MS); - } - rcc->latency_monitor.roundtrip = -1; +cleanup: + pthread_mutex_unlock(&self->priv->client->lock); + if (local_error) { + g_warning("Failed to create channel client: %s", local_error->message); + g_propagate_error(error, local_error); } + return local_error == NULL; +} - return rcc; -error: - free(rcc); - reds_stream_free(stream); - pthread_mutex_unlock(&client->lock); - return NULL; -} - -RedChannelClient *red_channel_client_create_dummy(int size, - RedChannel *channel, - RedClient *client, - int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps) -{ - RedChannelClient *rcc = NULL; - - spice_assert(size >= sizeof(RedChannelClient)); - - pthread_mutex_lock(&client->lock); - if (!red_channel_client_pre_create_validate(channel, client)) { - goto error; - } - rcc = spice_malloc0(size); - rcc->refs = 1; - rcc->client = client; - rcc->channel = channel; - red_channel_ref(channel); - red_channel_client_set_remote_caps(rcc, num_common_caps, common_caps, num_caps, caps); - if (red_channel_client_test_remote_common_cap(rcc, SPICE_COMMON_CAP_MINI_HEADER)) { - rcc->incoming.header = mini_header_wrapper; - rcc->send_data.header = mini_header_wrapper; - rcc->is_mini_header = TRUE; - } else { - rcc->incoming.header = full_header_wrapper; - rcc->send_data.header = full_header_wrapper; - rcc->is_mini_header = FALSE; +RedChannelClient *red_channel_client_create(RedChannel *channel, RedClient *client, + RedsStream *stream, + int monitor_latency, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps) +{ + RedChannelClient *rcc; + GArray *common_caps_array = NULL, *caps_array = NULL; + + if (common_caps) { + common_caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*common_caps), num_common_caps); + g_array_append_vals(common_caps_array, common_caps, num_common_caps); } + if (caps) { + caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*caps), num_caps); + g_array_append_vals(caps_array, caps, num_caps); + } + rcc = g_initable_new(RED_TYPE_CHANNEL_CLIENT, + NULL, NULL, + "channel", channel, + "client", client, + "stream", stream, + "monitor-latency", monitor_latency, + "caps", caps_array, + "common-caps", common_caps_array, + NULL); - rcc->incoming.header.data = rcc->incoming.header_buf; - rcc->incoming.serial = 1; - ring_init(&rcc->pipe); + if (caps_array) + g_array_unref(caps_array); + if (common_caps_array) + g_array_unref(common_caps_array); - rcc->dummy = TRUE; - rcc->dummy_connected = TRUE; - red_channel_add_client(channel, rcc); - red_client_add_channel(client, rcc); - pthread_mutex_unlock(&client->lock); return rcc; -error: - pthread_mutex_unlock(&client->lock); - return NULL; } void red_channel_client_seamless_migration_done(RedChannelClient *rcc) { - rcc->wait_migrate_data = FALSE; + rcc->priv->wait_migrate_data = FALSE; - if (red_client_seamless_migration_done_for_channel(rcc->client, rcc)) { - if (rcc->latency_monitor.timer) { + if (red_client_seamless_migration_done_for_channel(rcc->priv->client, rcc)) { + if (rcc->priv->latency_monitor.timer) { red_channel_client_start_ping_timer(rcc, PING_TEST_IDLE_NET_TIMEOUT_MS); } - if (rcc->connectivity_monitor.timer) { - rcc->channel->core->timer_start(rcc->connectivity_monitor.timer, - rcc->connectivity_monitor.timeout); + if (rcc->priv->connectivity_monitor.timer) { + rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer, + rcc->priv->connectivity_monitor.timeout); } } } void red_channel_client_semi_seamless_migration_complete(RedChannelClient *rcc) { - if (rcc->latency_monitor.timer) { + if (rcc->priv->latency_monitor.timer) { red_channel_client_start_ping_timer(rcc, PING_TEST_IDLE_NET_TIMEOUT_MS); } } int red_channel_client_is_waiting_for_migrate_data(RedChannelClient *rcc) { - return rcc->wait_migrate_data; + return rcc->priv->wait_migrate_data; } void red_channel_client_default_migrate(RedChannelClient *rcc) { - if (rcc->latency_monitor.timer) { + if (rcc->priv->latency_monitor.timer) { red_channel_client_cancel_ping_timer(rcc); - rcc->channel->core->timer_remove(rcc->latency_monitor.timer); - rcc->latency_monitor.timer = NULL; + rcc->priv->channel->core->timer_remove(rcc->priv->latency_monitor.timer); + rcc->priv->latency_monitor.timer = NULL; } - if (rcc->connectivity_monitor.timer) { - rcc->channel->core->timer_remove(rcc->connectivity_monitor.timer); - rcc->connectivity_monitor.timer = NULL; + if (rcc->priv->connectivity_monitor.timer) { + rcc->priv->channel->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); } -void red_channel_client_ref(RedChannelClient *rcc) -{ - rcc->refs++; -} - -void red_channel_client_unref(RedChannelClient *rcc) -{ - if (--rcc->refs != 0) { - return; - } - - spice_debug("destroy rcc=%p", rcc); - - reds_stream_free(rcc->stream); - rcc->stream = NULL; - - if (rcc->send_data.main.marshaller) { - spice_marshaller_destroy(rcc->send_data.main.marshaller); - } - - if (rcc->send_data.urgent.marshaller) { - spice_marshaller_destroy(rcc->send_data.urgent.marshaller); - } - - red_channel_client_destroy_remote_caps(rcc); - if (rcc->channel) { - red_channel_unref(rcc->channel); - } - free(rcc); -} - void red_channel_client_destroy(RedChannelClient *rcc) { - rcc->destroying = TRUE; + rcc->priv->destroying = TRUE; red_channel_client_disconnect(rcc); red_client_remove_channel(rcc); - red_channel_client_unref(rcc); + g_object_unref(rcc); } void red_channel_client_shutdown(RedChannelClient *rcc) { - if (rcc->stream && !rcc->stream->shutdown) { - rcc->channel->core->watch_remove(rcc->stream->watch); - rcc->stream->watch = NULL; - shutdown(rcc->stream->socket, SHUT_RDWR); - rcc->stream->shutdown = TRUE; + if (rcc->priv->stream && !rcc->priv->stream->shutdown) { + 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; } } @@ -1034,25 +1210,25 @@ static void red_peer_handle_incoming(RedsStream *stream, IncomingHandler *handle void red_channel_client_receive(RedChannelClient *rcc) { - red_channel_client_ref(rcc); - red_peer_handle_incoming(rcc->stream, &rcc->incoming); - red_channel_client_unref(rcc); + g_object_ref(rcc); + red_peer_handle_incoming(rcc->priv->stream, &rcc->incoming); + g_object_unref(rcc); } void red_channel_client_send(RedChannelClient *rcc) { - red_channel_client_ref(rcc); - red_peer_handle_outgoing(rcc->stream, &rcc->outgoing); - red_channel_client_unref(rcc); + g_object_ref(rcc); + red_peer_handle_outgoing(rcc->priv->stream, &rcc->outgoing); + g_object_unref(rcc); } static inline RedPipeItem *red_channel_client_pipe_item_get(RedChannelClient *rcc) { RedPipeItem *item; - if (!rcc || rcc->send_data.blocked + if (!rcc || rcc->priv->send_data.blocked || red_channel_client_waiting_for_ack(rcc) - || !(item = (RedPipeItem *)ring_get_tail(&rcc->pipe))) { + || !(item = (RedPipeItem *)ring_get_tail(&rcc->priv->pipe))) { return NULL; } red_channel_client_pipe_remove(rcc, item); @@ -1063,44 +1239,44 @@ void red_channel_client_push(RedChannelClient *rcc) { RedPipeItem *pipe_item; - if (!rcc->during_send) { - rcc->during_send = TRUE; + if (!rcc->priv->during_send) { + rcc->priv->during_send = TRUE; } else { return; } - red_channel_client_ref(rcc); - if (rcc->send_data.blocked) { + g_object_ref(rcc); + if (rcc->priv->send_data.blocked) { red_channel_client_send(rcc); } - if (!red_channel_client_no_item_being_sent(rcc) && !rcc->send_data.blocked) { - rcc->send_data.blocked = TRUE; + if (!red_channel_client_no_item_being_sent(rcc) && !rcc->priv->send_data.blocked) { + rcc->priv->send_data.blocked = TRUE; spice_printerr("ERROR: an item waiting to be sent and not blocked"); } while ((pipe_item = red_channel_client_pipe_item_get(rcc))) { red_channel_client_send_item(rcc, pipe_item); } - if (red_channel_client_no_item_being_sent(rcc) && ring_is_empty(&rcc->pipe) - && rcc->stream->watch) { - rcc->channel->core->watch_update_mask(rcc->stream->watch, + 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); } - rcc->during_send = FALSE; - red_channel_client_unref(rcc); + rcc->priv->during_send = FALSE; + g_object_unref(rcc); } int red_channel_client_get_roundtrip_ms(RedChannelClient *rcc) { - if (rcc->latency_monitor.roundtrip < 0) { - return rcc->latency_monitor.roundtrip; + if (rcc->priv->latency_monitor.roundtrip < 0) { + return rcc->priv->latency_monitor.roundtrip; } - return rcc->latency_monitor.roundtrip / NSEC_PER_MILLISEC; + return rcc->priv->latency_monitor.roundtrip / NSEC_PER_MILLISEC; } void red_channel_client_init_outgoing_messages_window(RedChannelClient *rcc) { - rcc->ack_data.messages_window = 0; + rcc->priv->ack_data.messages_window = 0; red_channel_client_push(rcc); } @@ -1110,27 +1286,27 @@ static void red_channel_client_handle_pong(RedChannelClient *rcc, SpiceMsgPing * /* ignoring unexpected pongs, or post-migration pongs for pings that * started just before migration */ - if (ping->id != rcc->latency_monitor.id) { + if (ping->id != rcc->priv->latency_monitor.id) { spice_warning("ping-id (%u)!= pong-id %u", - rcc->latency_monitor.id, ping->id); + rcc->priv->latency_monitor.id, ping->id); return; } now = spice_get_monotonic_time_ns(); - if (rcc->latency_monitor.state == PING_STATE_WARMUP) { - rcc->latency_monitor.state = PING_STATE_LATENCY; + if (rcc->priv->latency_monitor.state == PING_STATE_WARMUP) { + rcc->priv->latency_monitor.state = PING_STATE_LATENCY; return; - } else if (rcc->latency_monitor.state != PING_STATE_LATENCY) { + } else if (rcc->priv->latency_monitor.state != PING_STATE_LATENCY) { spice_warning("unexpected"); return; } /* set TCP_NODELAY=0, in case we reverted it for the test*/ - if (!rcc->latency_monitor.tcp_nodelay) { + if (!rcc->priv->latency_monitor.tcp_nodelay) { int delay_val = 0; - if (setsockopt(rcc->stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, + if (setsockopt(rcc->priv->stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) { if (errno != ENOTSUP) { spice_warning("setsockopt failed, %s", strerror(errno)); @@ -1144,14 +1320,14 @@ static void red_channel_client_handle_pong(RedChannelClient *rcc, SpiceMsgPing * * threads or processes that are utilizing the network. We update the roundtrip * measurement with the minimal value we encountered till now. */ - if (rcc->latency_monitor.roundtrip < 0 || - now - ping->timestamp < rcc->latency_monitor.roundtrip) { - rcc->latency_monitor.roundtrip = now - ping->timestamp; - spice_debug("update roundtrip %.2f(ms)", ((double)rcc->latency_monitor.roundtrip)/NSEC_PER_MILLISEC); + if (rcc->priv->latency_monitor.roundtrip < 0 || + now - ping->timestamp < rcc->priv->latency_monitor.roundtrip) { + rcc->priv->latency_monitor.roundtrip = now - ping->timestamp; + spice_debug("update roundtrip %.2f(ms)", ((double)rcc->priv->latency_monitor.roundtrip)/NSEC_PER_MILLISEC); } - rcc->latency_monitor.last_pong_time = now; - rcc->latency_monitor.state = PING_STATE_NONE; + rcc->priv->latency_monitor.last_pong_time = now; + rcc->priv->latency_monitor.state = PING_STATE_NONE; red_channel_client_start_ping_timer(rcc, PING_TEST_TIMEOUT_MS); } @@ -1203,23 +1379,23 @@ int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size, spice_printerr("bad message size"); return FALSE; } - rcc->ack_data.client_generation = *(uint32_t *)(message); + rcc->priv->ack_data.client_generation = *(uint32_t *)(message); break; case SPICE_MSGC_ACK: - if (rcc->ack_data.client_generation == rcc->ack_data.generation) { - rcc->ack_data.messages_window -= rcc->ack_data.client_window; + if (rcc->priv->ack_data.client_generation == rcc->priv->ack_data.generation) { + rcc->priv->ack_data.messages_window -= rcc->priv->ack_data.client_window; red_channel_client_push(rcc); } break; case SPICE_MSGC_DISCONNECTING: break; case SPICE_MSGC_MIGRATE_FLUSH_MARK: - if (!rcc->wait_migrate_flush_mark) { + if (!rcc->priv->wait_migrate_flush_mark) { spice_error("unexpected flush mark"); return FALSE; } red_channel_handle_migrate_flush_mark(rcc); - rcc->wait_migrate_flush_mark = FALSE; + rcc->priv->wait_migrate_flush_mark = FALSE; break; case SPICE_MSGC_MIGRATE_DATA: red_channel_handle_migrate_data(rcc, size, message); @@ -1238,19 +1414,19 @@ void red_channel_client_init_send_data(RedChannelClient *rcc, uint16_t msg_type, { spice_assert(red_channel_client_no_item_being_sent(rcc)); spice_assert(msg_type != 0); - rcc->send_data.header.set_msg_type(&rcc->send_data.header, msg_type); - rcc->send_data.item = item; + rcc->priv->send_data.header.set_msg_type(&rcc->priv->send_data.header, msg_type); + rcc->priv->send_data.item = item; if (item) { - rcc->channel->channel_cbs.hold_item(rcc, item); + rcc->priv->channel->channel_cbs.hold_item(rcc, item); } } void red_channel_client_begin_send_message(RedChannelClient *rcc) { - SpiceMarshaller *m = rcc->send_data.marshaller; + SpiceMarshaller *m = rcc->priv->send_data.marshaller; // TODO - better check: type in channel_allowed_types. Better: type in channel_allowed_types(channel_state) - if (rcc->send_data.header.get_msg_type(&rcc->send_data.header) == 0) { + if (rcc->priv->send_data.header.get_msg_type(&rcc->priv->send_data.header) == 0) { spice_printerr("BUG: header->type == 0"); return; } @@ -1259,37 +1435,37 @@ void red_channel_client_begin_send_message(RedChannelClient *rcc) red_channel_client_cancel_ping_timer(rcc); spice_marshaller_flush(m); - rcc->send_data.size = spice_marshaller_get_total_size(m); - rcc->send_data.header.set_msg_size(&rcc->send_data.header, - rcc->send_data.size - rcc->send_data.header.header_size); - rcc->ack_data.messages_window++; - rcc->send_data.last_sent_serial = rcc->send_data.serial; - rcc->send_data.header.data = NULL; /* avoid writing to this until we have a new message */ + rcc->priv->send_data.size = spice_marshaller_get_total_size(m); + rcc->priv->send_data.header.set_msg_size(&rcc->priv->send_data.header, + rcc->priv->send_data.size - rcc->priv->send_data.header.header_size); + rcc->priv->ack_data.messages_window++; + rcc->priv->send_data.last_sent_serial = rcc->priv->send_data.serial; + rcc->priv->send_data.header.data = NULL; /* avoid writing to this until we have a new message */ red_channel_client_send(rcc); } SpiceMarshaller *red_channel_client_switch_to_urgent_sender(RedChannelClient *rcc) { spice_assert(red_channel_client_no_item_being_sent(rcc)); - spice_assert(rcc->send_data.header.data != NULL); - rcc->send_data.main.header_data = rcc->send_data.header.data; - rcc->send_data.main.item = rcc->send_data.item; + spice_assert(rcc->priv->send_data.header.data != NULL); + rcc->priv->send_data.main.header_data = rcc->priv->send_data.header.data; + rcc->priv->send_data.main.item = rcc->priv->send_data.item; - rcc->send_data.marshaller = rcc->send_data.urgent.marshaller; - rcc->send_data.item = NULL; + rcc->priv->send_data.marshaller = rcc->priv->send_data.urgent.marshaller; + rcc->priv->send_data.item = NULL; red_channel_client_reset_send_data(rcc); - return rcc->send_data.marshaller; + return rcc->priv->send_data.marshaller; } uint64_t red_channel_client_get_message_serial(RedChannelClient *rcc) { - return rcc->send_data.serial; + return rcc->priv->send_data.serial; } void red_channel_client_set_message_serial(RedChannelClient *rcc, uint64_t serial) { - rcc->send_data.last_sent_serial = serial; - rcc->send_data.serial = serial; + rcc->priv->send_data.last_sent_serial = serial; + rcc->priv->send_data.serial = serial; } static inline gboolean client_pipe_add(RedChannelClient *rcc, RedPipeItem *item, RingItem *pos) @@ -1300,12 +1476,12 @@ static inline gboolean client_pipe_add(RedChannelClient *rcc, RedPipeItem *item, red_channel_client_release_item(rcc, item, FALSE); return FALSE; } - if (ring_is_empty(&rcc->pipe) && rcc->stream->watch) { - rcc->channel->core->watch_update_mask(rcc->stream->watch, + 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); } - rcc->pipe_size++; + rcc->priv->pipe_size++; ring_add(pos, &item->link); return TRUE; } @@ -1313,7 +1489,7 @@ static inline gboolean client_pipe_add(RedChannelClient *rcc, RedPipeItem *item, void red_channel_client_pipe_add(RedChannelClient *rcc, RedPipeItem *item) { - client_pipe_add(rcc, item, &rcc->pipe); + client_pipe_add(rcc, item, &rcc->priv->pipe); } void red_channel_client_pipe_add_push(RedChannelClient *rcc, RedPipeItem *item) @@ -1339,12 +1515,12 @@ int red_channel_client_pipe_item_is_linked(RedChannelClient *rcc, void red_channel_client_pipe_add_tail(RedChannelClient *rcc, RedPipeItem *item) { - client_pipe_add(rcc, item, rcc->pipe.prev); + client_pipe_add(rcc, item, rcc->priv->pipe.prev); } void red_channel_client_pipe_add_tail_and_push(RedChannelClient *rcc, RedPipeItem *item) { - if (client_pipe_add(rcc, item, rcc->pipe.prev)) { + if (client_pipe_add(rcc, item, rcc->priv->pipe.prev)) { red_channel_client_push(rcc); } } @@ -1371,32 +1547,36 @@ void red_channel_client_pipe_add_empty_msg(RedChannelClient *rcc, int msg_type) gboolean red_channel_client_pipe_is_empty(RedChannelClient *rcc) { g_return_val_if_fail(rcc != NULL, TRUE); - return (rcc->pipe_size == 0) && (ring_is_empty(&rcc->pipe)); + return (rcc->priv->pipe_size == 0) && (ring_is_empty(&rcc->priv->pipe)); } uint32_t red_channel_client_get_pipe_size(RedChannelClient *rcc) { - return rcc->pipe_size; + return rcc->priv->pipe_size; } -int red_channel_client_is_connected(RedChannelClient *rcc) +static gboolean red_channel_client_default_is_connected(RedChannelClient *rcc) { - if (!rcc->dummy) { - return rcc->channel - && (g_list_find(rcc->channel->clients, rcc) != NULL); - } else { - return rcc->dummy_connected; - } + return rcc->priv->channel + && (g_list_find(rcc->priv->channel->clients, rcc) != NULL); +} + +gboolean red_channel_client_is_connected(RedChannelClient *rcc) +{ + RedChannelClientClass *klass = RED_CHANNEL_CLIENT_GET_CLASS(rcc); + + g_return_val_if_fail(klass->is_connected != NULL, FALSE); + return klass->is_connected(rcc); } static void red_channel_client_clear_sent_item(RedChannelClient *rcc) { - if (rcc->send_data.item) { - red_channel_client_release_item(rcc, rcc->send_data.item, TRUE); - rcc->send_data.item = NULL; + if (rcc->priv->send_data.item) { + red_channel_client_release_item(rcc, rcc->priv->send_data.item, TRUE); + rcc->priv->send_data.item = NULL; } - rcc->send_data.blocked = FALSE; - rcc->send_data.size = 0; + rcc->priv->send_data.blocked = FALSE; + rcc->priv->send_data.size = 0; } void red_channel_client_pipe_clear(RedChannelClient *rcc) @@ -1406,21 +1586,21 @@ void red_channel_client_pipe_clear(RedChannelClient *rcc) if (rcc) { red_channel_client_clear_sent_item(rcc); } - while ((item = (RedPipeItem *)ring_get_head(&rcc->pipe))) { + while ((item = (RedPipeItem *)ring_get_head(&rcc->priv->pipe))) { ring_remove(&item->link); red_channel_client_release_item(rcc, item, FALSE); } - rcc->pipe_size = 0; + rcc->priv->pipe_size = 0; } void red_channel_client_ack_zero_messages_window(RedChannelClient *rcc) { - rcc->ack_data.messages_window = 0; + rcc->priv->ack_data.messages_window = 0; } void red_channel_client_ack_set_client_window(RedChannelClient *rcc, int client_window) { - rcc->ack_data.client_window = client_window; + rcc->priv->ack_data.client_window = client_window; } void red_channel_client_push_set_ack(RedChannelClient *rcc) @@ -1428,77 +1608,68 @@ void red_channel_client_push_set_ack(RedChannelClient *rcc) red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SET_ACK); } -static void red_channel_client_disconnect_dummy(RedChannelClient *rcc) +static void red_channel_client_default_disconnect(RedChannelClient *rcc) { - RedChannel *channel = red_channel_client_get_channel(rcc); - GList *link; - spice_assert(rcc->dummy); - if (channel && (link = g_list_find(channel->clients, rcc))) { - spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel, - channel->type, channel->id); - red_channel_remove_client(channel, link->data); - } - rcc->dummy_connected = FALSE; -} + RedChannel *channel = rcc->priv->channel; -void red_channel_client_disconnect(RedChannelClient *rcc) -{ - RedChannel *channel = rcc->channel; - - if (rcc->dummy) { - red_channel_client_disconnect_dummy(rcc); - return; - } if (!red_channel_client_is_connected(rcc)) { return; } spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel, channel->type, channel->id); red_channel_client_pipe_clear(rcc); - if (rcc->stream->watch) { - channel->core->watch_remove(rcc->stream->watch); - rcc->stream->watch = NULL; + if (rcc->priv->stream->watch) { + channel->core->watch_remove(rcc->priv->stream->watch); + rcc->priv->stream->watch = NULL; } - if (rcc->latency_monitor.timer) { - channel->core->timer_remove(rcc->latency_monitor.timer); - rcc->latency_monitor.timer = NULL; + if (rcc->priv->latency_monitor.timer) { + channel->core->timer_remove(rcc->priv->latency_monitor.timer); + rcc->priv->latency_monitor.timer = NULL; } - if (rcc->connectivity_monitor.timer) { - channel->core->timer_remove(rcc->connectivity_monitor.timer); - rcc->connectivity_monitor.timer = NULL; + if (rcc->priv->connectivity_monitor.timer) { + channel->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); } +void red_channel_client_disconnect(RedChannelClient *rcc) +{ + RedChannelClientClass *klass = RED_CHANNEL_CLIENT_GET_CLASS(rcc); + + g_return_if_fail(klass->is_connected != NULL); + klass->disconnect(rcc); +} + int red_channel_client_is_blocked(RedChannelClient *rcc) { - return rcc && rcc->send_data.blocked; + return rcc && rcc->priv->send_data.blocked; } int red_channel_client_send_message_pending(RedChannelClient *rcc) { - return rcc->send_data.header.get_msg_type(&rcc->send_data.header) != 0; + return rcc->priv->send_data.header.get_msg_type(&rcc->priv->send_data.header) != 0; } SpiceMarshaller *red_channel_client_get_marshaller(RedChannelClient *rcc) { - return rcc->send_data.marshaller; + return rcc->priv->send_data.marshaller; } RedsStream *red_channel_client_get_stream(RedChannelClient *rcc) { - return rcc->stream; + return rcc->priv->stream; } RedClient *red_channel_client_get_client(RedChannelClient *rcc) { - return rcc->client; + return rcc->priv->client; } void red_channel_client_set_header_sub_list(RedChannelClient *rcc, uint32_t sub_list) { - rcc->send_data.header.set_msg_sub_list(&rcc->send_data.header, sub_list); + rcc->priv->send_data.header.set_msg_sub_list(&rcc->priv->send_data.header, sub_list); } /* TODO: more evil sync stuff. anything with the word wait in it's name. */ @@ -1517,7 +1688,7 @@ int red_channel_client_wait_pipe_item_sent(RedChannelClient *rcc, end_time = UINT64_MAX; } - rcc->channel->channel_cbs.hold_item(rcc, item); + rcc->priv->channel->channel_cbs.hold_item(rcc, item); if (red_channel_client_is_blocked(rcc)) { red_channel_client_receive(rcc); @@ -1577,7 +1748,7 @@ int red_channel_client_wait_outgoing_item(RedChannelClient *rcc, void red_channel_client_disconnect_if_pending_send(RedChannelClient *rcc) { - if (red_channel_client_is_blocked(rcc) || rcc->pipe_size > 0) { + if (red_channel_client_is_blocked(rcc) || rcc->priv->pipe_size > 0) { red_channel_client_disconnect(rcc); } else { spice_assert(red_channel_client_no_item_being_sent(rcc)); @@ -1586,7 +1757,7 @@ void red_channel_client_disconnect_if_pending_send(RedChannelClient *rcc) gboolean red_channel_client_no_item_being_sent(RedChannelClient *rcc) { - return !rcc || (rcc->send_data.size == 0); + return !rcc || (rcc->priv->send_data.size == 0); } void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc, @@ -1601,22 +1772,27 @@ gboolean red_channel_client_set_migration_seamless(RedChannelClient *rcc) { gboolean ret = FALSE; - if (rcc->channel->migration_flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) { - rcc->wait_migrate_data = TRUE; + if (rcc->priv->channel->migration_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->channel->type, rcc->channel->id, rcc, - rcc->wait_migrate_data); + 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); return ret; } void red_channel_client_set_destroying(RedChannelClient *rcc) { - rcc->destroying = TRUE; + rcc->priv->destroying = TRUE; } gboolean red_channel_client_is_destroying(RedChannelClient *rcc) { - return rcc->destroying; + return rcc->priv->destroying; +} + +GQuark spice_server_error_quark(void) +{ + return g_quark_from_static_string("spice-server-error-quark"); } diff --git a/server/red-channel-client.h b/server/red-channel-client.h index cf70abd..d8cc317 100644 --- a/server/red-channel-client.h +++ b/server/red-channel-client.h @@ -18,15 +18,141 @@ #ifndef _H_RED_CHANNEL_CLIENT #define _H_RED_CHANNEL_CLIENT +#include <glib-object.h> +#include <gio/gio.h> + +#include <spice/protocol.h> + #include "common/marshaller.h" +#include "demarshallers.h" #include "red-pipe-item.h" #include "reds-stream.h" +G_BEGIN_DECLS + +#define MAX_HEADER_SIZE sizeof(SpiceDataHeader) +#define CLIENT_ACK_WINDOW 20 + +#ifndef IOV_MAX +#define IOV_MAX 1024 +#endif + + +#define RED_TYPE_CHANNEL_CLIENT red_channel_client_get_type() + +#define RED_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHANNEL_CLIENT, RedChannelClient)) +#define RED_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_CHANNEL_CLIENT, RedChannelClientClass)) +#define RED_IS_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_CHANNEL_CLIENT)) +#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 RedChannelClient RedChannelClient; +typedef struct RedChannelClientClass RedChannelClientClass; +typedef struct RedChannelClientPrivate RedChannelClientPrivate; + +typedef struct SpiceDataHeaderOpaque SpiceDataHeaderOpaque; + +typedef uint16_t (*get_msg_type_proc)(SpiceDataHeaderOpaque *header); +typedef uint32_t (*get_msg_size_proc)(SpiceDataHeaderOpaque *header); +typedef void (*set_msg_type_proc)(SpiceDataHeaderOpaque *header, uint16_t type); +typedef void (*set_msg_size_proc)(SpiceDataHeaderOpaque *header, uint32_t size); +typedef void (*set_msg_serial_proc)(SpiceDataHeaderOpaque *header, uint64_t serial); +typedef void (*set_msg_sub_list_proc)(SpiceDataHeaderOpaque *header, uint32_t sub_list); + +struct SpiceDataHeaderOpaque { + uint8_t *data; + uint16_t header_size; + + set_msg_type_proc set_msg_type; + set_msg_size_proc set_msg_size; + set_msg_serial_proc set_msg_serial; + set_msg_sub_list_proc set_msg_sub_list; + + get_msg_type_proc get_msg_type; + get_msg_size_proc get_msg_size; +}; + +typedef int (*handle_message_proc)(void *opaque, + uint16_t type, uint32_t size, uint8_t *msg); +typedef int (*handle_parsed_proc)(void *opaque, uint32_t size, uint16_t type, void *message); +typedef uint8_t *(*alloc_msg_recv_buf_proc)(void *opaque, uint16_t type, uint32_t size); +typedef void (*release_msg_recv_buf_proc)(void *opaque, + uint16_t type, uint32_t size, uint8_t *msg); +typedef void (*on_incoming_error_proc)(void *opaque); +typedef void (*on_input_proc)(void *opaque, int n); + +typedef struct IncomingHandlerInterface { + handle_message_proc handle_message; + alloc_msg_recv_buf_proc alloc_msg_buf; + on_incoming_error_proc on_error; // recv error or handle_message error + release_msg_recv_buf_proc release_msg_buf; // for errors + // The following is an optional alternative to handle_message, used if not null + spice_parse_channel_func_t parser; + handle_parsed_proc handle_parsed; + on_input_proc on_input; +} IncomingHandlerInterface; + +typedef struct IncomingHandler { + IncomingHandlerInterface *cb; + void *opaque; + uint8_t header_buf[MAX_HEADER_SIZE]; + SpiceDataHeaderOpaque header; + uint32_t header_pos; + uint8_t *msg; // data of the msg following the header. allocated by alloc_msg_buf. + uint32_t msg_pos; + uint64_t serial; +} IncomingHandler; + +typedef int (*get_outgoing_msg_size_proc)(void *opaque); +typedef void (*prepare_outgoing_proc)(void *opaque, struct iovec *vec, int *vec_size, int pos); +typedef void (*on_outgoing_error_proc)(void *opaque); +typedef void (*on_outgoing_block_proc)(void *opaque); +typedef void (*on_outgoing_msg_done_proc)(void *opaque); +typedef void (*on_output_proc)(void *opaque, int n); + +typedef struct OutgoingHandlerInterface { + get_outgoing_msg_size_proc get_msg_size; + prepare_outgoing_proc prepare; + on_outgoing_error_proc on_error; + on_outgoing_block_proc on_block; + on_outgoing_msg_done_proc on_msg_done; + on_output_proc on_output; +} OutgoingHandlerInterface; + +typedef struct OutgoingHandler { + OutgoingHandlerInterface *cb; + void *opaque; + struct iovec vec_buf[IOV_MAX]; + int vec_size; + struct iovec *vec; + int pos; + int size; +} OutgoingHandler; + +struct RedChannelClient +{ + GObject parent; + + /* protected */ + OutgoingHandler outgoing; + IncomingHandler incoming; + + RedChannelClientPrivate *priv; +}; + +struct RedChannelClientClass +{ + GObjectClass parent_class; + + gboolean (*is_connected)(RedChannelClient *rcc); + void (*disconnect)(RedChannelClient *rcc); +}; + +GType red_channel_client_get_type(void) G_GNUC_CONST; /* * When an error occurs over a channel, we treat it as a warning @@ -40,22 +166,13 @@ typedef struct RedChannelClient RedChannelClient; red_channel_client_shutdown(rcc); \ } while (0) -RedChannelClient *red_channel_client_create(int size, RedChannel *channel, +RedChannelClient *red_channel_client_create(RedChannel *channel, RedClient *client, RedsStream *stream, int monitor_latency, int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps); -RedChannelClient *red_channel_client_create_dummy(int size, - RedChannel *channel, - RedClient *client, - int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps); - -void red_channel_client_ref(RedChannelClient *rcc); -void red_channel_client_unref(RedChannelClient *rcc); - -int red_channel_client_is_connected(RedChannelClient *rcc); +gboolean red_channel_client_is_connected(RedChannelClient *rcc); void red_channel_client_default_migrate(RedChannelClient *rcc); int red_channel_client_is_waiting_for_migrate_data(RedChannelClient *rcc); void red_channel_client_destroy(RedChannelClient *rcc); @@ -171,6 +288,28 @@ gboolean red_channel_client_set_migration_seamless(RedChannelClient *rcc); void red_channel_client_set_destroying(RedChannelClient *rcc); gboolean red_channel_client_is_destroying(RedChannelClient *rcc); -#define RED_CHANNEL_CLIENT(Client) ((RedChannelClient *)(Client)) +#define SPICE_SERVER_ERROR spice_server_error_quark() +GQuark spice_server_error_quark(void); + +typedef enum +{ + SPICE_SERVER_ERROR_FAILED +} SpiceServerError; + +/* Messages handled by red_channel + * SET_ACK - sent to client on channel connection + * Note that the numbers don't have to correspond to spice message types, + * but we keep the 100 first allocated for base channel approach. + * */ +enum { + RED_PIPE_ITEM_TYPE_SET_ACK=1, + RED_PIPE_ITEM_TYPE_MIGRATE, + RED_PIPE_ITEM_TYPE_EMPTY_MSG, + RED_PIPE_ITEM_TYPE_PING, + + RED_PIPE_ITEM_TYPE_CHANNEL_BASE=101, +}; + +G_END_DECLS #endif /* _H_RED_CHANNEL_CLIENT */ diff --git a/server/red-channel.h b/server/red-channel.h index af6be18..5c1d555 100644 --- a/server/red-channel.h +++ b/server/red-channel.h @@ -30,123 +30,16 @@ #include "spice.h" #include "red-common.h" -#include "demarshallers.h" +#include "main-channel-client.h" #include "reds-stream.h" #include "stat.h" #include "red-pipe-item.h" #include "red-channel-client.h" -#define MAX_SEND_BUFS 1000 -#define CLIENT_ACK_WINDOW 20 - -#ifndef IOV_MAX -#define IOV_MAX 1024 -#endif - -#define MAX_HEADER_SIZE sizeof(SpiceDataHeader) - -/* Basic interface for channels, without using the RedChannel interface. - The intention is to move towards one channel interface gradually. - At the final stage, this interface shouldn't be exposed. Only RedChannel will use it. */ - -typedef struct SpiceDataHeaderOpaque SpiceDataHeaderOpaque; - -typedef uint16_t (*get_msg_type_proc)(SpiceDataHeaderOpaque *header); -typedef uint32_t (*get_msg_size_proc)(SpiceDataHeaderOpaque *header); -typedef void (*set_msg_type_proc)(SpiceDataHeaderOpaque *header, uint16_t type); -typedef void (*set_msg_size_proc)(SpiceDataHeaderOpaque *header, uint32_t size); -typedef void (*set_msg_serial_proc)(SpiceDataHeaderOpaque *header, uint64_t serial); -typedef void (*set_msg_sub_list_proc)(SpiceDataHeaderOpaque *header, uint32_t sub_list); - -struct SpiceDataHeaderOpaque { - uint8_t *data; - uint16_t header_size; - - set_msg_type_proc set_msg_type; - set_msg_size_proc set_msg_size; - set_msg_serial_proc set_msg_serial; - set_msg_sub_list_proc set_msg_sub_list; - - get_msg_type_proc get_msg_type; - get_msg_size_proc get_msg_size; -}; - -typedef int (*handle_message_proc)(void *opaque, - uint16_t type, uint32_t size, uint8_t *msg); -typedef int (*handle_parsed_proc)(void *opaque, uint32_t size, uint16_t type, void *message); -typedef uint8_t *(*alloc_msg_recv_buf_proc)(void *opaque, uint16_t type, uint32_t size); -typedef void (*release_msg_recv_buf_proc)(void *opaque, - uint16_t type, uint32_t size, uint8_t *msg); -typedef void (*on_incoming_error_proc)(void *opaque); -typedef void (*on_input_proc)(void *opaque, int n); - -typedef struct IncomingHandlerInterface { - handle_message_proc handle_message; - alloc_msg_recv_buf_proc alloc_msg_buf; - on_incoming_error_proc on_error; // recv error or handle_message error - release_msg_recv_buf_proc release_msg_buf; // for errors - // The following is an optional alternative to handle_message, used if not null - spice_parse_channel_func_t parser; - handle_parsed_proc handle_parsed; - on_input_proc on_input; -} IncomingHandlerInterface; - -typedef struct IncomingHandler { - IncomingHandlerInterface *cb; - void *opaque; - uint8_t header_buf[MAX_HEADER_SIZE]; - SpiceDataHeaderOpaque header; - uint32_t header_pos; - uint8_t *msg; // data of the msg following the header. allocated by alloc_msg_buf. - uint32_t msg_pos; - uint64_t serial; -} IncomingHandler; - -typedef int (*get_outgoing_msg_size_proc)(void *opaque); -typedef void (*prepare_outgoing_proc)(void *opaque, struct iovec *vec, int *vec_size, int pos); -typedef void (*on_outgoing_error_proc)(void *opaque); -typedef void (*on_outgoing_block_proc)(void *opaque); -typedef void (*on_outgoing_msg_done_proc)(void *opaque); -typedef void (*on_output_proc)(void *opaque, int n); - -typedef struct OutgoingHandlerInterface { - get_outgoing_msg_size_proc get_msg_size; - prepare_outgoing_proc prepare; - on_outgoing_error_proc on_error; - on_outgoing_block_proc on_block; - on_outgoing_msg_done_proc on_msg_done; - on_output_proc on_output; -} OutgoingHandlerInterface; - -typedef struct OutgoingHandler { - OutgoingHandlerInterface *cb; - void *opaque; - struct iovec vec_buf[IOV_MAX]; - int vec_size; - struct iovec *vec; - int pos; - int size; -} OutgoingHandler; - /* Red Channel interface */ typedef struct RedChannel RedChannel; typedef struct RedClient RedClient; -typedef struct MainChannelClient MainChannelClient; - -/* Messages handled by red_channel - * SET_ACK - sent to client on channel connection - * Note that the numbers don't have to correspond to spice message types, - * but we keep the 100 first allocated for base channel approach. - * */ -enum { - RED_PIPE_ITEM_TYPE_SET_ACK=1, - RED_PIPE_ITEM_TYPE_MIGRATE, - RED_PIPE_ITEM_TYPE_EMPTY_MSG, - RED_PIPE_ITEM_TYPE_PING, - - RED_PIPE_ITEM_TYPE_CHANNEL_BASE=101, -}; typedef uint8_t *(*channel_alloc_msg_recv_buf_proc)(RedChannelClient *channel, uint16_t type, uint32_t size); diff --git a/server/red-worker.h b/server/red-worker.h index 5afc4e0..501128b 100644 --- a/server/red-worker.h +++ b/server/red-worker.h @@ -25,7 +25,6 @@ typedef struct RedWorker RedWorker; -#define COMMON_GRAPHICS_CHANNEL_CLIENT(Client) ((CommonGraphicsChannelClient*)(Client)) #define COMMON_CLIENT_TIMEOUT (NSEC_PER_SEC * 30) #define CHANNEL_RECEIVE_BUF_SIZE 1024 diff --git a/server/reds.c b/server/reds.c index b89d6ee..9f5551e 100644 --- a/server/reds.c +++ b/server/reds.c @@ -937,8 +937,7 @@ static void vdi_port_on_free_self_token(void *opaque) static void vdi_port_remove_client(RedClient *client, void *opaque) { - red_channel_client_shutdown(main_channel_client_get_base( - red_client_get_main(client))); + red_channel_client_shutdown(RED_CHANNEL_CLIENT(red_client_get_main(client))); } /****************************************************************************/ @@ -1052,7 +1051,7 @@ void reds_on_main_agent_start(RedsState *reds, MainChannelClient *mcc, uint32_t return; } spice_assert(reds->vdagent->st && reds->vdagent->st == dev_state); - rcc = main_channel_client_get_base(mcc); + rcc = RED_CHANNEL_CLIENT(mcc); client = red_channel_client_get_client(rcc); reds->agent_dev->priv->client_agent_started = TRUE; /* @@ -1092,7 +1091,7 @@ void reds_on_main_agent_start(RedsState *reds, MainChannelClient *mcc, uint32_t void reds_on_main_agent_tokens(RedsState *reds, MainChannelClient *mcc, uint32_t num_tokens) { - RedClient *client = red_channel_client_get_client(main_channel_client_get_base(mcc)); + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(mcc)); if (!reds->vdagent) { return; } @@ -1119,7 +1118,7 @@ uint8_t *reds_get_agent_data_buffer(RedsState *reds, MainChannelClient *mcc, siz } spice_assert(dev->priv->recv_from_client_buf == NULL); - client = red_channel_client_get_client(main_channel_client_get_base(mcc)); + client = red_channel_client_get_client(RED_CHANNEL_CLIENT(mcc)); dev->priv->recv_from_client_buf = red_char_device_write_buffer_get(RED_CHAR_DEVICE(dev), client, size + sizeof(VDIChunkHeader)); @@ -1197,7 +1196,7 @@ void reds_on_main_agent_data(RedsState *reds, MainChannelClient *mcc, void *mess reds_on_main_agent_monitors_config(reds, mcc, message, size); return; case AGENT_MSG_FILTER_PROTO_ERROR: - red_channel_client_shutdown(main_channel_client_get_base(mcc)); + red_channel_client_shutdown(RED_CHANNEL_CLIENT(mcc)); return; } @@ -1484,7 +1483,7 @@ int reds_handle_migrate_data(RedsState *reds, MainChannelClient *mcc, } else { spice_debug("agent was not attached on the source host"); if (reds->vdagent) { - RedClient *client = red_channel_client_get_client(main_channel_client_get_base(mcc)); + RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(mcc)); /* red_char_device_client_remove disables waiting for migration data */ red_char_device_client_remove(RED_CHAR_DEVICE(agent_dev), client); main_channel_push_agent_connected(reds->main_channel); @@ -1948,7 +1947,7 @@ int reds_on_migrate_dst_set_seamless(RedsState *reds, MainChannelClient *mcc, ui if (reds->allow_multiple_clients || src_version > SPICE_MIGRATION_PROTOCOL_VERSION) { reds->dst_do_seamless_migrate = FALSE; } else { - RedChannelClient *rcc = main_channel_client_get_base(mcc); + RedChannelClient *rcc = RED_CHANNEL_CLIENT(mcc); RedClient *client = red_channel_client_get_client(rcc); red_client_set_migration_seamless(client); diff --git a/server/reds.h b/server/reds.h index fbf7372..5cce2c8 100644 --- a/server/reds.h +++ b/server/reds.h @@ -29,6 +29,7 @@ #include "char-device.h" #include "spice.h" #include "red-channel.h" +#include "main-channel-client.h" #include "main-dispatcher.h" #include "migration-protocol.h" diff --git a/server/smartcard-channel-client.c b/server/smartcard-channel-client.c new file mode 100644 index 0000000..a7264e9 --- /dev/null +++ b/server/smartcard-channel-client.c @@ -0,0 +1,417 @@ +/* + 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/>. +*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "red-channel-client-private.h" +#include "smartcard-channel-client.h" + +G_DEFINE_TYPE(SmartCardChannelClient, smart_card_channel_client, RED_TYPE_CHANNEL_CLIENT) + +#define SMARTCARD_CHANNEL_CLIENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_SMARTCARD_CHANNEL_CLIENT, SmartCardChannelClientPrivate)) + +struct SmartCardChannelClientPrivate +{ + RedChannelClient base; + RedCharDeviceSmartcard *smartcard; + + /* read_from_client/write_to_device buffer. + * The beginning of the buffer should always be VSCMsgHeader*/ + RedCharDeviceWriteBuffer *write_buf; + int msg_in_write_buf; /* was the client msg received into a RedCharDeviceWriteBuffer + * or was it explicitly malloced */ +}; + +typedef struct RedErrorItem { + RedPipeItem base; + VSCMsgHeader vheader; + VSCMsgError error; +} RedErrorItem; + +static void smart_card_channel_client_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 smart_card_channel_client_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 smart_card_channel_client_dispose(GObject *object) +{ + G_OBJECT_CLASS(smart_card_channel_client_parent_class)->dispose(object); +} + +static void smart_card_channel_client_finalize(GObject *object) +{ + SmartCardChannelClient *self = SMARTCARD_CHANNEL_CLIENT(object); + + if (self->priv->smartcard) + g_object_remove_weak_pointer(G_OBJECT(self->priv->smartcard), + (gpointer*)&self->priv->smartcard); + G_OBJECT_CLASS(smart_card_channel_client_parent_class)->finalize(object); +} + +static void smart_card_channel_client_class_init(SmartCardChannelClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(SmartCardChannelClientPrivate)); + + object_class->get_property = smart_card_channel_client_get_property; + object_class->set_property = smart_card_channel_client_set_property; + object_class->dispose = smart_card_channel_client_dispose; + object_class->finalize = smart_card_channel_client_finalize; +} + +static void +smart_card_channel_client_init(SmartCardChannelClient *self) +{ + self->priv = SMARTCARD_CHANNEL_CLIENT_PRIVATE(self); +} + +SmartCardChannelClient* smartcard_channel_client_create(RedChannel *channel, + RedClient *client, RedsStream *stream, + int monitor_latency, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps) +{ + SmartCardChannelClient *rcc; + GArray *common_caps_array = NULL, *caps_array = NULL; + + if (common_caps) { + common_caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*common_caps), num_common_caps); + g_array_append_vals(common_caps_array, common_caps, num_common_caps); + } + if (caps) { + caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*caps), num_caps); + g_array_append_vals(caps_array, caps, num_caps); + } + + rcc = g_initable_new(RED_TYPE_CHANNEL_CLIENT, + NULL, NULL, + "channel", channel, + "client", client, + "stream", stream, + "monitor-latency", monitor_latency, + "caps", caps_array, + "common-caps", common_caps_array, + NULL); + + if (caps_array) + g_array_unref(caps_array); + if (common_caps_array) + g_array_unref(common_caps_array); + + return rcc; +} + +uint8_t *smartcard_channel_client_alloc_msg_rcv_buf(RedChannelClient *rcc, + uint16_t type, + uint32_t size) +{ + SmartCardChannelClient *scc = SMARTCARD_CHANNEL_CLIENT(rcc); + RedClient *client = red_channel_client_get_client(rcc); + + /* todo: only one reader is actually supported. When we fix the code to support + * multiple readers, we will porbably associate different devices to + * differenc channels */ + if (!scc->priv->smartcard) { + scc->priv->msg_in_write_buf = FALSE; + return spice_malloc(size); + } else { + RedCharDeviceSmartcard *smartcard; + + spice_assert(smartcard_get_n_readers() == 1); + smartcard = scc->priv->smartcard; + spice_assert(smartcard_char_device_get_client(smartcard) || scc->priv->smartcard); + spice_assert(!scc->priv->write_buf); + scc->priv->write_buf = + red_char_device_write_buffer_get(RED_CHAR_DEVICE(smartcard), client, + size); + + if (!scc->priv->write_buf) { + spice_error("failed to allocate write buffer"); + return NULL; + } + scc->priv->msg_in_write_buf = TRUE; + return scc->priv->write_buf->buf; + } +} + +void smartcard_channel_client_release_msg_rcv_buf(RedChannelClient *rcc, + uint16_t type, + uint32_t size, + uint8_t *msg) +{ + SmartCardChannelClient *scc = SMARTCARD_CHANNEL_CLIENT(rcc); + + /* todo: only one reader is actually supported. When we fix the code to support + * multiple readers, we will porbably associate different devices to + * differenc channels */ + + if (!scc->priv->msg_in_write_buf) { + spice_assert(!scc->priv->write_buf); + free(msg); + } else { + if (scc->priv->write_buf) { /* msg hasn't been pushed to the guest */ + spice_assert(scc->priv->write_buf->buf == msg); + red_char_device_write_buffer_release(RED_CHAR_DEVICE(scc->priv->smartcard), + scc->priv->write_buf); + scc->priv->write_buf = NULL; + } + } +} + +void smartcard_channel_client_on_disconnect(RedChannelClient *rcc) +{ + SmartCardChannelClient *scc = SMARTCARD_CHANNEL_CLIENT(rcc); + RedCharDeviceSmartcard *device = scc->priv->smartcard; + + if (device) { + smartcard_char_device_detach_client(device, scc); + smartcard_char_device_notify_reader_remove(device); + } +} + +void smartcard_channel_client_send_data(RedChannelClient *rcc, + SpiceMarshaller *m, + RedPipeItem *item, + VSCMsgHeader *vheader) +{ + spice_assert(rcc); + spice_assert(vheader); + red_channel_client_init_send_data(rcc, SPICE_MSG_SMARTCARD_DATA, item); + spice_marshaller_add_ref(m, (uint8_t*)vheader, sizeof(VSCMsgHeader)); + if (vheader->length > 0) { + spice_marshaller_add_ref(m, (uint8_t*)(vheader+1), vheader->length); + } +} + +void smartcard_channel_client_send_error(RedChannelClient *rcc, SpiceMarshaller *m, RedPipeItem *item) +{ + RedErrorItem* error_item = (RedErrorItem*)item; + + smartcard_channel_client_send_data(rcc, m, item, &error_item->vheader); +} + +static void smartcard_channel_client_push_error(RedChannelClient *rcc, + uint32_t reader_id, + VSCErrorCode error) +{ + RedErrorItem *error_item = spice_new0(RedErrorItem, 1); + + red_pipe_item_init(&error_item->base, RED_PIPE_ITEM_TYPE_ERROR); + + error_item->base.type = RED_PIPE_ITEM_TYPE_ERROR; + error_item->vheader.reader_id = reader_id; + error_item->vheader.type = VSC_Error; + error_item->vheader.length = sizeof(error_item->error); + error_item->error.code = error; + red_channel_client_pipe_add_push(rcc, &error_item->base); +} + +void smartcard_channel_client_add_reader(SmartCardChannelClient *scc, + uint8_t *name) +{ + if (!scc->priv->smartcard) { /* we already tried to attach a reader to the client + when it connected */ + SpiceCharDeviceInstance *char_device = smartcard_readers_get_unattached(); + + if (!char_device) { + smartcard_channel_client_push_error(RED_CHANNEL_CLIENT(scc), + VSCARD_UNDEFINED_READER_ID, + VSC_CANNOT_ADD_MORE_READERS); + return; + } + smartcard_char_device_attach_client(char_device, scc); + } + smartcard_char_device_notify_reader_add(scc->priv->smartcard); + // The device sends a VSC_Error message, we will let it through, no + // need to send our own. We already set the correct reader_id, from + // our RedCharDeviceSmartcard. +} + +void smartcard_channel_client_remove_reader(SmartCardChannelClient *scc, + uint32_t reader_id) +{ + SpiceCharDeviceInstance *char_device = smartcard_readers_get(reader_id); + RedCharDeviceSmartcard *dev; + + if (char_device == NULL) { + smartcard_channel_client_push_error(RED_CHANNEL_CLIENT(scc), + reader_id, VSC_GENERAL_ERROR); + return; + } + + dev = red_char_device_opaque_get(char_device->st); + spice_assert(scc->priv->smartcard == dev); + if (!smartcard_char_device_notify_reader_remove(dev)) { + smartcard_channel_client_push_error(RED_CHANNEL_CLIENT(scc), + reader_id, VSC_GENERAL_ERROR); + return; + } +} + +RedCharDeviceSmartcard* smartcard_channel_client_get_device(SmartCardChannelClient *scc) +{ + return scc->priv->smartcard; +} + +static void smartcard_channel_client_write_to_reader(SmartCardChannelClient *scc) +{ + g_return_if_fail(scc); + + smartcard_channel_write_to_reader(scc->priv->write_buf); + scc->priv->write_buf = NULL; +} + + +int smartcard_channel_client_handle_message(RedChannelClient *rcc, + uint16_t type, + uint32_t size, + uint8_t *msg) +{ + VSCMsgHeader* vheader = (VSCMsgHeader*)msg; + SmartCardChannelClient *scc = SMARTCARD_CHANNEL_CLIENT(rcc); + + if (type != SPICE_MSGC_SMARTCARD_DATA) { + /* Handles seamless migration protocol. Also handles ack's, + * spicy sends them while spicec does not */ + return red_channel_client_handle_message(rcc, size, type, msg); + } + + spice_assert(size == vheader->length + sizeof(VSCMsgHeader)); + switch (vheader->type) { + case VSC_ReaderAdd: + smartcard_channel_client_add_reader(scc, msg + sizeof(VSCMsgHeader)); + return TRUE; + break; + case VSC_ReaderRemove: + smartcard_channel_client_remove_reader(scc, vheader->reader_id); + return TRUE; + break; + case VSC_Init: + // ignore - we should never get this anyway + return TRUE; + break; + case VSC_Error: + case VSC_ATR: + case VSC_CardRemove: + case VSC_APDU: + break; // passed on to device + default: + printf("ERROR: unexpected message on smartcard channel\n"); + return TRUE; + } + + /* todo: fix */ + if (vheader->reader_id >= smartcard_get_n_readers()) { + spice_printerr("ERROR: received message for non existing reader: %d, %d, %d", vheader->reader_id, + vheader->type, vheader->length); + return FALSE; + } + spice_assert(scc->priv->write_buf->buf == msg); + smartcard_channel_client_write_to_reader(scc); + + return TRUE; +} + +int smartcard_channel_client_handle_migrate_data(RedChannelClient *rcc, + uint32_t size, + void *message) +{ + SmartCardChannelClient *scc; + SpiceMigrateDataHeader *header; + SpiceMigrateDataSmartcard *mig_data; + + scc = SMARTCARD_CHANNEL_CLIENT(rcc); + header = (SpiceMigrateDataHeader *)message; + mig_data = (SpiceMigrateDataSmartcard *)(header + 1); + if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataSmartcard)) { + spice_error("bad message size"); + return FALSE; + } + if (!migration_protocol_validate_header(header, + SPICE_MIGRATE_DATA_SMARTCARD_MAGIC, + SPICE_MIGRATE_DATA_SMARTCARD_VERSION)) { + spice_error("bad header"); + return FALSE; + } + + if (!mig_data->base.connected) { /* client wasn't attached to a smartcard */ + return TRUE; + } + + if (!scc->priv->smartcard) { + SpiceCharDeviceInstance *char_device = smartcard_readers_get_unattached(); + + if (!char_device) { + spice_warning("no unattached device available"); + return TRUE; + } else { + smartcard_char_device_attach_client(char_device, scc); + } + } + spice_debug("reader added %d partial read_size %u", mig_data->reader_added, mig_data->read_size); + + return smartcard_char_device_handle_migrate_data(scc->priv->smartcard, + mig_data); +} + +int smartcard_channel_client_handle_migrate_flush_mark(RedChannelClient *rcc) +{ + red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SMARTCARD_MIGRATE_DATA); + return TRUE; +} + +void smartcard_channel_client_set_char_device(SmartCardChannelClient *scc, + RedCharDeviceSmartcard *device) +{ + if (device == scc->priv->smartcard) + return; + + if (scc->priv->smartcard) + g_object_remove_weak_pointer(G_OBJECT(scc->priv->smartcard), + (gpointer*)&scc->priv->smartcard); + + scc->priv->smartcard = device; + g_object_add_weak_pointer(G_OBJECT(scc->priv->smartcard), + (gpointer*)&scc->priv->smartcard); +} + +RedCharDeviceSmartcard* smartcard_channel_client_get_char_device(SmartCardChannelClient *scc) +{ + return scc->priv->smartcard; +} + diff --git a/server/smartcard-channel-client.h b/server/smartcard-channel-client.h new file mode 100644 index 0000000..c451665 --- /dev/null +++ b/server/smartcard-channel-client.h @@ -0,0 +1,113 @@ +/* + 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 SMARTCARD_CHANNEL_CLIENT_H__ +#define SMARTCARD_CHANNEL_CLIENT_H__ + +#include <glib-object.h> +#include "smartcard.h" + +G_BEGIN_DECLS + +#define TYPE_SMARTCARD_CHANNEL_CLIENT smart_card_channel_client_get_type() + +#define SMARTCARD_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_SMARTCARD_CHANNEL_CLIENT, SmartCardChannelClient)) +#define SMARTCARD_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_SMARTCARD_CHANNEL_CLIENT, SmartCardChannelClientClass)) +#define IS_SMARTCARD_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_SMARTCARD_CHANNEL_CLIENT)) +#define IS_SMARTCARD_CHANNEL_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_SMARTCARD_CHANNEL_CLIENT)) +#define SMARTCARD_CHANNEL_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_SMARTCARD_CHANNEL_CLIENT, SmartCardChannelClientClass)) + +typedef struct SmartCardChannelClient SmartCardChannelClient; +typedef struct SmartCardChannelClientClass SmartCardChannelClientClass; +typedef struct SmartCardChannelClientPrivate SmartCardChannelClientPrivate; + +struct SmartCardChannelClient +{ + RedChannelClient parent; + + SmartCardChannelClientPrivate *priv; +}; + +struct SmartCardChannelClientClass +{ + RedChannelClientClass parent_class; +}; + +GType smart_card_channel_client_get_type(void) G_GNUC_CONST; + +SmartCardChannelClient* smartcard_channel_client_create(RedChannel *channel, + RedClient *client, RedsStream *stream, + int monitor_latency, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps); + +uint8_t* smartcard_channel_client_alloc_msg_rcv_buf(RedChannelClient *rcc, + uint16_t type, + uint32_t size); + +void smartcard_channel_client_release_msg_rcv_buf(RedChannelClient *rcc, + uint16_t type, + uint32_t size, + uint8_t *msg); + +int smartcard_channel_client_handle_migrate_flush_mark(RedChannelClient *rcc); + +void smartcard_channel_client_on_disconnect(RedChannelClient *rcc); + +void smartcard_channel_client_send_data(RedChannelClient *rcc, + SpiceMarshaller *m, + RedPipeItem *item, + VSCMsgHeader *vheader); + +void smartcard_channel_client_send_error(RedChannelClient *rcc, + SpiceMarshaller *m, + RedPipeItem *item); + +void smartcard_channel_client_add_reader(SmartCardChannelClient *scc, + uint8_t *name); + +void smartcard_channel_client_remove_reader(SmartCardChannelClient *scc, + uint32_t reader_id); + +RedCharDeviceSmartcard* smartcard_channel_client_get_device(SmartCardChannelClient *scc); + +int smartcard_channel_client_handle_message(RedChannelClient *rcc, + uint16_t type, + uint32_t size, + uint8_t *msg); + +int smartcard_channel_client_handle_migrate_data(RedChannelClient *rcc, + uint32_t size, + void *message); + +void smartcard_channel_client_set_char_device(SmartCardChannelClient *scc, + RedCharDeviceSmartcard *device); + +RedCharDeviceSmartcard* smartcard_channel_client_get_char_device(SmartCardChannelClient *scc); + +void smartcard_channel_client_release_msg_rcv_buf(RedChannelClient *rcc, + uint16_t type, + uint32_t size, + uint8_t *msg); + +uint8_t *smartcard_channel_client_alloc_msg_rcv_buf(RedChannelClient *rcc, + uint16_t type, + uint32_t size); + +G_END_DECLS + +#endif /* SMARTCARD_CHANNEL_CLIENT_H__ */ diff --git a/server/smartcard.c b/server/smartcard.c index e544f19..ec42960 100644 --- a/server/smartcard.c +++ b/server/smartcard.c @@ -30,8 +30,8 @@ #include "reds.h" #include "char-device.h" -#include "red-channel-client-private.h" #include "smartcard.h" +#include "smartcard-channel-client.h" #include "migration-protocol.h" /* @@ -49,17 +49,6 @@ // Maximal length of APDU #define APDUBufSize 270 -typedef struct SmartCardChannelClient { - RedChannelClient base; - RedCharDeviceSmartcard *smartcard; - - /* read_from_client/write_to_device buffer. - * The beginning of the buffer should always be VSCMsgHeader*/ - RedCharDeviceWriteBuffer *write_buf; - int msg_in_write_buf; /* was the client msg received into a RedCharDeviceWriteBuffer - * or was it explicitly malloced */ -} SmartCardChannelClient; - 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)) @@ -76,18 +65,6 @@ struct RedCharDeviceSmartcardPrivate { int reader_added; // has reader_add been sent to the device }; -enum { - RED_PIPE_ITEM_TYPE_ERROR = RED_PIPE_ITEM_TYPE_CHANNEL_BASE, - RED_PIPE_ITEM_TYPE_SMARTCARD_DATA, - RED_PIPE_ITEM_TYPE_SMARTCARD_MIGRATE_DATA, -}; - -typedef struct RedErrorItem { - RedPipeItem base; - VSCMsgHeader vheader; - VSCMsgError error; -} RedErrorItem; - typedef struct RedMsgItem { RedPipeItem base; @@ -106,12 +83,7 @@ static struct Readers { SpiceCharDeviceInstance* sin[SMARTCARD_MAX_READERS]; } g_smartcard_readers = {0, {NULL}}; -static SpiceCharDeviceInstance* smartcard_readers_get_unattached(void); -static SpiceCharDeviceInstance* smartcard_readers_get(uint32_t reader_id); static int smartcard_char_device_add_to_readers(RedsState *reds, SpiceCharDeviceInstance *sin); -static void smartcard_char_device_attach_client( - SpiceCharDeviceInstance *char_device, SmartCardChannelClient *scc); -static void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_buf); static RedMsgItem *smartcard_char_device_on_message_from_device( RedCharDeviceSmartcard *dev, VSCMsgHeader *header); @@ -171,9 +143,11 @@ static void smartcard_send_msg_to_client(RedPipeItem *msg, void *opaque) { RedCharDeviceSmartcard *dev = opaque; - spice_assert(dev->priv->scc && dev->priv->scc->base.client == client); - smartcard_channel_client_pipe_add_push(&dev->priv->scc->base, (RedPipeItem *)msg); + RedClient *this_client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dev->priv->scc)); + spice_assert(dev->priv->scc && this_client == client); + smartcard_channel_client_pipe_add_push(RED_CHANNEL_CLIENT(dev->priv->scc), + (RedPipeItem *)msg); } static void smartcard_send_tokens_to_client(RedClient *client, uint32_t tokens, void *opaque) @@ -184,10 +158,11 @@ static void smartcard_send_tokens_to_client(RedClient *client, uint32_t tokens, static void smartcard_remove_client(RedClient *client, void *opaque) { RedCharDeviceSmartcard *dev = opaque; + RedClient *this_client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dev->priv->scc)); spice_printerr("smartcard dev %p, client %p", dev, client); - spice_assert(dev->priv->scc && dev->priv->scc->base.client == client); - red_channel_client_shutdown(&dev->priv->scc->base); + spice_assert(dev->priv->scc && this_client == client); + red_channel_client_shutdown(RED_CHANNEL_CLIENT(dev->priv->scc)); } RedMsgItem *smartcard_char_device_on_message_from_device(RedCharDeviceSmartcard *dev, @@ -214,7 +189,8 @@ RedMsgItem *smartcard_char_device_on_message_from_device(RedCharDeviceSmartcard /* We patch the reader_id, since the device only knows about itself, and * we know about the sum of readers. */ sent_header->reader_id = dev->priv->reader_id; - return smartcard_get_vsc_msg_item(&dev->priv->scc->base, sent_header); + return smartcard_get_vsc_msg_item(RED_CHANNEL_CLIENT(dev->priv->scc), + sent_header); } return NULL; } @@ -232,7 +208,7 @@ static int smartcard_char_device_add_to_readers(RedsState *reds, SpiceCharDevice return 0; } -static SpiceCharDeviceInstance *smartcard_readers_get(uint32_t reader_id) +SpiceCharDeviceInstance *smartcard_readers_get(uint32_t reader_id) { spice_assert(reader_id < g_smartcard_readers.num); return g_smartcard_readers.sin[reader_id]; @@ -240,7 +216,7 @@ static SpiceCharDeviceInstance *smartcard_readers_get(uint32_t reader_id) /* TODO: fix implementation for multiple readers. Each reader should have a separated * channel */ -static SpiceCharDeviceInstance *smartcard_readers_get_unattached(void) +SpiceCharDeviceInstance *smartcard_readers_get_unattached(void) { int i; RedCharDeviceSmartcard* dev; @@ -289,7 +265,7 @@ RedCharDevice *smartcard_device_connect(RedsState *reds, SpiceCharDeviceInstance return RED_CHAR_DEVICE(dev); } -static void smartcard_char_device_notify_reader_add(RedCharDeviceSmartcard *dev) +void smartcard_char_device_notify_reader_add(RedCharDeviceSmartcard *dev) { RedCharDeviceWriteBuffer *write_buf; VSCMsgHeader *vheader; @@ -307,44 +283,43 @@ static void smartcard_char_device_notify_reader_add(RedCharDeviceSmartcard *dev) smartcard_channel_write_to_reader(write_buf); } -static void smartcard_char_device_attach_client(SpiceCharDeviceInstance *char_device, - SmartCardChannelClient *scc) +void smartcard_char_device_attach_client(SpiceCharDeviceInstance *char_device, + SmartCardChannelClient *scc) { RedCharDeviceSmartcard *dev = red_char_device_opaque_get(char_device->st); int client_added; - spice_assert(!scc->smartcard && !dev->priv->scc); + spice_assert(!smartcard_channel_client_get_char_device(scc) && !dev->priv->scc); dev->priv->scc = scc; - scc->smartcard = dev; + smartcard_channel_client_set_char_device(scc, dev); client_added = red_char_device_client_add(RED_CHAR_DEVICE(dev), - scc->base.client, + red_channel_client_get_client(RED_CHANNEL_CLIENT(scc)), FALSE, /* no flow control yet */ 0, /* send queue size */ ~0, ~0, - red_channel_client_is_waiting_for_migrate_data( - &scc->base)); + red_channel_client_is_waiting_for_migrate_data(RED_CHANNEL_CLIENT(scc))); if (!client_added) { spice_warning("failed"); dev->priv->scc = NULL; - scc->smartcard = NULL; - red_channel_client_disconnect(&scc->base); + smartcard_channel_client_set_char_device(scc, NULL); + red_channel_client_disconnect(RED_CHANNEL_CLIENT(scc)); } } -static void smartcard_char_device_notify_reader_remove(RedCharDeviceSmartcard *dev) +gboolean smartcard_char_device_notify_reader_remove(RedCharDeviceSmartcard *dev) { RedCharDeviceWriteBuffer *write_buf; VSCMsgHeader *vheader; if (!dev->priv->reader_added) { spice_debug("reader add was never sent to the device"); - return; + return FALSE; } write_buf = red_char_device_write_buffer_get(RED_CHAR_DEVICE(dev), NULL, sizeof(vheader)); if (!write_buf) { spice_error("failed to allocate write buffer"); - return; + return FALSE; } dev->priv->reader_added = FALSE; vheader = (VSCMsgHeader *)write_buf->buf; @@ -352,98 +327,28 @@ static void smartcard_char_device_notify_reader_remove(RedCharDeviceSmartcard *d vheader->reader_id = dev->priv->reader_id; vheader->length = 0; smartcard_channel_write_to_reader(write_buf); -} -static void smartcard_char_device_detach_client(SmartCardChannelClient *scc) -{ - RedCharDeviceSmartcard *dev; - - if (!scc->smartcard) { - return; - } - dev = scc->smartcard; - spice_assert(dev->priv->scc == scc); - red_char_device_client_remove(RED_CHAR_DEVICE(dev), scc->base.client); - scc->smartcard = NULL; - dev->priv->scc = NULL; -} - -static int smartcard_channel_client_config_socket(RedChannelClient *rcc) -{ return TRUE; } -static uint8_t *smartcard_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, - uint16_t type, - uint32_t size) +void smartcard_char_device_detach_client(RedCharDeviceSmartcard *smartcard, + SmartCardChannelClient *scc) { - SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - - /* todo: only one reader is actually supported. When we fix the code to support - * multiple readers, we will porbably associate different devices to - * differenc channels */ - if (!scc->smartcard) { - scc->msg_in_write_buf = FALSE; - return spice_malloc(size); - } else { - RedCharDeviceSmartcard *dev; - - spice_assert(g_smartcard_readers.num == 1); - dev = scc->smartcard; - spice_assert(dev->priv->scc || scc->smartcard); - spice_assert(!scc->write_buf); - scc->write_buf = red_char_device_write_buffer_get(RED_CHAR_DEVICE(dev), rcc->client, size); - - if (!scc->write_buf) { - spice_error("failed to allocate write buffer"); - return NULL; - } - scc->msg_in_write_buf = TRUE; - return scc->write_buf->buf; - } + spice_assert(smartcard->priv->scc == scc); + red_char_device_client_remove(RED_CHAR_DEVICE(smartcard), + red_channel_client_get_client(RED_CHANNEL_CLIENT(scc))); + smartcard_channel_client_set_char_device(scc, NULL); + smartcard->priv->scc = NULL; } -static void smartcard_channel_release_msg_rcv_buf(RedChannelClient *rcc, - uint16_t type, - uint32_t size, - uint8_t *msg) -{ - SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - - /* todo: only one reader is actually supported. When we fix the code to support - * multiple readers, we will porbably associate different devices to - * differenc channels */ - - if (!scc->msg_in_write_buf) { - spice_assert(!scc->write_buf); - free(msg); - } else { - if (scc->write_buf) { /* msg hasn't been pushed to the guest */ - spice_assert(scc->write_buf->buf == msg); - red_char_device_write_buffer_release(RED_CHAR_DEVICE(scc->smartcard), scc->write_buf); - scc->write_buf = NULL; - } - } -} - -static void smartcard_channel_send_data(RedChannelClient *rcc, SpiceMarshaller *m, - RedPipeItem *item, VSCMsgHeader *vheader) +static int smartcard_channel_client_config_socket(RedChannelClient *rcc) { - spice_assert(rcc); - spice_assert(vheader); - red_channel_client_init_send_data(rcc, SPICE_MSG_SMARTCARD_DATA, item); - spice_marshaller_add_ref(m, (uint8_t*)vheader, sizeof(VSCMsgHeader)); - if (vheader->length > 0) { - spice_marshaller_add_ref(m, (uint8_t*)(vheader+1), vheader->length); - } + return TRUE; } -static void smartcard_channel_send_error( - RedChannelClient *rcc, SpiceMarshaller *m, RedPipeItem *item) +SmartCardChannelClient* smartcard_char_device_get_client(RedCharDeviceSmartcard *smartcard) { - RedErrorItem* error_item = (RedErrorItem*)item; - - smartcard_channel_send_data(rcc, m, item, &error_item->vheader); + return smartcard->priv->scc; } static void smartcard_channel_send_msg(RedChannelClient *rcc, @@ -451,7 +356,7 @@ static void smartcard_channel_send_msg(RedChannelClient *rcc, { RedMsgItem* msg_item = (RedMsgItem*)item; - smartcard_channel_send_data(rcc, m, item, msg_item->vheader); + smartcard_channel_client_send_data(rcc, m, item, msg_item->vheader); } static void smartcard_channel_send_migrate_data(RedChannelClient *rcc, @@ -461,8 +366,8 @@ static void smartcard_channel_send_migrate_data(RedChannelClient *rcc, RedCharDeviceSmartcard *dev; SpiceMarshaller *m2; - scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - dev = scc->smartcard; + scc = SMARTCARD_CHANNEL_CLIENT(rcc); + dev = smartcard_channel_client_get_char_device(scc); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item); spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SMARTCARD_MAGIC); spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SMARTCARD_VERSION); @@ -489,7 +394,7 @@ static void smartcard_channel_send_item(RedChannelClient *rcc, RedPipeItem *item switch (item->type) { case RED_PIPE_ITEM_TYPE_ERROR: - smartcard_channel_send_error(rcc, m, item); + smartcard_channel_client_send_error(rcc, m, item); break; case RED_PIPE_ITEM_TYPE_SMARTCARD_DATA: smartcard_channel_send_msg(rcc, m, item); @@ -515,18 +420,6 @@ static void smartcard_channel_release_pipe_item(RedChannelClient *rcc, } } -static void smartcard_channel_on_disconnect(RedChannelClient *rcc) -{ - SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - - if (scc->smartcard) { - RedCharDeviceSmartcard *dev = scc->smartcard; - - smartcard_char_device_detach_client(scc); - smartcard_char_device_notify_reader_remove(dev); - } -} - /* this is called from both device input and client input. since the device is * a usb device, the context is still the main thread (kvm_main_loop, timers) * so no mutex is required. */ @@ -536,20 +429,6 @@ static void smartcard_channel_client_pipe_add_push(RedChannelClient *rcc, red_channel_client_pipe_add_push(rcc, item); } -static void smartcard_push_error(RedChannelClient *rcc, uint32_t reader_id, VSCErrorCode error) -{ - RedErrorItem *error_item = spice_new0(RedErrorItem, 1); - - red_pipe_item_init(&error_item->base, RED_PIPE_ITEM_TYPE_ERROR); - - error_item->base.type = RED_PIPE_ITEM_TYPE_ERROR; - error_item->vheader.reader_id = reader_id; - error_item->vheader.type = VSC_Error; - error_item->vheader.length = sizeof(error_item->error); - error_item->error.code = error; - smartcard_channel_client_pipe_add_push(rcc, &error_item->base); -} - static void smartcard_free_vsc_msg_item(RedMsgItem *item) { free(item->vheader); @@ -567,47 +446,7 @@ static RedMsgItem *smartcard_get_vsc_msg_item(RedChannelClient *rcc, return msg_item; } -static void smartcard_remove_reader(SmartCardChannelClient *scc, uint32_t reader_id) -{ - SpiceCharDeviceInstance *char_device = smartcard_readers_get(reader_id); - RedCharDeviceSmartcard *dev; - - if (char_device == NULL) { - smartcard_push_error(&scc->base, reader_id, - VSC_GENERAL_ERROR); - return; - } - - dev = red_char_device_opaque_get(char_device->st); - if (dev->priv->reader_added == FALSE) { - smartcard_push_error(&scc->base, reader_id, - VSC_GENERAL_ERROR); - return; - } - spice_assert(scc->smartcard == dev); - smartcard_char_device_notify_reader_remove(dev); -} - -static void smartcard_add_reader(SmartCardChannelClient *scc, uint8_t *name) -{ - if (!scc->smartcard) { /* we already tried to attach a reader to the client - when it connected */ - SpiceCharDeviceInstance *char_device = smartcard_readers_get_unattached(); - - if (!char_device) { - smartcard_push_error(&scc->base, VSCARD_UNDEFINED_READER_ID, - VSC_CANNOT_ADD_MORE_READERS); - return; - } - smartcard_char_device_attach_client(char_device, scc); - } - smartcard_char_device_notify_reader_add(scc->smartcard); - // The device sends a VSC_Error message, we will let it through, no - // need to send our own. We already set the correct reader_id, from - // our RedCharDeviceSmartcard. -} - -static void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_buf) +void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_buf) { SpiceCharDeviceInstance *sin; RedCharDeviceSmartcard *dev; @@ -620,7 +459,8 @@ static void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_bu spice_assert(vheader->reader_id <= g_smartcard_readers.num); sin = g_smartcard_readers.sin[vheader->reader_id]; dev = (RedCharDeviceSmartcard *)red_char_device_opaque_get(sin->st); - spice_assert(!dev->priv->scc || dev == dev->priv->scc->smartcard); + spice_assert(!dev->priv->scc || + dev == smartcard_channel_client_get_device(dev->priv->scc)); /* protocol requires messages to be in network endianess */ vheader->type = htonl(vheader->type); vheader->length = htonl(vheader->length); @@ -629,15 +469,6 @@ static void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_bu /* pushing the buffer to the write queue; It will be released * when it will be fully consumed by the device */ red_char_device_write_buffer_add(sin->st, write_buf); - if (dev->priv->scc && write_buf == dev->priv->scc->write_buf) { - dev->priv->scc->write_buf = NULL; - } -} - -static int smartcard_channel_client_handle_migrate_flush_mark(RedChannelClient *rcc) -{ - red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SMARTCARD_MIGRATE_DATA); - return TRUE; } static void smartcard_device_restore_partial_read(RedCharDeviceSmartcard *dev, @@ -657,96 +488,13 @@ static void smartcard_device_restore_partial_read(RedCharDeviceSmartcard *dev, dev->priv->buf_pos = dev->priv->buf + mig_data->read_size; } -static int smartcard_channel_client_handle_migrate_data(RedChannelClient *rcc, - uint32_t size, void *message) +int smartcard_char_device_handle_migrate_data(RedCharDeviceSmartcard *smartcard, + SpiceMigrateDataSmartcard *mig_data) { - SmartCardChannelClient *scc; - SpiceMigrateDataHeader *header; - SpiceMigrateDataSmartcard *mig_data; - - scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - header = (SpiceMigrateDataHeader *)message; - mig_data = (SpiceMigrateDataSmartcard *)(header + 1); - if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataSmartcard)) { - spice_error("bad message size"); - return FALSE; - } - if (!migration_protocol_validate_header(header, - SPICE_MIGRATE_DATA_SMARTCARD_MAGIC, - SPICE_MIGRATE_DATA_SMARTCARD_VERSION)) { - spice_error("bad header"); - return FALSE; - } - - if (!mig_data->base.connected) { /* client wasn't attached to a smartcard */ - return TRUE; - } + smartcard->priv->reader_added = mig_data->reader_added; - if (!scc->smartcard) { - SpiceCharDeviceInstance *char_device = smartcard_readers_get_unattached(); - - if (!char_device) { - spice_warning("no unattached device available"); - return TRUE; - } else { - smartcard_char_device_attach_client(char_device, scc); - } - } - spice_debug("reader added %d partial read_size %u", mig_data->reader_added, mig_data->read_size); - scc->smartcard->priv->reader_added = mig_data->reader_added; - - smartcard_device_restore_partial_read(scc->smartcard, mig_data); - return red_char_device_restore(RED_CHAR_DEVICE(scc->smartcard), &mig_data->base); -} - -static int smartcard_channel_handle_message(RedChannelClient *rcc, - uint16_t type, - uint32_t size, - uint8_t *msg) -{ - VSCMsgHeader* vheader = (VSCMsgHeader*)msg; - SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - - if (type != SPICE_MSGC_SMARTCARD_DATA) { - /* Handles seamless migration protocol. Also handles ack's, - * spicy sends them while spicec does not */ - return red_channel_client_handle_message(rcc, size, type, msg); - } - - spice_assert(size == vheader->length + sizeof(VSCMsgHeader)); - switch (vheader->type) { - case VSC_ReaderAdd: - smartcard_add_reader(scc, msg + sizeof(VSCMsgHeader)); - return TRUE; - break; - case VSC_ReaderRemove: - smartcard_remove_reader(scc, vheader->reader_id); - return TRUE; - break; - case VSC_Init: - // ignore - we should never get this anyway - return TRUE; - break; - case VSC_Error: - case VSC_ATR: - case VSC_CardRemove: - case VSC_APDU: - break; // passed on to device - default: - printf("ERROR: unexpected message on smartcard channel\n"); - return TRUE; - } - - /* todo: fix */ - if (vheader->reader_id >= g_smartcard_readers.num) { - spice_printerr("ERROR: received message for non existing reader: %d, %d, %d", vheader->reader_id, - vheader->type, vheader->length); - return FALSE; - } - spice_assert(scc->write_buf->buf == msg); - smartcard_channel_write_to_reader(scc->write_buf); - - return TRUE; + smartcard_device_restore_partial_read(smartcard, mig_data); + return red_char_device_restore(RED_CHAR_DEVICE(smartcard), &mig_data->base); } static void smartcard_channel_hold_pipe_item(RedChannelClient *rcc, @@ -764,18 +512,17 @@ static void smartcard_connect_client(RedChannel *channel, RedClient *client, SmartCardChannelClient *scc; - scc = (SmartCardChannelClient *)red_channel_client_create(sizeof(SmartCardChannelClient), - channel, - client, - stream, - FALSE, - num_common_caps, common_caps, - num_caps, caps); + scc = smartcard_channel_client_create(channel, + client, + stream, + FALSE, + num_common_caps, common_caps, + num_caps, caps); if (!scc) { return; } - red_channel_client_ack_zero_messages_window(&scc->base); + red_channel_client_ack_zero_messages_window(RED_CHANNEL_CLIENT(scc)); if (char_device) { smartcard_char_device_attach_client(char_device, scc); @@ -795,12 +542,12 @@ static void smartcard_init(RedsState *reds) spice_assert(!g_smartcard_channel); channel_cbs.config_socket = smartcard_channel_client_config_socket; - channel_cbs.on_disconnect = smartcard_channel_on_disconnect; + 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_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = smartcard_channel_release_msg_rcv_buf; + 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; @@ -809,7 +556,7 @@ static void smartcard_init(RedsState *reds) reds_get_core_interface(reds), SPICE_CHANNEL_SMARTCARD, 0, FALSE /* handle_acks */, - smartcard_channel_handle_message, + smartcard_channel_client_handle_message, &channel_cbs, migration_flags); @@ -830,9 +577,6 @@ red_char_device_smartcard_finalize(GObject *object) RedCharDeviceSmartcard *self = RED_CHAR_DEVICE_SMARTCARD(object); free(self->priv->buf); - if (self->priv->scc) { - self->priv->scc->smartcard = NULL; - } G_OBJECT_CLASS(red_char_device_smartcard_parent_class)->finalize(object); } @@ -863,3 +607,8 @@ red_char_device_smartcard_init(RedCharDeviceSmartcard *self) self->priv->buf = spice_malloc(self->priv->buf_size); self->priv->buf_pos = self->priv->buf; } + +uint32_t smartcard_get_n_readers(void) +{ + return g_smartcard_readers.num; +} diff --git a/server/smartcard.h b/server/smartcard.h index 6bda594..87e3b16 100644 --- a/server/smartcard.h +++ b/server/smartcard.h @@ -19,6 +19,7 @@ #define __SMART_CARD_H__ #include <glib-object.h> +#include "char-device.h" #define RED_TYPE_CHAR_DEVICE_SMARTCARD red_char_device_smartcard_get_type() @@ -31,6 +32,7 @@ typedef struct RedCharDeviceSmartcard RedCharDeviceSmartcard; typedef struct RedCharDeviceSmartcardClass RedCharDeviceSmartcardClass; typedef struct RedCharDeviceSmartcardPrivate RedCharDeviceSmartcardPrivate; +typedef struct SmartCardChannelClient SmartCardChannelClient; struct RedCharDeviceSmartcard { @@ -51,5 +53,24 @@ GType red_char_device_smartcard_get_type(void) G_GNUC_CONST; */ RedCharDevice *smartcard_device_connect(RedsState *reds, SpiceCharDeviceInstance *char_device); void smartcard_device_disconnect(SpiceCharDeviceInstance *char_device); +void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_buf); +SpiceCharDeviceInstance* smartcard_readers_get(uint32_t reader_id); +SpiceCharDeviceInstance *smartcard_readers_get_unattached(void); +uint32_t smartcard_get_n_readers(void); +void smartcard_char_device_notify_reader_add(RedCharDeviceSmartcard *smartcard); +void smartcard_char_device_attach_client(SpiceCharDeviceInstance *smartcard, + SmartCardChannelClient *scc); +gboolean smartcard_char_device_notify_reader_remove(RedCharDeviceSmartcard *smartcard); +void smartcard_char_device_detach_client(RedCharDeviceSmartcard *smartcard, + SmartCardChannelClient *scc); +SmartCardChannelClient* smartcard_char_device_get_client(RedCharDeviceSmartcard *smartcard); +int smartcard_char_device_handle_migrate_data(RedCharDeviceSmartcard *smartcard, + SpiceMigrateDataSmartcard *mig_data); + +enum { + RED_PIPE_ITEM_TYPE_ERROR = RED_PIPE_ITEM_TYPE_CHANNEL_BASE, + RED_PIPE_ITEM_TYPE_SMARTCARD_DATA, + RED_PIPE_ITEM_TYPE_SMARTCARD_MIGRATE_DATA, +}; #endif // __SMART_CARD_H__ diff --git a/server/sound.c b/server/sound.c index 05ce9b5..e87428f 100644 --- a/server/sound.c +++ b/server/sound.c @@ -31,10 +31,13 @@ #include "spice.h" #include "red-common.h" +#include "dummy-channel-client.h" #include "main-channel.h" #include "main-channel-client.h" #include "reds.h" #include "red-qxl.h" +/* FIXME: for now, allow the sound channel to access private RedChannelClient + * data... */ #include "red-channel-client-private.h" #include "sound.h" #include "common/snd_codec.h" @@ -524,7 +527,7 @@ static inline int snd_reset_send_data(SndChannel *channel, uint16_t verb) return FALSE; } - header = &channel->channel_client->send_data.header; + header = &channel->channel_client->priv->send_data.header; spice_marshaller_reset(channel->send_data.marshaller); header->data = spice_marshaller_reserve_space(channel->send_data.marshaller, header->header_size); @@ -534,7 +537,7 @@ static inline int snd_reset_send_data(SndChannel *channel, uint16_t verb) header->set_msg_size(header, 0); header->set_msg_type(header, verb); channel->send_data.serial++; - if (!channel->channel_client->is_mini_header) { + if (!channel->channel_client->priv->is_mini_header) { header->set_msg_serial(header, channel->send_data.serial); header->set_msg_sub_list(header, 0); } @@ -544,7 +547,7 @@ static inline int snd_reset_send_data(SndChannel *channel, uint16_t verb) static int snd_begin_send_message(SndChannel *channel) { - SpiceDataHeaderOpaque *header = &channel->channel_client->send_data.header; + SpiceDataHeaderOpaque *header = &channel->channel_client->priv->send_data.header; spice_marshaller_flush(channel->send_data.marshaller); channel->send_data.size = spice_marshaller_get_total_size(channel->send_data.marshaller); @@ -976,11 +979,9 @@ static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_i channel->on_message_done = on_message_done; channel->cleanup = cleanup; - channel->channel_client = red_channel_client_create_dummy(sizeof(RedChannelClient), - worker->base_channel, - client, - num_common_caps, common_caps, - num_caps, caps); + channel->channel_client = + dummy_channel_client_create(worker->base_channel, client, + num_common_caps, common_caps, num_caps, caps); if (!channel->channel_client) { goto error2; } @@ -997,12 +998,13 @@ error1: static void snd_disconnect_channel_client(RedChannelClient *rcc) { SndWorker *worker; + RedChannel *channel = red_channel_client_get_channel(rcc); - spice_assert(rcc->channel); - spice_assert(rcc->channel->data); - worker = (SndWorker *)rcc->channel->data; + spice_assert(channel); + spice_assert(channel->data); + worker = (SndWorker *)channel->data; - spice_debug("channel-type=%d", rcc->channel->type); + spice_debug("channel-type=%d", channel->type); if (worker->connection) { spice_assert(worker->connection->channel_client == rcc); snd_disconnect_channel(worker->connection); @@ -1145,7 +1147,7 @@ void snd_set_playback_latency(RedClient *client, uint32_t latency) for (; now; now = now->next) { if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection && - now->connection->channel_client->client == client) { + red_channel_client_get_client(now->connection->channel_client) == client) { if (red_channel_client_test_remote_cap(now->connection->channel_client, SPICE_PLAYBACK_CAP_LATENCY)) { @@ -1266,11 +1268,12 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt static void snd_record_migrate_channel_client(RedChannelClient *rcc) { SndWorker *worker; + RedChannel *channel = red_channel_client_get_channel(rcc); spice_debug(NULL); - spice_assert(rcc->channel); - spice_assert(rcc->channel->data); - worker = (SndWorker *)rcc->channel->data; + spice_assert(channel); + spice_assert(channel->data); + worker = (SndWorker *)channel->data; if (worker->connection) { spice_assert(worker->connection->channel_client == rcc); @@ -1495,10 +1498,11 @@ static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStre static void snd_playback_migrate_channel_client(RedChannelClient *rcc) { SndWorker *worker; + RedChannel *channel = red_channel_client_get_channel(rcc); - spice_assert(rcc->channel); - spice_assert(rcc->channel->data); - worker = (SndWorker *)rcc->channel->data; + spice_assert(channel); + spice_assert(channel->data); + worker = (SndWorker *)channel->data; spice_debug(NULL); if (worker->connection) { diff --git a/server/spice-server.h b/server/spice-server.h index d309f18..4921fbe 100644 --- a/server/spice-server.h +++ b/server/spice-server.h @@ -69,6 +69,22 @@ int spice_server_add_interface(SpiceServer *s, SpiceBaseInstance *sin); int spice_server_remove_interface(SpiceBaseInstance *sin); +/* XXX This definition is here only to make glib generation + * of enumerators possible + */ +#if 0 +typedef enum { + SPICE_IMAGE_COMPRESSION_INVALID = 0, + SPICE_IMAGE_COMPRESSION_OFF = 1, + SPICE_IMAGE_COMPRESSION_AUTO_GLZ = 2, + SPICE_IMAGE_COMPRESSION_AUTO_LZ = 3, + SPICE_IMAGE_COMPRESSION_QUIC = 4, + SPICE_IMAGE_COMPRESSION_GLZ = 5, + SPICE_IMAGE_COMPRESSION_LZ = 6, + SPICE_IMAGE_COMPRESSION_LZ4 = 7, +} spice_image_compression_t; +#endif + // Needed for backward API compatibility typedef SpiceImageCompression spice_image_compression_t; #define SPICE_IMAGE_COMPRESS_INVALID SPICE_IMAGE_COMPRESSION_INVALID diff --git a/server/spicevmc.c b/server/spicevmc.c index bfd93c1..0b37b4a 100644 --- a/server/spicevmc.c +++ b/server/spicevmc.c @@ -485,10 +485,8 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client, return; } - rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream, - FALSE, - num_common_caps, common_caps, - num_caps, caps); + rcc = red_channel_client_create(channel, client, stream, FALSE, + num_common_caps, common_caps, num_caps, caps); if (!rcc) { return; } diff --git a/server/stream.c b/server/stream.c index f98177e..47a6924 100644 --- a/server/stream.c +++ b/server/stream.c @@ -341,7 +341,7 @@ static void before_reattach_stream(DisplayChannel *display, agent = dcc_get_stream_agent(dcc, index); if (!dcc_use_mjpeg_encoder_rate_control(dcc) && - !common_graphics_channel_client_is_low_bandwidth((CommonGraphicsChannelClient*)dcc)) { + !common_graphics_channel_client_is_low_bandwidth(COMMON_GRAPHICS_CHANNEL_CLIENT(dcc))) { continue; } @@ -640,7 +640,7 @@ static uint64_t get_initial_bit_rate(DisplayChannelClient *dcc, Stream *stream) * If the network info is not initialized due to another reason, * the low_bandwidth flag is FALSE. */ - bit_rate = common_graphics_channel_client_is_low_bandwidth((CommonGraphicsChannelClient*)dcc) ? + bit_rate = common_graphics_channel_client_is_low_bandwidth(COMMON_GRAPHICS_CHANNEL_CLIENT(dcc)) ? RED_STREAM_DEFAULT_LOW_START_BIT_RATE : RED_STREAM_DEFAULT_HIGH_START_BIT_RATE; } diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c index 8e577c9..1d543d1 100644 --- a/server/tests/test_display_base.c +++ b/server/tests/test_display_base.c @@ -32,6 +32,7 @@ #include "test_display_base.h" #include "red-channel.h" +#include "common/mem.h" #ifndef PATH_MAX #define PATH_MAX 4096 -- 2.4.11 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel