diff options
author | Jonathon Jongsma <jjongsma@redhat.com> | 2016-10-13 16:49:16 -0500 |
---|---|---|
committer | Jonathon Jongsma <jjongsma@redhat.com> | 2016-10-25 11:32:13 -0500 |
commit | 96e94c6f32d4345ea25ef7a474fbd92de03b38ad (patch) | |
tree | 52f9450b21b97f737b261bcaf1675c783894286e /server | |
parent | 8f43c8348c5404107ef0641ad6f9c389ee7eb6b7 (diff) |
Convert RedChannel hierarchy to GObject
Acked-by: Frediano Ziglio <fziglio@redhat.com>
Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
Diffstat (limited to 'server')
35 files changed, 1933 insertions, 905 deletions
diff --git a/server/Makefile.am b/server/Makefile.am index dff1ad2b..7aada48c 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.c \ + dummy-channel.h \ dummy-channel-client.c \ dummy-channel-client.h \ red-common.h \ diff --git a/server/common-graphics-channel.c b/server/common-graphics-channel.c index 8af1c107..e3a3deda 100644 --- a/server/common-graphics-channel.c +++ b/server/common-graphics-channel.c @@ -29,6 +29,11 @@ #define CHANNEL_RECEIVE_BUF_SIZE 1024 +G_DEFINE_ABSTRACT_TYPE(CommonGraphicsChannel, common_graphics_channel, RED_TYPE_CHANNEL) + +#define GRAPHICS_CHANNEL_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannelPrivate)) + struct CommonGraphicsChannelPrivate { QXLInstance *qxl; @@ -43,7 +48,7 @@ struct CommonGraphicsChannelPrivate static uint8_t *common_alloc_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size) { RedChannel *channel = red_channel_client_get_channel(rcc); - CommonGraphicsChannel *common = SPICE_CONTAINEROF(channel, CommonGraphicsChannel, base); + CommonGraphicsChannel *common = COMMON_GRAPHICS_CHANNEL(channel); /* SPICE_MSGC_MIGRATE_DATA is the only client message whose size is dynamic */ if (type == SPICE_MSGC_MIGRATE_DATA) { @@ -65,6 +70,48 @@ static void common_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32 } } + +enum { + PROP0, + PROP_QXL +}; + +static void +common_graphics_channel_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + CommonGraphicsChannel *self = COMMON_GRAPHICS_CHANNEL(object); + + switch (property_id) + { + case PROP_QXL: + g_value_set_pointer(value, self->priv->qxl); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +common_graphics_channel_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + CommonGraphicsChannel *self = COMMON_GRAPHICS_CHANNEL(object); + + switch (property_id) + { + case PROP_QXL: + self->priv->qxl = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + int common_channel_config_socket(RedChannelClient *rcc) { RedClient *client = red_channel_client_get_client(rcc); @@ -106,40 +153,36 @@ int common_channel_config_socket(RedChannelClient *rcc) return TRUE; } -CommonGraphicsChannel* common_graphics_channel_new(RedsState *server, - QXLInstance *qxl, - const SpiceCoreInterfaceInternal *core, - int size, uint32_t channel_type, - int migration_flags, - ChannelCbs *channel_cbs, - channel_handle_parsed_proc handle_parsed) + +static void +common_graphics_channel_class_init(CommonGraphicsChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + g_type_class_add_private(klass, sizeof(CommonGraphicsChannelPrivate)); + + object_class->get_property = common_graphics_channel_get_property; + object_class->set_property = common_graphics_channel_set_property; + + channel_class->config_socket = common_channel_config_socket; + channel_class->alloc_recv_buf = common_alloc_recv_buf; + channel_class->release_recv_buf = common_release_recv_buf; + + g_object_class_install_property(object_class, + PROP_QXL, + g_param_spec_pointer("qxl", + "qxl", + "QXLInstance for this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +common_graphics_channel_init(CommonGraphicsChannel *self) { - RedChannel *channel = NULL; - CommonGraphicsChannel *common; - - spice_return_val_if_fail(channel_cbs, NULL); - spice_return_val_if_fail(!channel_cbs->alloc_recv_buf, NULL); - spice_return_val_if_fail(!channel_cbs->release_recv_buf, NULL); - - if (!channel_cbs->config_socket) - channel_cbs->config_socket = common_channel_config_socket; - channel_cbs->alloc_recv_buf = common_alloc_recv_buf; - channel_cbs->release_recv_buf = common_release_recv_buf; - - channel = red_channel_create_parser(size, server, - core, channel_type, - qxl->id, TRUE /* handle_acks */, - spice_get_client_channel_parser(channel_type, NULL), - handle_parsed, - channel_cbs, - migration_flags); - spice_return_val_if_fail(channel, NULL); - - common = COMMON_GRAPHICS_CHANNEL(channel); - /* FIXME remove leak */ - common->priv = g_new0(CommonGraphicsChannelPrivate, 1); - common->priv->qxl = qxl; - return common; + self->priv = GRAPHICS_CHANNEL_PRIVATE(self); } void common_graphics_channel_set_during_target_migrate(CommonGraphicsChannel *self, gboolean value) diff --git a/server/common-graphics-channel.h b/server/common-graphics-channel.h index 97cd63be..a8c3f3d4 100644 --- a/server/common-graphics-channel.h +++ b/server/common-graphics-channel.h @@ -18,21 +18,47 @@ #ifndef _COMMON_GRAPHICS_CHANNEL_H #define _COMMON_GRAPHICS_CHANNEL_H +#include <glib-object.h> + #include "red-channel.h" #include "red-channel-client.h" +G_BEGIN_DECLS + int common_channel_config_socket(RedChannelClient *rcc); #define COMMON_CLIENT_TIMEOUT (NSEC_PER_SEC * 30) +#define TYPE_COMMON_GRAPHICS_CHANNEL common_graphics_channel_get_type() + +#define COMMON_GRAPHICS_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannel)) +#define COMMON_GRAPHICS_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannelClass)) +#define COMMON_IS_GRAPHICS_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_COMMON_GRAPHICS_CHANNEL)) +#define COMMON_IS_GRAPHICS_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COMMON_GRAPHICS_CHANNEL)) +#define COMMON_GRAPHICS_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_COMMON_GRAPHICS_CHANNEL, CommonGraphicsChannelClass)) + +typedef struct CommonGraphicsChannel CommonGraphicsChannel; +typedef struct CommonGraphicsChannelClass CommonGraphicsChannelClass; typedef struct CommonGraphicsChannelPrivate CommonGraphicsChannelPrivate; -typedef struct CommonGraphicsChannel { - RedChannel base; // Must be the first thing + +struct CommonGraphicsChannel +{ + RedChannel parent; CommonGraphicsChannelPrivate *priv; -} CommonGraphicsChannel; +}; + +struct CommonGraphicsChannelClass +{ + RedChannelClass parent_class; +}; -#define COMMON_GRAPHICS_CHANNEL(Channel) ((CommonGraphicsChannel*)(Channel)) +GType common_graphics_channel_get_type(void) G_GNUC_CONST; void common_graphics_channel_set_during_target_migrate(CommonGraphicsChannel *self, gboolean value); gboolean common_graphics_channel_get_during_target_migrate(CommonGraphicsChannel *self); @@ -76,12 +102,6 @@ static inline void red_pipes_add_verb(RedChannel *channel, uint16_t verb) red_channel_apply_clients_data(channel, red_pipe_add_verb_proxy, GUINT_TO_POINTER(verb)); } -CommonGraphicsChannel* common_graphics_channel_new(RedsState *server, - QXLInstance *qxl, - const SpiceCoreInterfaceInternal *core, - int size, uint32_t channel_type, - int migration_flags, - ChannelCbs *channel_cbs, - channel_handle_parsed_proc handle_parsed); +G_END_DECLS #endif /* _COMMON_GRAPHICS_CHANNEL_H */ diff --git a/server/cursor-channel.c b/server/cursor-channel.c index cfaf55d9..dbe3dfc0 100644 --- a/server/cursor-channel.c +++ b/server/cursor-channel.c @@ -28,8 +28,6 @@ #include "reds.h" #include "red-qxl.h" -#define CURSOR_CHANNEL(channel) ((CursorChannel*)(channel)) - typedef struct CursorItem { QXLInstance *qxl; int refs; @@ -38,13 +36,10 @@ typedef struct CursorItem { G_STATIC_ASSERT(sizeof(CursorItem) <= QXL_CURSUR_DEVICE_DATA_SIZE); -typedef struct RedCursorPipeItem { - RedPipeItem base; - CursorItem *cursor_item; -} RedCursorPipeItem; +struct CursorChannel +{ + CommonGraphicsChannel parent; -typedef struct CursorChannelPrivate CursorChannelPrivate; -struct CursorChannelPrivate { CursorItem *item; int cursor_visible; SpicePoint16 cursor_position; @@ -53,12 +48,18 @@ struct CursorChannelPrivate { uint32_t mouse_mode; }; -struct CursorChannel { - CommonGraphicsChannel common; // Must be the first thing - - CursorChannelPrivate priv[1]; +struct CursorChannelClass +{ + CommonGraphicsChannelClass parent_class; }; +typedef struct RedCursorPipeItem { + RedPipeItem base; + CursorItem *cursor_item; +} RedCursorPipeItem; + +G_DEFINE_TYPE(CursorChannel, cursor_channel, TYPE_COMMON_GRAPHICS_CHANNEL) + static void cursor_pipe_item_free(RedPipeItem *pipe_item); static CursorItem *cursor_item_new(QXLInstance *qxl, RedCursorCmd *cmd) @@ -105,10 +106,10 @@ static void cursor_item_unref(CursorItem *item) static void cursor_set_item(CursorChannel *cursor, CursorItem *item) { - if (cursor->priv->item) - cursor_item_unref(cursor->priv->item); + if (cursor->item) + cursor_item_unref(cursor->item); - cursor->priv->item = item ? cursor_item_ref(item) : NULL; + cursor->item = item ? cursor_item_ref(item) : NULL; } static RedPipeItem *new_cursor_pipe_item(RedChannelClient *rcc, void *data, int num) @@ -197,12 +198,12 @@ static void red_marshall_cursor_init(CursorChannelClient *ccc, SpiceMarshaller * cursor_channel = CURSOR_CHANNEL(red_channel_client_get_channel(rcc)); red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_INIT, NULL); - msg.visible = cursor_channel->priv->cursor_visible; - msg.position = cursor_channel->priv->cursor_position; - msg.trail_length = cursor_channel->priv->cursor_trail_length; - msg.trail_frequency = cursor_channel->priv->cursor_trail_frequency; + msg.visible = cursor_channel->cursor_visible; + msg.position = cursor_channel->cursor_position; + msg.trail_length = cursor_channel->cursor_trail_length; + msg.trail_frequency = cursor_channel->cursor_trail_frequency; - cursor_fill(ccc, &msg.cursor, cursor_channel->priv->item, &info); + cursor_fill(ccc, &msg.cursor, cursor_channel->item, &info); spice_marshall_msg_cursor_init(base_marshaller, &msg); add_buf_from_info(base_marshaller, &info); } @@ -212,8 +213,7 @@ static void cursor_marshall(CursorChannelClient *ccc, RedCursorPipeItem *cursor_pipe_item) { RedChannelClient *rcc = RED_CHANNEL_CLIENT(ccc); - CursorChannel *cursor_channel = SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), - CursorChannel, common.base); + CursorChannel *cursor_channel = CURSOR_CHANNEL(red_channel_client_get_channel(rcc)); CursorItem *item = cursor_pipe_item->cursor_item; RedPipeItem *pipe_item = &cursor_pipe_item->base; RedCursorCmd *cmd; @@ -237,7 +237,7 @@ static void cursor_marshall(CursorChannelClient *ccc, red_channel_client_init_send_data(rcc, SPICE_MSG_CURSOR_SET, pipe_item); cursor_set.position = cmd->u.set.position; - cursor_set.visible = cursor_channel->priv->cursor_visible; + cursor_set.visible = cursor_channel->cursor_visible; cursor_fill(ccc, &cursor_set.cursor, item, &info); spice_marshall_msg_cursor_set(m, &cursor_set); @@ -307,24 +307,14 @@ static void cursor_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_it CursorChannel* cursor_channel_new(RedsState *server, QXLInstance *qxl, const SpiceCoreInterfaceInternal *core) { - CursorChannel *cursor_channel; - CommonGraphicsChannel *channel = NULL; - ChannelCbs cbs = { - .on_disconnect = cursor_channel_client_on_disconnect, - .send_item = cursor_channel_send_item, - }; - spice_info("create cursor channel"); - channel = common_graphics_channel_new(server, qxl, core, - sizeof(CursorChannel), - SPICE_CHANNEL_CURSOR, 0, - &cbs, red_channel_client_handle_message); - - cursor_channel = CURSOR_CHANNEL(channel); - cursor_channel->priv->cursor_visible = TRUE; - cursor_channel->priv->mouse_mode = SPICE_MOUSE_MODE_SERVER; - - return cursor_channel; + return g_object_new(TYPE_CURSOR_CHANNEL, + "spice-server", server, + "core-interface", core, + "channel-type", SPICE_CHANNEL_CURSOR, + "migration-flags", 0, + "qxl", qxl, + NULL); } void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd *cursor_cmd) @@ -341,31 +331,31 @@ void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd *cursor_cmd) switch (cursor_cmd->type) { case QXL_CURSOR_SET: - cursor->priv->cursor_visible = cursor_cmd->u.set.visible; + cursor->cursor_visible = cursor_cmd->u.set.visible; cursor_set_item(cursor, cursor_item); break; case QXL_CURSOR_MOVE: - cursor_show = !cursor->priv->cursor_visible; - cursor->priv->cursor_visible = TRUE; - cursor->priv->cursor_position = cursor_cmd->u.position; + cursor_show = !cursor->cursor_visible; + cursor->cursor_visible = TRUE; + cursor->cursor_position = cursor_cmd->u.position; break; case QXL_CURSOR_HIDE: - cursor->priv->cursor_visible = FALSE; + cursor->cursor_visible = FALSE; break; case QXL_CURSOR_TRAIL: - cursor->priv->cursor_trail_length = cursor_cmd->u.trail.length; - cursor->priv->cursor_trail_frequency = cursor_cmd->u.trail.frequency; + cursor->cursor_trail_length = cursor_cmd->u.trail.length; + cursor->cursor_trail_frequency = cursor_cmd->u.trail.frequency; break; default: spice_warning("invalid cursor command %u", cursor_cmd->type); return; } - if (red_channel_is_connected(&cursor->common.base) && - (cursor->priv->mouse_mode == SPICE_MOUSE_MODE_SERVER + if (red_channel_is_connected(RED_CHANNEL(cursor)) && + (cursor->mouse_mode == SPICE_MOUSE_MODE_SERVER || cursor_cmd->type != QXL_CURSOR_MOVE || cursor_show)) { - red_channel_pipes_new_add(&cursor->common.base, + red_channel_pipes_new_add(RED_CHANNEL(cursor), new_cursor_pipe_item, cursor_item); } @@ -374,21 +364,21 @@ void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd *cursor_cmd) void cursor_channel_reset(CursorChannel *cursor) { - RedChannel *channel = &cursor->common.base; + RedChannel *channel = RED_CHANNEL(cursor); spice_return_if_fail(cursor); cursor_set_item(cursor, NULL); - cursor->priv->cursor_visible = TRUE; - cursor->priv->cursor_position.x = cursor->priv->cursor_position.y = 0; - cursor->priv->cursor_trail_length = cursor->priv->cursor_trail_frequency = 0; + cursor->cursor_visible = TRUE; + cursor->cursor_position.x = cursor->cursor_position.y = 0; + cursor->cursor_trail_length = cursor->cursor_trail_frequency = 0; if (red_channel_is_connected(channel)) { red_channel_pipes_add_type(channel, RED_PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE); if (!common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(cursor))) { red_pipes_add_verb(channel, SPICE_MSG_CURSOR_RESET); } - if (!red_channel_wait_all_sent(&cursor->common.base, + if (!red_channel_wait_all_sent(channel, COMMON_CLIENT_TIMEOUT)) { red_channel_apply_clients(channel, red_channel_client_disconnect_if_pending_send); @@ -400,7 +390,7 @@ static void cursor_channel_init_client(CursorChannel *cursor, CursorChannelClien { spice_return_if_fail(cursor); - if (!red_channel_is_connected(&cursor->common.base) + if (!red_channel_is_connected(RED_CHANNEL(cursor)) || common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(cursor))) { spice_debug("during_target_migrate: skip init"); return; @@ -413,7 +403,7 @@ static void cursor_channel_init_client(CursorChannel *cursor, CursorChannelClien red_channel_pipes_add_type(RED_CHANNEL(cursor), RED_PIPE_ITEM_TYPE_CURSOR_INIT); } -void cursor_channel_init(CursorChannel *cursor) +void cursor_channel_do_init(CursorChannel *cursor) { cursor_channel_init_client(cursor, NULL); } @@ -422,7 +412,7 @@ void cursor_channel_set_mouse_mode(CursorChannel *cursor, uint32_t mode) { spice_return_if_fail(cursor); - cursor->priv->mouse_mode = mode; + cursor->mouse_mode = mode; } void cursor_channel_connect(CursorChannel *cursor, RedClient *client, RedsStream *stream, @@ -447,3 +437,22 @@ void cursor_channel_connect(CursorChannel *cursor, RedClient *client, RedsStream cursor_channel_init_client(cursor, ccc); } + +static void +cursor_channel_class_init(CursorChannelClass *klass) +{ + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_CURSOR, NULL); + channel_class->handle_parsed = red_channel_client_handle_message; + + channel_class->on_disconnect = cursor_channel_client_on_disconnect; + channel_class->send_item = cursor_channel_send_item; +} + +static void +cursor_channel_init(CursorChannel *self) +{ + self->cursor_visible = TRUE; + self->mouse_mode = SPICE_MOUSE_MODE_SERVER; +} diff --git a/server/cursor-channel.h b/server/cursor-channel.h index bbc48687..26b2cbb2 100644 --- a/server/cursor-channel.h +++ b/server/cursor-channel.h @@ -21,12 +21,27 @@ #include "common-graphics-channel.h" #include "red-parse-qxl.h" +G_BEGIN_DECLS + /** * This type it's a RedChannel class which implement cursor (mouse) * movements. * A pointer to CursorChannel can be converted to a RedChannel. */ typedef struct CursorChannel CursorChannel; +typedef struct CursorChannelClass CursorChannelClass; + +#define TYPE_CURSOR_CHANNEL cursor_channel_get_type() + +#define CURSOR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_CURSOR_CHANNEL, CursorChannel)) +#define CURSOR_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_CURSOR_CHANNEL, CursorChannelClass)) +#define IS_CURSOR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_CURSOR_CHANNEL)) +#define IS_CURSOR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_CURSOR_CHANNEL)) +#define CURSOR_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_CURSOR_CHANNEL, CursorChannelClass)) + +GType cursor_channel_get_type(void) G_GNUC_CONST; /** * Create CursorChannel. @@ -48,7 +63,7 @@ CursorChannel* cursor_channel_new (RedsState *server, QXLInstance */ void cursor_channel_disconnect (CursorChannel *cursor); void cursor_channel_reset (CursorChannel *cursor); -void cursor_channel_init (CursorChannel *cursor); +void cursor_channel_do_init (CursorChannel *cursor); void cursor_channel_process_cmd (CursorChannel *cursor, RedCursorCmd *cursor_cmd); void cursor_channel_set_mouse_mode(CursorChannel *cursor, uint32_t mode); @@ -70,4 +85,6 @@ void cursor_channel_connect (CursorChannel *cursor, RedClien */ void cursor_channel_client_migrate(RedChannelClient *client); +G_END_DECLS + #endif /* CURSOR_CHANNEL_H_ */ diff --git a/server/dcc-send.c b/server/dcc-send.c index e33f428f..ef67f971 100644 --- a/server/dcc-send.c +++ b/server/dcc-send.c @@ -20,7 +20,7 @@ #endif #include "dcc-private.h" -#include "display-channel.h" +#include "display-channel-private.h" #include "red-channel-client-private.h" #include <common/marshaller.h> @@ -185,8 +185,9 @@ static void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc, SpiceImage *image, SpiceImage *io_image, int is_lossy) { + DisplayChannel *display_channel = + DISPLAY_CHANNEL(red_channel_client_get_channel(rcc)); DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc); - DisplayChannel *display_channel = DCC_TO_DC(dcc); if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { spice_assert(image->descriptor.width * image->descriptor.height > 0); @@ -1817,7 +1818,7 @@ static void display_channel_marshall_migrate_data(RedChannelClient *rcc, ImageEncoders *encoders = dcc_get_encoders(dcc); SpiceMigrateDataDisplay display_data = {0,}; - display_channel = DCC_TO_DC(dcc); + display_channel = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc)); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, NULL); spice_marshaller_add_uint32(base_marshaller, SPICE_MIGRATE_DATA_DISPLAY_MAGIC); @@ -2120,8 +2121,8 @@ static void marshall_qxl_drawable(RedChannelClient *rcc, spice_return_if_fail(rcc); Drawable *item = dpi->drawable; - DisplayChannel *display = SPICE_CONTAINEROF(red_channel_client_get_channel(rcc), - DisplayChannel, common.base); + DisplayChannel *display = + DISPLAY_CHANNEL(red_channel_client_get_channel(rcc)); spice_return_if_fail(display); /* allow sized frames to be streamed, even if they where replaced by another frame, since diff --git a/server/dcc.c b/server/dcc.c index 5c05c3b1..d430d43a 100644 --- a/server/dcc.c +++ b/server/dcc.c @@ -21,6 +21,7 @@ #include "dcc-private.h" #include "display-channel.h" +#include "display-channel-private.h" #include "red-channel-client-private.h" #include "main-channel-client.h" #include "spice-server-enums.h" diff --git a/server/dcc.h b/server/dcc.h index 7a07981e..e4fe788f 100644 --- a/server/dcc.h +++ b/server/dcc.h @@ -23,7 +23,6 @@ #include "image-encoders.h" #include "image-cache.h" #include "pixmap-cache.h" -#include "red-worker.h" #include "display-limits.h" #include "red-channel-client.h" diff --git a/server/display-channel-private.h b/server/display-channel-private.h new file mode 100644 index 00000000..38330da6 --- /dev/null +++ b/server/display-channel-private.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2009-2015 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef DISPLAY_CHANNEL_PRIVATE_H_ +#define DISPLAY_CHANNEL_PRIVATE_H_ + +#include "display-channel.h" + +struct DisplayChannelPrivate +{ + DisplayChannel *pub; + + uint32_t bits_unique; + + MonitorsConfig *monitors_config; + + uint32_t renderer; + int enable_jpeg; + int enable_zlib_glz_wrap; + + Ring current_list; // of TreeItem + uint32_t current_size; + + uint32_t drawable_count; + _Drawable drawables[NUM_DRAWABLES]; + _Drawable *free_drawables; + + int stream_video; + GArray *video_codecs; + uint32_t stream_count; + Stream streams_buf[NUM_STREAMS]; + Stream *free_streams; + Ring streams; + ItemTrace items_trace[NUM_TRACE_ITEMS]; + uint32_t next_item_trace; + uint64_t streams_size_total; + + RedSurface surfaces[NUM_SURFACES]; + uint32_t n_surfaces; + SpiceImageSurfaces image_surfaces; + + ImageCache image_cache; + + int gl_draw_async_count; + +/* TODO: some day unify this, make it more runtime.. */ + stat_info_t add_stat; + stat_info_t exclude_stat; + stat_info_t __exclude_stat; +#ifdef RED_WORKER_STAT + uint32_t add_count; + uint32_t add_with_shadow_count; +#endif +#ifdef RED_STATISTICS + uint64_t *cache_hits_counter; + uint64_t *add_to_cache_counter; + uint64_t *non_cache_counter; +#endif + ImageEncoderSharedData encoder_shared_data; +}; + +#endif /* DISPLAY_CHANNEL_PRIVATE_H_ */ diff --git a/server/display-channel.c b/server/display-channel.c index 0b8d6b50..bcf26bb6 100644 --- a/server/display-channel.c +++ b/server/display-channel.c @@ -20,7 +20,69 @@ #include <common/sw_canvas.h> -#include "display-channel.h" +#include "display-channel-private.h" + +G_DEFINE_TYPE(DisplayChannel, display_channel, TYPE_COMMON_GRAPHICS_CHANNEL) + +enum { + PROP0, + PROP_N_SURFACES, + PROP_VIDEO_CODECS +}; + +static void +display_channel_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + DisplayChannel *self = DISPLAY_CHANNEL(object); + + switch (property_id) + { + case PROP_N_SURFACES: + g_value_set_uint(value, self->priv->n_surfaces); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +display_channel_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + DisplayChannel *self = DISPLAY_CHANNEL(object); + + switch (property_id) + { + case PROP_N_SURFACES: + self->priv->n_surfaces = g_value_get_uint(value); + self->priv->n_surfaces = MIN(self->priv->n_surfaces, NUM_SURFACES); + break; + case PROP_VIDEO_CODECS: + if (self->priv->video_codecs) { + g_array_unref(self->priv->video_codecs); + } + self->priv->video_codecs = g_array_ref(g_value_get_boxed(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +display_channel_finalize(GObject *object) +{ + DisplayChannel *self = DISPLAY_CHANNEL(object); + + G_OBJECT_CLASS(display_channel_parent_class)->finalize(object); + + g_free(self->priv); + g_array_unref(self->priv->video_codecs); +} static void drawable_draw(DisplayChannel *display, Drawable *drawable); static Drawable *display_channel_drawable_try_new(DisplayChannel *display, @@ -43,12 +105,16 @@ void display_channel_compress_stats_reset(DisplayChannel *display) image_encoder_shared_stat_reset(&display->priv->encoder_shared_data); } -void display_channel_compress_stats_print(const DisplayChannel *display_channel) +void display_channel_compress_stats_print(DisplayChannel *display_channel) { #ifdef COMPRESS_STAT + uint32_t id; + spice_return_if_fail(display_channel); - spice_info("==> Compression stats for display %u", display_channel->common.base.id); + g_object_get(display_channel, "id", &id, NULL); + + spice_info("==> Compression stats for display %u", id); image_encoder_shared_stat_print(&display_channel->priv->encoder_shared_data); #endif } @@ -150,6 +216,11 @@ void display_channel_set_video_codecs(DisplayChannel *display, GArray *video_cod display->priv->video_codecs = g_array_ref(video_codecs); } +int display_channel_get_stream_video(DisplayChannel *display) +{ + return display->priv->stream_video; +} + static void stop_streams(DisplayChannel *display) { Ring *ring = &display->priv->streams; @@ -280,7 +351,7 @@ static void pipes_add_drawable_after(DisplayChannel *display, pipes_add_drawable(display, drawable); return; } - if (num_other_linked != g_list_length(display->common.base.clients)) { + if (num_other_linked != red_channel_get_n_clients(RED_CHANNEL(display))) { GListIter iter; spice_debug("TODO: not O(n^2)"); FOREACH_DCC(display, iter, dcc) { @@ -1115,18 +1186,18 @@ void display_channel_process_draw(DisplayChannel *display, RedDrawable *red_draw int display_channel_wait_for_migrate_data(DisplayChannel *display) { uint64_t end_time = spice_get_monotonic_time_ns() + DISPLAY_CLIENT_MIGRATE_DATA_TIMEOUT; - RedChannel *channel = &display->common.base; RedChannelClient *rcc; int ret = FALSE; + GList *clients = red_channel_get_clients(RED_CHANNEL(display)); - if (!red_channel_is_waiting_for_migrate_data(&display->common.base)) { + if (!red_channel_is_waiting_for_migrate_data(RED_CHANNEL(display))) { return FALSE; } spice_debug(NULL); - spice_warn_if_fail(g_list_length(channel->clients) == 1); + spice_warn_if_fail(g_list_length(clients) == 1); - rcc = g_list_nth_data(channel->clients, 0); + rcc = g_list_nth_data(clients, 0); g_object_ref(rcc); for (;;) { @@ -1831,8 +1902,7 @@ void display_channel_create_surface(DisplayChannel *display, uint32_t surface_id if (display->priv->renderer == RED_RENDERER_INVALID) { int i; - QXLInstance *qxl = common_graphics_channel_get_qxl(COMMON_GRAPHICS_CHANNEL(display)); - RedsState *reds = red_qxl_get_server(qxl->st); + RedsState *reds = red_channel_get_server(RED_CHANNEL(display)); GArray *renderers = reds_get_renderers(reds); for (i = 0; i < renderers->len; i++) { uint32_t renderer = g_array_index(renderers, uint32_t, i); @@ -1900,7 +1970,7 @@ static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces, uint32_t su spice_return_val_if_fail(display_channel_validate_surface(display, surface_id), NULL); - return display->priv->surfaces[surface_id].context.canvas; + return p->surfaces[surface_id].context.canvas; } DisplayChannel* display_channel_new(RedsState *reds, @@ -1911,53 +1981,76 @@ DisplayChannel* display_channel_new(RedsState *reds, uint32_t n_surfaces) { DisplayChannel *display; - ChannelCbs cbs = { - .on_disconnect = on_disconnect, - .send_item = dcc_send_item, - .handle_migrate_flush_mark = handle_migrate_flush_mark, - .handle_migrate_data = handle_migrate_data, - .handle_migrate_data_get_serial = handle_migrate_data_get_serial, - .config_socket = dcc_config_socket - }; + + /* FIXME: migrate is not used...? */ + spice_info("create display channel"); + display = g_object_new(TYPE_DISPLAY_CHANNEL, + "spice-server", reds, + "core-interface", core, + "channel-type", SPICE_CHANNEL_DISPLAY, + "migration-flags", + (SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + "qxl", qxl, + "n-surfaces", n_surfaces, + "video-codecs", video_codecs, + NULL); + if (display) { + display_channel_set_stream_video(display, stream_video); + } + return display; +} + +static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces, uint32_t surface_id); +static void drawables_init(DisplayChannel *display); +static void +display_channel_init(DisplayChannel *self) +{ static SpiceImageSurfacesOps image_surfaces_ops = { image_surfaces_get, }; - spice_info("create display channel"); - display = DISPLAY_CHANNEL(common_graphics_channel_new( - reds, qxl, core, sizeof(*display), SPICE_CHANNEL_DISPLAY, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER, - &cbs, dcc_handle_message)); - spice_return_val_if_fail(display, NULL); - display->priv->pub = display; - - clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID; - stat_init(&display->priv->add_stat, "add", stat_clock); - stat_init(&display->priv->exclude_stat, "exclude", stat_clock); - stat_init(&display->priv->__exclude_stat, "__exclude", stat_clock); -#ifdef RED_STATISTICS - RedChannel *channel = RED_CHANNEL(display); - display->priv->cache_hits_counter = stat_add_counter(reds, channel->stat, - "cache_hits", TRUE); - display->priv->add_to_cache_counter = stat_add_counter(reds, channel->stat, - "add_to_cache", TRUE); - display->priv->non_cache_counter = stat_add_counter(reds, channel->stat, - "non_cache", TRUE); -#endif - image_encoder_shared_init(&display->priv->encoder_shared_data); + /* must be manually allocated here since g_type_class_add_private() only + * supports structs smaller than 64k */ + self->priv = g_new0(DisplayChannelPrivate, 1); + self->priv->pub = self; - display->priv->n_surfaces = MIN(n_surfaces, NUM_SURFACES); - display->priv->renderer = RED_RENDERER_INVALID; + image_encoder_shared_init(&self->priv->encoder_shared_data); - ring_init(&display->priv->current_list); - display->priv->image_surfaces.ops = &image_surfaces_ops; - drawables_init(display); - image_cache_init(&display->priv->image_cache); - display->priv->stream_video = stream_video; - display->priv->video_codecs = g_array_ref(video_codecs); - display_channel_init_streams(display); + ring_init(&self->priv->current_list); + drawables_init(self); + self->priv->image_surfaces.ops = &image_surfaces_ops; +} - return display; +static void +display_channel_constructed(GObject *object) +{ + DisplayChannel *self = DISPLAY_CHANNEL(object); + + G_OBJECT_CLASS(display_channel_parent_class)->constructed(object); + + spice_assert(self->priv->video_codecs); + + self->priv->renderer = RED_RENDERER_INVALID; + + stat_init(&self->priv->add_stat, "add", CLOCK_THREAD_CPUTIME_ID); + stat_init(&self->priv->exclude_stat, "exclude", CLOCK_THREAD_CPUTIME_ID); + stat_init(&self->priv->__exclude_stat, "__exclude", CLOCK_THREAD_CPUTIME_ID); +#ifdef RED_STATISTICS + RedsState *reds = red_channel_get_server(RED_CHANNEL(self)); + RedChannel *channel = RED_CHANNEL(self); + self->priv->cache_hits_counter = + stat_add_counter(reds, red_channel_get_stat_node(channel), + "cache_hits", TRUE); + self->priv->add_to_cache_counter = + stat_add_counter(reds, red_channel_get_stat_node(channel), + "add_to_cache", TRUE); + self->priv->non_cache_counter = + stat_add_counter(reds, red_channel_get_stat_node(channel), + "non_cache", TRUE); +#endif + image_cache_init(&self->priv->image_cache); + self->priv->stream_video = SPICE_STREAM_VIDEO_OFF; + display_channel_init_streams(self); } void display_channel_process_surface_cmd(DisplayChannel *display, @@ -2113,6 +2206,48 @@ void display_channel_reset_image_cache(DisplayChannel *self) image_cache_reset(&self->priv->image_cache); } +static void +display_channel_class_init(DisplayChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + object_class->get_property = display_channel_get_property; + object_class->set_property = display_channel_set_property; + object_class->constructed = display_channel_constructed; + object_class->finalize = display_channel_finalize; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL); + channel_class->handle_parsed = dcc_handle_message; + + channel_class->on_disconnect = on_disconnect; + channel_class->send_item = dcc_send_item; + channel_class->handle_migrate_flush_mark = handle_migrate_flush_mark; + channel_class->handle_migrate_data = handle_migrate_data; + channel_class->handle_migrate_data_get_serial = handle_migrate_data_get_serial; + channel_class->config_socket = dcc_config_socket; + + g_object_class_install_property(object_class, + PROP_N_SURFACES, + g_param_spec_uint("n-surfaces", + "number of surfaces", + "Number of surfaces for this channel", + 0, G_MAXUINT, + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property(object_class, + PROP_VIDEO_CODECS, + g_param_spec_boxed("video-codecs", + "video codecs", + "Video Codecs", + G_TYPE_ARRAY, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS)); +} + void display_channel_debug_oom(DisplayChannel *display, const char *msg) { RedChannel *channel = RED_CHANNEL(display); diff --git a/server/display-channel.h b/server/display-channel.h index 3762e54e..d7829690 100644 --- a/server/display-channel.h +++ b/server/display-channel.h @@ -37,7 +37,6 @@ #include "migration-protocol.h" #include "main-dispatcher.h" #include "spice-bitmap-utils.h" -#include "image-cache.h" #include "utils.h" #include "tree.h" #include "stream.h" @@ -45,7 +44,36 @@ #include "image-encoders.h" #include "common-graphics-channel.h" -#define DISPLAY_CHANNEL(channel) ((DisplayChannel*)(channel)) +G_BEGIN_DECLS + +#define TYPE_DISPLAY_CHANNEL display_channel_get_type() + +#define DISPLAY_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DISPLAY_CHANNEL, DisplayChannel)) +#define DISPLAY_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DISPLAY_CHANNEL, DisplayChannelClass)) +#define IS_DISPLAY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DISPLAY_CHANNEL)) +#define IS_DISPLAY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DISPLAY_CHANNEL)) +#define DISPLAY_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DISPLAY_CHANNEL, DisplayChannelClass)) + +typedef struct DisplayChannel DisplayChannel; +typedef struct DisplayChannelClass DisplayChannelClass; +typedef struct DisplayChannelPrivate DisplayChannelPrivate; + +struct DisplayChannel +{ + CommonGraphicsChannel parent; + + DisplayChannelPrivate *priv; +}; + +struct DisplayChannelClass +{ + CommonGraphicsChannelClass parent_class; +}; + +GType display_channel_get_type(void) G_GNUC_CONST; typedef struct DependItem { Drawable *drawable; @@ -154,69 +182,8 @@ struct _Drawable { } u; }; -typedef struct DisplayChannelPrivate DisplayChannelPrivate; -/* FIXME: move to separate file */ -struct DisplayChannelPrivate -{ - DisplayChannel *pub; - - uint32_t bits_unique; - - MonitorsConfig *monitors_config; - - uint32_t renderer; - int enable_jpeg; - int enable_zlib_glz_wrap; - - Ring current_list; // of TreeItem - uint32_t current_size; - - uint32_t drawable_count; - _Drawable drawables[NUM_DRAWABLES]; - _Drawable *free_drawables; - - int stream_video; - GArray *video_codecs; - uint32_t stream_count; - Stream streams_buf[NUM_STREAMS]; - Stream *free_streams; - Ring streams; - ItemTrace items_trace[NUM_TRACE_ITEMS]; - uint32_t next_item_trace; - uint64_t streams_size_total; - - RedSurface surfaces[NUM_SURFACES]; - uint32_t n_surfaces; - SpiceImageSurfaces image_surfaces; - - ImageCache image_cache; - - int gl_draw_async_count; - -/* TODO: some day unify this, make it more runtime.. */ - stat_info_t add_stat; - stat_info_t exclude_stat; - stat_info_t __exclude_stat; -#ifdef RED_WORKER_STAT - uint32_t add_count; - uint32_t add_with_shadow_count; -#endif -#ifdef RED_STATISTICS - uint64_t *cache_hits_counter; - uint64_t *add_to_cache_counter; - uint64_t *non_cache_counter; -#endif - ImageEncoderSharedData encoder_shared_data; -}; - -struct DisplayChannel { - CommonGraphicsChannel common; // Must be the first thing - - DisplayChannelPrivate priv[1]; -}; - #define FOREACH_DCC(_channel, _iter, _data) \ - GLIST_FOREACH((_channel ? RED_CHANNEL(_channel)->clients : NULL), \ + GLIST_FOREACH((_channel ? red_channel_get_clients(RED_CHANNEL(_channel)) : NULL), \ _iter, DisplayChannelClient, _data) int display_channel_get_stream_id(DisplayChannel *display, Stream *stream); @@ -262,8 +229,9 @@ void display_channel_set_stream_video (DisplayCha int stream_video); void display_channel_set_video_codecs (DisplayChannel *display, GArray *video_codecs); +int display_channel_get_stream_video (DisplayChannel *display); int display_channel_get_streams_timeout (DisplayChannel *display); -void display_channel_compress_stats_print (const DisplayChannel *display); +void display_channel_compress_stats_print (DisplayChannel *display); void display_channel_compress_stats_reset (DisplayChannel *display); void display_channel_surface_unref (DisplayChannel *display, uint32_t surface_id); @@ -415,4 +383,6 @@ static inline void region_add_clip_rects(QRegion *rgn, SpiceClipRects *data) } } +G_END_DECLS + #endif /* DISPLAY_CHANNEL_H_ */ diff --git a/server/dummy-channel-client.c b/server/dummy-channel-client.c index 1b72137a..b7fee6fa 100644 --- a/server/dummy-channel-client.c +++ b/server/dummy-channel-client.c @@ -37,9 +37,11 @@ struct DummyChannelClientPrivate static int dummy_channel_client_pre_create_validate(RedChannel *channel, RedClient *client) { - if (red_client_get_channel(client, channel->type, channel->id)) { + uint32_t type, id; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + if (red_client_get_channel(client, type, id)) { spice_printerr("Error client %p: duplicate channel type %d id %d", - client, channel->type, channel->id); + client, type, id); return FALSE; } return TRUE; @@ -54,6 +56,9 @@ static gboolean dummy_channel_client_initable_init(GInitable *initable, RedChannelClient *rcc = RED_CHANNEL_CLIENT(self); RedClient *client = red_channel_client_get_client(rcc); RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type, id; + + g_object_get(channel, "channel-type", &type, "id", &id, NULL); pthread_mutex_lock(&client->lock); if (!dummy_channel_client_pre_create_validate(channel, client)) { @@ -61,7 +66,7 @@ static gboolean dummy_channel_client_initable_init(GInitable *initable, SPICE_SERVER_ERROR, SPICE_SERVER_ERROR_FAILED, "Client %p: duplicate channel type %d id %d", - client, channel->type, channel->id); + client, type, id); goto cleanup; } @@ -94,10 +99,12 @@ static void dummy_channel_client_disconnect(RedChannelClient *rcc) DummyChannelClient *self = DUMMY_CHANNEL_CLIENT(rcc); RedChannel *channel = red_channel_client_get_channel(rcc); GList *link; + uint32_t type, id; - if (channel && (link = g_list_find(channel->clients, rcc))) { + if (channel && (link = g_list_find(red_channel_get_clients(channel), rcc))) { + g_object_get(channel, "channel-type", &type, "id", &id, NULL); spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel, - channel->type, channel->id); + type, id); red_channel_remove_client(channel, link->data); } self->priv->connected = FALSE; diff --git a/server/dummy-channel.c b/server/dummy-channel.c new file mode 100644 index 00000000..3b65bce3 --- /dev/null +++ b/server/dummy-channel.c @@ -0,0 +1,49 @@ +/* dummy-channel.c */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "dummy-channel.h" + +G_DEFINE_TYPE(DummyChannel, dummy_channel, RED_TYPE_CHANNEL) + +static void +dummy_channel_class_init(DummyChannelClass *klass) +{ +} + +static void +dummy_channel_init(DummyChannel *self) +{ +} + +// TODO: red_worker can use this one +static void dummy_watch_update_mask(SpiceWatch *watch, int event_mask) +{ +} + +static SpiceWatch *dummy_watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) +{ + return NULL; // apparently allowed? +} + +static void dummy_watch_remove(SpiceWatch *watch) +{ +} + +// TODO: actually, since I also use channel_client_dummym, no need for core. Can be NULL +static const SpiceCoreInterface dummy_core = { + .watch_update_mask = dummy_watch_update_mask, + .watch_add = dummy_watch_add, + .watch_remove = dummy_watch_remove, +}; + +RedChannel *dummy_channel_new(RedsState *reds, uint32_t type, uint32_t id) +{ + return g_object_new(TYPE_DUMMY_CHANNEL, + "spice-server", reds, + "core-interface", &dummy_core, + "channel-type", type, + "id", id, + NULL); +} diff --git a/server/dummy-channel.h b/server/dummy-channel.h new file mode 100644 index 00000000..95276339 --- /dev/null +++ b/server/dummy-channel.h @@ -0,0 +1,60 @@ +/* + Copyright (C) 2009-2015 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __DUMMY_CHANNEL_H__ +#define __DUMMY_CHANNEL_H__ + +#include <glib-object.h> + +#include "red-channel.h" + +G_BEGIN_DECLS + +// TODO: tmp, for channels that don't use RedChannel yet (e.g., snd channel), but +// do use the client callbacks. So the channel clients are not connected (the channel doesn't +// have list of them, but they do have a link to the channel, and the client has a list of them) + +#define TYPE_DUMMY_CHANNEL dummy_channel_get_type() + +#define DUMMY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DUMMY_CHANNEL, DummyChannel)) +#define DUMMY_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DUMMY_CHANNEL, DummyChannelClass)) +#define _IS_DUMMY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DUMMY_CHANNEL)) +#define _IS_DUMMY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DUMMY_CHANNEL)) +#define DUMMY_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DUMMY_CHANNEL, DummyChannelClass)) + +typedef struct DummyChannel DummyChannel; +typedef struct DummyChannelClass DummyChannelClass; + +struct DummyChannel +{ + RedChannel parent; +}; + +struct DummyChannelClass +{ + RedChannelClass parent_class; +}; + +GType dummy_channel_get_type(void) G_GNUC_CONST; + +RedChannel *dummy_channel_new(RedsState *reds, uint32_t type, uint32_t id); + +G_END_DECLS + +#endif /* __DUMMY_CHANNEL_H__ */ diff --git a/server/inputs-channel.c b/server/inputs-channel.c index d0f534a8..aadcf4b5 100644 --- a/server/inputs-channel.c +++ b/server/inputs-channel.c @@ -57,6 +57,27 @@ #define RECEIVE_BUF_SIZE \ (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE) +struct InputsChannel +{ + RedChannel parent; + + uint8_t recv_buf[RECEIVE_BUF_SIZE]; + VDAgentMouseState mouse_state; + int src_during_migrate; + SpiceTimer *key_modifiers_timer; + + SpiceKbdInstance *keyboard; + SpiceMouseInstance *mouse; + SpiceTabletInstance *tablet; +}; + +struct InputsChannelClass +{ + RedChannelClass parent_class; +}; + +G_DEFINE_TYPE(InputsChannel, inputs_channel, RED_TYPE_CHANNEL) + struct SpiceKbdState { uint8_t push_ext_type; @@ -105,18 +126,6 @@ RedsState* spice_tablet_state_get_server(SpiceTabletState *st) return st->reds; } -struct InputsChannel { - RedChannel base; - uint8_t recv_buf[RECEIVE_BUF_SIZE]; - VDAgentMouseState mouse_state; - int src_during_migrate; - SpiceTimer *key_modifiers_timer; - - SpiceKbdInstance *keyboard; - SpiceMouseInstance *mouse; - SpiceTabletInstance *tablet; -}; - typedef struct RedKeyModifiersPipeItem { RedPipeItem base; uint8_t modifiers; @@ -275,7 +284,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui InputsChannel *inputs_channel = INPUTS_CHANNEL(red_channel_client_get_channel(rcc)); InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc); uint32_t i; - RedsState *reds = red_channel_get_server(&inputs_channel->base); + RedsState *reds = red_channel_get_server(RED_CHANNEL(inputs_channel)); switch (type) { case SPICE_MSGC_INPUTS_KEY_DOWN: { @@ -359,12 +368,15 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui reds_handle_agent_mouse_event(reds, &inputs_channel->mouse_state); } else if (inputs_channel_get_tablet(inputs_channel)) { SpiceTabletInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif, SpiceTabletInterface, base); - sif->wheel(inputs_channel_get_tablet(inputs_channel), dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state)); + sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif, + SpiceTabletInterface, base); + sif->wheel(inputs_channel_get_tablet(inputs_channel), dz, + RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state)); } } else if (inputs_channel_get_mouse(inputs_channel)) { SpiceMouseInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif, SpiceMouseInterface, base); + sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif, + SpiceMouseInterface, base); sif->motion(inputs_channel_get_mouse(inputs_channel), 0, 0, dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state)); } @@ -379,12 +391,15 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui reds_handle_agent_mouse_event(reds, &inputs_channel->mouse_state); } else if (inputs_channel_get_tablet(inputs_channel)) { SpiceTabletInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif, SpiceTabletInterface, base); - sif->buttons(inputs_channel_get_tablet(inputs_channel), RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state)); + sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif, + SpiceTabletInterface, base); + sif->buttons(inputs_channel_get_tablet(inputs_channel), + RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state)); } } else if (inputs_channel_get_mouse(inputs_channel)) { SpiceMouseInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif, SpiceMouseInterface, base); + sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif, + SpiceMouseInterface, base); sif->buttons(inputs_channel_get_mouse(inputs_channel), RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state)); } @@ -519,11 +534,11 @@ static void inputs_migrate(RedChannelClient *rcc) static void inputs_channel_push_keyboard_modifiers(InputsChannel *inputs, uint8_t modifiers) { - if (!inputs || !red_channel_is_connected(&inputs->base) || + if (!inputs || !red_channel_is_connected(RED_CHANNEL(inputs)) || inputs->src_during_migrate) { return; } - red_channel_pipes_new_add_push(&inputs->base, + red_channel_pipes_new_add_push(RED_CHANNEL(inputs), red_inputs_key_modifiers_item_new, (void*)&modifiers); } @@ -569,44 +584,65 @@ static int inputs_channel_handle_migrate_data(RedChannelClient *rcc, InputsChannel* inputs_channel_new(RedsState *reds) { - ChannelCbs channel_cbs = { NULL, }; + return g_object_new(TYPE_INPUTS_CHANNEL, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", (int)SPICE_CHANNEL_INPUTS, + "id", 0, + "handle-acks", FALSE, + "migration-flags", + (guint)(SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + NULL); + +} + +static void +inputs_channel_constructed(GObject *object) +{ ClientCbs client_cbs = { NULL, }; - InputsChannel *inputs; - - channel_cbs.config_socket = inputs_channel_config_socket; - channel_cbs.on_disconnect = inputs_channel_on_disconnect; - channel_cbs.send_item = inputs_channel_send_item; - channel_cbs.alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = inputs_channel_release_msg_rcv_buf; - channel_cbs.handle_migrate_data = inputs_channel_handle_migrate_data; - channel_cbs.handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark; - - inputs = INPUTS_CHANNEL(red_channel_create_parser( - sizeof(InputsChannel), - reds, - reds_get_core_interface(reds), - SPICE_CHANNEL_INPUTS, 0, - FALSE, /* handle_acks */ - spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL), - inputs_channel_handle_parsed, - &channel_cbs, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER)); - - if (!inputs) { - spice_error("failed to allocate Inputs Channel"); - } + InputsChannel *self = INPUTS_CHANNEL(object); + RedsState *reds = red_channel_get_server(RED_CHANNEL(self)); + + G_OBJECT_CLASS(inputs_channel_parent_class)->constructed(object); client_cbs.connect = inputs_connect; client_cbs.migrate = inputs_migrate; - red_channel_register_client_cbs(&inputs->base, &client_cbs, NULL); + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); - red_channel_set_cap(&inputs->base, SPICE_INPUTS_CAP_KEY_SCANCODE); - reds_register_channel(reds, &inputs->base); + red_channel_set_cap(RED_CHANNEL(self), SPICE_INPUTS_CAP_KEY_SCANCODE); + reds_register_channel(reds, RED_CHANNEL(self)); - if (!(inputs->key_modifiers_timer = reds_core_timer_add(reds, key_modifiers_sender, inputs))) { + self->key_modifiers_timer = reds_core_timer_add(reds, key_modifiers_sender, self); + if (!self->key_modifiers_timer) { spice_error("key modifiers timer create failed"); } - return inputs; +} + +static void +inputs_channel_init(InputsChannel *self) +{ +} + + +static void +inputs_channel_class_init(InputsChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + object_class->constructed = inputs_channel_constructed; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL); + channel_class->handle_parsed = inputs_channel_handle_parsed; + + /* channel callbacks */ + channel_class->config_socket = inputs_channel_config_socket; + channel_class->on_disconnect = inputs_channel_on_disconnect; + channel_class->send_item = inputs_channel_send_item; + channel_class->alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf; + channel_class->release_recv_buf = inputs_channel_release_msg_rcv_buf; + channel_class->handle_migrate_data = inputs_channel_handle_migrate_data; + channel_class->handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark; } static SpiceKbdInstance* inputs_channel_get_keyboard(InputsChannel *inputs) @@ -621,7 +657,7 @@ int inputs_channel_set_keyboard(InputsChannel *inputs, SpiceKbdInstance *keyboar return -1; } inputs->keyboard = keyboard; - inputs->keyboard->st = spice_kbd_state_new(red_channel_get_server(&inputs->base)); + inputs->keyboard->st = spice_kbd_state_new(red_channel_get_server(RED_CHANNEL(inputs))); return 0; } diff --git a/server/inputs-channel.h b/server/inputs-channel.h index ae84eed1..26569fa6 100644 --- a/server/inputs-channel.h +++ b/server/inputs-channel.h @@ -22,15 +22,30 @@ // This include should only be used by reds.c and inputs-channel.c #include <stdint.h> +#include <glib-object.h> #include <spice/vd_agent.h> #include "red-channel.h" -#define INPUTS_CHANNEL(channel) ((InputsChannel*)(channel)) +G_BEGIN_DECLS + +#define TYPE_INPUTS_CHANNEL inputs_channel_get_type() + +#define INPUTS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_INPUTS_CHANNEL, InputsChannel)) +#define INPUTS_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_INPUTS_CHANNEL, InputsChannelClass)) +#define INPUTS_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_INPUTS_CHANNEL)) +#define INPUTS_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_INPUTS_CHANNEL)) +#define INPUTS_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_INPUTS_CHANNEL, InputsChannelClass)) typedef struct InputsChannel InputsChannel; +typedef struct InputsChannelClass InputsChannelClass; + +GType inputs_channel_get_type(void) G_GNUC_CONST; InputsChannel* inputs_channel_new(RedsState *reds); + const VDAgentMouseState *inputs_channel_get_mouse_state(InputsChannel *inputs); void inputs_channel_on_keyboard_leds_change(InputsChannel *inputs, uint8_t leds); void inputs_channel_set_tablet_logical_size(InputsChannel *inputs, int x_res, int y_res); @@ -44,4 +59,6 @@ RedsState* spice_tablet_state_get_server(SpiceTabletState *dev); RedsState* spice_kbd_state_get_server(SpiceKbdState *dev); gboolean inputs_channel_is_src_during_migrate(InputsChannel *inputs); +G_END_DECLS + #endif diff --git a/server/main-channel-client.c b/server/main-channel-client.c index b47b1e0d..daa3dfc8 100644 --- a/server/main-channel-client.c +++ b/server/main-channel-client.c @@ -435,7 +435,7 @@ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, spice_printerr("client %p connected: %d seamless %d", client, success, seamless); if (mcc->priv->mig_wait_connect) { RedChannel *channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc)); - MainChannel *main_channel = SPICE_CONTAINEROF(channel, MainChannel, base); + MainChannel *main_channel = MAIN_CHANNEL(channel); mcc->priv->mig_wait_connect = FALSE; mcc->priv->mig_connect_ok = success; @@ -453,7 +453,7 @@ void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc, uint32_t src_version) { RedChannel *channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc)); - if (reds_on_migrate_dst_set_seamless(channel->reds, mcc, src_version)) { + if (reds_on_migrate_dst_set_seamless(red_channel_get_server(channel), mcc, src_version)) { mcc->priv->seamless_mig_dst = TRUE; red_channel_client_pipe_add_empty_msg(RED_CHANNEL_CLIENT(mcc), SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK); @@ -553,7 +553,7 @@ void main_channel_client_migrate_dst_complete(MainChannelClient *mcc) if (mcc->priv->mig_wait_prev_complete) { if (mcc->priv->mig_wait_prev_try_seamless) { RedChannel *channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(mcc)); - spice_assert(g_list_length(channel->clients) == 1); + spice_assert(red_channel_get_n_clients(channel) == 1); red_channel_client_pipe_add_type(RED_CHANNEL_CLIENT(mcc), RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS); } else { @@ -609,9 +609,11 @@ static void do_ping_client(MainChannelClient *mcc, if (has_interval && interval > 0) { mcc->priv->ping_interval = interval * MSEC_PER_SEC; } - reds_core_timer_start(channel->reds, mcc->priv->ping_timer, mcc->priv->ping_interval); + reds_core_timer_start(red_channel_get_server(channel), + mcc->priv->ping_timer, mcc->priv->ping_interval); } else if (!strcmp(opt, "off")) { - reds_core_timer_cancel(channel->reds, mcc->priv->ping_timer); + reds_core_timer_cancel(red_channel_get_server(channel), + mcc->priv->ping_timer); } else { return; } @@ -624,11 +626,13 @@ static void ping_timer_cb(void *opaque) if (!red_channel_client_is_connected(RED_CHANNEL_CLIENT(mcc))) { spice_printerr("not connected to peer, ping off"); - reds_core_timer_cancel(channel->reds, mcc->priv->ping_timer); + reds_core_timer_cancel(red_channel_get_server(channel), + mcc->priv->ping_timer); return; } do_ping_client(mcc, NULL, 0, 0); - reds_core_timer_start(channel->reds, mcc->priv->ping_timer, mcc->priv->ping_interval); + reds_core_timer_start(red_channel_get_server(channel), + mcc->priv->ping_timer, mcc->priv->ping_interval); } #endif /* RED_STATISTICS */ @@ -693,7 +697,8 @@ uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc) void main_channel_client_migrate(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); - reds_on_main_channel_migrate(channel->reds, MAIN_CHANNEL_CLIENT(rcc)); + reds_on_main_channel_migrate(red_channel_get_server(channel), + MAIN_CHANNEL_CLIENT(rcc)); red_channel_client_default_migrate(rcc); } @@ -754,7 +759,7 @@ static void main_channel_marshall_channels(RedChannelClient *rcc, RedChannel *channel = red_channel_client_get_channel(rcc); red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST, item); - channels_info = reds_msg_channels_new(channel->reds); + channels_info = reds_msg_channels_new(red_channel_get_server(channel)); spice_marshall_msg_main_channels_list(m, channels_info); free(channels_info); } @@ -829,7 +834,8 @@ static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc, { RedChannel *channel = red_channel_client_get_channel(rcc); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item); - reds_marshall_migrate_data(channel->reds, m); // TODO: from reds split. ugly separation. + // TODO: from reds split. ugly separation. + reds_marshall_migrate_data(red_channel_get_server(channel), m); } static void main_channel_marshall_init(RedChannelClient *rcc, @@ -847,7 +853,7 @@ static void main_channel_marshall_init(RedChannelClient *rcc, if (item->is_client_mouse_allowed) { init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT; } - init.agent_connected = reds_has_vdagent(channel->reds); + init.agent_connected = reds_has_vdagent(red_channel_get_server(channel)); init.agent_tokens = REDS_AGENT_WINDOW_SIZE; init.multi_media_time = item->multi_media_time; init.ram_hint = item->ram_hint; @@ -891,11 +897,9 @@ static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelCl { RedChannel *channel = red_channel_client_get_channel(rcc); SpiceMsgMainMigrationBegin migrate; - MainChannel *main_ch; red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN, item); - main_ch = SPICE_CONTAINEROF(channel, MainChannel, base); - main_channel_fill_migrate_dst_info(main_ch, &migrate.dst_info); + main_channel_fill_migrate_dst_info(MAIN_CHANNEL(channel), &migrate.dst_info); spice_marshall_msg_main_migrate_begin(m, &migrate); } @@ -905,11 +909,9 @@ static void main_channel_marshall_migrate_begin_seamless(SpiceMarshaller *m, { RedChannel *channel = red_channel_client_get_channel(rcc); SpiceMsgMainMigrateBeginSeamless migrate_seamless; - MainChannel *main_ch; red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS, item); - main_ch = SPICE_CONTAINEROF(channel, MainChannel, base); - main_channel_fill_migrate_dst_info(main_ch, &migrate_seamless.dst_info); + main_channel_fill_migrate_dst_info(MAIN_CHANNEL(channel), &migrate_seamless.dst_info); migrate_seamless.src_mig_version = SPICE_MIGRATION_PROTOCOL_VERSION; spice_marshall_msg_main_migrate_begin_seamless(m, &migrate_seamless); } @@ -935,7 +937,7 @@ static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelC spice_printerr(""); red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST, item); - main_ch = SPICE_CONTAINEROF(channel, MainChannel, base); + main_ch = MAIN_CHANNEL(channel); mig_target = main_channel_get_migration_target(main_ch); migrate.port = mig_target->port; migrate.sport = mig_target->sport; diff --git a/server/main-channel.c b/server/main-channel.c index e69e34eb..cf5ee6ae 100644 --- a/server/main-channel.c +++ b/server/main-channel.c @@ -27,9 +27,30 @@ #include "main-channel.h" #include "main-channel-client.h" +// approximate max receive message size for main channel +#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \ + (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE) + +struct MainChannel +{ + RedChannel parent; + + uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE]; + // TODO: add refs and release (afrer all clients completed migration in one way or the other?) + RedsMigSpice mig_target; + int num_clients_mig_wait; +}; + +struct MainChannelClass +{ + RedChannelClass parent_class; +}; + +G_DEFINE_TYPE(MainChannel, main_channel, RED_TYPE_CHANNEL) + int main_channel_is_connected(MainChannel *main_chan) { - return red_channel_is_connected(&main_chan->base); + return red_channel_is_connected(RED_CHANNEL(main_chan)); } /* @@ -76,27 +97,29 @@ void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, .is_client_mouse_allowed=is_client_mouse_allowed, }; - red_channel_pipes_new_add_push(&main_chan->base, + red_channel_pipes_new_add_push(RED_CHANNEL(main_chan), main_mouse_mode_item_new, &info); } void main_channel_push_agent_connected(MainChannel *main_chan) { - if (red_channel_test_remote_cap(&main_chan->base, SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) { - red_channel_pipes_add_type(&main_chan->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS); + if (red_channel_test_remote_cap(RED_CHANNEL(main_chan), + SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) { + red_channel_pipes_add_type(RED_CHANNEL(main_chan), + RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS); } else { - red_channel_pipes_add_empty_msg(&main_chan->base, SPICE_MSG_MAIN_AGENT_CONNECTED); + red_channel_pipes_add_empty_msg(RED_CHANNEL(main_chan), SPICE_MSG_MAIN_AGENT_CONNECTED); } } void main_channel_push_agent_disconnected(MainChannel *main_chan) { - red_channel_pipes_add_type(&main_chan->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED); + red_channel_pipes_add_type(RED_CHANNEL(main_chan), RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED); } static void main_channel_push_migrate_data_item(MainChannel *main_chan) { - red_channel_pipes_add_type(&main_chan->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA); + red_channel_pipes_add_type(RED_CHANNEL(main_chan), RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA); } static int main_channel_handle_migrate_data(RedChannelClient *rcc, @@ -107,7 +130,7 @@ static int main_channel_handle_migrate_data(RedChannelClient *rcc, SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message; /* not supported with multi-clients */ - spice_assert(g_list_length(channel->clients) == 1); + spice_assert(red_channel_get_n_clients(channel) == 1); if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataMain)) { spice_printerr("bad message size %u", size); @@ -119,7 +142,9 @@ static int main_channel_handle_migrate_data(RedChannelClient *rcc, spice_error("bad header"); return FALSE; } - return reds_handle_migrate_data(channel->reds, mcc, (SpiceMigrateDataMain *)(header + 1), size); + return reds_handle_migrate_data(red_channel_get_server(channel), mcc, + (SpiceMigrateDataMain *)(header + 1), + size); } void main_channel_push_multi_media_time(MainChannel *main_chan, int time) @@ -128,7 +153,7 @@ void main_channel_push_multi_media_time(MainChannel *main_chan, int time) .time = time, }; - red_channel_pipes_new_add_push(&main_chan->base, + red_channel_pipes_new_add_push(RED_CHANNEL(main_chan), main_multi_media_time_item_new, &info); } @@ -150,15 +175,16 @@ static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target) { main_channel_fill_mig_target(main_chan, mig_target); - red_channel_pipes_add_type(&main_chan->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); + red_channel_pipes_add_type(RED_CHANNEL(main_chan), RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); } static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message) { RedChannel *channel = red_channel_client_get_channel(rcc); - MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base); + MainChannel *main_chan = MAIN_CHANNEL(channel); MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); + RedsState *reds = red_channel_get_server(channel); switch (type) { case SPICE_MSGC_MAIN_AGENT_START: { @@ -169,18 +195,18 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint return FALSE; } tokens = (SpiceMsgcMainAgentStart *)message; - reds_on_main_agent_start(channel->reds, mcc, tokens->num_tokens); + reds_on_main_agent_start(reds, mcc, tokens->num_tokens); break; } case SPICE_MSGC_MAIN_AGENT_DATA: { - reds_on_main_agent_data(channel->reds, mcc, message, size); + reds_on_main_agent_data(reds, mcc, message, size); break; } case SPICE_MSGC_MAIN_AGENT_TOKEN: { SpiceMsgcMainAgentTokens *tokens; tokens = (SpiceMsgcMainAgentTokens *)message; - reds_on_main_agent_tokens(channel->reds, mcc, tokens->num_tokens); + reds_on_main_agent_tokens(reds, mcc, tokens->num_tokens); break; } case SPICE_MSGC_MAIN_ATTACH_CHANNELS: @@ -204,7 +230,7 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint ((SpiceMsgcMainMigrateDstDoSeamless *)message)->src_version); break; case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST: - reds_on_main_mouse_mode_request(channel->reds, message, size); + reds_on_main_mouse_mode_request(reds, message, size); break; case SPICE_MSGC_PONG: { main_channel_client_handle_pong(mcc, (SpiceMsgPing *)message, size); @@ -226,11 +252,11 @@ static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, uint32_t size) { RedChannel *channel = red_channel_client_get_channel(rcc); - MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base); + MainChannel *main_chan = MAIN_CHANNEL(channel); MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); if (type == SPICE_MSGC_MAIN_AGENT_DATA) { - return reds_get_agent_data_buffer(channel->reds, mcc, size); + return reds_get_agent_data_buffer(red_channel_get_server(channel), mcc, size); } else { return main_chan->recv_buf; } @@ -243,7 +269,7 @@ static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc, { RedChannel *channel = red_channel_client_get_channel(rcc); if (type == SPICE_MSGC_MAIN_AGENT_DATA) { - reds_release_agent_data_buffer(channel->reds, msg); + reds_release_agent_data_buffer(red_channel_get_server(channel), msg); } } @@ -256,8 +282,7 @@ static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); spice_debug(NULL); - main_channel_push_migrate_data_item(SPICE_CONTAINEROF(channel, - MainChannel, base)); + main_channel_push_migrate_data_item(MAIN_CHANNEL(channel)); return TRUE; } @@ -282,12 +307,14 @@ MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client, int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen) { - return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1; + return main_chan ? + getsockname(red_channel_get_first_socket(RED_CHANNEL(main_chan)), sa, salen) : -1; } int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen) { - return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1; + return main_chan ? + getpeername(red_channel_get_first_socket(RED_CHANNEL(main_chan)), sa, salen) : -1; } // TODO: ? shouldn't it disonnect all clients? or shutdown all main_channels? @@ -295,42 +322,64 @@ void main_channel_close(MainChannel *main_chan) { int socketfd; - if (main_chan && (socketfd = red_channel_get_first_socket(&main_chan->base)) != -1) { + if (main_chan && (socketfd = red_channel_get_first_socket(RED_CHANNEL(main_chan))) != -1) { close(socketfd); } } MainChannel* main_channel_new(RedsState *reds) { - RedChannel *channel; - ChannelCbs channel_cbs = { NULL, }; - ClientCbs client_cbs = {NULL, }; + // TODO: set the migration flag of the channel + return g_object_new(TYPE_MAIN_CHANNEL, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", (gint)SPICE_CHANNEL_MAIN, + "id", 0, + "handle-acks", FALSE, /* handle_acks */ + "migration-flags", + (SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + NULL); +} + +static void +main_channel_constructed(GObject *object) +{ + MainChannel *self = MAIN_CHANNEL(object); + ClientCbs client_cbs = { NULL, }; - channel_cbs.config_socket = main_channel_config_socket; - channel_cbs.on_disconnect = main_channel_client_on_disconnect; - channel_cbs.send_item = main_channel_client_send_item; - channel_cbs.alloc_recv_buf = main_channel_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = main_channel_release_msg_rcv_buf; - channel_cbs.handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark; - channel_cbs.handle_migrate_data = main_channel_handle_migrate_data; + G_OBJECT_CLASS(main_channel_parent_class)->constructed(object); - // TODO: set the migration flag of the channel - channel = red_channel_create_parser(sizeof(MainChannel), reds, - reds_get_core_interface(reds), - SPICE_CHANNEL_MAIN, 0, - FALSE, /* handle_acks */ - spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL), - main_channel_handle_parsed, - &channel_cbs, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER); - spice_assert(channel); - red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); - red_channel_set_cap(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE); + red_channel_set_cap(RED_CHANNEL(self), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); + red_channel_set_cap(RED_CHANNEL(self), SPICE_MAIN_CAP_SEAMLESS_MIGRATE); client_cbs.migrate = main_channel_client_migrate; - red_channel_register_client_cbs(channel, &client_cbs, NULL); + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); +} - return MAIN_CHANNEL(channel); +static void +main_channel_init(MainChannel *self) +{ +} + +static void +main_channel_class_init(MainChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + object_class->constructed = main_channel_constructed; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL); + channel_class->handle_parsed = main_channel_handle_parsed; + + /* channel callbacks */ + channel_class->config_socket = main_channel_config_socket; + channel_class->on_disconnect = main_channel_client_on_disconnect; + channel_class->send_item = main_channel_client_send_item; + channel_class->alloc_recv_buf = main_channel_alloc_msg_rcv_buf; + channel_class->release_recv_buf = main_channel_release_msg_rcv_buf; + channel_class->handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark; + channel_class->handle_migrate_data = main_channel_handle_migrate_data; } static int main_channel_connect_semi_seamless(MainChannel *main_channel) @@ -351,7 +400,7 @@ static int main_channel_connect_seamless(MainChannel *main_channel) GListIter iter; RedChannelClient *rcc; - spice_assert(g_list_length(main_channel->base.clients) == 1); + spice_assert(red_channel_get_n_clients(RED_CHANNEL(main_channel)) == 1); FOREACH_CLIENT(main_channel, iter, rcc) { MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); @@ -375,8 +424,10 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta return main_channel_connect_semi_seamless(main_channel); } else { RedChannelClient *rcc; + GList *clients = red_channel_get_clients(RED_CHANNEL(main_channel)); - rcc = g_list_nth_data(main_channel->base.clients, 0); + /* just test the first one */ + rcc = g_list_nth_data(clients, 0); if (!red_channel_client_test_remote_cap(rcc, SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) { @@ -408,7 +459,7 @@ int main_channel_migrate_src_complete(MainChannel *main_chan, int success) spice_printerr(""); - if (!main_chan->base.clients) { + if (!red_channel_get_clients(RED_CHANNEL(main_chan))) { spice_printerr("no peer connected"); return 0; } diff --git a/server/main-channel.h b/server/main-channel.h index 6e89f14a..19beb7c0 100644 --- a/server/main-channel.h +++ b/server/main-channel.h @@ -19,22 +19,34 @@ #define __MAIN_CHANNEL_H__ #include <stdint.h> +#include <glib-object.h> #include <spice/vd_agent.h> #include <common/marshaller.h> #include "red-channel.h" -#define MAIN_CHANNEL(channel) ((MainChannel*)(channel)) +G_BEGIN_DECLS + +#define TYPE_MAIN_CHANNEL main_channel_get_type() + +#define MAIN_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_MAIN_CHANNEL, MainChannel)) +#define MAIN_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_MAIN_CHANNEL, MainChannelClass)) +#define IS_MAIN_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_MAIN_CHANNEL)) +#define IS_MAIN_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_MAIN_CHANNEL)) +#define MAIN_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_MAIN_CHANNEL, MainChannelClass)) + +typedef struct MainChannel MainChannel; +typedef struct MainChannelClass MainChannelClass; + +GType main_channel_get_type(void) G_GNUC_CONST; // TODO: Defines used to calculate receive buffer size, and also by reds.c // other options: is to make a reds_main_consts.h, to duplicate defines. #define REDS_AGENT_WINDOW_SIZE 10 #define REDS_NUM_INTERNAL_AGENT_MESSAGES 1 -// approximate max receive message size for main channel -#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \ - (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE) - struct RedsMigSpice { char *host; char *cert_subject; @@ -43,14 +55,6 @@ struct RedsMigSpice { }; typedef struct RedsMigSpice RedsMigSpice; -typedef struct MainChannel { - RedChannel base; - uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE]; - RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?) - int num_clients_mig_wait; -} MainChannel; - - MainChannel *main_channel_new(RedsState *reds); RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id); /* This is a 'clone' from the reds.h Channel.link callback to allow passing link_id */ @@ -83,4 +87,6 @@ int main_channel_migrate_src_complete(MainChannel *main_chan, int success); void main_channel_on_migrate_connected(MainChannel *main_channel, gboolean success, gboolean seamless); +G_END_DECLS + #endif diff --git a/server/red-channel-client-private.h b/server/red-channel-client-private.h index 83fef237..e77cdf9e 100644 --- a/server/red-channel-client-private.h +++ b/server/red-channel-client-private.h @@ -21,6 +21,25 @@ #include "red-channel.h" #include "red-channel-client.h" +typedef struct RedChannelClientLatencyMonitor { + int state; + uint64_t last_pong_time; + SpiceTimer *timer; + uint32_t id; + int tcp_nodelay; + int warmup_was_sent; + + int64_t roundtrip; +} RedChannelClientLatencyMonitor; + +typedef struct RedChannelClientConnectivityMonitor { + int state; + uint32_t out_bytes; + uint32_t in_bytes; + uint32_t timeout; + SpiceTimer *timer; +} RedChannelClientConnectivityMonitor; + typedef struct OutgoingHandler { OutgoingHandlerInterface *cb; void *opaque; diff --git a/server/red-channel-client.c b/server/red-channel-client.c index 9426b137..c562e8ed 100644 --- a/server/red-channel-client.c +++ b/server/red-channel-client.c @@ -93,6 +93,8 @@ typedef struct MarkerPipeItem { static void red_channel_client_start_ping_timer(RedChannelClient *rcc, uint32_t timeout) { + SpiceCoreInterfaceInternal *core; + if (!rcc->priv->latency_monitor.timer) { return; } @@ -100,11 +102,15 @@ static void red_channel_client_start_ping_timer(RedChannelClient *rcc, uint32_t return; } rcc->priv->latency_monitor.state = PING_STATE_TIMER; - rcc->priv->channel->core->timer_start(rcc->priv->latency_monitor.timer, timeout); + + core = red_channel_get_core_interface(rcc->priv->channel); + core->timer_start(rcc->priv->latency_monitor.timer, timeout); } static void red_channel_client_cancel_ping_timer(RedChannelClient *rcc) { + SpiceCoreInterfaceInternal *core; + if (!rcc->priv->latency_monitor.timer) { return; } @@ -112,7 +118,8 @@ static void red_channel_client_cancel_ping_timer(RedChannelClient *rcc) return; } - rcc->priv->channel->core->timer_cancel(rcc->priv->latency_monitor.timer); + core = red_channel_get_core_interface(rcc->priv->channel); + core->timer_cancel(rcc->priv->latency_monitor.timer); rcc->priv->latency_monitor.state = PING_STATE_NONE; } @@ -187,10 +194,10 @@ red_channel_client_set_property(GObject *object, break; case PROP_CHANNEL: if (self->priv->channel) - red_channel_unref(self->priv->channel); + g_object_unref(self->priv->channel); self->priv->channel = g_value_get_pointer(value); if (self->priv->channel) - red_channel_ref(self->priv->channel); + g_object_ref(self->priv->channel); break; case PROP_CLIENT: self->priv->client = g_value_get_pointer(value); @@ -243,7 +250,7 @@ red_channel_client_finalize(GObject *object) red_channel_client_destroy_remote_caps(self); if (self->priv->channel) { - red_channel_unref(self->priv->channel); + g_object_unref(self->priv->channel); } G_OBJECT_CLASS(red_channel_client_parent_class)->finalize(object); @@ -361,7 +368,6 @@ void red_channel_client_on_output(void *opaque, int n) if (rcc->priv->connectivity_monitor.timer) { rcc->priv->connectivity_monitor.out_bytes += n; } - stat_inc_counter(reds, rcc->priv->channel->out_bytes_counter, n); } void red_channel_client_on_input(void *opaque, int n) @@ -391,12 +397,14 @@ void red_channel_client_prepare_out_msg(void *opaque, struct iovec *vec, void red_channel_client_on_out_block(void *opaque) { + SpiceCoreInterfaceInternal *core; RedChannelClient *rcc = RED_CHANNEL_CLIENT(opaque); rcc->priv->send_data.blocked = TRUE; - rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch, - SPICE_WATCH_EVENT_READ | - SPICE_WATCH_EVENT_WRITE); + core = red_channel_get_core_interface(rcc->priv->channel); + core->watch_update_mask(rcc->priv->stream->watch, + SPICE_WATCH_EVENT_READ | + SPICE_WATCH_EVENT_WRITE); } static inline int red_channel_client_urgent_marshaller_is_active(RedChannelClient *rcc) @@ -439,9 +447,9 @@ static void red_channel_client_send_migrate(RedChannelClient *rcc) SpiceMsgMigrate migrate; red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE, NULL); - migrate.flags = rcc->priv->channel->migration_flags; + g_object_get(rcc->priv->channel, "migration-flags", &migrate.flags, NULL); spice_marshall_msg_migrate(rcc->priv->send_data.marshaller, &migrate); - if (rcc->priv->channel->migration_flags & SPICE_MIGRATE_NEED_FLUSH) { + if (migrate.flags & SPICE_MIGRATE_NEED_FLUSH) { rcc->priv->wait_migrate_flush_mark = TRUE; } @@ -515,7 +523,7 @@ static void red_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *ite case RED_PIPE_ITEM_TYPE_MARKER: break; default: - rcc->priv->channel->channel_cbs.send_item(rcc, item); + red_channel_send_item(rcc->priv->channel, rcc, item); break; } red_pipe_item_unref(item); @@ -558,9 +566,10 @@ void red_channel_client_on_out_msg_done(void *opaque) red_channel_client_release_sent_item(rcc); if (rcc->priv->send_data.blocked) { + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); rcc->priv->send_data.blocked = FALSE; - rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch, - SPICE_WATCH_EVENT_READ); + core->watch_update_mask(rcc->priv->stream->watch, + SPICE_WATCH_EVENT_READ); } if (red_channel_client_urgent_marshaller_is_active(rcc)) { @@ -645,8 +654,13 @@ static void red_channel_client_ping_timer(void *opaque) static inline int red_channel_client_waiting_for_ack(RedChannelClient *rcc) { - return (rcc->priv->channel->handle_acks && - (rcc->priv->ack_data.messages_window > rcc->priv->ack_data.client_window * 2)); + gboolean handle_acks; + g_object_get(rcc->priv->channel, + "handle-acks", &handle_acks, + NULL); + + return (handle_acks && (rcc->priv->ack_data.messages_window > + rcc->priv->ack_data.client_window * 2)); } /* @@ -687,6 +701,7 @@ static void red_channel_client_connectivity_timer(void *opaque) } if (is_alive) { + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); monitor->in_bytes = 0; monitor->out_bytes = 0; if (rcc->priv->send_data.blocked || red_channel_client_waiting_for_ack(rcc)) { @@ -697,18 +712,24 @@ static void red_channel_client_connectivity_timer(void *opaque) } else { monitor->state = CONNECTIVITY_STATE_CONNECTED; } - rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer, + core->timer_start(rcc->priv->connectivity_monitor.timer, rcc->priv->connectivity_monitor.timeout); } else { + uint32_t type, id; + g_object_get(rcc->priv->channel, + "channel-type", &type, + "id", &id, + NULL); monitor->state = CONNECTIVITY_STATE_DISCONNECTED; spice_warning("rcc %p on channel %d:%d has been unresponsive for more than %u ms, disconnecting", - rcc, rcc->priv->channel->type, rcc->priv->channel->id, monitor->timeout); + rcc, type, id, monitor->timeout); red_channel_client_disconnect(rcc); } } void red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc, uint32_t timeout_ms) { + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); if (!red_channel_client_is_connected(rcc)) { return; } @@ -721,8 +742,8 @@ void red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc, uin * on this channel. */ if (rcc->priv->latency_monitor.timer == NULL) { - rcc->priv->latency_monitor.timer = rcc->priv->channel->core->timer_add( - rcc->priv->channel->core, red_channel_client_ping_timer, rcc); + rcc->priv->latency_monitor.timer = core->timer_add( + core, red_channel_client_ping_timer, rcc); if (!red_client_during_migrate_at_target(rcc->priv->client)) { red_channel_client_start_ping_timer(rcc, PING_TEST_IDLE_NET_TIMEOUT_MS); } @@ -730,12 +751,12 @@ void red_channel_client_start_connectivity_monitoring(RedChannelClient *rcc, uin } if (rcc->priv->connectivity_monitor.timer == NULL) { rcc->priv->connectivity_monitor.state = CONNECTIVITY_STATE_CONNECTED; - rcc->priv->connectivity_monitor.timer = rcc->priv->channel->core->timer_add( - rcc->priv->channel->core, red_channel_client_connectivity_timer, rcc); + rcc->priv->connectivity_monitor.timer = core->timer_add( + core, red_channel_client_connectivity_timer, rcc); rcc->priv->connectivity_monitor.timeout = timeout_ms; if (!red_client_during_migrate_at_target(rcc->priv->client)) { - rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer, - rcc->priv->connectivity_monitor.timeout); + core->timer_start(rcc->priv->connectivity_monitor.timer, + rcc->priv->connectivity_monitor.timeout); } } } @@ -832,9 +853,11 @@ static const SpiceDataHeaderOpaque mini_header_wrapper = {NULL, sizeof(SpiceMini static int red_channel_client_pre_create_validate(RedChannel *channel, RedClient *client) { - if (red_client_get_channel(client, channel->type, channel->id)) { + uint32_t type, id; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + if (red_client_get_channel(client, type, id)) { spice_printerr("Error client %p: duplicate channel type %d id %d", - client, channel->type, channel->id); + client, type, id); return FALSE; } return TRUE; @@ -845,24 +868,28 @@ static gboolean red_channel_client_initable_init(GInitable *initable, GError **error) { GError *local_error = NULL; + SpiceCoreInterfaceInternal *core; RedChannelClient *self = RED_CHANNEL_CLIENT(initable); pthread_mutex_lock(&self->priv->client->lock); if (!red_channel_client_pre_create_validate(self->priv->channel, self->priv->client)) { + uint32_t id, type; + g_object_get(self->priv->channel, + "channel-type", &type, + "id", &id, + NULL); g_set_error(&local_error, SPICE_SERVER_ERROR, SPICE_SERVER_ERROR_FAILED, "Client %p: duplicate channel type %d id %d", - self->priv->client, self->priv->channel->type, - self->priv->channel->id); + self->priv->client, type, id); goto cleanup; } + core = red_channel_get_core_interface(self->priv->channel); if (self->priv->monitor_latency && reds_stream_get_family(self->priv->stream) != AF_UNIX) { self->priv->latency_monitor.timer = - self->priv->channel->core->timer_add(self->priv->channel->core, - red_channel_client_ping_timer, - self); + core->timer_add(core, red_channel_client_ping_timer, self); if (!self->priv->client->during_target_migrate) { red_channel_client_start_ping_timer(self, @@ -872,27 +899,26 @@ static gboolean red_channel_client_initable_init(GInitable *initable, } self->incoming.opaque = self; - self->incoming.cb = &self->priv->channel->incoming_cb; + self->incoming.cb = red_channel_get_incoming_handler(self->priv->channel); self->incoming.header.data = self->incoming.header_buf; self->priv->outgoing.opaque = self; - self->priv->outgoing.cb = &self->priv->channel->outgoing_cb; + self->priv->outgoing.cb = red_channel_get_outgoing_handler(self->priv->channel); self->priv->outgoing.pos = 0; self->priv->outgoing.size = 0; g_queue_init(&self->priv->pipe); 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 = g_list_length(self->priv->channel->clients); + core->watch_add(core, self->priv->stream->socket, + SPICE_WATCH_EVENT_READ, + red_channel_client_event, + self); + self->priv->id = red_channel_get_n_clients(self->priv->channel); red_channel_add_client(self->priv->channel, self); red_client_add_channel(self->priv->client, self); - if (!self->priv->channel->channel_cbs.config_socket(self)) { + if (!red_channel_config_socket(self->priv->channel, self)) { g_set_error_literal(&local_error, SPICE_SERVER_ERROR, SPICE_SERVER_ERROR_FAILED, @@ -953,8 +979,9 @@ static void red_channel_client_seamless_migration_done(RedChannelClient *rcc) red_channel_client_start_ping_timer(rcc, PING_TEST_IDLE_NET_TIMEOUT_MS); } if (rcc->priv->connectivity_monitor.timer) { - rcc->priv->channel->core->timer_start(rcc->priv->connectivity_monitor.timer, - rcc->priv->connectivity_monitor.timeout); + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); + core->timer_start(rcc->priv->connectivity_monitor.timer, + rcc->priv->connectivity_monitor.timeout); } } } @@ -973,13 +1000,14 @@ int red_channel_client_is_waiting_for_migrate_data(RedChannelClient *rcc) void red_channel_client_default_migrate(RedChannelClient *rcc) { + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); if (rcc->priv->latency_monitor.timer) { red_channel_client_cancel_ping_timer(rcc); - rcc->priv->channel->core->timer_remove(rcc->priv->latency_monitor.timer); + core->timer_remove(rcc->priv->latency_monitor.timer); rcc->priv->latency_monitor.timer = NULL; } if (rcc->priv->connectivity_monitor.timer) { - rcc->priv->channel->core->timer_remove(rcc->priv->connectivity_monitor.timer); + core->timer_remove(rcc->priv->connectivity_monitor.timer); rcc->priv->connectivity_monitor.timer = NULL; } red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_MIGRATE); @@ -996,7 +1024,8 @@ void red_channel_client_destroy(RedChannelClient *rcc) void red_channel_client_shutdown(RedChannelClient *rcc) { if (rcc->priv->stream && !rcc->priv->stream->shutdown) { - rcc->priv->channel->core->watch_remove(rcc->priv->stream->watch); + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(rcc->priv->channel); + core->watch_remove(rcc->priv->stream->watch); rcc->priv->stream->watch = NULL; shutdown(rcc->priv->stream->socket, SHUT_RDWR); rcc->priv->stream->shutdown = TRUE; @@ -1227,8 +1256,10 @@ void red_channel_client_push(RedChannelClient *rcc) } if (red_channel_client_no_item_being_sent(rcc) && g_queue_is_empty(&rcc->priv->pipe) && rcc->priv->stream->watch) { - rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch, - SPICE_WATCH_EVENT_READ); + SpiceCoreInterfaceInternal *core; + core = red_channel_get_core_interface(rcc->priv->channel); + core->watch_update_mask(rcc->priv->stream->watch, + SPICE_WATCH_EVENT_READ); } rcc->priv->during_send = FALSE; g_object_unref(rcc); @@ -1302,8 +1333,9 @@ static void red_channel_client_handle_pong(RedChannelClient *rcc, SpiceMsgPing * static void red_channel_client_handle_migrate_flush_mark(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); - if (channel->channel_cbs.handle_migrate_flush_mark) { - channel->channel_cbs.handle_migrate_flush_mark(rcc); + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel); + if (klass->handle_migrate_flush_mark) { + klass->handle_migrate_flush_mark(rcc); } } @@ -1319,20 +1351,24 @@ static void red_channel_client_handle_migrate_data(RedChannelClient *rcc, void *message) { RedChannel *channel = red_channel_client_get_channel(rcc); + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel); + uint32_t type, id; + + g_object_get(channel, "channel-type", &type, "id", &id, NULL); spice_debug("channel type %d id %d rcc %p size %u", - channel->type, channel->id, rcc, size); - if (!channel->channel_cbs.handle_migrate_data) { + type, id, rcc, size); + if (!klass->handle_migrate_data) { return; } if (!red_channel_client_is_waiting_for_migrate_data(rcc)) { spice_channel_client_error(rcc, "unexpected"); return; } - if (channel->channel_cbs.handle_migrate_data_get_serial) { + if (klass->handle_migrate_data_get_serial) { red_channel_client_set_message_serial(rcc, - channel->channel_cbs.handle_migrate_data_get_serial(rcc, size, message)); + klass->handle_migrate_data_get_serial(rcc, size, message)); } - if (!channel->channel_cbs.handle_migrate_data(rcc, size, message)) { + if (!klass->handle_migrate_data(rcc, size, message)) { spice_channel_client_error(rcc, "handle_migrate_data failed"); return; } @@ -1448,9 +1484,10 @@ static inline gboolean prepare_pipe_add(RedChannelClient *rcc, RedPipeItem *item return FALSE; } if (g_queue_is_empty(&rcc->priv->pipe) && rcc->priv->stream->watch) { - rcc->priv->channel->core->watch_update_mask(rcc->priv->stream->watch, - SPICE_WATCH_EVENT_READ | - SPICE_WATCH_EVENT_WRITE); + SpiceCoreInterfaceInternal *core; + core = red_channel_get_core_interface(rcc->priv->channel); + core->watch_update_mask(rcc->priv->stream->watch, + SPICE_WATCH_EVENT_READ | SPICE_WATCH_EVENT_WRITE); } return TRUE; } @@ -1562,7 +1599,7 @@ gboolean red_channel_client_is_mini_header(RedChannelClient *rcc) static gboolean red_channel_client_default_is_connected(RedChannelClient *rcc) { return rcc->priv->channel - && (g_list_find(rcc->priv->channel->clients, rcc) != NULL); + && (g_list_find(red_channel_get_clients(rcc->priv->channel), rcc) != NULL); } gboolean red_channel_client_is_connected(RedChannelClient *rcc) @@ -1613,27 +1650,30 @@ void red_channel_client_push_set_ack(RedChannelClient *rcc) static void red_channel_client_default_disconnect(RedChannelClient *rcc) { RedChannel *channel = rcc->priv->channel; + SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(channel); + uint32_t type, id; if (!red_channel_client_is_connected(rcc)) { return; } + g_object_get(channel, "channel-type", &type, "id", &id, NULL); spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel, - channel->type, channel->id); + type, id); red_channel_client_pipe_clear(rcc); if (rcc->priv->stream->watch) { - channel->core->watch_remove(rcc->priv->stream->watch); + core->watch_remove(rcc->priv->stream->watch); rcc->priv->stream->watch = NULL; } if (rcc->priv->latency_monitor.timer) { - channel->core->timer_remove(rcc->priv->latency_monitor.timer); + core->timer_remove(rcc->priv->latency_monitor.timer); rcc->priv->latency_monitor.timer = NULL; } if (rcc->priv->connectivity_monitor.timer) { - channel->core->timer_remove(rcc->priv->connectivity_monitor.timer); + core->timer_remove(rcc->priv->connectivity_monitor.timer); rcc->priv->connectivity_monitor.timer = NULL; } red_channel_remove_client(channel, rcc); - channel->channel_cbs.on_disconnect(rcc); + red_channel_on_disconnect(channel, rcc); } void red_channel_client_disconnect(RedChannelClient *rcc) @@ -1800,13 +1840,18 @@ void red_channel_client_pipe_remove_and_release_pos(RedChannelClient *rcc, gboolean red_channel_client_set_migration_seamless(RedChannelClient *rcc) { gboolean ret = FALSE; - - if (rcc->priv->channel->migration_flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) { + uint32_t type, id, flags; + + g_object_get(rcc->priv->channel, + "channel-type", &type, + "id", &id, + "migration-flags", &flags, + NULL); + if (flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) { rcc->priv->wait_migrate_data = TRUE; ret = TRUE; } - spice_debug("channel type %d id %d rcc %p wait data %d", rcc->priv->channel->type, - rcc->priv->channel->id, rcc, + spice_debug("channel type %d id %d rcc %p wait data %d", type, id, rcc, rcc->priv->wait_migrate_data); return ret; diff --git a/server/red-channel-client.h b/server/red-channel-client.h index c2c64070..862d1c9e 100644 --- a/server/red-channel-client.h +++ b/server/red-channel-client.h @@ -62,8 +62,10 @@ GType red_channel_client_get_type(void) G_GNUC_CONST; #define spice_channel_client_error(rcc, format, ...) \ do { \ RedChannel *_ch = red_channel_client_get_channel(rcc); \ + uint32_t _type, _id; \ + g_object_get(_ch, "channel-type", &_type, "id", &_id, NULL); \ spice_warning("rcc %p type %u id %u: " format, rcc, \ - _ch->type, _ch->id, ## __VA_ARGS__); \ + type, id, ## __VA_ARGS__); \ red_channel_client_shutdown(rcc); \ } while (0) diff --git a/server/red-channel.c b/server/red-channel.c index af49824d..3b14fbfe 100644 --- a/server/red-channel.c +++ b/server/red-channel.c @@ -69,9 +69,137 @@ * from the channel's thread. */ -void red_channel_receive(RedChannel *channel) +G_DEFINE_ABSTRACT_TYPE(RedChannel, red_channel, G_TYPE_OBJECT) + +#define CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), RED_TYPE_CHANNEL, RedChannelPrivate)) + +struct RedChannelPrivate +{ + uint32_t type; + uint32_t id; + + SpiceCoreInterfaceInternal *core; + gboolean handle_acks; + + // RedChannel will hold only connected channel clients + // (logic - when pushing pipe item to all channel clients, there + // is no need to go over disconnect clients) + // . While client will hold the channel clients till it is destroyed + // and then it will destroy them as well. + // However RCC still holds a reference to the Channel. + // Maybe replace these logic with ref count? + // TODO: rename to 'connected_clients'? + GList *clients; + + RedChannelCapabilities local_caps; + uint32_t migration_flags; + + void *data; + + OutgoingHandlerInterface outgoing_cb; + IncomingHandlerInterface incoming_cb; + + ClientCbs client_cbs; + // TODO: when different channel_clients are in different threads + // from Channel -> need to protect! + pthread_t thread_id; + RedsState *reds; +#ifdef RED_STATISTICS + StatNodeRef stat; + uint64_t *out_bytes_counter; +#endif +}; + +enum { + PROP0, + PROP_SPICE_SERVER, + PROP_CORE_INTERFACE, + PROP_TYPE, + PROP_ID, + PROP_HANDLE_ACKS, + PROP_MIGRATION_FLAGS +}; + +static void +red_channel_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_receive, NULL); + RedChannel *self = RED_CHANNEL(object); + + switch (property_id) + { + case PROP_SPICE_SERVER: + g_value_set_pointer(value, self->priv->reds); + break; + case PROP_CORE_INTERFACE: + g_value_set_pointer(value, self->priv->core); + break; + case PROP_TYPE: + g_value_set_int(value, self->priv->type); + break; + case PROP_ID: + g_value_set_uint(value, self->priv->id); + break; + case PROP_HANDLE_ACKS: + g_value_set_boolean(value, self->priv->handle_acks); + break; + case PROP_MIGRATION_FLAGS: + g_value_set_uint(value, self->priv->migration_flags); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +red_channel_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + RedChannel *self = RED_CHANNEL(object); + + switch (property_id) + { + case PROP_SPICE_SERVER: + self->priv->reds = g_value_get_pointer(value); + break; + case PROP_CORE_INTERFACE: + self->priv->core = g_value_get_pointer(value); + break; + case PROP_TYPE: + self->priv->type = g_value_get_int(value); + break; + case PROP_ID: + self->priv->id = g_value_get_uint(value); + break; + case PROP_HANDLE_ACKS: + self->priv->handle_acks = g_value_get_boolean(value); + break; + case PROP_MIGRATION_FLAGS: + self->priv->migration_flags = g_value_get_uint(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +red_channel_finalize(GObject *object) +{ + RedChannel *self = RED_CHANNEL(object); + + if (self->priv->local_caps.num_common_caps) { + free(self->priv->local_caps.common_caps); + } + + if (self->priv->local_caps.num_caps) { + free(self->priv->local_caps.caps); + } + + G_OBJECT_CLASS(red_channel_parent_class)->finalize(object); } static void red_channel_client_default_peer_on_error(RedChannelClient *rcc) @@ -79,10 +207,164 @@ static void red_channel_client_default_peer_on_error(RedChannelClient *rcc) red_channel_client_disconnect(rcc); } +static void red_channel_on_output(void *opaque, int n) +{ + RedChannelClient *rcc = opaque; + RedChannel *self = red_channel_client_get_channel(rcc); + + red_channel_client_on_output(opaque, n); + + stat_inc_counter(self->priv->reds, self->priv->out_bytes_counter, n); +} + +static void +red_channel_constructed(GObject *object) +{ + RedChannel *self = RED_CHANNEL(object); + spice_debug("%p: channel type %d id %d thread_id 0x%lx", self, + self->priv->type, self->priv->id, self->priv->thread_id); + + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + + spice_assert(klass->config_socket && klass->on_disconnect && + klass->alloc_recv_buf && klass->release_recv_buf); + spice_assert(klass->handle_migrate_data || + !(self->priv->migration_flags & SPICE_MIGRATE_NEED_DATA_TRANSFER)); + + self->priv->incoming_cb.alloc_msg_buf = + (alloc_msg_recv_buf_proc)klass->alloc_recv_buf; + self->priv->incoming_cb.release_msg_buf = + (release_msg_recv_buf_proc)klass->release_recv_buf; + self->priv->incoming_cb.handle_message = (handle_message_proc)klass->handle_message; + self->priv->incoming_cb.handle_parsed = (handle_parsed_proc)klass->handle_parsed; + self->priv->incoming_cb.parser = klass->parser; + + G_OBJECT_CLASS(red_channel_parent_class)->constructed(object); +} + +static void red_channel_client_default_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, + int migration, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps) +{ + spice_error("not implemented"); +} + +static void red_channel_client_default_disconnect(RedChannelClient *base) +{ + red_channel_client_disconnect(base); +} + +static void +red_channel_class_init(RedChannelClass *klass) +{ + GParamSpec *spec; + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof (RedChannelPrivate)); + + object_class->get_property = red_channel_get_property; + object_class->set_property = red_channel_set_property; + object_class->finalize = red_channel_finalize; + object_class->constructed = red_channel_constructed; + + spec = g_param_spec_pointer("spice-server", + "spice-server", + "The spice server associated with this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_SPICE_SERVER, spec); + + spec = g_param_spec_pointer("core-interface", + "core-interface", + "The SpiceCoreInterface server associated with this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_CORE_INTERFACE, spec); + + /* FIXME: generate enums for this in spice-common? */ + spec = g_param_spec_int("channel-type", + "channel type", + "Type of this channel", + 0, + SPICE_END_CHANNEL, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_TYPE, spec); + + spec = g_param_spec_uint("id", + "id", + "ID of this channel", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_ID, spec); + + spec = g_param_spec_boolean("handle-acks", + "Handle ACKs", + "Whether this channel handles ACKs", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_HANDLE_ACKS, spec); + + spec = g_param_spec_uint("migration-flags", + "migration flags", + "Migration flags for this channel", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_MIGRATION_FLAGS, spec); +} + +static void +red_channel_init(RedChannel *self) +{ + self->priv = CHANNEL_PRIVATE(self); + + red_channel_set_common_cap(self, SPICE_COMMON_CAP_MINI_HEADER); + self->priv->thread_id = pthread_self(); + self->priv->out_bytes_counter = 0; + + // TODO: send incoming_cb as parameters instead of duplicating? + self->priv->incoming_cb.on_error = + (on_incoming_error_proc)red_channel_client_default_peer_on_error; + self->priv->incoming_cb.on_input = red_channel_client_on_input; + self->priv->outgoing_cb.get_msg_size = red_channel_client_get_out_msg_size; + self->priv->outgoing_cb.prepare = red_channel_client_prepare_out_msg; + self->priv->outgoing_cb.on_block = red_channel_client_on_out_block; + self->priv->outgoing_cb.on_error = + (on_outgoing_error_proc)red_channel_client_default_peer_on_error; + self->priv->outgoing_cb.on_msg_done = red_channel_client_on_out_msg_done; + self->priv->outgoing_cb.on_output = red_channel_on_output; + + self->priv->client_cbs.connect = red_channel_client_default_connect; + self->priv->client_cbs.disconnect = red_channel_client_default_disconnect; + self->priv->client_cbs.migrate = red_channel_client_default_migrate; +} + + +void red_channel_receive(RedChannel *channel) +{ + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_receive, NULL); +} + void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc) { spice_assert(rcc); - channel->clients = g_list_prepend(channel->clients, rcc); + channel->priv->clients = g_list_prepend(channel->priv->clients, rcc); } int red_channel_test_remote_common_cap(RedChannel *channel, uint32_t cap) @@ -137,7 +419,7 @@ gboolean red_client_seamless_migration_done_for_channel(RedClient *client) int red_channel_is_waiting_for_migrate_data(RedChannel *channel) { RedChannelClient *rcc; - guint n_clients = g_list_length(channel->clients); + guint n_clients = g_list_length(channel->priv->clients); if (!red_channel_is_connected(channel)) { return FALSE; @@ -147,186 +429,44 @@ int red_channel_is_waiting_for_migrate_data(RedChannel *channel) return FALSE; } spice_assert(n_clients == 1); - rcc = g_list_nth_data(channel->clients, 0); + rcc = g_list_nth_data(channel->priv->clients, 0); return red_channel_client_is_waiting_for_migrate_data(rcc); } -static void red_channel_client_default_connect(RedChannel *channel, RedClient *client, - RedsStream *stream, - int migration, - int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps) -{ - spice_error("not implemented"); -} - -static void red_channel_client_default_disconnect(RedChannelClient *base) -{ - red_channel_client_disconnect(base); -} - -RedChannel *red_channel_create(int size, - RedsState *reds, - const SpiceCoreInterfaceInternal *core, - uint32_t type, uint32_t id, - int handle_acks, - channel_handle_message_proc handle_message, - const ChannelCbs *channel_cbs, - uint32_t migration_flags) -{ - RedChannel *channel; - ClientCbs client_cbs = { NULL, }; - - spice_assert(size >= sizeof(*channel)); - spice_assert(channel_cbs->config_socket && channel_cbs->on_disconnect && handle_message && - channel_cbs->alloc_recv_buf); - spice_assert(channel_cbs->handle_migrate_data || - !(migration_flags & SPICE_MIGRATE_NEED_DATA_TRANSFER)); - channel = spice_malloc0(size); - channel->type = type; - channel->id = id; - channel->refs = 1; - channel->handle_acks = handle_acks; - channel->migration_flags = migration_flags; - channel->channel_cbs = *channel_cbs; - - channel->reds = reds; - channel->core = core; - - // TODO: send incoming_cb as parameters instead of duplicating? - channel->incoming_cb.alloc_msg_buf = (alloc_msg_recv_buf_proc)channel_cbs->alloc_recv_buf; - channel->incoming_cb.release_msg_buf = (release_msg_recv_buf_proc)channel_cbs->release_recv_buf; - channel->incoming_cb.handle_message = (handle_message_proc)handle_message; - channel->incoming_cb.on_error = - (on_incoming_error_proc)red_channel_client_default_peer_on_error; - channel->incoming_cb.on_input = red_channel_client_on_input; - channel->outgoing_cb.get_msg_size = red_channel_client_get_out_msg_size; - channel->outgoing_cb.prepare = red_channel_client_prepare_out_msg; - channel->outgoing_cb.on_block = red_channel_client_on_out_block; - channel->outgoing_cb.on_error = - (on_outgoing_error_proc)red_channel_client_default_peer_on_error; - channel->outgoing_cb.on_msg_done = red_channel_client_on_out_msg_done; - channel->outgoing_cb.on_output = red_channel_client_on_output; - - client_cbs.connect = red_channel_client_default_connect; - client_cbs.disconnect = red_channel_client_default_disconnect; - client_cbs.migrate = red_channel_client_default_migrate; - - red_channel_register_client_cbs(channel, &client_cbs, NULL); - red_channel_set_common_cap(channel, SPICE_COMMON_CAP_MINI_HEADER); - - channel->thread_id = pthread_self(); - - channel->out_bytes_counter = 0; - - spice_debug("channel type %d id %d thread_id 0x%lx", - channel->type, channel->id, channel->thread_id); - return channel; -} - -// TODO: red_worker can use this one -static void dummy_watch_update_mask(SpiceWatch *watch, int event_mask) -{ -} - -static SpiceWatch *dummy_watch_add(const SpiceCoreInterfaceInternal *iface, - int fd, int event_mask, SpiceWatchFunc func, void *opaque) -{ - return NULL; // apparently allowed? -} - -static void dummy_watch_remove(SpiceWatch *watch) -{ -} - -// TODO: actually, since I also use channel_client_dummy, no need for core. Can be NULL -static const SpiceCoreInterfaceInternal dummy_core = { - .watch_update_mask = dummy_watch_update_mask, - .watch_add = dummy_watch_add, - .watch_remove = dummy_watch_remove, -}; - -RedChannel *red_channel_create_dummy(int size, RedsState *reds, uint32_t type, uint32_t id) -{ - RedChannel *channel; - ClientCbs client_cbs = { NULL, }; - - spice_assert(size >= sizeof(*channel)); - channel = spice_malloc0(size); - channel->type = type; - channel->id = id; - channel->refs = 1; - channel->reds = reds; - channel->core = &dummy_core; - client_cbs.connect = red_channel_client_default_connect; - client_cbs.disconnect = red_channel_client_default_disconnect; - client_cbs.migrate = red_channel_client_default_migrate; - - red_channel_register_client_cbs(channel, &client_cbs, NULL); - red_channel_set_common_cap(channel, SPICE_COMMON_CAP_MINI_HEADER); - - channel->thread_id = pthread_self(); - spice_debug("channel type %d id %d thread_id 0x%lx", - channel->type, channel->id, channel->thread_id); - - channel->out_bytes_counter = 0; - - return channel; -} - -static int do_nothing_handle_message(RedChannelClient *rcc, - uint16_t type, - uint32_t size, - uint8_t *msg) -{ - return TRUE; -} - -RedChannel *red_channel_create_parser(int size, - RedsState *reds, - const SpiceCoreInterfaceInternal *core, - uint32_t type, uint32_t id, - int handle_acks, - spice_parse_channel_func_t parser, - channel_handle_parsed_proc handle_parsed, - const ChannelCbs *channel_cbs, - uint32_t migration_flags) +void red_channel_set_stat_node(RedChannel *channel, StatNodeRef stat) { - RedChannel *channel = red_channel_create(size, reds, core, type, id, - handle_acks, - do_nothing_handle_message, - channel_cbs, - migration_flags); - channel->incoming_cb.handle_parsed = (handle_parsed_proc)handle_parsed; - channel->incoming_cb.parser = parser; + spice_return_if_fail(channel != NULL); + spice_return_if_fail(channel->priv->stat == 0); - return channel; +#ifdef RED_STATISTICS + channel->priv->stat = stat; + channel->priv->out_bytes_counter = + stat_add_counter(channel->priv->reds, stat, "out_bytes", TRUE); +#endif } -void red_channel_set_stat_node(RedChannel *channel, StatNodeRef stat) +StatNodeRef red_channel_get_stat_node(RedChannel *channel) { - spice_return_if_fail(channel != NULL); - spice_return_if_fail(channel->stat == 0); - #ifdef RED_STATISTICS - channel->stat = stat; - channel->out_bytes_counter = stat_add_counter(channel->reds, stat, "out_bytes", TRUE); + return channel->priv->stat; #endif + return 0; } -void red_channel_register_client_cbs(RedChannel *channel, const ClientCbs *client_cbs, gpointer cbs_data) +void red_channel_register_client_cbs(RedChannel *channel, const ClientCbs *client_cbs, + gpointer cbs_data) { - spice_assert(client_cbs->connect || channel->type == SPICE_CHANNEL_MAIN); - channel->client_cbs.connect = client_cbs->connect; + spice_assert(client_cbs->connect || channel->priv->type == SPICE_CHANNEL_MAIN); + channel->priv->client_cbs.connect = client_cbs->connect; if (client_cbs->disconnect) { - channel->client_cbs.disconnect = client_cbs->disconnect; + channel->priv->client_cbs.disconnect = client_cbs->disconnect; } if (client_cbs->migrate) { - channel->client_cbs.migrate = client_cbs->migrate; + channel->priv->client_cbs.migrate = client_cbs->migrate; } - channel->data = cbs_data; + channel->priv->data = cbs_data; } static void add_capability(uint32_t **caps, int *num_caps, uint32_t cap) @@ -343,32 +483,13 @@ static void add_capability(uint32_t **caps, int *num_caps, uint32_t cap) void red_channel_set_common_cap(RedChannel *channel, uint32_t cap) { - add_capability(&channel->local_caps.common_caps, &channel->local_caps.num_common_caps, cap); + add_capability(&channel->priv->local_caps.common_caps, + &channel->priv->local_caps.num_common_caps, cap); } void red_channel_set_cap(RedChannel *channel, uint32_t cap) { - add_capability(&channel->local_caps.caps, &channel->local_caps.num_caps, cap); -} - -void red_channel_ref(RedChannel *channel) -{ - channel->refs++; -} - -void red_channel_unref(RedChannel *channel) -{ - if (--channel->refs == 0) { - if (channel->local_caps.num_common_caps) { - free(channel->local_caps.common_caps); - } - - if (channel->local_caps.num_caps) { - free(channel->local_caps.caps); - } - - free(channel); - } + add_capability(&channel->priv->local_caps.caps, &channel->priv->local_caps.num_caps, cap); } void red_channel_destroy(RedChannel *channel) @@ -377,13 +498,13 @@ void red_channel_destroy(RedChannel *channel) return; } - g_list_foreach(channel->clients, (GFunc)red_channel_client_destroy, NULL); - red_channel_unref(channel); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_destroy, NULL); + g_object_unref(channel); } void red_channel_send(RedChannel *channel) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_send, NULL); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_send, NULL); } void red_channel_push(RedChannel *channel) @@ -392,14 +513,15 @@ void red_channel_push(RedChannel *channel) return; } - g_list_foreach(channel->clients, (GFunc)red_channel_client_push, NULL); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_push, NULL); } // TODO: this function doesn't make sense because the window should be client (WAN/LAN) // specific void red_channel_init_outgoing_messages_window(RedChannel *channel) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_init_outgoing_messages_window, NULL); + g_list_foreach(channel->priv->clients, + (GFunc)red_channel_client_init_outgoing_messages_window, NULL); } static void red_channel_client_pipe_add_type_proxy(gpointer data, gpointer user_data) @@ -410,7 +532,7 @@ static void red_channel_client_pipe_add_type_proxy(gpointer data, gpointer user_ void red_channel_pipes_add_type(RedChannel *channel, int pipe_item_type) { - g_list_foreach(channel->clients, red_channel_client_pipe_add_type_proxy, + g_list_foreach(channel->priv->clients, red_channel_client_pipe_add_type_proxy, GINT_TO_POINTER(pipe_item_type)); } @@ -422,12 +544,13 @@ static void red_channel_client_pipe_add_empty_msg_proxy(gpointer data, gpointer void red_channel_pipes_add_empty_msg(RedChannel *channel, int msg_type) { - g_list_foreach(channel->clients, red_channel_client_pipe_add_empty_msg_proxy, GINT_TO_POINTER(msg_type)); + g_list_foreach(channel->priv->clients, red_channel_client_pipe_add_empty_msg_proxy, + GINT_TO_POINTER(msg_type)); } int red_channel_is_connected(RedChannel *channel) { - return channel && channel->clients; + return channel && channel->priv->clients; } void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc) @@ -435,19 +558,19 @@ void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc) GList *link; g_return_if_fail(channel == red_channel_client_get_channel(rcc)); - if (!pthread_equal(pthread_self(), channel->thread_id)) { + if (!pthread_equal(pthread_self(), channel->priv->thread_id)) { spice_warning("channel type %d id %d - " "channel->thread_id (0x%lx) != pthread_self (0x%lx)." "If one of the threads is != io-thread && != vcpu-thread, " "this might be a BUG", - channel->type, channel->id, - channel->thread_id, pthread_self()); + channel->priv->type, channel->priv->id, + channel->priv->thread_id, pthread_self()); } spice_return_if_fail(channel); - link = g_list_find(channel->clients, rcc); + link = g_list_find(channel->priv->clients, rcc); spice_return_if_fail(link != NULL); - channel->clients = g_list_remove_link(channel->clients, link); + channel->priv->clients = g_list_remove_link(channel->priv->clients, link); // TODO: should we set rcc->channel to NULL??? } @@ -461,17 +584,35 @@ void red_client_remove_channel(RedChannelClient *rcc) void red_channel_disconnect(RedChannel *channel) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_disconnect, NULL); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_disconnect, NULL); +} + +void red_channel_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, int migration, int num_common_caps, + uint32_t *common_caps, int num_caps, uint32_t *caps) +{ + channel->priv->client_cbs.connect(channel, client, stream, migration, + num_common_caps, common_caps, num_caps, + caps); } void red_channel_apply_clients(RedChannel *channel, channel_client_callback cb) { - g_list_foreach(channel->clients, (GFunc)cb, NULL); + g_list_foreach(channel->priv->clients, (GFunc)cb, NULL); } void red_channel_apply_clients_data(RedChannel *channel, channel_client_callback_data cb, void *data) { - g_list_foreach(channel->clients, (GFunc)cb, data); + g_list_foreach(channel->priv->clients, (GFunc)cb, data); +} + +GList *red_channel_get_clients(RedChannel *channel) +{ + return channel->priv->clients; +} +guint red_channel_get_n_clients(RedChannel *channel) +{ + return g_list_length(channel->priv->clients); } int red_channel_all_blocked(RedChannel *channel) @@ -479,7 +620,7 @@ int red_channel_all_blocked(RedChannel *channel) GListIter iter; RedChannelClient *rcc; - if (!channel || !channel->clients) { + if (!channel || !channel->priv->clients) { return FALSE; } FOREACH_CLIENT(channel, iter, rcc) { @@ -508,10 +649,10 @@ int red_channel_get_first_socket(RedChannel *channel) RedChannelClient *rcc; RedsStream *stream; - if (!channel || !channel->clients) { + if (!channel || !channel->priv->clients) { return -1; } - rcc = g_list_nth_data(channel->clients, 0); + rcc = g_list_nth_data(channel->priv->clients, 0); stream = red_channel_client_get_stream(rcc); return stream->socket; @@ -600,7 +741,7 @@ void red_client_migrate(RedClient *client) FOREACH_CHANNEL_CLIENT(client, iter, rcc) { channel = red_channel_client_get_channel(rcc); if (red_channel_client_is_connected(rcc)) { - channel->client_cbs.migrate(rcc); + channel->priv->client_cbs.migrate(rcc); } } } @@ -629,7 +770,7 @@ void red_client_destroy(RedClient *client) // to wait for disconnection) // TODO: should we go back to async. For this we need to use // ref count for channel clients. - channel->client_cbs.disconnect(rcc); + channel->priv->client_cbs.disconnect(rcc); spice_assert(red_channel_client_pipe_is_empty(rcc)); spice_assert(red_channel_client_no_item_being_sent(rcc)); red_channel_client_destroy(rcc); @@ -647,7 +788,7 @@ RedChannelClient *red_client_get_channel(RedClient *client, int type, int id) FOREACH_CHANNEL_CLIENT(client, iter, rcc) { RedChannel *channel; channel = red_channel_client_get_channel(rcc); - if (channel->type == type && channel->id == id) { + if (channel->priv->type == type && channel->priv->id == id) { ret = rcc; break; } @@ -847,5 +988,52 @@ int red_channel_wait_all_sent(RedChannel *channel, RedsState* red_channel_get_server(RedChannel *channel) { - return channel->reds; + return channel->priv->reds; +} + +SpiceCoreInterfaceInternal* red_channel_get_core_interface(RedChannel *channel) +{ + return channel->priv->core; +} + +int red_channel_config_socket(RedChannel *self, RedChannelClient *rcc) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + + return klass->config_socket(rcc); +} + +void red_channel_on_disconnect(RedChannel *self, RedChannelClient *rcc) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + + klass->on_disconnect(rcc); +} + +void red_channel_send_item(RedChannel *self, RedChannelClient *rcc, RedPipeItem *item) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_if_fail(klass->send_item); + + klass->send_item(rcc, item); +} + +IncomingHandlerInterface* red_channel_get_incoming_handler(RedChannel *self) +{ + return &self->priv->incoming_cb; +} + +OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel *self) +{ + return &self->priv->outgoing_cb; +} + +void red_channel_reset_thread_id(RedChannel *self) +{ + self->priv->thread_id = pthread_self(); +} + +const RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self) +{ + return &self->priv->local_caps; } diff --git a/server/red-channel.h b/server/red-channel.h index 52996469..89aab8d5 100644 --- a/server/red-channel.h +++ b/server/red-channel.h @@ -24,6 +24,7 @@ #include <pthread.h> #include <limits.h> +#include <glib-object.h> #include <common/ring.h> #include <common/marshaller.h> @@ -34,6 +35,8 @@ #include "stat.h" #include "red-pipe-item.h" +G_BEGIN_DECLS + typedef struct SpiceDataHeaderOpaque SpiceDataHeaderOpaque; typedef uint16_t (*get_msg_type_proc)(SpiceDataHeaderOpaque *header); @@ -125,23 +128,6 @@ typedef void (*channel_client_connect_proc)(RedChannel *channel, RedClient *clie typedef void (*channel_client_disconnect_proc)(RedChannelClient *base); typedef void (*channel_client_migrate_proc)(RedChannelClient *base); -// TODO: add ASSERTS for thread_id in client and channel calls -// -/* - * callbacks that are triggered from channel client stream events. - * They are called from the thread that listen to the stream events. - */ -typedef struct { - channel_configure_socket_proc config_socket; - channel_disconnect_proc on_disconnect; - channel_send_pipe_item_proc send_item; - channel_alloc_msg_recv_buf_proc alloc_recv_buf; - channel_release_msg_recv_buf_proc release_recv_buf; - channel_handle_migrate_flush_mark_proc handle_migrate_flush_mark; - channel_handle_migrate_data_proc handle_migrate_data; - channel_handle_migrate_data_get_serial_proc handle_migrate_data_get_serial; -} ChannelCbs; - /* * callbacks that are triggered from client events. @@ -153,107 +139,84 @@ typedef struct { channel_client_migrate_proc migrate; } ClientCbs; -typedef struct RedChannelCapabilities { - int num_common_caps; - uint32_t *common_caps; - int num_caps; - uint32_t *caps; -} RedChannelCapabilities; - static inline gboolean test_capability(const uint32_t *caps, int num_caps, uint32_t cap) { return VD_AGENT_HAS_CAPABILITY(caps, num_caps, cap); } -typedef struct RedChannelClientLatencyMonitor { - int state; - uint64_t last_pong_time; - SpiceTimer *timer; - uint32_t id; - int tcp_nodelay; - int warmup_was_sent; - - int64_t roundtrip; -} RedChannelClientLatencyMonitor; +#define RED_TYPE_CHANNEL red_channel_get_type() -typedef struct RedChannelClientConnectivityMonitor { - int state; - uint32_t out_bytes; - uint32_t in_bytes; - uint32_t timeout; - SpiceTimer *timer; -} RedChannelClientConnectivityMonitor; +#define RED_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHANNEL, RedChannel)) +#define RED_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_CHANNEL, RedChannelClass)) +#define RED_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_CHANNEL)) +#define RED_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_CHANNEL)) +#define RED_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHANNEL, RedChannelClass)) -struct RedChannel { - uint32_t type; - uint32_t id; - - uint32_t refs; - - RingItem link; // channels link for reds - - const SpiceCoreInterfaceInternal *core; - int handle_acks; - - // RedChannel will hold only connected channel clients (logic - when pushing pipe item to all channel clients, there - // is no need to go over disconnect clients) - // . While client will hold the channel clients till it is destroyed - // and then it will destroy them as well. - // However RCC still holds a reference to the Channel. - // Maybe replace these logic with ref count? - // TODO: rename to 'connected_clients'? - GList *clients; +typedef struct RedChannel RedChannel; +typedef struct RedChannelClass RedChannelClass; +typedef struct RedChannelPrivate RedChannelPrivate; - OutgoingHandlerInterface outgoing_cb; - IncomingHandlerInterface incoming_cb; +struct RedChannel +{ + GObject parent; - ChannelCbs channel_cbs; - ClientCbs client_cbs; + RedChannelPrivate *priv; +}; - RedChannelCapabilities local_caps; - uint32_t migration_flags; +struct RedChannelClass +{ + GObjectClass parent_class; - void *data; + /* subclasses must implement either handle_message(), or both parser() and + * handle_parsed() */ + channel_handle_message_proc handle_message; + spice_parse_channel_func_t parser; + channel_handle_parsed_proc handle_parsed; - // TODO: when different channel_clients are in different threads from Channel -> need to protect! - pthread_t thread_id; - RedsState *reds; -#ifdef RED_STATISTICS - StatNodeRef stat; - uint64_t *out_bytes_counter; -#endif + // TODO: add ASSERTS for thread_id in client and channel calls + /* + * callbacks that are triggered from channel client stream events. + * They are called from the thread that listen to the stream events. + */ + channel_configure_socket_proc config_socket; + channel_disconnect_proc on_disconnect; + channel_send_pipe_item_proc send_item; + channel_alloc_msg_recv_buf_proc alloc_recv_buf; + channel_release_msg_recv_buf_proc release_recv_buf; + channel_handle_migrate_flush_mark_proc handle_migrate_flush_mark; + channel_handle_migrate_data_proc handle_migrate_data; + channel_handle_migrate_data_get_serial_proc handle_migrate_data_get_serial; }; #define FOREACH_CLIENT(_channel, _iter, _data) \ - GLIST_FOREACH((_channel ? RED_CHANNEL(_channel)->clients : NULL), \ + GLIST_FOREACH((_channel ? red_channel_get_clients(RED_CHANNEL(_channel)) : NULL), \ _iter, RedChannelClient, _data) -#define RED_CHANNEL(Channel) ((RedChannel *)(Channel)) +/* Red Channel interface */ -/* if one of the callbacks should cause disconnect, use red_channel_shutdown and don't - * explicitly destroy the channel */ -RedChannel *red_channel_create(int size, - RedsState *reds, - const SpiceCoreInterfaceInternal *core, - uint32_t type, uint32_t id, - int handle_acks, - channel_handle_message_proc handle_message, - const ChannelCbs *channel_cbs, - uint32_t migration_flags); +typedef struct RedChannelCapabilities { + int num_common_caps; + uint32_t *common_caps; + int num_caps; + uint32_t *caps; +} RedChannelCapabilities; + +GType red_channel_get_type(void) G_GNUC_CONST; /* alternative constructor, meant for marshaller based (inputs,main) channels, * will become default eventually */ +/* RedChannel *red_channel_create_parser(int size, RedsState *reds, const SpiceCoreInterfaceInternal *core, uint32_t type, uint32_t id, - int handle_acks, + gboolean handle_acks, spice_parse_channel_func_t parser, channel_handle_parsed_proc handle_parsed, - const ChannelCbs *channel_cbs, uint32_t migration_flags); -void red_channel_ref(RedChannel *channel); -void red_channel_unref(RedChannel *channel); + */ void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc); void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc); @@ -264,11 +227,6 @@ void red_channel_register_client_cbs(RedChannel *channel, const ClientCbs *clien void red_channel_set_common_cap(RedChannel *channel, uint32_t cap); void red_channel_set_cap(RedChannel *channel, uint32_t cap); -// TODO: tmp, for channels that don't use RedChannel yet (e.g., snd channel), but -// do use the client callbacks. So the channel clients are not connected (the channel doesn't -// have list of them, but they do have a link to the channel, and the client has a list of them) -RedChannel *red_channel_create_dummy(int size, RedsState *reds, uint32_t type, uint32_t id); - int red_channel_is_connected(RedChannel *channel); /* seamless migration is supported for only one client. This routine @@ -332,6 +290,9 @@ void red_channel_receive(RedChannel *channel); void red_channel_send(RedChannel *channel); // For red_worker void red_channel_disconnect(RedChannel *channel); +void red_channel_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, int migration, int num_common_caps, + uint32_t *common_caps, int num_caps, uint32_t *caps); /* return the sum of all the rcc pipe size */ uint32_t red_channel_max_pipe_size(RedChannel *channel); @@ -344,8 +305,26 @@ uint32_t red_channel_sum_pipes_size(RedChannel *channel); typedef void (*channel_client_callback)(RedChannelClient *rcc); typedef void (*channel_client_callback_data)(RedChannelClient *rcc, void *data); void red_channel_apply_clients(RedChannel *channel, channel_client_callback v); -void red_channel_apply_clients_data(RedChannel *channel, channel_client_callback_data v, void * data); +void red_channel_apply_clients_data(RedChannel *channel, channel_client_callback_data v, + void *data); +GList *red_channel_get_clients(RedChannel *channel); +guint red_channel_get_n_clients(RedChannel *channel); struct RedsState* red_channel_get_server(RedChannel *channel); +SpiceCoreInterfaceInternal* red_channel_get_core_interface(RedChannel *channel); + +/* channel callback function */ +int red_channel_config_socket(RedChannel *self, RedChannelClient *rcc); +void red_channel_on_disconnect(RedChannel *self, RedChannelClient *rcc); +void red_channel_send_item(RedChannel *self, RedChannelClient *rcc, RedPipeItem *item); +void red_channel_reset_thread_id(RedChannel *self); +StatNodeRef red_channel_get_stat_node(RedChannel *channel); + +/* FIXME: do these even need to be in RedChannel? It's really only used in + * RedChannelClient. Needs refactoring */ +IncomingHandlerInterface* red_channel_get_incoming_handler(RedChannel *self); +OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel *self); + +const RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self); struct RedClient { RedsState *reds; @@ -419,4 +398,6 @@ int red_channel_wait_all_sent(RedChannel *channel, #define CHANNEL_BLOCKED_SLEEP_DURATION 10000 //micro +G_END_DECLS + #endif diff --git a/server/red-parse-qxl.h b/server/red-parse-qxl.h index 0da20ad0..d5e7b6bf 100644 --- a/server/red-parse-qxl.h +++ b/server/red-parse-qxl.h @@ -65,6 +65,8 @@ static inline RedDrawable *red_drawable_ref(RedDrawable *drawable) return drawable; } +void red_drawable_unref(RedDrawable *red_drawable); + typedef struct RedUpdateCmd { QXLReleaseInfoExt release_info_ext; SpiceRect area; diff --git a/server/red-qxl.c b/server/red-qxl.c index fa51195b..19cff950 100644 --- a/server/red-qxl.c +++ b/server/red-qxl.c @@ -82,7 +82,7 @@ static void red_qxl_set_display_peer(RedChannel *channel, RedClient *client, Dispatcher *dispatcher; spice_debug("%s", ""); - dispatcher = (Dispatcher *)channel->data; + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); payload.client = client; payload.stream = stream; payload.migration = migration; @@ -109,7 +109,7 @@ static void red_qxl_disconnect_display_peer(RedChannelClient *rcc) return; } - dispatcher = (Dispatcher *)channel->data; + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); spice_printerr(""); payload.rcc = rcc; @@ -126,11 +126,14 @@ static void red_qxl_display_migrate(RedChannelClient *rcc) RedWorkerMessageDisplayMigrate payload; Dispatcher *dispatcher; RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type, id; + if (!channel) { return; } - dispatcher = (Dispatcher *)channel->data; - spice_printerr("channel type %u id %u", channel->type, channel->id); + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); + spice_printerr("channel type %u id %u", type, id); payload.rcc = rcc; dispatcher_send_message(dispatcher, RED_WORKER_MESSAGE_DISPLAY_MIGRATE, @@ -143,7 +146,7 @@ static void red_qxl_set_cursor_peer(RedChannel *channel, RedClient *client, Reds uint32_t *caps) { RedWorkerMessageCursorConnect payload = {0,}; - Dispatcher *dispatcher = (Dispatcher *)channel->data; + Dispatcher *dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); spice_printerr(""); payload.client = client; payload.stream = stream; @@ -171,7 +174,7 @@ static void red_qxl_disconnect_cursor_peer(RedChannelClient *rcc) return; } - dispatcher = (Dispatcher *)channel->data; + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); spice_printerr(""); payload.rcc = rcc; @@ -185,12 +188,14 @@ static void red_qxl_cursor_migrate(RedChannelClient *rcc) RedWorkerMessageCursorMigrate payload; Dispatcher *dispatcher; RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type, id; if (!channel) { return; } - dispatcher = (Dispatcher *)channel->data; - spice_printerr("channel type %u id %u", channel->type, channel->id); + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); + spice_printerr("channel type %u id %u", type, id); payload.rcc = rcc; dispatcher_send_message(dispatcher, RED_WORKER_MESSAGE_CURSOR_MIGRATE, diff --git a/server/red-replay-qxl.c b/server/red-replay-qxl.c index b5baded8..78de48b4 100644 --- a/server/red-replay-qxl.c +++ b/server/red-replay-qxl.c @@ -24,7 +24,7 @@ #include <zlib.h> #include <pthread.h> #include "reds.h" -#include "red-worker.h" +#include "red-qxl.h" #include "red-common.h" #include "memslot.h" #include "red-parse-qxl.h" diff --git a/server/red-worker.c b/server/red-worker.c index 40e58f2f..f7f07266 100644 --- a/server/red-worker.c +++ b/server/red-worker.c @@ -23,16 +23,12 @@ #include <stdio.h> #include <stdarg.h> -#include <fcntl.h> -#include <sys/socket.h> -#include <netinet/in.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <poll.h> #include <pthread.h> -#include <netinet/tcp.h> #include <openssl/ssl.h> #include <inttypes.h> #include <glib.h> @@ -93,8 +89,8 @@ struct RedWorker { static int display_is_connected(RedWorker *worker) { - return (worker->display_channel && red_channel_is_connected( - &worker->display_channel->common.base)); + return worker->display_channel && + red_channel_is_connected(RED_CHANNEL(worker->display_channel)); } void red_drawable_unref(RedDrawable *red_drawable) @@ -261,7 +257,7 @@ static int red_process_display(RedWorker *worker, int *ring_is_empty) spice_error("bad command type"); } n++; - if (red_channel_all_blocked(&worker->display_channel->common.base) + if (red_channel_all_blocked(RED_CHANNEL(worker->display_channel)) || spice_get_monotonic_time_ns() - start > NSEC_PER_SEC / 100) { worker->event_timeout = 0; return n; @@ -404,7 +400,7 @@ static void guest_set_client_capabilities(RedWorker *worker) return; } if ((worker->display_channel == NULL) || - (RED_CHANNEL(worker->display_channel)->clients == NULL)) { + (red_channel_get_n_clients(RED_CHANNEL(worker->display_channel)) == 0)) { red_qxl_set_client_capabilities(worker->qxl, FALSE, caps); } else { // Take least common denominator @@ -541,12 +537,12 @@ static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id, if (!worker->driver_cap_monitors_config) { red_worker_push_monitors_config(worker); } - red_pipes_add_verb(&worker->display_channel->common.base, + red_pipes_add_verb(RED_CHANNEL(worker->display_channel), SPICE_MSG_DISPLAY_MARK); - red_channel_push(&worker->display_channel->common.base); + red_channel_push(RED_CHANNEL(worker->display_channel)); } - cursor_channel_init(worker->cursor_channel); + cursor_channel_do_init(worker->cursor_channel); } static void handle_dev_create_primary_surface(void *opaque, void *payload) @@ -665,7 +661,7 @@ static void handle_dev_oom(void *opaque, void *payload) RedWorker *worker = opaque; DisplayChannel *display = worker->display_channel; - RedChannel *display_red_channel = &display->common.base; + RedChannel *display_red_channel = RED_CHANNEL(display); int ring_is_empty; spice_return_if_fail(worker->running); @@ -1371,6 +1367,7 @@ RedWorker* red_worker_new(QXLInstance *qxl, channel = RED_CHANNEL(worker->cursor_channel); red_channel_set_stat_node(channel, stat_add_node(reds, worker->stat, "cursor_channel", TRUE)); red_channel_register_client_cbs(channel, client_cursor_cbs, dispatcher); + g_object_set_data(G_OBJECT(channel), "dispatcher", dispatcher); reds_register_channel(reds, channel); // TODO: handle seemless migration. Temp, setting migrate to FALSE @@ -1378,10 +1375,10 @@ RedWorker* red_worker_new(QXLInstance *qxl, reds_get_streaming_video(reds), reds_get_video_codecs(reds), init_info.n_surfaces); - channel = RED_CHANNEL(worker->display_channel); red_channel_set_stat_node(channel, stat_add_node(reds, worker->stat, "display_channel", TRUE)); red_channel_register_client_cbs(channel, client_display_cbs, dispatcher); + g_object_set_data(G_OBJECT(channel), "dispatcher", dispatcher); red_channel_set_cap(channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG); red_channel_set_cap(channel, SPICE_DISPLAY_CAP_PREF_COMPRESSION); red_channel_set_cap(channel, SPICE_DISPLAY_CAP_STREAM_REPORT); @@ -1398,8 +1395,8 @@ SPICE_GNUC_NORETURN static void *red_worker_main(void *arg) spice_assert(MAX_PIPE_SIZE > WIDE_CLIENT_ACK_WINDOW && MAX_PIPE_SIZE > NARROW_CLIENT_ACK_WINDOW); //ensure wakeup by ack message - RED_CHANNEL(worker->cursor_channel)->thread_id = pthread_self(); - RED_CHANNEL(worker->display_channel)->thread_id = pthread_self(); + red_channel_reset_thread_id(RED_CHANNEL(worker->cursor_channel)); + red_channel_reset_thread_id(RED_CHANNEL(worker->display_channel)); GMainLoop *loop = g_main_loop_new(worker->core.main_context, FALSE); g_main_loop_run(loop); diff --git a/server/red-worker.h b/server/red-worker.h index cf61a672..53b92b38 100644 --- a/server/red-worker.h +++ b/server/red-worker.h @@ -30,6 +30,4 @@ RedWorker* red_worker_new(QXLInstance *qxl, const ClientCbs *client_display_cbs); bool red_worker_run(RedWorker *worker); -void red_drawable_unref(RedDrawable *red_drawable); - #endif diff --git a/server/reds-private.h b/server/reds-private.h index 7af93bac..cc13ba7a 100644 --- a/server/reds-private.h +++ b/server/reds-private.h @@ -114,8 +114,7 @@ struct RedsState { between the 2 servers */ GList *mig_target_clients; - int num_of_channels; - Ring channels; + GList *channels; int mouse_mode; int is_client_mouse_allowed; int dispatcher_allows_client_mouse; diff --git a/server/reds.c b/server/reds.c index fc116e05..de0d3569 100644 --- a/server/reds.c +++ b/server/reds.c @@ -455,15 +455,13 @@ void stat_remove_counter(RedsState *reds, uint64_t *counter) void reds_register_channel(RedsState *reds, RedChannel *channel) { spice_assert(reds); - ring_add(&reds->channels, &channel->link); - reds->num_of_channels++; + reds->channels = g_list_prepend(reds->channels, channel); } void reds_unregister_channel(RedsState *reds, RedChannel *channel) { - if (ring_item_is_linked(&channel->link)) { - ring_remove(&channel->link); - reds->num_of_channels--; + if (g_list_find(reds->channels, channel)) { + reds->channels = g_list_remove(reds->channels, channel); } else { spice_warning("not found"); } @@ -471,11 +469,13 @@ void reds_unregister_channel(RedsState *reds, RedChannel *channel) static RedChannel *reds_find_channel(RedsState *reds, uint32_t type, uint32_t id) { - RingItem *now; + GListIter it; + RedChannel *channel; - RING_FOREACH(now, &reds->channels) { - RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link); - if (channel->type == type && channel->id == id) { + GLIST_FOREACH(reds->channels, it, RedChannel, channel) { + uint32_t this_type, this_id; + g_object_get(channel, "channel-type", &this_type, "id", &this_id, NULL); + if (this_type == type && this_id == id) { return channel; } } @@ -739,7 +739,7 @@ static void reds_agent_remove(RedsState *reds) reds->vdagent = NULL; reds_update_mouse_mode(reds); if (reds_main_channel_connected(reds) && - !red_channel_is_waiting_for_migrate_data(&reds->main_channel->base)) { + !red_channel_is_waiting_for_migrate_data(RED_CHANNEL(reds->main_channel))) { main_channel_push_agent_disconnected(reds->main_channel); } } @@ -983,7 +983,9 @@ SPICE_GNUC_VISIBLE int spice_server_get_num_clients(SpiceServer *reds) static int channel_supports_multiple_clients(RedChannel *channel) { - switch (channel->type) { + uint32_t type; + g_object_get(channel, "channel-type", &type, NULL); + switch (type) { case SPICE_CHANNEL_MAIN: case SPICE_CHANNEL_DISPLAY: case SPICE_CHANNEL_CURSOR: @@ -995,23 +997,25 @@ static int channel_supports_multiple_clients(RedChannel *channel) static void reds_fill_channels(RedsState *reds, SpiceMsgChannels *channels_info) { - RingItem *now; + GListIter it; + RedChannel *channel; int used_channels = 0; - RING_FOREACH(now, &reds->channels) { - RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link); + GLIST_FOREACH(reds->channels, it, RedChannel, channel) { + uint32_t type, id; if (reds->num_clients > 1 && !channel_supports_multiple_clients(channel)) { continue; } - channels_info->channels[used_channels].type = channel->type; - channels_info->channels[used_channels].id = channel->id; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + channels_info->channels[used_channels].type = type; + channels_info->channels[used_channels].id = id; used_channels++; } channels_info->num_of_channels = used_channels; - if (used_channels != reds->num_of_channels) { - spice_warning("sent %d out of %d", used_channels, reds->num_of_channels); + if (used_channels != g_list_length(reds->channels)) { + spice_warning("sent %d out of %d", used_channels, g_list_length(reds->channels)); } } @@ -1022,7 +1026,7 @@ SpiceMsgChannels *reds_msg_channels_new(RedsState *reds) spice_assert(reds != NULL); channels_info = (SpiceMsgChannels *)spice_malloc(sizeof(SpiceMsgChannels) - + reds->num_of_channels * sizeof(SpiceChannelId)); + + g_list_length(reds->channels) * sizeof(SpiceChannelId)); reds_fill_channels(reds, channels_info); @@ -1522,7 +1526,7 @@ static int reds_send_link_ack(RedsState *reds, RedLinkInfo *link) SpiceLinkHeader header; SpiceLinkReply ack; RedChannel *channel; - RedChannelCapabilities *channel_caps; + const RedChannelCapabilities *channel_caps; BUF_MEM *bmBuf; BIO *bio = NULL; int ret = FALSE; @@ -1543,12 +1547,12 @@ static int reds_send_link_ack(RedsState *reds, RedLinkInfo *link) return FALSE; } spice_assert(reds->main_channel); - channel = &reds->main_channel->base; + channel = RED_CHANNEL(reds->main_channel); } reds_channel_init_auth_caps(link, channel); /* make sure common caps are set */ - channel_caps = &channel->local_caps; + channel_caps = red_channel_get_local_capabilities(channel); ack.num_common_caps = GUINT32_TO_LE(channel_caps->num_common_caps); ack.num_channel_caps = GUINT32_TO_LE(channel_caps->num_caps); hdr_size += channel_caps->num_common_caps * sizeof(uint32_t); @@ -1856,13 +1860,13 @@ static void reds_channel_do_link(RedChannel *channel, RedClient *client, spice_assert(stream); caps = (uint32_t *)((uint8_t *)link_msg + link_msg->caps_offset); - channel->client_cbs.connect(channel, client, stream, - red_client_during_migrate_at_target(client), - link_msg->num_common_caps, - link_msg->num_common_caps ? caps : NULL, - link_msg->num_channel_caps, - link_msg->num_channel_caps ? - caps + link_msg->num_common_caps : NULL); + red_channel_connect(channel, client, stream, + red_client_during_migrate_at_target(client), + link_msg->num_common_caps, + link_msg->num_common_caps ? caps : NULL, + link_msg->num_channel_caps, + link_msg->num_channel_caps ? + caps + link_msg->num_common_caps : NULL); } /* @@ -3106,7 +3110,7 @@ static RedCharDevice *attach_to_red_agent(RedsState *reds, SpiceCharDeviceInstan dev->priv->plug_generation++; if (dev->priv->mig_data || - red_channel_is_waiting_for_migrate_data(&reds->main_channel->base)) { + red_channel_is_waiting_for_migrate_data(RED_CHANNEL(reds->main_channel))) { /* Migration in progress (code is running on the destination host): * 1. Add the client to spice char device, if it was not already added. * 2.a If this (qemu-kvm state load side of migration) happens first @@ -3430,7 +3434,7 @@ static int do_spice_init(RedsState *reds, SpiceCoreInterface *core_interface) ring_init(&reds->clients); reds->num_clients = 0; reds->main_dispatcher = main_dispatcher_new(reds, reds->core); - ring_init(&reds->channels); + reds->channels = NULL; reds->mig_target_clients = NULL; reds->char_devices = NULL; reds->mig_wait_disconnect_clients = NULL; @@ -4090,7 +4094,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *reds, const cha * be valid (see reds_reset_vdp for more details). */ try_seamless = reds->seamless_migration_enabled && - red_channel_test_remote_cap(&reds->main_channel->base, + red_channel_test_remote_cap(RED_CHANNEL(reds->main_channel), SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS); /* main channel will take care of clients that are still during migration (at target)*/ if (main_channel_migrate_connect(reds->main_channel, reds->config->mig_spice, diff --git a/server/smartcard.c b/server/smartcard.c index 13eed803..655eaf7b 100644 --- a/server/smartcard.c +++ b/server/smartcard.c @@ -49,9 +49,61 @@ // Maximal length of APDU #define APDUBufSize 270 +#define RED_TYPE_SMARTCARD_CHANNEL red_smartcard_channel_get_type() + +#define RED_SMARTCARD_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannel)) +#define RED_SMARTCARD_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelClass)) +#define RED_IS_SMARTCARD_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_SMARTCARD_CHANNEL)) +#define RED_IS_SMARTCARD_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_SMARTCARD_CHANNEL)) +#define RED_SMARTCARD_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelClass)) + +typedef struct RedSmartcardChannel RedSmartcardChannel; +typedef struct RedSmartcardChannelClass RedSmartcardChannelClass; + +struct RedSmartcardChannel +{ + RedChannel parent; +}; + +struct RedSmartcardChannelClass +{ + RedChannelClass parent_class; +}; + +GType red_smartcard_channel_get_type(void) G_GNUC_CONST; + +G_DEFINE_TYPE(RedSmartcardChannel, red_smartcard_channel, RED_TYPE_CHANNEL) + +static void +red_smartcard_channel_init(RedSmartcardChannel *self) +{ +} + +static RedSmartcardChannel * +red_smartcard_channel_new(RedsState *reds) +{ + return g_object_new(RED_TYPE_SMARTCARD_CHANNEL, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", SPICE_CHANNEL_SMARTCARD, + "id", 0, + "handle-acks", FALSE /* handle_acks */, + "migration-flags", + (SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + NULL); +} + + G_DEFINE_TYPE(RedCharDeviceSmartcard, red_char_device_smartcard, RED_TYPE_CHAR_DEVICE) -#define RED_CHAR_DEVICE_SMARTCARD_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RED_TYPE_CHAR_DEVICE_SMARTCARD, RedCharDeviceSmartcardPrivate)) +#define RED_CHAR_DEVICE_SMARTCARD_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), RED_TYPE_CHAR_DEVICE_SMARTCARD, \ + RedCharDeviceSmartcardPrivate)) struct RedCharDeviceSmartcardPrivate { uint32_t reader_id; @@ -74,10 +126,6 @@ typedef struct RedMsgItem { static RedMsgItem *smartcard_get_vsc_msg_item(RedChannelClient *rcc, VSCMsgHeader *vheader); static void smartcard_channel_client_pipe_add_push(RedChannelClient *rcc, RedPipeItem *item); -typedef struct SmartCardChannel { - RedChannel base; -} SmartCardChannel; - static struct Readers { uint32_t num; SpiceCharDeviceInstance* sin[SMARTCARD_MAX_READERS]; @@ -519,41 +567,49 @@ static void smartcard_connect_client(RedChannel *channel, RedClient *client, } } -SmartCardChannel *g_smartcard_channel; - -static void smartcard_init(RedsState *reds) +static void +red_smartcard_channel_constructed(GObject *object) { - ChannelCbs channel_cbs = { NULL, }; + RedSmartcardChannel *self = RED_SMARTCARD_CHANNEL(object); + RedsState *reds = red_channel_get_server(RED_CHANNEL(self)); ClientCbs client_cbs = { NULL, }; - uint32_t migration_flags = SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER; - - spice_assert(!g_smartcard_channel); - channel_cbs.config_socket = smartcard_channel_client_config_socket; - channel_cbs.on_disconnect = smartcard_channel_client_on_disconnect; - channel_cbs.send_item = smartcard_channel_send_item; - channel_cbs.alloc_recv_buf = smartcard_channel_client_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = smartcard_channel_client_release_msg_rcv_buf; - channel_cbs.handle_migrate_flush_mark = smartcard_channel_client_handle_migrate_flush_mark; - channel_cbs.handle_migrate_data = smartcard_channel_client_handle_migrate_data; - - g_smartcard_channel = (SmartCardChannel*)red_channel_create(sizeof(SmartCardChannel), - reds, - reds_get_core_interface(reds), - SPICE_CHANNEL_SMARTCARD, 0, - FALSE /* handle_acks */, - smartcard_channel_client_handle_message, - &channel_cbs, - migration_flags); - - if (!g_smartcard_channel) { - spice_error("failed to allocate Smartcard Channel"); - } + G_OBJECT_CLASS(red_smartcard_channel_parent_class)->constructed(object); client_cbs.connect = smartcard_connect_client; - red_channel_register_client_cbs(&g_smartcard_channel->base, &client_cbs, NULL); + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); + + reds_register_channel(reds, RED_CHANNEL(self)); +} + +static void +red_smartcard_channel_class_init(RedSmartcardChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + object_class->constructed = red_smartcard_channel_constructed; + + channel_class->handle_message = smartcard_channel_client_handle_message, + + channel_class->config_socket = smartcard_channel_client_config_socket; + channel_class->on_disconnect = smartcard_channel_client_on_disconnect; + channel_class->send_item = smartcard_channel_send_item; + channel_class->alloc_recv_buf = smartcard_channel_client_alloc_msg_rcv_buf; + channel_class->release_recv_buf = smartcard_channel_client_release_msg_rcv_buf; + channel_class->handle_migrate_flush_mark = smartcard_channel_client_handle_migrate_flush_mark; + channel_class->handle_migrate_data = smartcard_channel_client_handle_migrate_data; + +} + +/* FIXME: remove global */ +RedSmartcardChannel *g_smartcard_channel; + +static void smartcard_init(RedsState *reds) +{ + spice_assert(!g_smartcard_channel); - reds_register_channel(reds, &g_smartcard_channel->base); + g_smartcard_channel = red_smartcard_channel_new(reds); } diff --git a/server/sound.c b/server/sound.c index db23e955..c222c8d6 100644 --- a/server/sound.c +++ b/server/sound.c @@ -31,6 +31,7 @@ #include "spice.h" #include "red-common.h" +#include "dummy-channel.h" #include "dummy-channel-client.h" #include "main-channel.h" #include "reds.h" @@ -218,6 +219,7 @@ static void snd_disconnect_channel(SndChannel *channel) SndWorker *worker; RedsState *reds; RedChannel *red_channel; + uint32_t type; if (!channel || !channel->stream) { spice_debug("not connected"); @@ -225,8 +227,9 @@ static void snd_disconnect_channel(SndChannel *channel) } red_channel = red_channel_client_get_channel(channel->channel_client); reds = snd_channel_get_server(channel); + g_object_get(red_channel, "channel-type", &type, NULL); spice_debug("SndChannel=%p rcc=%p type=%d", - channel, channel->channel_client, red_channel->type); + channel, channel->channel_client, type); worker = channel->worker; channel->cleanup(channel); red_channel_client_disconnect(worker->connection->channel_client); @@ -998,12 +1001,14 @@ static void snd_disconnect_channel_client(RedChannelClient *rcc) { SndWorker *worker; RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type; spice_assert(channel); - spice_assert(channel->data); - worker = (SndWorker *)channel->data; + worker = (SndWorker *)g_object_get_data(G_OBJECT(channel), "sound-worker"); + spice_assert(worker); + g_object_get(channel, "channel-type", &type, NULL); - spice_debug("channel-type=%d", channel->type); + spice_debug("channel-type=%d", type); if (worker->connection) { spice_assert(worker->connection->channel_client == rcc); snd_disconnect_channel(worker->connection); @@ -1145,7 +1150,9 @@ void snd_set_playback_latency(RedClient *client, uint32_t latency) SndWorker *now = workers; for (; now; now = now->next) { - if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection && + uint32_t type; + g_object_get(now->base_channel, "channel-type", &type, NULL); + if (type == SPICE_CHANNEL_PLAYBACK && now->connection && red_channel_client_get_client(now->connection->channel_client) == client) { if (red_channel_client_test_remote_cap(now->connection->channel_client, @@ -1213,7 +1220,7 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt int migration, int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps) { - SndWorker *worker = channel->data; + SndWorker *worker = g_object_get_data(G_OBJECT(channel), "sound-worker"); PlaybackChannel *playback_channel; SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker); @@ -1242,7 +1249,8 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt SPICE_PLAYBACK_CAP_CELT_0_5_1); int client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client, SPICE_PLAYBACK_CAP_OPUS); - int playback_compression = reds_config_get_playback_compression(channel->reds); + int playback_compression = + reds_config_get_playback_compression(red_channel_get_server(channel)); int desired_mode = snd_desired_audio_mode(playback_compression, st->frequency, client_can_celt, client_can_opus); playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW; @@ -1271,8 +1279,8 @@ static void snd_record_migrate_channel_client(RedChannelClient *rcc) spice_debug(NULL); spice_assert(channel); - spice_assert(channel->data); - worker = (SndWorker *)channel->data; + worker = (SndWorker *)g_object_get_data(G_OBJECT(channel), "sound-worker"); + spice_assert(worker); if (worker->connection) { spice_assert(worker->connection->channel_client == rcc); @@ -1462,7 +1470,7 @@ static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStre int migration, int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps) { - SndWorker *worker = channel->data; + SndWorker *worker = g_object_get_data(G_OBJECT(channel), "sound-worker"); RecordChannel *record_channel; SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker); @@ -1500,8 +1508,8 @@ static void snd_playback_migrate_channel_client(RedChannelClient *rcc) RedChannel *channel = red_channel_client_get_channel(rcc); spice_assert(channel); - spice_assert(channel->data); - worker = (SndWorker *)channel->data; + worker = (SndWorker *)g_object_get_data(G_OBJECT(channel), "sound-worker"); + spice_assert(worker); spice_debug(NULL); if (worker->connection) { @@ -1542,8 +1550,9 @@ void snd_attach_playback(RedsState *reds, SpicePlaybackInstance *sin) sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */ // TODO: Make RedChannel base of worker? instead of assigning it to channel->data - channel = red_channel_create_dummy(sizeof(RedChannel), reds, SPICE_CHANNEL_PLAYBACK, 0); + channel = dummy_channel_new(reds, SPICE_CHANNEL_PLAYBACK, 0); + g_object_set_data(G_OBJECT(channel), "sound-worker", playback_worker); client_cbs.connect = snd_set_playback_peer; client_cbs.disconnect = snd_disconnect_channel_client; client_cbs.migrate = snd_playback_migrate_channel_client; @@ -1571,8 +1580,9 @@ void snd_attach_record(RedsState *reds, SpiceRecordInstance *sin) sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */ // TODO: Make RedChannel base of worker? instead of assigning it to channel->data - channel = red_channel_create_dummy(sizeof(RedChannel), reds, SPICE_CHANNEL_RECORD, 0); + channel = dummy_channel_new(reds, SPICE_CHANNEL_RECORD, 0); + g_object_set_data(G_OBJECT(channel), "sound-worker", record_worker); client_cbs.connect = snd_set_record_peer; client_cbs.disconnect = snd_disconnect_channel_client; client_cbs.migrate = snd_record_migrate_channel_client; @@ -1628,7 +1638,9 @@ void snd_set_playback_compression(int on) SndWorker *now = workers; for (; now; now = now->next) { - if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) { + uint32_t type; + g_object_get(now->base_channel, "channel-type", &type, NULL); + if (type == SPICE_CHANNEL_PLAYBACK && now->connection) { PlaybackChannel* playback = (PlaybackChannel*)now->connection; SpicePlaybackState *st = SPICE_CONTAINEROF(now, SpicePlaybackState, worker); int client_can_celt = red_channel_client_test_remote_cap(playback->base.channel_client, diff --git a/server/spicevmc.c b/server/spicevmc.c index e710111d..0f4c6185 100644 --- a/server/spicevmc.c +++ b/server/spicevmc.c @@ -57,23 +57,18 @@ typedef struct RedVmcPipeItem { uint32_t buf_used; } RedVmcPipeItem; -typedef struct SpiceVmcState { - RedChannel channel; /* Must be the first item */ - RedChannelClient *rcc; - RedCharDevice *chardev; - SpiceCharDeviceInstance *chardev_sin; - RedVmcPipeItem *pipe_item; - RedCharDeviceWriteBuffer *recv_from_client_buf; - uint8_t port_opened; -} SpiceVmcState; - #define RED_TYPE_CHAR_DEVICE_SPICEVMC red_char_device_spicevmc_get_type() -#define RED_CHAR_DEVICE_SPICEVMC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmc)) -#define RED_CHAR_DEVICE_SPICEVMC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmcClass)) -#define RED_IS_CHAR_DEVICE_SPICEVMC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_CHAR_DEVICE_SPICEVMC)) -#define RED_IS_CHAR_DEVICE_SPICEVMC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_CHAR_DEVICE_SPICEVMC)) -#define RED_CHAR_DEVICE_SPICEVMC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmcClass)) +#define RED_CHAR_DEVICE_SPICEVMC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmc)) +#define RED_CHAR_DEVICE_SPICEVMC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmcClass)) +#define RED_IS_CHAR_DEVICE_SPICEVMC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_CHAR_DEVICE_SPICEVMC)) +#define RED_IS_CHAR_DEVICE_SPICEVMC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_CHAR_DEVICE_SPICEVMC)) +#define RED_CHAR_DEVICE_SPICEVMC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmcClass)) typedef struct RedCharDeviceSpiceVmc RedCharDeviceSpiceVmc; typedef struct RedCharDeviceSpiceVmcClass RedCharDeviceSpiceVmcClass; @@ -94,6 +89,202 @@ static RedCharDevice *red_char_device_spicevmc_new(SpiceCharDeviceInstance *sin, G_DEFINE_TYPE(RedCharDeviceSpiceVmc, red_char_device_spicevmc, RED_TYPE_CHAR_DEVICE) +#define RED_CHAR_DEVICE_SPICEVMC_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmcPrivate)) + +#define SPICE_TYPE_VMC_STATE spice_vmc_state_get_type() + +#define SPICE_VMC_STATE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), SPICE_TYPE_VMC_STATE, SpiceVmcState)) +#define SPICE_VMC_STATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SPICE_TYPE_VMC_STATE, SpiceVmcStateClass)) +#define SPICE_IS_VMC_STATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SPICE_TYPE_VMC_STATE)) +#define SPICE_IS_VMC_STATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SPICE_TYPE_VMC_STATE)) +#define SPICE_VMC_STATE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), SPICE_TYPE_VMC_STATE, SpiceVmcStateClass)) + +typedef struct SpiceVmcState SpiceVmcState; +typedef struct SpiceVmcStateClass SpiceVmcStateClass; + +struct SpiceVmcState +{ + RedChannel parent; + + RedChannelClient *rcc; + RedCharDevice *chardev; + SpiceCharDeviceInstance *chardev_sin; + RedVmcPipeItem *pipe_item; + RedCharDeviceWriteBuffer *recv_from_client_buf; + uint8_t port_opened; +}; + +struct SpiceVmcStateClass +{ + RedChannelClass parent_class; +}; + +GType spice_vmc_state_get_type(void) G_GNUC_CONST; + +G_DEFINE_TYPE(SpiceVmcState, spice_vmc_state, RED_TYPE_CHANNEL) + + +#define SPICE_TYPE_VMC_STATE_USBREDIR spice_vmc_state_usbredir_get_type() +typedef struct +{ + SpiceVmcState parent; +} SpiceVmcStateUsbredir; + +typedef struct +{ + SpiceVmcStateClass parent_class; +} SpiceVmcStateUsbredirClass; + +GType spice_vmc_state_usbredir_get_type(void) G_GNUC_CONST; +static void spice_vmc_state_usbredir_init(SpiceVmcStateUsbredir *self) +{ +} +G_DEFINE_TYPE(SpiceVmcStateUsbredir, spice_vmc_state_usbredir, SPICE_TYPE_VMC_STATE) + + +#define SPICE_TYPE_VMC_STATE_WEBDAV spice_vmc_state_webdav_get_type() +typedef struct +{ + SpiceVmcState parent; +} SpiceVmcStateWebdav; + +typedef struct +{ + SpiceVmcStateClass parent_class; +} SpiceVmcStateWebdavClass; + +GType spice_vmc_state_webdav_get_type(void) G_GNUC_CONST; +static void spice_vmc_state_webdav_init(SpiceVmcStateWebdav *self) +{ +} +G_DEFINE_TYPE(SpiceVmcStateWebdav, spice_vmc_state_webdav, SPICE_TYPE_VMC_STATE) + + +#define SPICE_TYPE_VMC_STATE_PORT spice_vmc_state_port_get_type() +typedef struct +{ + SpiceVmcState parent; +} SpiceVmcStatePort; + +typedef struct +{ + SpiceVmcStateClass parent_class; +} SpiceVmcStatePortClass; + +GType spice_vmc_state_port_get_type(void) G_GNUC_CONST; +static void spice_vmc_state_port_init(SpiceVmcStatePort *self) +{ +} +G_DEFINE_TYPE(SpiceVmcStatePort, spice_vmc_state_port, SPICE_TYPE_VMC_STATE) + +enum { + PROP0, + PROP_DEVICE_INSTANCE +}; + +static void +spice_vmc_state_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + SpiceVmcState *self = SPICE_VMC_STATE(object); + + switch (property_id) + { + case PROP_DEVICE_INSTANCE: + g_value_set_pointer(value, self->chardev_sin); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +spice_vmc_state_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + SpiceVmcState *self = SPICE_VMC_STATE(object); + + switch (property_id) + { + case PROP_DEVICE_INSTANCE: + self->chardev_sin = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void spicevmc_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, int migration, int num_common_caps, + uint32_t *common_caps, int num_caps, uint32_t *caps); + +static void +spice_vmc_state_constructed(GObject *object) +{ + SpiceVmcState *self = SPICE_VMC_STATE(object); + ClientCbs client_cbs = { NULL, }; + RedsState *reds = red_channel_get_server(RED_CHANNEL(self)); + + G_OBJECT_CLASS(spice_vmc_state_parent_class)->constructed(object); + + client_cbs.connect = spicevmc_connect; + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); + +#ifdef USE_LZ4 + red_channel_set_cap(RED_CHANNEL(self), SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4); +#endif + + red_channel_init_outgoing_messages_window(RED_CHANNEL(self)); + + self->chardev = red_char_device_spicevmc_new(self->chardev_sin, reds, self); + + reds_register_channel(reds, RED_CHANNEL(self)); +} + +static void +spice_vmc_state_init(SpiceVmcState *self) +{ +} + +static SpiceVmcState *spice_vmc_state_new(RedsState *reds, uint8_t channel_type, + SpiceCharDeviceInstance *sin) +{ + GType gtype = G_TYPE_NONE; + static uint8_t id[256] = { 0, }; + + switch (channel_type) { + case SPICE_CHANNEL_USBREDIR: + gtype = SPICE_TYPE_VMC_STATE_USBREDIR; + break; + case SPICE_CHANNEL_WEBDAV: + gtype = SPICE_TYPE_VMC_STATE_WEBDAV; + break; + case SPICE_CHANNEL_PORT: + gtype = SPICE_TYPE_VMC_STATE_PORT; + break; + default: + g_error("Unsupported channel_type for spice_vmc_state_new(): %u", channel_type); + } + return g_object_new(gtype, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", channel_type, + "id", id[channel_type]++, + "handle-acks", FALSE, + "migration-flags", + (SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + "device-instance", sin, + NULL); +} + typedef struct RedPortInitPipeItem { RedPipeItem base; char* name; @@ -136,7 +327,7 @@ static RedVmcPipeItem* try_compress_lz4(SpiceVmcState *state, int n, RedVmcPipeI /* n <= threshold - data will not be compressed */ return NULL; } - if (!red_channel_test_remote_cap(&state->channel, SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4)) { + if (!red_channel_test_remote_cap(RED_CHANNEL(state), SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4)) { /* Client doesn't have compression cap - data will not be compressed */ return NULL; } @@ -220,7 +411,7 @@ static void spicevmc_chardev_send_msg_to_client(RedPipeItem *msg, static SpiceVmcState *spicevmc_red_channel_client_get_state(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); - return SPICE_CONTAINEROF(channel, SpiceVmcState, channel); + return SPICE_VMC_STATE(channel); } static void spicevmc_port_send_init(RedChannelClient *rcc) @@ -267,8 +458,10 @@ static int spicevmc_red_channel_client_config_socket(RedChannelClient *rcc) int delay_val = 1; RedsStream *stream = red_channel_client_get_stream(rcc); RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type; - if (channel->type == SPICE_CHANNEL_USBREDIR) { + g_object_get(channel, "channel-type", &type, NULL); + if (type == SPICE_CHANNEL_USBREDIR) { if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) != 0) { if (errno != ENOTSUP && errno != ENOPROTOOPT) { @@ -556,6 +749,61 @@ static void spicevmc_red_channel_send_item(RedChannelClient *rcc, red_channel_client_begin_send_message(rcc); } + +static void +spice_vmc_state_class_init(SpiceVmcStateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + object_class->get_property = spice_vmc_state_get_property; + object_class->set_property = spice_vmc_state_set_property; + object_class->constructed = spice_vmc_state_constructed; + + channel_class->handle_parsed = spicevmc_red_channel_client_handle_message_parsed; + + channel_class->config_socket = spicevmc_red_channel_client_config_socket; + channel_class->on_disconnect = spicevmc_red_channel_client_on_disconnect; + channel_class->send_item = spicevmc_red_channel_send_item; + channel_class->alloc_recv_buf = spicevmc_red_channel_alloc_msg_rcv_buf; + channel_class->release_recv_buf = spicevmc_red_channel_release_msg_rcv_buf; + channel_class->handle_migrate_flush_mark = spicevmc_channel_client_handle_migrate_flush_mark; + channel_class->handle_migrate_data = spicevmc_channel_client_handle_migrate_data; + + g_object_class_install_property(object_class, + PROP_DEVICE_INSTANCE, + g_param_spec_pointer("device-instance", + "device instance", + "Device instance for this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +spice_vmc_state_usbredir_class_init(SpiceVmcStateUsbredirClass *klass) +{ + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_USBREDIR, NULL); +} + +static void +spice_vmc_state_webdav_class_init(SpiceVmcStateWebdavClass *klass) +{ + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_WEBDAV, NULL); +} + +static void +spice_vmc_state_port_class_init(SpiceVmcStatePortClass *klass) +{ + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_PORT, NULL); +} + static void spicevmc_connect(RedChannel *channel, RedClient *client, RedsStream *stream, int migration, int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps) @@ -564,13 +812,15 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client, SpiceVmcState *state; SpiceCharDeviceInstance *sin; SpiceCharDeviceInterface *sif; + uint32_t type, id; - state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel); + state = SPICE_VMC_STATE(channel); sin = state->chardev_sin; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); if (state->rcc) { spice_printerr("channel client %d:%d (%p) already connected, refusing second connection", - channel->type, channel->id, state->rcc); + type, id, state->rcc); // TODO: notify client in advance about the in use channel using // SPICE_MSG_MAIN_CHANNEL_IN_USE (for example) reds_stream_free(stream); @@ -606,38 +856,8 @@ RedCharDevice *spicevmc_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin, uint8_t channel_type) { - static uint8_t id[256] = { 0, }; - SpiceVmcState *state; - ChannelCbs channel_cbs = { NULL, }; - ClientCbs client_cbs = { NULL, }; + SpiceVmcState *state = spice_vmc_state_new(reds, channel_type, sin); - channel_cbs.config_socket = spicevmc_red_channel_client_config_socket; - channel_cbs.on_disconnect = spicevmc_red_channel_client_on_disconnect; - channel_cbs.send_item = spicevmc_red_channel_send_item; - channel_cbs.alloc_recv_buf = spicevmc_red_channel_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = spicevmc_red_channel_release_msg_rcv_buf; - channel_cbs.handle_migrate_flush_mark = spicevmc_channel_client_handle_migrate_flush_mark; - channel_cbs.handle_migrate_data = spicevmc_channel_client_handle_migrate_data; - - state = (SpiceVmcState*)red_channel_create_parser(sizeof(SpiceVmcState), reds, - reds_get_core_interface(reds), channel_type, id[channel_type]++, - FALSE /* handle_acks */, - spice_get_client_channel_parser(channel_type, NULL), - spicevmc_red_channel_client_handle_message_parsed, - &channel_cbs, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER); - red_channel_init_outgoing_messages_window(&state->channel); - - client_cbs.connect = spicevmc_connect; - red_channel_register_client_cbs(&state->channel, &client_cbs, NULL); -#ifdef USE_LZ4 - red_channel_set_cap(&state->channel, SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4); -#endif - - state->chardev = red_char_device_spicevmc_new(sin, reds, state); - state->chardev_sin = sin; - - reds_register_channel(reds, &state->channel); return state->chardev; } @@ -650,15 +870,14 @@ void spicevmc_device_disconnect(RedsState *reds, SpiceCharDeviceInstance *sin) state = (SpiceVmcState *)red_char_device_opaque_get((RedCharDevice*)sin->st); red_char_device_write_buffer_release(state->chardev, &state->recv_from_client_buf); - /* FIXME */ red_char_device_destroy((RedCharDevice*)sin->st); state->chardev = NULL; sin->st = NULL; - reds_unregister_channel(reds, &state->channel); + reds_unregister_channel(reds, RED_CHANNEL(state)); free(state->pipe_item); - red_channel_destroy(&state->channel); + red_channel_destroy(RED_CHANNEL(state)); } SPICE_GNUC_VISIBLE void spice_server_port_event(SpiceCharDeviceInstance *sin, uint8_t event) diff --git a/server/stream.c b/server/stream.c index 65331113..4e702229 100644 --- a/server/stream.c +++ b/server/stream.c @@ -19,7 +19,7 @@ #endif #include "stream.h" -#include "display-channel.h" +#include "display-channel-private.h" #include "main-channel-client.h" #define FPS_TEST_INTERVAL 1 @@ -198,7 +198,7 @@ static void update_copy_graduality(DisplayChannel *display, Drawable *drawable) SpiceBitmap *bitmap; spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY); - if (display->priv->stream_video != SPICE_STREAM_VIDEO_FILTER) { + if (display_channel_get_stream_video(display) != SPICE_STREAM_VIDEO_FILTER) { drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID; return; } |