diff options
author | Jonathon Jongsma <jjongsma@redhat.com> | 2015-05-11 16:58:01 -0500 |
---|---|---|
committer | Jonathon Jongsma <jjongsma@redhat.com> | 2016-10-07 16:58:36 -0500 |
commit | b8440f7051ee0a55bab82611e600d1a4d6447f54 (patch) | |
tree | a9196aa5475914de27dc2dd7ce85b33c7685dcb8 | |
parent | 6d5e9ce139189ac662f85592275644568960d8d3 (diff) |
Convert RedChannel heirarchy to GObjectreview-20161007
FIXME: this commit appears to introduce a vdagent-related crash in
vdi_port_read_one_msg_from_device(). sin->st is NULL.
37 files changed, 2193 insertions, 1043 deletions
diff --git a/server/Makefile.am b/server/Makefile.am index 3382946b..843341e5 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 68715406..d8ee9ddb 100644 --- a/server/common-graphics-channel.c +++ b/server/common-graphics-channel.c @@ -29,6 +29,10 @@ #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; @@ -44,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) { @@ -66,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); @@ -107,39 +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); - 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 c6c3f482..6961375d 100644 --- a/server/common-graphics-channel.h +++ b/server/common-graphics-channel.h @@ -18,21 +18,42 @@ #ifndef _COMMON_GRAPHICS_CHANNEL_H #define _COMMON_GRAPHICS_CHANNEL_H +#include <glib-object.h> + #include "red-channel.h" -#include "red-worker.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 +97,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 e701d120..cf19c7ff 100644 --- a/server/cursor-channel.c +++ b/server/cursor-channel.c @@ -26,6 +26,7 @@ #include "cursor-channel.h" #include "cursor-channel-client.h" #include "reds.h" +#include "red-qxl.h" typedef struct CursorChannelClient CursorChannelClient; @@ -48,8 +49,12 @@ typedef struct RedCursorPipeItem { CursorItem *cursor_item; } RedCursorPipeItem; -typedef struct CursorChannelPrivate CursorChannelPrivate; -struct CursorChannelPrivate { +G_DEFINE_TYPE(CursorChannel, cursor_channel, TYPE_COMMON_GRAPHICS_CHANNEL) + +#define CURSOR_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_CURSOR_CHANNEL, CursorChannelPrivate)) + +struct CursorChannelPrivate +{ CursorItem *item; int cursor_visible; SpicePoint16 cursor_position; @@ -58,12 +63,6 @@ struct CursorChannelPrivate { uint32_t mouse_mode; }; -struct CursorChannel { - CommonGraphicsChannel common; // Must be the first thing - - CursorChannelPrivate priv[1]; -}; - static void cursor_pipe_item_free(RedPipeItem *pipe_item); static CursorItem *cursor_item_new(QXLInstance *qxl, RedCursorCmd *cmd) @@ -217,8 +216,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; @@ -312,24 +310,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) @@ -366,11 +354,11 @@ void cursor_channel_process_cmd(CursorChannel *cursor, RedCursorCmd *cursor_cmd) return; } - if (red_channel_is_connected(&cursor->common.base) && + if (red_channel_is_connected(RED_CHANNEL(cursor)) && (cursor->priv->mouse_mode == SPICE_MOUSE_MODE_SERVER || cursor_cmd->type != QXL_CURSOR_MOVE || cursor_show)) { - red_channel_pipes_new_add(&cursor->common.base, + red_channel_pipes_new_add(RED_CHANNEL(cursor), new_cursor_pipe_item, cursor_item); } @@ -379,7 +367,7 @@ 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); @@ -393,9 +381,9 @@ void cursor_channel_reset(CursorChannel *cursor) if (!common_graphics_channel_get_during_target_migrate(COMMON_GRAPHICS_CHANNEL(cursor))) { red_pipes_add_verb(channel, SPICE_MSG_CURSOR_RESET); } - if (!red_channel_wait_all_sent(&cursor->common.base, + if (!red_channel_wait_all_sent(RED_CHANNEL(cursor), COMMON_CLIENT_TIMEOUT)) { - red_channel_apply_clients(channel, + red_channel_apply_clients(RED_CHANNEL(cursor), red_channel_client_disconnect_if_pending_send); } } @@ -405,7 +393,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; @@ -418,7 +406,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); } @@ -452,3 +440,25 @@ 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); + + g_type_class_add_private(klass, sizeof(CursorChannelPrivate)); + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_CURSOR, NULL); + channel_class->handle_parsed = red_channel_client_handle_message; + + channel_class->on_disconnect = cursor_channel_client_on_disconnect; + channel_class->send_item = cursor_channel_send_item; +} + +static void +cursor_channel_init(CursorChannel *self) +{ + self->priv = CURSOR_CHANNEL_PRIVATE(self); + self->priv->cursor_visible = TRUE; + self->priv->mouse_mode = SPICE_MOUSE_MODE_SERVER; +} diff --git a/server/cursor-channel.h b/server/cursor-channel.h index 0f2b43da..8b3bc17f 100644 --- a/server/cursor-channel.h +++ b/server/cursor-channel.h @@ -19,14 +19,40 @@ # define CURSOR_CHANNEL_H_ #include "common-graphics-channel.h" +#include "red-parse-qxl.h" + +G_BEGIN_DECLS + +#define TYPE_CURSOR_CHANNEL cursor_channel_get_type() + +#define CURSOR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_CURSOR_CHANNEL, CursorChannel)) +#define CURSOR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_CURSOR_CHANNEL, CursorChannelClass)) +#define IS_CURSOR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_CURSOR_CHANNEL)) +#define IS_CURSOR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_CURSOR_CHANNEL)) +#define CURSOR_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_CURSOR_CHANNEL, CursorChannelClass)) -#define CURSOR_CHANNEL(channel) ((CursorChannel*)channel) /** * 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; +typedef struct CursorChannelPrivate CursorChannelPrivate; + +struct CursorChannel +{ + CommonGraphicsChannel parent; + + CursorChannelPrivate *priv; +}; + +struct CursorChannelClass +{ + CommonGraphicsChannelClass parent_class; +}; + +GType cursor_channel_get_type(void) G_GNUC_CONST; /** * Create CursorChannel. @@ -48,7 +74,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 +96,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 021c2416..ab446394 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 "spice-server-enums.h" diff --git a/server/dcc.h b/server/dcc.h index 2456f09f..e4fe788f 100644 --- a/server/dcc.h +++ b/server/dcc.h @@ -23,8 +23,8 @@ #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" G_BEGIN_DECLS 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 69edd359..19f0aca4 100644 --- a/server/display-channel.c +++ b/server/display-channel.c @@ -20,7 +20,131 @@ #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); + 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 +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 + QXLInstance *qxl = common_graphics_channel_get_qxl(&self->parent); + RedsState *reds = red_qxl_get_server(qxl->st); + RedChannel *channel = RED_CHANNEL(self); + self->priv->cache_hits_counter = + stat_add_counter(reds, red_channel_get_stat_node(channel), + "cache_hits", TRUE); + self->priv->add_to_cache_counter = + stat_add_counter(reds, red_channel_get_stat_node(channel), + "add_to_cache", TRUE); + self->priv->non_cache_counter = + stat_add_counter(reds, red_channel_get_stat_node(channel), + "non_cache", TRUE); +#endif + image_cache_init(&self->priv->image_cache); + self->priv->stream_video = SPICE_STREAM_VIDEO_OFF; + display_channel_init_streams(self); +} + +static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces, uint32_t surface_id) +{ + DisplayChannelPrivate *p = SPICE_CONTAINEROF(surfaces, DisplayChannelPrivate, image_surfaces); + DisplayChannel *display = p->pub; + + spice_return_val_if_fail(display_channel_validate_surface(display, surface_id), NULL); + + return p->surfaces[surface_id].context.canvas; +} + +static void drawables_init(DisplayChannel *display); +static void +display_channel_init(DisplayChannel *self) +{ + static SpiceImageSurfacesOps image_surfaces_ops = { + image_surfaces_get, + }; + + /* 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; + + image_encoder_shared_init(&self->priv->encoder_shared_data); + + ring_init(&self->priv->current_list); + drawables_init(self); + self->priv->image_surfaces.ops = &image_surfaces_ops; +} static void drawable_draw(DisplayChannel *display, Drawable *drawable); static Drawable *display_channel_drawable_try_new(DisplayChannel *display, @@ -43,12 +167,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 +278,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 +413,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 +1248,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 (;;) { @@ -1893,16 +2026,6 @@ static int handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *messa return dcc_handle_migrate_data(DISPLAY_CHANNEL_CLIENT(rcc), size, message); } -static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces, uint32_t surface_id) -{ - DisplayChannelPrivate *p = SPICE_CONTAINEROF(surfaces, DisplayChannelPrivate, image_surfaces); - DisplayChannel *display = p->pub; - - spice_return_val_if_fail(display_channel_validate_surface(display, surface_id), NULL); - - return display->priv->surfaces[surface_id].context.canvas; -} - DisplayChannel* display_channel_new(RedsState *reds, QXLInstance *qxl, const SpiceCoreInterfaceInternal *core, @@ -1911,52 +2034,21 @@ 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 - }; - static SpiceImageSurfacesOps image_surfaces_ops = { - image_surfaces_get, - }; + /* FIXME: migrate is not used...? */ 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); - - display->priv->n_surfaces = n_surfaces; - display->priv->renderer = RED_RENDERER_INVALID; - - 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); - + 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; } @@ -2085,7 +2177,6 @@ void display_channel_update_monitors_config(DisplayChannel *display, QXLMonitorsConfig *config, uint16_t count, uint16_t max_allowed) { - if (display->priv->monitors_config) monitors_config_unref(display->priv->monitors_config); @@ -2113,6 +2204,50 @@ void display_channel_reset_image_cache(DisplayChannel *self) image_cache_reset(&self->priv->image_cache); } +static void +display_channel_class_init(DisplayChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + g_type_class_add_private(klass, sizeof(DisplayChannelPrivate)); + + object_class->get_property = display_channel_get_property; + object_class->set_property = display_channel_set_property; + object_class->constructed = display_channel_constructed; + object_class->finalize = display_channel_finalize; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL); + channel_class->handle_parsed = dcc_handle_message; + + channel_class->on_disconnect = on_disconnect; + channel_class->send_item = 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 998970e9..9ac90465 100644 --- a/server/display-channel.h +++ b/server/display-channel.h @@ -20,32 +20,59 @@ #include <setjmp.h> #include <common/rect.h> +#include <common/sw_canvas.h> -#include "reds-stream.h" #include "cache-item.h" -#include "pixmap-cache.h" -#include "stat.h" -#include "reds.h" -#include "memslot.h" -#include "red-parse-qxl.h" -#include "red-record-qxl.h" +#include "image-encoders.h" +#include "dcc.h" #include "demarshallers.h" -#include "red-channel.h" -#include "red-qxl.h" #include "dispatcher.h" #include "main-channel.h" -#include "migration-protocol.h" #include "main-dispatcher.h" +#include "memslot.h" +#include "migration-protocol.h" +#include "video-encoder.h" +#include "pixmap-cache.h" +#include "red-channel.h" +#include "red-qxl.h" +#include "red-parse-qxl.h" +#include "red-record-qxl.h" +#include "reds-stream.h" +#include "reds.h" #include "spice-bitmap-utils.h" -#include "image-cache.h" -#include "utils.h" -#include "tree.h" +#include "stat.h" #include "stream.h" -#include "dcc.h" -#include "image-encoders.h" #include "common-graphics-channel.h" +#include "tree.h" +#include "utils.h" + +G_BEGIN_DECLS + +#define TYPE_DISPLAY_CHANNEL display_channel_get_type() + +#define DISPLAY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DISPLAY_CHANNEL, DisplayChannel)) +#define DISPLAY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DISPLAY_CHANNEL, DisplayChannelClass)) +#define IS_DISPLAY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DISPLAY_CHANNEL)) +#define IS_DISPLAY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DISPLAY_CHANNEL)) +#define DISPLAY_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DISPLAY_CHANNEL, DisplayChannelClass)) + +typedef struct DisplayChannel DisplayChannel; +typedef struct DisplayChannelClass DisplayChannelClass; +typedef struct DisplayChannelPrivate DisplayChannelPrivate; + +struct DisplayChannel +{ + CommonGraphicsChannel parent; + + DisplayChannelPrivate *priv; +}; + +struct DisplayChannelClass +{ + CommonGraphicsChannelClass parent_class; +}; -#define DISPLAY_CHANNEL(channel) ((DisplayChannel*)channel) +GType display_channel_get_type(void) G_GNUC_CONST; typedef struct DependItem { Drawable *drawable; @@ -154,69 +181,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 +228,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 +382,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..6ec78429 --- /dev/null +++ b/server/dummy-channel.c @@ -0,0 +1,58 @@ +/* dummy-channel.c */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "dummy-channel.h" + +G_DEFINE_TYPE(DummyChannel, dummy_channel, RED_TYPE_CHANNEL) + +#define DUMMY_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_DUMMY_CHANNEL, DummyChannelPrivate)) + +struct DummyChannelPrivate +{ + gpointer padding; +}; + +static void +dummy_channel_class_init(DummyChannelClass *klass) +{ + g_type_class_add_private(klass, sizeof(DummyChannelPrivate)); +} + +static void +dummy_channel_init(DummyChannel *self) +{ + self->priv = DUMMY_CHANNEL_PRIVATE(self); +} + +// TODO: red_worker can use this one +static void dummy_watch_update_mask(SpiceWatch *watch, int event_mask) +{ +} + +static SpiceWatch *dummy_watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) +{ + return NULL; // apparently allowed? +} + +static void dummy_watch_remove(SpiceWatch *watch) +{ +} + +// TODO: actually, since I also use channel_client_dummym, no need for core. Can be NULL +static const SpiceCoreInterface dummy_core = { + .watch_update_mask = dummy_watch_update_mask, + .watch_add = dummy_watch_add, + .watch_remove = dummy_watch_remove, +}; + +RedChannel *dummy_channel_new(RedsState *reds, uint32_t type, uint32_t id) +{ + return g_object_new(TYPE_DUMMY_CHANNEL, + "spice-server", reds, + "core-interface", &dummy_core, + "channel-type", type, + "id", id, + NULL); +} diff --git a/server/dummy-channel.h b/server/dummy-channel.h new file mode 100644 index 00000000..dd2f0059 --- /dev/null +++ b/server/dummy-channel.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2009-2015 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __DUMMY_CHANNEL_H__ +#define __DUMMY_CHANNEL_H__ + +#include <glib-object.h> + +#include "red-channel.h" + +G_BEGIN_DECLS + +// TODO: tmp, for channels that don't use RedChannel yet (e.g., snd channel), but +// do use the client callbacks. So the channel clients are not connected (the channel doesn't +// have list of them, but they do have a link to the channel, and the client has a list of them) + +#define TYPE_DUMMY_CHANNEL dummy_channel_get_type() + +#define DUMMY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DUMMY_CHANNEL, DummyChannel)) +#define DUMMY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DUMMY_CHANNEL, DummyChannelClass)) +#define _IS_DUMMY_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DUMMY_CHANNEL)) +#define _IS_DUMMY_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DUMMY_CHANNEL)) +#define DUMMY_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DUMMY_CHANNEL, DummyChannelClass)) + +typedef struct DummyChannel DummyChannel; +typedef struct DummyChannelClass DummyChannelClass; +typedef struct DummyChannelPrivate DummyChannelPrivate; + +struct DummyChannel +{ + RedChannel parent; + + DummyChannelPrivate *priv; +}; + +struct DummyChannelClass +{ + RedChannelClass parent_class; +}; + +GType dummy_channel_get_type(void) G_GNUC_CONST; + +RedChannel *dummy_channel_new(RedsState *reds, uint32_t type, uint32_t id); + +G_END_DECLS + +#endif /* __DUMMY_CHANNEL_H__ */ diff --git a/server/inputs-channel.c b/server/inputs-channel.c index 83c1360d..c351dad3 100644 --- a/server/inputs-channel.c +++ b/server/inputs-channel.c @@ -57,6 +57,83 @@ #define RECEIVE_BUF_SIZE \ (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE) +G_DEFINE_TYPE(InputsChannel, inputs_channel, RED_TYPE_CHANNEL) + +#define CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_INPUTS_CHANNEL, InputsChannelPrivate)) + +struct InputsChannelPrivate +{ + uint8_t recv_buf[RECEIVE_BUF_SIZE]; + VDAgentMouseState mouse_state; + int src_during_migrate; + SpiceTimer *key_modifiers_timer; + + SpiceKbdInstance *keyboard; + SpiceMouseInstance *mouse; + SpiceTabletInstance *tablet; +}; + + +static void +inputs_channel_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +inputs_channel_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void inputs_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, int migration, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps); +static void inputs_migrate(RedChannelClient *rcc); +static void key_modifiers_sender(void *opaque); + +static void +inputs_channel_constructed(GObject *object) +{ + ClientCbs client_cbs = { NULL, }; + InputsChannel *self = INPUTS_CHANNEL(object); + RedsState *reds = red_channel_get_server(RED_CHANNEL(self)); + + G_OBJECT_CLASS(inputs_channel_parent_class)->constructed(object); + + client_cbs.connect = inputs_connect; + client_cbs.migrate = inputs_migrate; + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); + + red_channel_set_cap(RED_CHANNEL(self), SPICE_INPUTS_CAP_KEY_SCANCODE); + reds_register_channel(reds, RED_CHANNEL(self)); + + if (!(self->priv->key_modifiers_timer = reds_core_timer_add(reds, key_modifiers_sender, self))) { + spice_error("key modifiers timer create failed"); + } +} + +static void +inputs_channel_init(InputsChannel *self) +{ + self->priv = CHANNEL_PRIVATE(self); +} + struct SpiceKbdState { uint8_t push_ext_type; @@ -105,18 +182,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; @@ -138,26 +203,26 @@ void inputs_channel_set_tablet_logical_size(InputsChannel *inputs, int x_res, in { SpiceTabletInterface *sif; - sif = SPICE_UPCAST(SpiceTabletInterface, inputs->tablet->base.sif); - sif->set_logical_size(inputs->tablet, x_res, y_res); + sif = SPICE_UPCAST(SpiceTabletInterface, inputs->priv->tablet->base.sif); + sif->set_logical_size(inputs->priv->tablet, x_res, y_res); } const VDAgentMouseState *inputs_channel_get_mouse_state(InputsChannel *inputs) { - return &inputs->mouse_state; + return &inputs->priv->mouse_state; } static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size) { - InputsChannel *inputs_channel = INPUTS_CHANNEL(red_channel_client_get_channel(rcc)); + InputsChannel *inputs = INPUTS_CHANNEL(red_channel_client_get_channel(rcc)); if (size > RECEIVE_BUF_SIZE) { spice_printerr("error: too large incoming message"); return NULL; } - return inputs_channel->recv_buf; + return inputs->priv->recv_buf; } static void inputs_channel_release_msg_rcv_buf(RedChannelClient *rcc, @@ -183,7 +248,7 @@ static void inputs_channel_release_msg_rcv_buf(RedChannelClient *rcc, static void activate_modifiers_watch(InputsChannel *inputs, RedsState *reds) { - reds_core_timer_start(reds, inputs->key_modifiers_timer, KEY_MODIFIERS_TTL); + reds_core_timer_start(reds, inputs->priv->key_modifiers_timer, KEY_MODIFIERS_TTL); } static void kbd_push_scan(SpiceKbdInstance *sin, uint8_t scan) @@ -259,7 +324,7 @@ static void inputs_channel_send_item(RedChannelClient *rcc, RedPipeItem *base) red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK, base); break; case RED_PIPE_ITEM_MIGRATE_DATA: - ((InputsChannel*)red_channel_client_get_channel(rcc))->src_during_migrate = FALSE; + ((InputsChannel*)red_channel_client_get_channel(rcc))->priv->src_during_migrate = FALSE; inputs_channel_client_send_migrate_data(rcc, m, base); break; default: @@ -272,10 +337,10 @@ static void inputs_channel_send_item(RedChannelClient *rcc, RedPipeItem *base) static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message) { - InputsChannel *inputs_channel = (InputsChannel *)red_channel_client_get_channel(rcc); + InputsChannel *inputs = INPUTS_CHANNEL(red_channel_client_get_channel(rcc)); InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc); uint32_t i; - RedsState *reds = red_channel_get_server(&inputs_channel->base); + RedsState *reds = red_channel_get_server(RED_CHANNEL(inputs)); switch (type) { case SPICE_MSGC_INPUTS_KEY_DOWN: { @@ -283,7 +348,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui if (key_down->code == CAPS_LOCK_SCAN_CODE || key_down->code == NUM_LOCK_SCAN_CODE || key_down->code == SCROLL_LOCK_SCAN_CODE) { - activate_modifiers_watch(inputs_channel, reds); + activate_modifiers_watch(inputs, reds); } } case SPICE_MSGC_INPUTS_KEY_UP: { @@ -293,19 +358,19 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui if (code == 0) { break; } - kbd_push_scan(inputs_channel_get_keyboard(inputs_channel), code); + kbd_push_scan(inputs_channel_get_keyboard(inputs), code); } break; } case SPICE_MSGC_INPUTS_KEY_SCANCODE: { uint8_t *code = message; for (i = 0; i < size; i++) { - kbd_push_scan(inputs_channel_get_keyboard(inputs_channel), code[i]); + kbd_push_scan(inputs_channel_get_keyboard(inputs), code[i]); } break; } case SPICE_MSGC_INPUTS_MOUSE_MOTION: { - SpiceMouseInstance *mouse = inputs_channel_get_mouse(inputs_channel); + SpiceMouseInstance *mouse = inputs_channel_get_mouse(inputs); SpiceMsgcMouseMotion *mouse_motion = message; inputs_channel_client_on_mouse_motion(icc); @@ -320,7 +385,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui } case SPICE_MSGC_INPUTS_MOUSE_POSITION: { SpiceMsgcMousePosition *pos = message; - SpiceTabletInstance *tablet = inputs_channel_get_tablet(inputs_channel); + SpiceTabletInstance *tablet = inputs_channel_get_tablet(inputs); inputs_channel_client_on_mouse_motion(icc); if (reds_get_mouse_mode(reds) != SPICE_MOUSE_MODE_CLIENT) { @@ -333,7 +398,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state)); break; } - VDAgentMouseState *mouse_state = &inputs_channel->mouse_state; + VDAgentMouseState *mouse_state = &inputs->priv->mouse_state; mouse_state->x = pos->x; mouse_state->y = pos->y; mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state); @@ -351,20 +416,20 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui } if (reds_get_mouse_mode(reds) == SPICE_MOUSE_MODE_CLIENT) { if (reds_config_get_agent_mouse(reds) && reds_has_vdagent(reds)) { - inputs_channel->mouse_state.buttons = + inputs->priv->mouse_state.buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) | (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) | (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0); - reds_handle_agent_mouse_event(reds, &inputs_channel->mouse_state); - } else if (inputs_channel_get_tablet(inputs_channel)) { + reds_handle_agent_mouse_event(reds, &inputs->priv->mouse_state); + } else if (inputs_channel_get_tablet(inputs)) { SpiceTabletInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif, SpiceTabletInterface, base); - sif->wheel(inputs_channel_get_tablet(inputs_channel), dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state)); + sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs)->base.sif, SpiceTabletInterface, base); + sif->wheel(inputs_channel_get_tablet(inputs), dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state)); } - } else if (inputs_channel_get_mouse(inputs_channel)) { + } else if (inputs_channel_get_mouse(inputs)) { SpiceMouseInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif, SpiceMouseInterface, base); - sif->motion(inputs_channel_get_mouse(inputs_channel), 0, 0, dz, + sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs)->base.sif, SpiceMouseInterface, base); + sif->motion(inputs_channel_get_mouse(inputs), 0, 0, dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state)); } break; @@ -373,18 +438,18 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui SpiceMsgcMouseRelease *mouse_release = message; if (reds_get_mouse_mode(reds) == SPICE_MOUSE_MODE_CLIENT) { if (reds_config_get_agent_mouse(reds) && reds_has_vdagent(reds)) { - inputs_channel->mouse_state.buttons = + inputs->priv->mouse_state.buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state); - reds_handle_agent_mouse_event(reds, &inputs_channel->mouse_state); - } else if (inputs_channel_get_tablet(inputs_channel)) { + reds_handle_agent_mouse_event(reds, &inputs->priv->mouse_state); + } else if (inputs_channel_get_tablet(inputs)) { SpiceTabletInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif, SpiceTabletInterface, base); - sif->buttons(inputs_channel_get_tablet(inputs_channel), RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state)); + sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs)->base.sif, SpiceTabletInterface, base); + sif->buttons(inputs_channel_get_tablet(inputs), RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state)); } - } else if (inputs_channel_get_mouse(inputs_channel)) { + } else if (inputs_channel_get_mouse(inputs)) { SpiceMouseInterface *sif; - sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif, SpiceMouseInterface, base); - sif->buttons(inputs_channel_get_mouse(inputs_channel), + sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs)->base.sif, SpiceMouseInterface, base); + sif->buttons(inputs_channel_get_mouse(inputs), RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state)); } break; @@ -392,7 +457,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui case SPICE_MSGC_INPUTS_KEY_MODIFIERS: { SpiceMsgcKeyModifiers *modifiers = message; uint8_t leds; - SpiceKbdInstance *keyboard = inputs_channel_get_keyboard(inputs_channel); + SpiceKbdInstance *keyboard = inputs_channel_get_keyboard(inputs); if (!keyboard) { break; @@ -413,7 +478,7 @@ static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, ui kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE); kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE | 0x80); } - activate_modifiers_watch(inputs_channel, reds); + activate_modifiers_watch(inputs, reds); break; } case SPICE_MSGC_DISCONNECTING: @@ -512,17 +577,17 @@ static void inputs_connect(RedChannel *channel, RedClient *client, static void inputs_migrate(RedChannelClient *rcc) { InputsChannel *inputs = (InputsChannel*)red_channel_client_get_channel(rcc); - inputs->src_during_migrate = TRUE; + inputs->priv->src_during_migrate = TRUE; red_channel_client_default_migrate(rcc); } static void inputs_channel_push_keyboard_modifiers(InputsChannel *inputs, uint8_t modifiers) { - if (!inputs || !red_channel_is_connected(&inputs->base) || - inputs->src_during_migrate) { + if (!inputs || !red_channel_is_connected(RED_CHANNEL(inputs)) || + inputs->priv->src_during_migrate) { return; } - red_channel_pipes_new_add_push(&inputs->base, + red_channel_pipes_new_add_push(RED_CHANNEL(inputs), red_inputs_key_modifiers_item_new, (void*)&modifiers); } @@ -566,109 +631,105 @@ static int inputs_channel_handle_migrate_data(RedChannelClient *rcc, return TRUE; } -InputsChannel* inputs_channel_new(RedsState *reds) +static void +inputs_channel_class_init(InputsChannelClass *klass) { - ChannelCbs channel_cbs = { NULL, }; - ClientCbs client_cbs = { NULL, }; - InputsChannel *inputs; - - channel_cbs.config_socket = inputs_channel_config_socket; - channel_cbs.on_disconnect = inputs_channel_on_disconnect; - channel_cbs.send_item = inputs_channel_send_item; - channel_cbs.alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = inputs_channel_release_msg_rcv_buf; - channel_cbs.handle_migrate_data = inputs_channel_handle_migrate_data; - channel_cbs.handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark; - - inputs = (InputsChannel *)red_channel_create_parser( - sizeof(InputsChannel), - reds, - reds_get_core_interface(reds), - SPICE_CHANNEL_INPUTS, 0, - FALSE, /* handle_acks */ - spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL), - inputs_channel_handle_parsed, - &channel_cbs, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER); - - if (!inputs) { - spice_error("failed to allocate Inputs Channel"); - } + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); - client_cbs.connect = inputs_connect; - client_cbs.migrate = inputs_migrate; - red_channel_register_client_cbs(&inputs->base, &client_cbs, NULL); + g_type_class_add_private(klass, sizeof(InputsChannelPrivate)); - red_channel_set_cap(&inputs->base, SPICE_INPUTS_CAP_KEY_SCANCODE); - reds_register_channel(reds, &inputs->base); + object_class->get_property = inputs_channel_get_property; + object_class->set_property = inputs_channel_set_property; + object_class->constructed = inputs_channel_constructed; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL); + channel_class->handle_parsed = inputs_channel_handle_parsed; + + /* channel callbacks */ + channel_class->config_socket = inputs_channel_config_socket; + channel_class->on_disconnect = inputs_channel_on_disconnect; + channel_class->send_item = inputs_channel_send_item; + channel_class->alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf; + channel_class->release_recv_buf = inputs_channel_release_msg_rcv_buf; + channel_class->handle_migrate_data = inputs_channel_handle_migrate_data; + channel_class->handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark; +} + +InputsChannel* inputs_channel_new(RedsState *reds) +{ + return g_object_new(TYPE_INPUTS_CHANNEL, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", (int)SPICE_CHANNEL_INPUTS, + "id", 0, + "handle-acks", FALSE, + "migration-flags", (guint)(SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + NULL); - if (!(inputs->key_modifiers_timer = reds_core_timer_add(reds, key_modifiers_sender, inputs))) { - spice_error("key modifiers timer create failed"); - } - return inputs; } static SpiceKbdInstance* inputs_channel_get_keyboard(InputsChannel *inputs) { - return inputs->keyboard; + return inputs->priv->keyboard; } int inputs_channel_set_keyboard(InputsChannel *inputs, SpiceKbdInstance *keyboard) { - if (inputs->keyboard) { + if (inputs->priv->keyboard) { spice_printerr("already have keyboard"); return -1; } - inputs->keyboard = keyboard; - inputs->keyboard->st = spice_kbd_state_new(red_channel_get_server(&inputs->base)); + inputs->priv->keyboard = keyboard; + inputs->priv->keyboard->st = spice_kbd_state_new(red_channel_get_server(RED_CHANNEL(inputs))); return 0; } static SpiceMouseInstance* inputs_channel_get_mouse(InputsChannel *inputs) { - return inputs->mouse; + return inputs->priv->mouse; } int inputs_channel_set_mouse(InputsChannel *inputs, SpiceMouseInstance *mouse) { - if (inputs->mouse) { + if (inputs->priv->mouse) { spice_printerr("already have mouse"); return -1; } - inputs->mouse = mouse; - inputs->mouse->st = spice_mouse_state_new(); + inputs->priv->mouse = mouse; + inputs->priv->mouse->st = spice_mouse_state_new(); return 0; } static SpiceTabletInstance* inputs_channel_get_tablet(InputsChannel *inputs) { - return inputs->tablet; + return inputs->priv->tablet; } int inputs_channel_set_tablet(InputsChannel *inputs, SpiceTabletInstance *tablet, RedsState *reds) { - if (inputs->tablet) { + if (inputs->priv->tablet) { spice_printerr("already have tablet"); return -1; } - inputs->tablet = tablet; - inputs->tablet->st = spice_tablet_state_new(); - inputs->tablet->st->reds = reds; + inputs->priv->tablet = tablet; + inputs->priv->tablet->st = spice_tablet_state_new(); + inputs->priv->tablet->st->reds = reds; return 0; } int inputs_channel_has_tablet(InputsChannel *inputs) { - return inputs != NULL && inputs->tablet != NULL; + return inputs != NULL && inputs->priv->tablet != NULL; } void inputs_channel_detach_tablet(InputsChannel *inputs, SpiceTabletInstance *tablet) { spice_printerr(""); - inputs->tablet = NULL; + inputs->priv->tablet = NULL; } gboolean inputs_channel_is_src_during_migrate(InputsChannel *inputs) { - return inputs->src_during_migrate; + return inputs->priv->src_during_migrate; } diff --git a/server/inputs-channel.h b/server/inputs-channel.h index 70010945..a1ce85f3 100644 --- a/server/inputs-channel.h +++ b/server/inputs-channel.h @@ -22,14 +22,41 @@ // 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; +typedef struct InputsChannelPrivate InputsChannelPrivate; + +struct InputsChannel +{ + RedChannel parent; + + InputsChannelPrivate *priv; +}; + +struct InputsChannelClass +{ + RedChannelClass parent_class; +}; + +GType inputs_channel_get_type(void) G_GNUC_CONST; InputsChannel* inputs_channel_new(RedsState *reds); + 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); @@ -43,4 +70,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 b30083fb..eb9256d9 100644 --- a/server/main-channel-client.c +++ b/server/main-channel-client.c @@ -435,15 +435,11 @@ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, spice_printerr("client %p connected: %d seamless %d", client, success, seamless); if (mcc->priv->mig_wait_connect) { 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; - spice_assert(main_channel->num_clients_mig_wait); - spice_assert(!seamless || main_channel->num_clients_mig_wait == 1); - if (!--main_channel->num_clients_mig_wait) { - reds_on_main_migrate_connected(channel->reds, seamless && success); - } + main_channel_on_migrate_connected(main_channel, seamless && success); } else { if (success) { spice_printerr("client %p MIGRATE_CANCEL", client); @@ -457,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); @@ -557,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 { @@ -613,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; } @@ -628,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 */ @@ -697,14 +697,14 @@ uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc) void main_channel_client_migrate(RedChannelClient *rcc) { RedChannel *channel = red_channel_client_get_channel(rcc); - reds_on_main_channel_migrate(channel->reds, MAIN_CHANNEL_CLIENT(rcc)); + reds_on_main_channel_migrate(red_channel_get_server(channel), + MAIN_CHANNEL_CLIENT(rcc)); red_channel_client_default_migrate(rcc); } gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc) { RedChannelClient *rcc = RED_CHANNEL_CLIENT(mcc); - MainChannel* main_channel = MAIN_CHANNEL(red_channel_client_get_channel(rcc)); if (red_channel_client_test_remote_cap(rcc, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) { RedClient *client = red_channel_client_get_client(rcc); @@ -718,7 +718,6 @@ gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc) mcc->priv->mig_wait_connect = TRUE; } mcc->priv->mig_connect_ok = FALSE; - main_channel->num_clients_mig_wait++; return TRUE; } return FALSE; @@ -760,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); } @@ -835,7 +834,7 @@ static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc, { RedChannel *channel = red_channel_client_get_channel(rcc); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item); - reds_marshall_migrate_data(channel->reds, m); // TODO: from reds split. ugly separation. + reds_marshall_migrate_data(red_channel_get_server(channel), m); // TODO: from reds split. ugly separation. } static void main_channel_marshall_init(RedChannelClient *rcc, @@ -853,7 +852,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; @@ -878,7 +877,7 @@ static void main_channel_marshall_notify(RedChannelClient *rcc, static void main_channel_fill_migrate_dst_info(MainChannel *main_channel, SpiceMigrationDstInfo *dst_info) { - RedsMigSpice *mig_dst = &main_channel->mig_target; + const RedsMigSpice *mig_dst = main_channel_peek_migration_target(main_channel); dst_info->port = mig_dst->port; dst_info->sport = mig_dst->sport; dst_info->host_size = strlen(mig_dst->host) + 1; @@ -897,11 +896,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); } @@ -911,11 +908,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); } @@ -936,18 +931,20 @@ static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelC { RedChannel *channel = red_channel_client_get_channel(rcc); SpiceMsgMainMigrationSwitchHost migrate; - MainChannel *main_ch; + MainChannel *main_chan; + const RedsMigSpice *mig_target; spice_printerr(""); red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST, item); - main_ch = SPICE_CONTAINEROF(channel, MainChannel, base); - migrate.port = main_ch->mig_target.port; - migrate.sport = main_ch->mig_target.sport; - migrate.host_size = strlen(main_ch->mig_target.host) + 1; - migrate.host_data = (uint8_t *)main_ch->mig_target.host; - if (main_ch->mig_target.cert_subject) { - migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1; - migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject; + main_chan = MAIN_CHANNEL(channel); + mig_target = main_channel_peek_migration_target(main_chan); + migrate.port = mig_target->port; + migrate.sport = mig_target->sport; + migrate.host_size = strlen(mig_target->host) + 1; + migrate.host_data = (uint8_t *)mig_target->host; + if (mig_target->cert_subject) { + migrate.cert_subject_size = strlen(mig_target->cert_subject) + 1; + migrate.cert_subject_data = (uint8_t *)mig_target->cert_subject; } else { migrate.cert_subject_size = 0; migrate.cert_subject_data = NULL; diff --git a/server/main-channel-client.h b/server/main-channel-client.h index 360c61e9..b5825db4 100644 --- a/server/main-channel-client.h +++ b/server/main-channel-client.h @@ -24,9 +24,6 @@ G_BEGIN_DECLS -/* FIXME: remove extra MainChannel typedef when possible */ -typedef struct MainChannel MainChannel; - #define TYPE_MAIN_CHANNEL_CLIENT main_channel_client_get_type() #define MAIN_CHANNEL_CLIENT(obj) \ @@ -56,6 +53,8 @@ struct MainChannelClientClass RedChannelClientClass parent_class; }; +typedef struct MainChannel MainChannel; + GType main_channel_client_get_type(void) G_GNUC_CONST; MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client, diff --git a/server/main-channel.c b/server/main-channel.c index a1b8e319..d206550a 100644 --- a/server/main-channel.c +++ b/server/main-channel.c @@ -26,9 +26,45 @@ #include "reds.h" #include "red-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) + +G_DEFINE_TYPE(MainChannel, main_channel, RED_TYPE_CHANNEL) + +#define MAIN_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_MAIN_CHANNEL, MainChannelPrivate)) + +struct MainChannelPrivate +{ + uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE]; + RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?) + int num_clients_mig_wait; +}; + +static void +main_channel_constructed(GObject *object) +{ + MainChannel *self = MAIN_CHANNEL(object); + ClientCbs client_cbs = { NULL, }; + + G_OBJECT_CLASS(main_channel_parent_class)->constructed(object); + + red_channel_set_cap(RED_CHANNEL(self), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); + red_channel_set_cap(RED_CHANNEL(self), SPICE_MAIN_CAP_SEAMLESS_MIGRATE); + + client_cbs.migrate = main_channel_client_migrate; + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); +} + +static void +main_channel_init(MainChannel *self) +{ + self->priv = MAIN_CHANNEL_PRIVATE(self); +} + 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)); } /* @@ -75,27 +111,27 @@ 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, @@ -106,7 +142,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); @@ -118,7 +154,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) @@ -127,37 +165,38 @@ void main_channel_push_multi_media_time(MainChannel *main_chan, int time) .time = time, }; - red_channel_pipes_new_add_push(&main_chan->base, + red_channel_pipes_new_add_push(RED_CHANNEL(main_chan), main_multi_media_time_item_new, &info); } static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target) { spice_assert(mig_target); - free(main_channel->mig_target.host); - main_channel->mig_target.host = spice_strdup(mig_target->host); - free(main_channel->mig_target.cert_subject); + free(main_channel->priv->mig_target.host); + main_channel->priv->mig_target.host = spice_strdup(mig_target->host); + free(main_channel->priv->mig_target.cert_subject); if (mig_target->cert_subject) { - main_channel->mig_target.cert_subject = spice_strdup(mig_target->cert_subject); + main_channel->priv->mig_target.cert_subject = spice_strdup(mig_target->cert_subject); } else { - main_channel->mig_target.cert_subject = NULL; + main_channel->priv->mig_target.cert_subject = NULL; } - main_channel->mig_target.port = mig_target->port; - main_channel->mig_target.sport = mig_target->sport; + main_channel->priv->mig_target.port = mig_target->port; + main_channel->priv->mig_target.sport = mig_target->sport; } void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target) { main_channel_fill_mig_target(main_chan, mig_target); - red_channel_pipes_add_type(&main_chan->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); + red_channel_pipes_add_type(RED_CHANNEL(main_chan), RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); } static 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: { @@ -168,18 +207,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: @@ -203,7 +242,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); @@ -225,13 +264,13 @@ static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, uint32_t size) { RedChannel *channel = red_channel_client_get_channel(rcc); - MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base); + MainChannel *main_chan = MAIN_CHANNEL(channel); MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); if (type == SPICE_MSGC_MAIN_AGENT_DATA) { - return reds_get_agent_data_buffer(channel->reds, mcc, size); + return reds_get_agent_data_buffer(red_channel_get_server(channel), mcc, size); } else { - return main_chan->recv_buf; + return main_chan->priv->recv_buf; } } @@ -242,7 +281,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); } } @@ -255,8 +294,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; } @@ -281,12 +319,12 @@ MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client, int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen) { - return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1; + return main_chan ? getsockname(red_channel_get_first_socket(RED_CHANNEL(main_chan)), sa, salen) : -1; } int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen) { - return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1; + return main_chan ? getpeername(red_channel_get_first_socket(RED_CHANNEL(main_chan)), sa, salen) : -1; } // TODO: ? shouldn't it disonnect all clients? or shutdown all main_channels? @@ -294,42 +332,22 @@ void main_channel_close(MainChannel *main_chan) { int socketfd; - if (main_chan && (socketfd = red_channel_get_first_socket(&main_chan->base)) != -1) { + if (main_chan && (socketfd = red_channel_get_first_socket(RED_CHANNEL(main_chan))) != -1) { close(socketfd); } } MainChannel* main_channel_new(RedsState *reds) { - RedChannel *channel; - ChannelCbs channel_cbs = { NULL, }; - ClientCbs client_cbs = {NULL, }; - - channel_cbs.config_socket = main_channel_config_socket; - channel_cbs.on_disconnect = main_channel_client_on_disconnect; - channel_cbs.send_item = main_channel_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; - // TODO: set the migration flag of the channel - channel = red_channel_create_parser(sizeof(MainChannel), reds, - reds_get_core_interface(reds), - SPICE_CHANNEL_MAIN, 0, - FALSE, /* handle_acks */ - spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL), - main_channel_handle_parsed, - &channel_cbs, - SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER); - spice_assert(channel); - red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); - red_channel_set_cap(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE); - - client_cbs.migrate = main_channel_client_migrate; - red_channel_register_client_cbs(channel, &client_cbs, NULL); - - return MAIN_CHANNEL(channel); + return g_object_new(TYPE_MAIN_CHANNEL, + "spice-server", reds, + "core-interface", reds_get_core_interface(reds), + "channel-type", (gint)SPICE_CHANNEL_MAIN, + "id", 0, + "handle-acks", FALSE, /* handle_acks */ + "migration-flags", (SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER), + NULL); } static int main_channel_connect_semi_seamless(MainChannel *main_channel) @@ -340,9 +358,9 @@ static int main_channel_connect_semi_seamless(MainChannel *main_channel) FOREACH_CLIENT(main_channel, iter, rcc) { MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); if (main_channel_client_connect_semi_seamless(mcc)) - main_channel->num_clients_mig_wait++; + main_channel->priv->num_clients_mig_wait++; } - return main_channel->num_clients_mig_wait; + return main_channel->priv->num_clients_mig_wait; } static int main_channel_connect_seamless(MainChannel *main_channel) @@ -350,21 +368,21 @@ 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); main_channel_client_connect_seamless(mcc); - main_channel->num_clients_mig_wait++; + main_channel->priv->num_clients_mig_wait++; } - return main_channel->num_clients_mig_wait; + return main_channel->priv->num_clients_mig_wait; } int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target, int try_seamless) { main_channel_fill_mig_target(main_channel, mig_target); - main_channel->num_clients_mig_wait = 0; + main_channel->priv->num_clients_mig_wait = 0; if (!main_channel_is_connected(main_channel)) { return 0; @@ -374,8 +392,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)) { @@ -396,7 +416,7 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan) MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); main_channel_client_migrate_cancel_wait(mcc); } - main_chan->num_clients_mig_wait = 0; + main_chan->priv->num_clients_mig_wait = 0; } int main_channel_migrate_src_complete(MainChannel *main_chan, int success) @@ -407,7 +427,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; } @@ -419,3 +439,41 @@ int main_channel_migrate_src_complete(MainChannel *main_chan, int success) } return semi_seamless_count; } + +void main_channel_on_migrate_connected(MainChannel *main_channel, gboolean seamless) +{ + g_return_if_fail(main_channel->priv->num_clients_mig_wait); + g_warn_if_fail(!seamless || main_channel->priv->num_clients_mig_wait == 1); + if (!--main_channel->priv->num_clients_mig_wait) { + reds_on_main_migrate_connected(red_channel_get_server(RED_CHANNEL(main_channel)), + seamless); + } +} + +static void +main_channel_class_init(MainChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); + + g_type_class_add_private(klass, sizeof(MainChannelPrivate)); + + object_class->constructed = main_channel_constructed; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL); + channel_class->handle_parsed = main_channel_handle_parsed; + + /* channel callbacks */ + channel_class->config_socket = main_channel_config_socket; + channel_class->on_disconnect = main_channel_client_on_disconnect; + channel_class->send_item = main_channel_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; +} + +const RedsMigSpice* main_channel_peek_migration_target(MainChannel *main_chan) +{ + return &main_chan->priv->mig_target; +} diff --git a/server/main-channel.h b/server/main-channel.h index caea0142..09adb18a 100644 --- a/server/main-channel.h +++ b/server/main-channel.h @@ -19,23 +19,46 @@ #define __MAIN_CHANNEL_H__ #include <stdint.h> +#include <glib-object.h> #include <spice/vd_agent.h> #include <common/marshaller.h> #include "red-channel.h" #include "main-channel-client.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; +typedef struct MainChannelPrivate MainChannelPrivate; + +struct MainChannel +{ + RedChannel parent; + + MainChannelPrivate *priv; +}; + +struct MainChannelClass +{ + RedChannelClass parent_class; +}; + +GType main_channel_get_type(void) G_GNUC_CONST; // TODO: Defines used to calculate receive buffer size, and also by reds.c // other options: is to make a reds_main_consts.h, to duplicate defines. #define REDS_AGENT_WINDOW_SIZE 10 #define REDS_NUM_INTERNAL_AGENT_MESSAGES 1 -// approximate max receive message size for main channel -#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \ - (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE) - struct RedsMigSpice { char *host; char *cert_subject; @@ -44,13 +67,6 @@ struct RedsMigSpice { }; typedef struct RedsMigSpice RedsMigSpice; -typedef struct MainChannel { - RedChannel base; - uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE]; - RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?) - int num_clients_mig_wait; -} MainChannel; - MainChannel *main_channel_new(RedsState *reds); RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id); @@ -78,7 +94,11 @@ void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_targe int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target, int try_seamless); void main_channel_migrate_cancel_wait(MainChannel *main_chan); +const RedsMigSpice* main_channel_peek_migration_target(MainChannel *main_chan); /* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/ int main_channel_migrate_src_complete(MainChannel *main_chan, int success); +void main_channel_on_migrate_connected(MainChannel *main_channel, gboolean seamless); + +G_END_DECLS #endif diff --git a/server/red-channel-client-private.h b/server/red-channel-client-private.h index f94e042d..76d73452 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; + struct RedChannelClientPrivate { RedChannel *channel; diff --git a/server/red-channel-client.c b/server/red-channel-client.c index e860390c..694ff965 100644 --- a/server/red-channel-client.c +++ b/server/red-channel-client.c @@ -92,6 +92,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; } @@ -99,11 +101,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; } @@ -111,7 +117,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; } @@ -186,10 +193,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); @@ -242,7 +249,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); @@ -360,7 +367,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) @@ -390,12 +396,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) @@ -438,9 +446,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; } @@ -514,7 +522,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); @@ -557,9 +565,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)) { @@ -644,8 +653,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)); } /* @@ -686,6 +700,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)) { @@ -696,18 +711,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; } @@ -720,8 +741,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); } @@ -729,12 +750,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); } } } @@ -831,9 +852,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; @@ -844,24 +867,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, @@ -871,27 +898,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->outgoing.opaque = self; - self->outgoing.cb = &self->priv->channel->outgoing_cb; + self->outgoing.cb = red_channel_get_outgoing_handler(self->priv->channel); self->outgoing.pos = 0; self->outgoing.size = 0; 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, @@ -952,8 +978,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); } } } @@ -972,13 +999,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); @@ -995,7 +1023,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; @@ -1226,8 +1255,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); @@ -1301,8 +1332,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); } } @@ -1318,20 +1350,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; } @@ -1447,9 +1483,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; } @@ -1561,7 +1598,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) @@ -1612,27 +1649,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) @@ -1799,13 +1839,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 0b180b3e..3a09510d 100644 --- a/server/red-channel-client.h +++ b/server/red-channel-client.h @@ -49,10 +49,10 @@ G_BEGIN_DECLS #define RED_CHANNEL_CLIENT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHANNEL_CLIENT, RedChannelClientClass)) -typedef struct RedChannel RedChannel; typedef struct RedClient RedClient; typedef struct IncomingHandler IncomingHandler; +typedef struct RedChannel RedChannel; typedef struct RedChannelClient RedChannelClient; typedef struct RedChannelClientClass RedChannelClientClass; typedef struct RedChannelClientPrivate RedChannelClientPrivate; @@ -66,8 +66,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..6269a808 100644 --- a/server/red-channel.c +++ b/server/red-channel.c @@ -69,9 +69,135 @@ * from the channel's thread. */ -void red_channel_receive(RedChannel *channel) +G_DEFINE_ABSTRACT_TYPE(RedChannel, red_channel, G_TYPE_OBJECT) + +#define CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), RED_TYPE_CHANNEL, RedChannelPrivate)) + +struct RedChannelPrivate +{ + uint32_t type; + uint32_t id; + + SpiceCoreInterfaceInternal *core; + gboolean handle_acks; + + // RedChannel will hold only connected channel clients (logic - when pushing pipe item to all channel clients, there + // is no need to go over disconnect clients) + // . While client will hold the channel clients till it is destroyed + // and then it will destroy them as well. + // However RCC still holds a reference to the Channel. + // Maybe replace these logic with ref count? + // TODO: rename to 'connected_clients'? + GList *clients; + + RedChannelCapabilities local_caps; + uint32_t migration_flags; + + void *data; + + OutgoingHandlerInterface outgoing_cb; + IncomingHandlerInterface incoming_cb; + + ClientCbs client_cbs; + // TODO: when different channel_clients are in different threads from Channel -> need to protect! + pthread_t thread_id; + RedsState *reds; +#ifdef RED_STATISTICS + StatNodeRef stat; + uint64_t *out_bytes_counter; +#endif +}; + +enum { + PROP0, + PROP_SPICE_SERVER, + PROP_CORE_INTERFACE, + PROP_TYPE, + PROP_ID, + PROP_HANDLE_ACKS, + PROP_MIGRATION_FLAGS +}; + +static void +red_channel_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + 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) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_receive, NULL); + 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 +205,163 @@ static void red_channel_client_default_peer_on_error(RedChannelClient *rcc) red_channel_client_disconnect(rcc); } +static void red_channel_on_output(void *opaque, int n) +{ + RedChannelClient *rcc = opaque; + RedChannel *self = red_channel_client_get_channel(rcc);; + + red_channel_client_on_output(opaque, n); + + stat_inc_counter(self->priv->reds, self->priv->out_bytes_counter, n); +} + +static void +red_channel_constructed(GObject *object) +{ + RedChannel *self = RED_CHANNEL(object); + spice_debug("%p: channel type %d id %d thread_id 0x%lx", self, + self->priv->type, self->priv->id, self->priv->thread_id); + + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + self->priv->incoming_cb.alloc_msg_buf = + (alloc_msg_recv_buf_proc)klass->alloc_recv_buf; + self->priv->incoming_cb.release_msg_buf = + (release_msg_recv_buf_proc)klass->release_recv_buf; + self->priv->incoming_cb.handle_message = (handle_message_proc)klass->handle_message; + self->priv->incoming_cb.handle_parsed = (handle_parsed_proc)klass->handle_parsed; + self->priv->incoming_cb.parser = klass->parser; + + G_OBJECT_CLASS(red_channel_parent_class)->constructed(object); +} + +static void red_channel_client_default_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, + int migration, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps) +{ + spice_error("not implemented"); +} + +static void red_channel_client_default_disconnect(RedChannelClient *base) +{ + red_channel_client_disconnect(base); +} + +static void +red_channel_class_init(RedChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof (RedChannelPrivate)); + + object_class->get_property = red_channel_get_property; + object_class->set_property = red_channel_set_property; + object_class->finalize = red_channel_finalize; + object_class->constructed = red_channel_constructed; + + g_object_class_install_property(object_class, + PROP_SPICE_SERVER, + g_param_spec_pointer("spice-server", + "spice-server", + "The spice server associated with this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_CORE_INTERFACE, + g_param_spec_pointer("core-interface", + "core-interface", + "The SpiceCoreInterface server associated with this channel", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /* FIXME: generate enums for this in spice-common? */ + g_object_class_install_property(object_class, + PROP_TYPE, + g_param_spec_int("channel-type", + "channel type", + "Type of this channel", + 0, + SPICE_END_CHANNEL, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_ID, + g_param_spec_uint("id", + "id", + "ID of this channel", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_HANDLE_ACKS, + g_param_spec_boolean("handle-acks", + "Handle ACKs", + "Whether this channel handles ACKs", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_MIGRATION_FLAGS, + g_param_spec_uint("migration-flags", + "migration flags", + "Migration flags for this channel", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +red_channel_init(RedChannel *self) +{ + self->priv = CHANNEL_PRIVATE(self); + + red_channel_set_common_cap(self, SPICE_COMMON_CAP_MINI_HEADER); + self->priv->thread_id = pthread_self(); + self->priv->out_bytes_counter = 0; + + // TODO: send incoming_cb as parameters instead of duplicating? + self->priv->incoming_cb.on_error = + (on_incoming_error_proc)red_channel_client_default_peer_on_error; + self->priv->incoming_cb.on_input = red_channel_client_on_input; + self->priv->outgoing_cb.get_msg_size = red_channel_client_get_out_msg_size; + self->priv->outgoing_cb.prepare = red_channel_client_prepare_out_msg; + self->priv->outgoing_cb.on_block = red_channel_client_on_out_block; + self->priv->outgoing_cb.on_error = + (on_outgoing_error_proc)red_channel_client_default_peer_on_error; + self->priv->outgoing_cb.on_msg_done = red_channel_client_on_out_msg_done; + self->priv->outgoing_cb.on_output = red_channel_on_output; + + self->priv->client_cbs.connect = red_channel_client_default_connect; + self->priv->client_cbs.disconnect = red_channel_client_default_disconnect; + self->priv->client_cbs.migrate = red_channel_client_default_migrate; +} + + +void red_channel_receive(RedChannel *channel) +{ + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_receive, NULL); +} + void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc) { spice_assert(rcc); - channel->clients = g_list_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 +416,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 +426,42 @@ 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) { - 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 +478,12 @@ static void add_capability(uint32_t **caps, int *num_caps, uint32_t cap) void red_channel_set_common_cap(RedChannel *channel, uint32_t cap) { - add_capability(&channel->local_caps.common_caps, &channel->local_caps.num_common_caps, cap); + add_capability(&channel->priv->local_caps.common_caps, &channel->priv->local_caps.num_common_caps, cap); } void red_channel_set_cap(RedChannel *channel, uint32_t cap) { - add_capability(&channel->local_caps.caps, &channel->local_caps.num_caps, cap); -} - -void red_channel_ref(RedChannel *channel) -{ - channel->refs++; -} - -void red_channel_unref(RedChannel *channel) -{ - if (--channel->refs == 0) { - if (channel->local_caps.num_common_caps) { - free(channel->local_caps.common_caps); - } - - if (channel->local_caps.num_caps) { - free(channel->local_caps.caps); - } - - free(channel); - } + add_capability(&channel->priv->local_caps.caps, &channel->priv->local_caps.num_caps, cap); } void red_channel_destroy(RedChannel *channel) @@ -377,13 +492,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 +507,14 @@ void red_channel_push(RedChannel *channel) return; } - g_list_foreach(channel->clients, (GFunc)red_channel_client_push, NULL); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_push, NULL); } // TODO: this function doesn't make sense because the window should be client (WAN/LAN) // specific void red_channel_init_outgoing_messages_window(RedChannel *channel) { - g_list_foreach(channel->clients, (GFunc)red_channel_client_init_outgoing_messages_window, NULL); + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_init_outgoing_messages_window, NULL); } static void red_channel_client_pipe_add_type_proxy(gpointer data, gpointer user_data) @@ -410,7 +525,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 +537,12 @@ static void red_channel_client_pipe_add_empty_msg_proxy(gpointer data, gpointer void red_channel_pipes_add_empty_msg(RedChannel *channel, int msg_type) { - g_list_foreach(channel->clients, red_channel_client_pipe_add_empty_msg_proxy, GINT_TO_POINTER(msg_type)); + g_list_foreach(channel->priv->clients, red_channel_client_pipe_add_empty_msg_proxy, GINT_TO_POINTER(msg_type)); } int red_channel_is_connected(RedChannel *channel) { - return channel && channel->clients; + return channel && channel->priv->clients; } void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc) @@ -435,19 +550,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 +576,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 +612,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 +641,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 +733,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 +762,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 +780,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 +980,99 @@ int red_channel_wait_all_sent(RedChannel *channel, RedsState* red_channel_get_server(RedChannel *channel) { - return channel->reds; + return channel->priv->reds; +} + +SpiceCoreInterfaceInternal* red_channel_get_core_interface(RedChannel *channel) +{ + return channel->priv->core; +} + +int red_channel_config_socket(RedChannel *self, RedChannelClient *rcc) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->config_socket, FALSE); + + return klass->config_socket(rcc); +} + +void red_channel_on_disconnect(RedChannel *self, RedChannelClient *rcc) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_if_fail(klass->on_disconnect); + + klass->on_disconnect(rcc); +} + +void red_channel_send_item(RedChannel *self, RedChannelClient *rcc, RedPipeItem *item) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_if_fail(klass->send_item); + + klass->send_item(rcc, item); +} + +uint8_t* red_channel_alloc_recv_buf(RedChannel *self, RedChannelClient *rcc, + uint16_t type, uint32_t size) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->alloc_recv_buf, NULL); + + return klass->alloc_recv_buf(rcc, type, size); +} + +void red_channel_release_recv_buf(RedChannel *self, RedChannelClient *rcc, + uint16_t type, uint32_t size, uint8_t *msg) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_if_fail(klass->release_recv_buf); + + klass->release_recv_buf(rcc, type, size, msg); +} + +int red_channel_handle_migrate_flush_mark(RedChannel *self, RedChannelClient *rcc) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->handle_migrate_flush_mark, FALSE); + + return klass->handle_migrate_flush_mark(rcc); +} + +int red_channel_handle_migrate_data(RedChannel *self, RedChannelClient *rcc, + uint32_t size, void *message) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->handle_migrate_data, FALSE); + + return klass->handle_migrate_data(rcc, size, message); +} + +uint64_t red_channel_handle_migrate_data_get_serial(RedChannel *self, + RedChannelClient *rcc, + uint32_t size, void *message) +{ + RedChannelClass *klass = RED_CHANNEL_GET_CLASS(self); + g_return_val_if_fail(klass->handle_migrate_data_get_serial, 0); + + return klass->handle_migrate_data_get_serial(rcc, size, message); +} + +IncomingHandlerInterface* red_channel_get_incoming_handler(RedChannel *self) +{ + return &self->priv->incoming_cb; +} + +OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel *self) +{ + return &self->priv->outgoing_cb; +} + +void red_channel_reset_thread_id(RedChannel *self) +{ + self->priv->thread_id = pthread_self(); +} + +RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self) +{ + return &self->priv->local_caps; } diff --git a/server/red-channel.h b/server/red-channel.h index 8818a043..bd0751c0 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,82 @@ 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; +#define RED_TYPE_CHANNEL red_channel_get_type() - int64_t roundtrip; -} RedChannelClientLatencyMonitor; +#define RED_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHANNEL, RedChannel)) +#define RED_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_CHANNEL, RedChannelClass)) +#define RED_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_CHANNEL)) +#define RED_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_CHANNEL)) +#define RED_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHANNEL, RedChannelClass)) -typedef struct RedChannelClientConnectivityMonitor { - int state; - uint32_t out_bytes; - uint32_t in_bytes; - uint32_t timeout; - SpiceTimer *timer; -} RedChannelClientConnectivityMonitor; - -struct RedChannel { - uint32_t type; - uint32_t id; - - uint32_t refs; - - RingItem link; // channels link for reds - - const SpiceCoreInterfaceInternal *core; - int handle_acks; - - // RedChannel will hold only connected channel clients (logic - when pushing pipe item to all channel clients, there - // is no need to go over disconnect clients) - // . While client will hold the channel clients till it is destroyed - // and then it will destroy them as well. - // However RCC still holds a reference to the Channel. - // Maybe replace these logic with ref count? - // TODO: rename to 'connected_clients'? - GList *clients; +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 +225,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 +288,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 +303,35 @@ 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); +uint8_t* red_channel_alloc_recv_buf(RedChannel *self, RedChannelClient *rcc, + uint16_t type, uint32_t size); +void red_channel_release_recv_buf(RedChannel *self, RedChannelClient *rcc, + uint16_t type, uint32_t size, uint8_t *msg); +int red_channel_handle_migrate_flush_mark(RedChannel *self, RedChannelClient *rcc); +int red_channel_handle_migrate_data(RedChannel *self, RedChannelClient *rcc, + uint32_t size, void *message); +uint64_t red_channel_handle_migrate_data_get_serial(RedChannel *self, + RedChannelClient *rcc, + uint32_t size, void *message); +void red_channel_reset_thread_id(RedChannel *self); +StatNodeRef red_channel_get_stat_node(RedChannel *channel); + +/* FIXME: do these even need to be in RedChannel? It's really only used in + * RedChannelClient. Needs refactoring */ +IncomingHandlerInterface* red_channel_get_incoming_handler(RedChannel *self); +OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel *self); + +RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self); struct RedClient { RedsState *reds; @@ -419,4 +405,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 e517b41c..2d4ca3fd 100644 --- a/server/red-qxl.c +++ b/server/red-qxl.c @@ -81,7 +81,7 @@ static void red_qxl_set_display_peer(RedChannel *channel, RedClient *client, Dispatcher *dispatcher; spice_debug("%s", ""); - dispatcher = (Dispatcher *)channel->data; + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); payload.client = client; payload.stream = stream; payload.migration = migration; @@ -108,7 +108,7 @@ static void red_qxl_disconnect_display_peer(RedChannelClient *rcc) return; } - dispatcher = (Dispatcher *)channel->data; + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); spice_printerr(""); payload.rcc = rcc; @@ -125,11 +125,14 @@ static void red_qxl_display_migrate(RedChannelClient *rcc) RedWorkerMessageDisplayMigrate payload; Dispatcher *dispatcher; RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type, id; + if (!channel) { return; } - dispatcher = (Dispatcher *)channel->data; - spice_printerr("channel type %u id %u", channel->type, channel->id); + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); + spice_printerr("channel type %u id %u", type, id); payload.rcc = rcc; dispatcher_send_message(dispatcher, RED_WORKER_MESSAGE_DISPLAY_MIGRATE, @@ -142,7 +145,7 @@ static void red_qxl_set_cursor_peer(RedChannel *channel, RedClient *client, Reds uint32_t *caps) { RedWorkerMessageCursorConnect payload = {0,}; - Dispatcher *dispatcher = (Dispatcher *)channel->data; + Dispatcher *dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); spice_printerr(""); payload.client = client; payload.stream = stream; @@ -170,7 +173,7 @@ static void red_qxl_disconnect_cursor_peer(RedChannelClient *rcc) return; } - dispatcher = (Dispatcher *)channel->data; + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); spice_printerr(""); payload.rcc = rcc; @@ -184,12 +187,14 @@ static void red_qxl_cursor_migrate(RedChannelClient *rcc) RedWorkerMessageCursorMigrate payload; Dispatcher *dispatcher; RedChannel *channel = red_channel_client_get_channel(rcc); + uint32_t type, id; if (!channel) { return; } - dispatcher = (Dispatcher *)channel->data; - spice_printerr("channel type %u id %u", channel->type, channel->id); + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher"); + spice_printerr("channel type %u id %u", type, id); payload.rcc = rcc; dispatcher_send_message(dispatcher, RED_WORKER_MESSAGE_CURSOR_MIGRATE, diff --git a/server/red-replay-qxl.c b/server/red-replay-qxl.c index 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 848619aa..8f1f1519 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 @@ -540,12 +536,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) @@ -662,7 +658,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); @@ -1369,6 +1365,7 @@ RedWorker* red_worker_new(QXLInstance *qxl, 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 @@ -1376,11 +1373,11 @@ 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); @@ -1397,8 +1394,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 370240fa..d41ebc6b 100644 --- a/server/red-worker.h +++ b/server/red-worker.h @@ -31,6 +31,4 @@ RedWorker* red_worker_new(QXLInstance *qxl, bool red_worker_run(RedWorker *worker); SpiceCoreInterfaceInternal* red_worker_get_core_interface(RedWorker *worker); -void red_drawable_unref(RedDrawable *red_drawable); - #endif diff --git a/server/reds-private.h b/server/reds-private.h index 36ef6c0a..7fc99cc1 100644 --- a/server/reds-private.h +++ b/server/reds-private.h @@ -122,8 +122,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 8dff12db..e6658f04 100644 --- a/server/reds.c +++ b/server/reds.c @@ -456,15 +456,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_append(reds->channels, channel); } void reds_unregister_channel(RedsState *reds, RedChannel *channel) { - if (ring_item_is_linked(&channel->link)) { - ring_remove(&channel->link); - reds->num_of_channels--; + if (g_list_find(reds->channels, channel)) { + reds->channels = g_list_remove(reds->channels, channel); } else { spice_warning("not found"); } @@ -472,11 +470,13 @@ void reds_unregister_channel(RedsState *reds, RedChannel *channel) static RedChannel *reds_find_channel(RedsState *reds, uint32_t type, uint32_t id) { - RingItem *now; + GList *l; - RING_FOREACH(now, &reds->channels) { - RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link); - if (channel->type == type && channel->id == id) { + for (l = reds->channels; l != NULL; l = l->next) { + RedChannel *channel = l->data; + uint32_t this_type, this_id; + g_object_get(channel, "channel-type", &this_type, "id", &this_id, NULL); + if (this_type == type && this_id == id) { return channel; } } @@ -740,7 +740,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; + GList *l; int used_channels = 0; - RING_FOREACH(now, &reds->channels) { - RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link); + for (l = reds->channels; l != NULL; l = l->next) { + uint32_t type, id; + RedChannel *channel = l->data; if (reds->num_clients > 1 && !channel_supports_multiple_clients(channel)) { continue; } - channels_info->channels[used_channels].type = channel->type; - channels_info->channels[used_channels].id = channel->id; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); + channels_info->channels[used_channels].type = type; + channels_info->channels[used_channels].id = id; used_channels++; } channels_info->num_of_channels = used_channels; - if (used_channels != reds->num_of_channels) { - spice_warning("sent %d out of %d", used_channels, reds->num_of_channels); + if (used_channels != g_list_length(reds->channels)) { + spice_warning("sent %d out of %d", used_channels, g_list_length(reds->channels)); } } @@ -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); @@ -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); } /* @@ -3104,7 +3108,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 @@ -3428,7 +3432,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; @@ -4088,7 +4092,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..40048a99 100644 --- a/server/smartcard.c +++ b/server/smartcard.c @@ -49,6 +49,61 @@ // Maximal length of APDU #define APDUBufSize 270 +#define RED_TYPE_SMARTCARD_CHANNEL red_smartcard_channel_get_type() + +#define RED_SMARTCARD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannel)) +#define RED_SMARTCARD_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelClass)) +#define RED_IS_SMARTCARD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_SMARTCARD_CHANNEL)) +#define RED_IS_SMARTCARD_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_SMARTCARD_CHANNEL)) +#define RED_SMARTCARD_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelClass)) + +typedef struct RedSmartcardChannel RedSmartcardChannel; +typedef struct RedSmartcardChannelClass RedSmartcardChannelClass; +typedef struct RedSmartcardChannelPrivate RedSmartcardChannelPrivate; + +struct RedSmartcardChannel +{ + RedChannel parent; + + RedSmartcardChannelPrivate *priv; +}; + +struct RedSmartcardChannelClass +{ + RedChannelClass parent_class; +}; + +GType red_smartcard_channel_get_type(void) G_GNUC_CONST; + +G_DEFINE_TYPE(RedSmartcardChannel, red_smartcard_channel, RED_TYPE_CHANNEL) + +#define SMARTCARD_CHANNEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), RED_TYPE_SMARTCARD_CHANNEL, RedSmartcardChannelPrivate)) + +struct RedSmartcardChannelPrivate +{ + gpointer padding; +}; + +static void +red_smartcard_channel_init(RedSmartcardChannel *self) +{ + self->priv = SMARTCARD_CHANNEL_PRIVATE(self); +} + +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)) @@ -74,10 +129,6 @@ typedef struct RedMsgItem { static RedMsgItem *smartcard_get_vsc_msg_item(RedChannelClient *rcc, VSCMsgHeader *vheader); static void smartcard_channel_client_pipe_add_push(RedChannelClient *rcc, RedPipeItem *item); -typedef struct SmartCardChannel { - RedChannel base; -} SmartCardChannel; - static struct Readers { uint32_t num; SpiceCharDeviceInstance* sin[SMARTCARD_MAX_READERS]; @@ -519,41 +570,51 @@ 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); + + g_type_class_add_private(klass, sizeof(RedSmartcardChannelPrivate)); + + object_class->constructed = red_smartcard_channel_constructed; + + channel_class->handle_message = smartcard_channel_client_handle_message, + + channel_class->config_socket = smartcard_channel_client_config_socket; + channel_class->on_disconnect = smartcard_channel_client_on_disconnect; + channel_class->send_item = smartcard_channel_send_item; + channel_class->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 4edf8ed3..7f2246cb 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" @@ -217,6 +218,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"); @@ -224,8 +226,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); @@ -997,12 +1000,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); @@ -1057,6 +1062,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_start(SpicePlaybackInstance *sin) sin->st->worker.active = 1; if (!channel) return; + spice_assert(!playback_channel->base.active); reds_disable_mm_time(snd_channel_get_server(channel)); playback_channel->base.active = TRUE; @@ -1076,6 +1082,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin) sin->st->worker.active = 0; if (!channel) return; + spice_assert(playback_channel->base.active); reds_enable_mm_time(snd_channel_get_server(channel)); playback_channel->base.active = FALSE; @@ -1144,7 +1151,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, @@ -1212,7 +1221,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); @@ -1241,7 +1250,7 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt SPICE_PLAYBACK_CAP_CELT_0_5_1); int client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client, SPICE_PLAYBACK_CAP_OPUS); - int playback_compression = reds_config_get_playback_compression(channel->reds); + int playback_compression = reds_config_get_playback_compression(red_channel_get_server(channel)); int desired_mode = snd_desired_audio_mode(playback_compression, st->frequency, client_can_celt, client_can_opus); playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW; @@ -1270,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); @@ -1461,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); @@ -1499,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) { @@ -1541,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; @@ -1570,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; @@ -1627,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..92c7a6b1 100644 --- a/server/spicevmc.c +++ b/server/spicevmc.c @@ -57,16 +57,6 @@ typedef struct RedVmcPipeItem { uint32_t buf_used; } RedVmcPipeItem; -typedef struct SpiceVmcState { - RedChannel channel; /* Must be the first item */ - RedChannelClient *rcc; - RedCharDevice *chardev; - SpiceCharDeviceInstance *chardev_sin; - RedVmcPipeItem *pipe_item; - RedCharDeviceWriteBuffer *recv_from_client_buf; - uint8_t port_opened; -} SpiceVmcState; - #define RED_TYPE_CHAR_DEVICE_SPICEVMC red_char_device_spicevmc_get_type() #define RED_CHAR_DEVICE_SPICEVMC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmc)) @@ -94,6 +84,207 @@ static RedCharDevice *red_char_device_spicevmc_new(SpiceCharDeviceInstance *sin, G_DEFINE_TYPE(RedCharDeviceSpiceVmc, red_char_device_spicevmc, RED_TYPE_CHAR_DEVICE) +#define RED_CHAR_DEVICE_SPICEVMC_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RED_TYPE_CHAR_DEVICE_SPICEVMC, RedCharDeviceSpiceVmcPrivate)) + +#define SPICE_TYPE_VMC_STATE spice_vmc_state_get_type() + +#define SPICE_VMC_STATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SPICE_TYPE_VMC_STATE, SpiceVmcState)) +#define SPICE_VMC_STATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SPICE_TYPE_VMC_STATE, SpiceVmcStateClass)) +#define SPICE_IS_VMC_STATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SPICE_TYPE_VMC_STATE)) +#define SPICE_IS_VMC_STATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SPICE_TYPE_VMC_STATE)) +#define SPICE_VMC_STATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SPICE_TYPE_VMC_STATE, SpiceVmcStateClass)) + +typedef struct SpiceVmcState SpiceVmcState; +typedef struct SpiceVmcStateClass SpiceVmcStateClass; +typedef struct SpiceVmcStatePrivate SpiceVmcStatePrivate; + +struct SpiceVmcState +{ + RedChannel parent; + + SpiceVmcStatePrivate *priv; +}; + +struct SpiceVmcStateClass +{ + RedChannelClass parent_class; +}; + +GType spice_vmc_state_get_type(void) G_GNUC_CONST; + +G_DEFINE_TYPE(SpiceVmcState, spice_vmc_state, RED_TYPE_CHANNEL) + + +#define 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) + +#define VMC_STATE_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), SPICE_TYPE_VMC_STATE, SpiceVmcStatePrivate)) + +struct SpiceVmcStatePrivate +{ + RedChannelClient *rcc; + RedCharDevice *chardev; + SpiceCharDeviceInstance *chardev_sin; + RedVmcPipeItem *pipe_item; + RedCharDeviceWriteBuffer *recv_from_client_buf; + uint8_t port_opened; +}; + +enum { + PROP0, + PROP_DEVICE_INSTANCE +}; + +static void +spice_vmc_state_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + SpiceVmcState *self = SPICE_VMC_STATE(object); + + switch (property_id) + { + case PROP_DEVICE_INSTANCE: + g_value_set_pointer(value, self->priv->chardev_sin); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +spice_vmc_state_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + SpiceVmcState *self = SPICE_VMC_STATE(object); + + switch (property_id) + { + case PROP_DEVICE_INSTANCE: + self->priv->chardev_sin = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void spicevmc_connect(RedChannel *channel, RedClient *client, + RedsStream *stream, int migration, int num_common_caps, + uint32_t *common_caps, int num_caps, uint32_t *caps); + +static void +spice_vmc_state_constructed(GObject *object) +{ + SpiceVmcState *self = SPICE_VMC_STATE(object); + ClientCbs client_cbs = { NULL, }; + RedsState *reds = red_channel_get_server(RED_CHANNEL(self)); + + G_OBJECT_CLASS(spice_vmc_state_parent_class)->constructed(object); + + client_cbs.connect = spicevmc_connect; + red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, NULL); + +#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->priv->chardev = red_char_device_spicevmc_new(self->priv->chardev_sin, + reds, self); + + reds_register_channel(reds, RED_CHANNEL(self)); +} + +static void +spice_vmc_state_init(SpiceVmcState *self) +{ + self->priv = VMC_STATE_PRIVATE(self); +} + +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; @@ -128,7 +319,7 @@ static RedVmcPipeItem* try_compress_lz4(SpiceVmcState *state, int n, RedVmcPipeI RedVmcPipeItem *msg_item_compressed; int compressed_data_count; - if (reds_stream_get_family(red_channel_client_get_stream(state->rcc)) == AF_UNIX) { + if (reds_stream_get_family(red_channel_client_get_stream(state->priv->rcc)) == AF_UNIX) { /* AF_LOCAL - data will not be compressed */ return NULL; } @@ -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; } @@ -171,18 +362,18 @@ static RedPipeItem *spicevmc_chardev_read_msg_from_dev(SpiceCharDeviceInstance * sif = spice_char_device_get_interface(sin); - if (!state->rcc) { + if (!state->priv->rcc) { return NULL; } - if (!state->pipe_item) { + if (!state->priv->pipe_item) { msg_item = spice_new0(RedVmcPipeItem, 1); msg_item->type = SPICE_DATA_COMPRESSION_TYPE_NONE; red_pipe_item_init(&msg_item->base, RED_PIPE_ITEM_TYPE_SPICEVMC_DATA); } else { - spice_assert(state->pipe_item->buf_used == 0); - msg_item = state->pipe_item; - state->pipe_item = NULL; + spice_assert(state->priv->pipe_item->buf_used == 0); + msg_item = state->priv->pipe_item; + state->priv->pipe_item = NULL; } n = sif->read(sin, msg_item->buf, @@ -201,7 +392,7 @@ static RedPipeItem *spicevmc_chardev_read_msg_from_dev(SpiceCharDeviceInstance * msg_item->buf_used = n; return &msg_item->base; } else { - state->pipe_item = msg_item; + state->priv->pipe_item = msg_item; return NULL; } } @@ -212,26 +403,26 @@ static void spicevmc_chardev_send_msg_to_client(RedPipeItem *msg, { SpiceVmcState *state = opaque; - spice_assert(red_channel_client_get_client(state->rcc) == client); + spice_assert(red_channel_client_get_client(state->priv->rcc) == client); red_pipe_item_ref(msg); - red_channel_client_pipe_add_push(state->rcc, msg); + red_channel_client_pipe_add_push(state->priv->rcc, 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) { SpiceVmcState *state = spicevmc_red_channel_client_get_state(rcc); - SpiceCharDeviceInstance *sin = state->chardev_sin; + SpiceCharDeviceInstance *sin = state->priv->chardev_sin; RedPortInitPipeItem *item = spice_malloc(sizeof(RedPortInitPipeItem)); red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_PORT_INIT); item->name = strdup(sin->portname); - item->opened = state->port_opened; + item->opened = state->priv->port_opened; red_channel_client_pipe_add_push(rcc, &item->base); } @@ -256,10 +447,10 @@ static void spicevmc_char_dev_remove_client(RedClient *client, void *opaque) SpiceVmcState *state = opaque; spice_printerr("vmc state %p, client %p", state, client); - spice_assert(state->rcc && - red_channel_client_get_client(state->rcc) == client); + spice_assert(state->priv->rcc && + red_channel_client_get_client(state->priv->rcc) == client); - red_channel_client_shutdown(state->rcc); + red_channel_client_shutdown(state->priv->rcc); } static int spicevmc_red_channel_client_config_socket(RedChannelClient *rcc) @@ -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) { @@ -294,14 +487,14 @@ static void spicevmc_red_channel_client_on_disconnect(RedChannelClient *rcc) state = spicevmc_red_channel_client_get_state(rcc); /* partial message which wasn't pushed to device */ - red_char_device_write_buffer_release(state->chardev, &state->recv_from_client_buf); + red_char_device_write_buffer_release(state->priv->chardev, &state->priv->recv_from_client_buf); - if (state->chardev) { - if (red_char_device_client_exists(state->chardev, client)) { - red_char_device_client_remove(state->chardev, client); + if (state->priv->chardev) { + if (red_char_device_client_exists(state->priv->chardev, client)) { + red_char_device_client_remove(state->priv->chardev, client); } else { spice_printerr("client %p have already been removed from char dev %p", - client, state->chardev); + client, state->priv->chardev); } } @@ -310,10 +503,10 @@ static void spicevmc_red_channel_client_on_disconnect(RedChannelClient *rcc) if (!red_channel_client_is_destroying(rcc)) red_channel_client_destroy(rcc); - state->rcc = NULL; - sif = spice_char_device_get_interface(state->chardev_sin); + state->priv->rcc = NULL; + sif = spice_char_device_get_interface(state->priv->chardev_sin); if (sif->state) { - sif->state(state->chardev_sin, 0); + sif->state(state->priv->chardev_sin, 0); } } @@ -342,7 +535,7 @@ static int spicevmc_channel_client_handle_migrate_data(RedChannelClient *rcc, spice_error("bad header"); return FALSE; } - return red_char_device_restore(state->chardev, &mig_data->base); + return red_char_device_restore(state->priv->chardev, &mig_data->base); } static int handle_compressed_msg(SpiceVmcState *state, RedChannelClient *rcc, @@ -352,7 +545,7 @@ static int handle_compressed_msg(SpiceVmcState *state, RedChannelClient *rcc, int decompressed_size; RedCharDeviceWriteBuffer *write_buf; - write_buf = red_char_device_write_buffer_get(state->chardev, + write_buf = red_char_device_write_buffer_get(state->priv->chardev, red_channel_client_get_client(rcc), compressed_data_msg->uncompressed_size); if (!write_buf) { @@ -372,16 +565,16 @@ static int handle_compressed_msg(SpiceVmcState *state, RedChannelClient *rcc, #endif default: spice_warning("Invalid Compression Type"); - red_char_device_write_buffer_release(state->chardev, &write_buf); + red_char_device_write_buffer_release(state->priv->chardev, &write_buf); return FALSE; } if (decompressed_size != compressed_data_msg->uncompressed_size) { spice_warning("Decompression Error"); - red_char_device_write_buffer_release(state->chardev, &write_buf); + red_char_device_write_buffer_release(state->priv->chardev, &write_buf); return FALSE; } write_buf->buf_used = decompressed_size; - red_char_device_write_buffer_add(state->chardev, write_buf); + red_char_device_write_buffer_add(state->priv->chardev, write_buf); return TRUE; } @@ -396,14 +589,14 @@ static int spicevmc_red_channel_client_handle_message_parsed(RedChannelClient *r SpiceCharDeviceInterface *sif; state = spicevmc_red_channel_client_get_state(rcc); - sif = spice_char_device_get_interface(state->chardev_sin); + sif = spice_char_device_get_interface(state->priv->chardev_sin); switch (type) { case SPICE_MSGC_SPICEVMC_DATA: - spice_assert(state->recv_from_client_buf->buf == msg); - state->recv_from_client_buf->buf_used = size; - red_char_device_write_buffer_add(state->chardev, state->recv_from_client_buf); - state->recv_from_client_buf = NULL; + spice_assert(state->priv->recv_from_client_buf->buf == msg); + state->priv->recv_from_client_buf->buf_used = size; + red_char_device_write_buffer_add(state->priv->chardev, state->priv->recv_from_client_buf); + state->priv->recv_from_client_buf = NULL; break; case SPICE_MSGC_SPICEVMC_COMPRESSED_DATA: return handle_compressed_msg(state, rcc, (SpiceMsgCompressedData*)msg); @@ -414,7 +607,7 @@ static int spicevmc_red_channel_client_handle_message_parsed(RedChannelClient *r return FALSE; } if (sif->base.minor_version >= 2 && sif->event != NULL) - sif->event(state->chardev_sin, *(uint8_t*)msg); + sif->event(state->priv->chardev_sin, *(uint8_t*)msg); break; default: return red_channel_client_handle_message(rcc, size, type, (uint8_t*)msg); @@ -434,16 +627,16 @@ static uint8_t *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, switch (type) { case SPICE_MSGC_SPICEVMC_DATA: - assert(!state->recv_from_client_buf); + assert(!state->priv->recv_from_client_buf); - state->recv_from_client_buf = red_char_device_write_buffer_get(state->chardev, - client, - size); - if (!state->recv_from_client_buf) { + state->priv->recv_from_client_buf = red_char_device_write_buffer_get(state->priv->chardev, + client, + size); + if (!state->priv->recv_from_client_buf) { spice_error("failed to allocate write buffer"); return NULL; } - return state->recv_from_client_buf->buf; + return state->priv->recv_from_client_buf->buf; default: return spice_malloc(size); @@ -463,7 +656,8 @@ static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc, switch (type) { case SPICE_MSGC_SPICEVMC_DATA: /* buffer wasn't pushed to device */ - red_char_device_write_buffer_release(state->chardev, &state->recv_from_client_buf); + red_char_device_write_buffer_release(state->priv->chardev, + &state->priv->recv_from_client_buf); break; default: free(msg); @@ -502,7 +696,7 @@ static void spicevmc_red_channel_send_migrate_data(RedChannelClient *rcc, spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SPICEVMC_MAGIC); spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SPICEVMC_VERSION); - red_char_device_migrate_data_marshall(state->chardev, m); + red_char_device_migrate_data_marshall(state->priv->chardev, m); } static void spicevmc_red_channel_send_port_init(RedChannelClient *rcc, @@ -556,6 +750,63 @@ 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); + + g_type_class_add_private(klass, sizeof(SpiceVmcStatePrivate)); + + object_class->get_property = spice_vmc_state_get_property; + object_class->set_property = spice_vmc_state_set_property; + object_class->constructed = spice_vmc_state_constructed; + + channel_class->handle_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 +815,15 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client, SpiceVmcState *state; SpiceCharDeviceInstance *sin; SpiceCharDeviceInterface *sif; + uint32_t type, id; - state = SPICE_CONTAINEROF(channel, SpiceVmcState, channel); - sin = state->chardev_sin; + state = SPICE_VMC_STATE(channel); + sin = state->priv->chardev_sin; + g_object_get(channel, "channel-type", &type, "id", &id, NULL); - if (state->rcc) { + if (state->priv->rcc) { spice_printerr("channel client %d:%d (%p) already connected, refusing second connection", - channel->type, channel->id, state->rcc); + type, id, state->priv->rcc); // TODO: notify client in advance about the in use channel using // SPICE_MSG_MAIN_CHANNEL_IN_USE (for example) reds_stream_free(stream); @@ -582,21 +835,21 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client, if (!rcc) { return; } - state->rcc = rcc; + state->priv->rcc = rcc; red_channel_client_ack_zero_messages_window(rcc); if (strcmp(sin->subtype, "port") == 0) { spicevmc_port_send_init(rcc); } - if (!red_char_device_client_add(state->chardev, client, FALSE, 0, ~0, ~0, + if (!red_char_device_client_add(state->priv->chardev, client, FALSE, 0, ~0, ~0, red_channel_client_is_waiting_for_migrate_data(rcc))) { spice_warning("failed to add client to spicevmc"); red_channel_client_disconnect(rcc); return; } - sif = spice_char_device_get_interface(state->chardev_sin); + sif = spice_char_device_get_interface(state->priv->chardev_sin); if (sif->state) { sif->state(sin, 1); } @@ -606,39 +859,9 @@ RedCharDevice *spicevmc_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin, uint8_t channel_type) { - static uint8_t id[256] = { 0, }; - SpiceVmcState *state; - ChannelCbs channel_cbs = { NULL, }; - ClientCbs client_cbs = { NULL, }; + 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; + return state->priv->chardev; } /* Must be called from RedClient handling thread. */ @@ -649,16 +872,16 @@ void spicevmc_device_disconnect(RedsState *reds, SpiceCharDeviceInstance *sin) /* FIXME */ state = (SpiceVmcState *)red_char_device_opaque_get((RedCharDevice*)sin->st); - red_char_device_write_buffer_release(state->chardev, &state->recv_from_client_buf); - + red_char_device_write_buffer_release(state->priv->chardev, + &state->priv->recv_from_client_buf); /* FIXME */ red_char_device_destroy((RedCharDevice*)sin->st); - state->chardev = NULL; + state->priv->chardev = NULL; sin->st = NULL; - reds_unregister_channel(reds, &state->channel); - free(state->pipe_item); - red_channel_destroy(&state->channel); + reds_unregister_channel(reds, RED_CHANNEL(state)); + free(state->priv->pipe_item); + red_channel_destroy(RED_CHANNEL(state)); } SPICE_GNUC_VISIBLE void spice_server_port_event(SpiceCharDeviceInstance *sin, uint8_t event) @@ -673,16 +896,16 @@ SPICE_GNUC_VISIBLE void spice_server_port_event(SpiceCharDeviceInstance *sin, ui /* FIXME */ state = (SpiceVmcState *)red_char_device_opaque_get((RedCharDevice*)sin->st); if (event == SPICE_PORT_EVENT_OPENED) { - state->port_opened = TRUE; + state->priv->port_opened = TRUE; } else if (event == SPICE_PORT_EVENT_CLOSED) { - state->port_opened = FALSE; + state->priv->port_opened = FALSE; } - if (state->rcc == NULL) { + if (state->priv->rcc == NULL) { return; } - spicevmc_port_send_event(state->rcc, event); + spicevmc_port_send_event(state->priv->rcc, event); } static void diff --git a/server/stream.c b/server/stream.c index b28c58bc..757e0b8a 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" #define FPS_TEST_INTERVAL 1 #define FOREACH_STREAMS(display, item) \ @@ -197,7 +197,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; } diff --git a/server/stream.h b/server/stream.h index 77223c88..a415e434 100644 --- a/server/stream.h +++ b/server/stream.h @@ -42,9 +42,6 @@ #define RED_STREAM_DEFAULT_LOW_START_BIT_RATE (2.5 * 1024 * 1024) // 2.5Mbps #define MAX_FPS 30 -/* move back to display_channel once struct private */ -typedef struct DisplayChannel DisplayChannel; - typedef struct Stream Stream; typedef struct RedStreamActivateReportItem { |