red-channel-client: Remove GObject type

Make all RedChannelClient hierarchy a C++ class.
This allows to use virtual methods.
Added a normal contructor instead or properties and g_object_new.

As we remove GObject conversion macros I added a macro XXX_CAST
to create a function to replace the old macro.
They will be removed when more type safety is introduced.

There's a new SPICE_CXX_GLIB_ALLOCATOR macro in red-common.h.
This macro, added to a class define the class allocator allowing
to use, in this case, GLib for allocation. This to avoid C++ library
dependency and to initialize all structure to 0 (not all fields
are manually initialized, will be improved with more encapsulation).

Currently the methods are mainly public, access will be modified
when more encapsulation (all functions in method) are done.

Some classes are now defined in the header, C++ uses access to
limit accessibility but for efficiency and type safety/inline and
other features require types to be defined in the headers.

Some fields were moved from XxxPrivate structure to class, C++
has accessibility.

Many destructors are defined as protected to forbid the use of
stack, this as these objects uses internal reference counting
to have normal pointers. Maybe in the future pointers like
std::shared_ptr could be used instead.

Reference counting is now implemented very easily using atomic
operations.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
This commit is contained in:
Frediano Ziglio 2019-05-19 22:00:39 +01:00 committed by Frediano Ziglio
parent 38cd152952
commit 176970f3f1
26 changed files with 593 additions and 1294 deletions

View File

@ -22,8 +22,6 @@
#include "dcc.h"
#include "red-client.h"
#define CHANNEL_RECEIVE_BUF_SIZE 1024
struct CommonGraphicsChannelPrivate
{
int during_target_migrate; /* TRUE when the client that is associated with the channel
@ -36,43 +34,32 @@ struct CommonGraphicsChannelPrivate
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(CommonGraphicsChannel, common_graphics_channel,
RED_TYPE_CHANNEL)
struct CommonGraphicsChannelClientPrivate {
uint8_t recv_buf[CHANNEL_RECEIVE_BUF_SIZE];
};
G_DEFINE_TYPE_WITH_PRIVATE(CommonGraphicsChannelClient, common_graphics_channel_client,
RED_TYPE_CHANNEL_CLIENT)
static uint8_t *common_alloc_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size)
uint8_t *CommonGraphicsChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
{
CommonGraphicsChannelClient *common = COMMON_GRAPHICS_CHANNEL_CLIENT(rcc);
/* SPICE_MSGC_MIGRATE_DATA is the only client message whose size is dynamic */
if (type == SPICE_MSGC_MIGRATE_DATA) {
return (uint8_t *) g_malloc(size);
}
if (size > sizeof(common->priv->recv_buf)) {
spice_warning("unexpected message size %u (max is %zd)", size,
sizeof(common->priv->recv_buf));
if (size > sizeof(recv_buf)) {
spice_warning("unexpected message size %u (max is %zd)", size, sizeof(recv_buf));
return NULL;
}
return common->priv->recv_buf;
return recv_buf;
}
static void common_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size,
uint8_t* msg)
void CommonGraphicsChannelClient::release_recv_buf(uint16_t type, uint32_t size, uint8_t* msg)
{
if (type == SPICE_MSGC_MIGRATE_DATA) {
g_free(msg);
}
}
bool common_channel_client_config_socket(RedChannelClient *rcc)
bool CommonGraphicsChannelClient::config_socket()
{
RedClient *client = rcc->get_client();
RedClient *client = get_client();
MainChannelClient *mcc = red_client_get_main(client);
RedStream *stream = rcc->get_stream();
RedStream *stream = get_stream();
gboolean is_low_bandwidth;
// TODO - this should be dynamic, not one time at channel creation
@ -87,8 +74,8 @@ bool common_channel_client_config_socket(RedChannelClient *rcc)
red_stream_set_no_delay(stream, !is_low_bandwidth);
}
// TODO: move wide/narrow ack setting to red_channel.
rcc->ack_set_client_window(is_low_bandwidth ? WIDE_CLIENT_ACK_WINDOW : NARROW_CLIENT_ACK_WINDOW);
return TRUE;
ack_set_client_window(is_low_bandwidth ? WIDE_CLIENT_ACK_WINDOW : NARROW_CLIENT_ACK_WINDOW);
return true;
}
@ -112,20 +99,3 @@ gboolean common_graphics_channel_get_during_target_migrate(CommonGraphicsChannel
{
return self->priv->during_target_migrate;
}
static void
common_graphics_channel_client_init(CommonGraphicsChannelClient *self)
{
self->priv = (CommonGraphicsChannelClientPrivate*)
common_graphics_channel_client_get_instance_private(self);
}
static void
common_graphics_channel_client_class_init(CommonGraphicsChannelClientClass *klass)
{
RedChannelClientClass *client_class = RED_CHANNEL_CLIENT_CLASS(klass);
client_class->config_socket = common_channel_client_config_socket;
client_class->alloc_recv_buf = common_alloc_recv_buf;
client_class->release_recv_buf = common_release_recv_buf;
}

View File

@ -25,8 +25,6 @@
G_BEGIN_DECLS
bool common_channel_client_config_socket(RedChannelClient *rcc);
#define COMMON_CLIENT_TIMEOUT (NSEC_PER_SEC * 30)
#define TYPE_COMMON_GRAPHICS_CHANNEL common_graphics_channel_get_type()
@ -65,34 +63,19 @@ enum {
RED_PIPE_ITEM_TYPE_COMMON_LAST
};
#define TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT common_graphics_channel_client_get_type()
#define COMMON_GRAPHICS_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT, \
CommonGraphicsChannelClient))
#define COMMON_GRAPHICS_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT, \
CommonGraphicsChannelClientClass))
#define COMMON_IS_GRAPHICS_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT))
#define COMMON_IS_GRAPHICS_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT))
#define COMMON_GRAPHICS_CHANNEL_CLIENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT, \
CommonGraphicsChannelClientClass))
struct CommonGraphicsChannelClientPrivate;
struct CommonGraphicsChannelClient: public RedChannelClient
class CommonGraphicsChannelClient: public RedChannelClient
{
CommonGraphicsChannelClientPrivate *priv;
};
enum { CHANNEL_RECEIVE_BUF_SIZE = 1024 };
uint8_t recv_buf[CHANNEL_RECEIVE_BUF_SIZE];
struct CommonGraphicsChannelClientClass {
RedChannelClientClass parent_class;
};
public:
using RedChannelClient::RedChannelClient;
GType common_graphics_channel_client_get_type(void) G_GNUC_CONST;
protected:
virtual uint8_t *alloc_recv_buf(uint16_t type, uint32_t size) override;
virtual void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg) override;
virtual bool config_socket() override;
};
G_END_DECLS

View File

@ -35,32 +35,13 @@
struct CursorChannelClientPrivate
{
SPICE_CXX_GLIB_ALLOCATOR
RedCacheItem *cursor_cache[CURSOR_CACHE_HASH_SIZE];
Ring cursor_cache_lru;
long cursor_cache_available;
Ring cursor_cache_lru = { nullptr, nullptr };
long cursor_cache_available = 0;
};
G_DEFINE_TYPE_WITH_PRIVATE(CursorChannelClient, cursor_channel_client,
TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT)
static void cursor_channel_client_on_disconnect(RedChannelClient *rcc);
static void
cursor_channel_client_class_init(CursorChannelClientClass *klass)
{
RedChannelClientClass *client_class = RED_CHANNEL_CLIENT_CLASS(klass);
client_class->on_disconnect = cursor_channel_client_on_disconnect;
}
static void
cursor_channel_client_init(CursorChannelClient *self)
{
self->priv = (CursorChannelClientPrivate*) cursor_channel_client_get_instance_private(self);
ring_init(&self->priv->cursor_cache_lru);
self->priv->cursor_cache_available = CLIENT_CURSOR_CACHE_SIZE;
}
#define CLIENT_CURSOR_CACHE
#include "cache-item.tmpl.cpp"
#undef CLIENT_CURSOR_CACHE
@ -74,12 +55,9 @@ void cursor_channel_client_reset_cursor_cache(CursorChannelClient *ccc)
red_cursor_cache_reset(ccc, CLIENT_CURSOR_CACHE_SIZE);
}
static void cursor_channel_client_on_disconnect(RedChannelClient *rcc)
void CursorChannelClient::on_disconnect()
{
if (!rcc) {
return;
}
cursor_channel_client_reset_cursor_cache(CURSOR_CHANNEL_CLIENT(rcc));
cursor_channel_client_reset_cursor_cache(this);
}
void cursor_channel_client_migrate(RedChannelClient *rcc)
@ -90,20 +68,32 @@ void cursor_channel_client_migrate(RedChannelClient *rcc)
RedChannelClient::default_migrate(rcc);
}
CursorChannelClient::CursorChannelClient(RedChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps):
CommonGraphicsChannelClient(channel, client, stream, caps)
{
priv = new CursorChannelClientPrivate();
ring_init(&priv->cursor_cache_lru);
priv->cursor_cache_available = CLIENT_CURSOR_CACHE_SIZE;
}
CursorChannelClient::~CursorChannelClient()
{
delete priv;
}
CursorChannelClient* cursor_channel_client_new(CursorChannel *cursor, RedClient *client, RedStream *stream,
int mig_target,
RedChannelCapabilities *caps)
{
CursorChannelClient *rcc;
auto rcc = new CursorChannelClient(RED_CHANNEL(cursor), client, stream, caps);
rcc = (CursorChannelClient*)
g_initable_new(TYPE_CURSOR_CHANNEL_CLIENT,
NULL, NULL,
"channel", cursor,
"client", client,
"stream", stream,
"caps", caps,
NULL);
if (!rcc->init()) {
rcc->unref();
rcc = nullptr;
}
common_graphics_channel_set_during_target_migrate(COMMON_GRAPHICS_CHANNEL(cursor), mig_target);
return rcc;

View File

@ -19,8 +19,6 @@
#ifndef CURSOR_CHANNEL_CLIENT_H_
#define CURSOR_CHANNEL_CLIENT_H_
#include <glib-object.h>
#include "cache-item.h"
#include "red-common.h"
#include "red-channel-client.h"
@ -29,33 +27,21 @@
G_BEGIN_DECLS
#define TYPE_CURSOR_CHANNEL_CLIENT cursor_channel_client_get_type()
#define CURSOR_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_CURSOR_CHANNEL_CLIENT, CursorChannelClient))
#define CURSOR_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), TYPE_CURSOR_CHANNEL_CLIENT, CursorChannelClientClass))
#define IS_CURSOR_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_CURSOR_CHANNEL_CLIENT))
#define IS_CURSOR_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_CURSOR_CHANNEL_CLIENT))
#define CURSOR_CHANNEL_CLIENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_CURSOR_CHANNEL_CLIENT, CursorChannelClientClass))
struct CursorChannelClientPrivate;
struct CursorChannelClient final: public CommonGraphicsChannelClient
class CursorChannelClient final: public CommonGraphicsChannelClient
{
CursorChannelClientPrivate *priv;
protected:
~CursorChannelClient();
public:
CursorChannelClient(RedChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps);
virtual void on_disconnect() override;
CursorChannelClientPrivate *priv = nullptr;
};
struct CursorChannelClientClass
{
RedChannelClientClass parent_class;
};
GType cursor_channel_client_get_type(void) G_GNUC_CONST;
CursorChannelClient* cursor_channel_client_new(CursorChannel *cursor,
RedClient *client,
RedStream *stream,

View File

@ -193,6 +193,8 @@ static inline void red_marshall_inval(RedChannelClient *rcc,
spice_marshall_msg_cursor_inval_one(base_marshaller, &inval_one);
}
XXX_CAST(RedChannelClient, CursorChannelClient, CURSOR_CHANNEL_CLIENT);
static void cursor_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
{
SpiceMarshaller *m = rcc->get_marshaller();
@ -211,7 +213,7 @@ static void cursor_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_it
break;
case RED_PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE:
cursor_channel_client_reset_cursor_cache(ccc);
rcc->init_send_data(SPICE_MSG_CURSOR_INVAL_ALL);
ccc->init_send_data(SPICE_MSG_CURSOR_INVAL_ALL);
break;
default:
spice_error("invalid pipe item type");

View File

@ -27,22 +27,24 @@
struct DisplayChannelClientPrivate
{
uint32_t id;
SPICE_CXX_GLIB_ALLOCATOR
uint32_t id = 0;
SpiceImageCompression image_compression;
spice_wan_compression_t jpeg_state;
spice_wan_compression_t zlib_glz_state;
ImageEncoders encoders;
int expect_init;
int expect_init = 0;
PixmapCache *pixmap_cache;
uint32_t pixmap_cache_generation;
int pending_pixmaps_sync;
PixmapCache *pixmap_cache = nullptr;
uint32_t pixmap_cache_generation = 0;
int pending_pixmaps_sync = 0;
RedCacheItem *palette_cache[PALETTE_CACHE_HASH_SIZE];
Ring palette_cache_lru;
long palette_cache_available;
Ring palette_cache_lru = { nullptr, nullptr };
long palette_cache_available = CLIENT_PALETTE_CACHE_SIZE;
struct {
FreeList free_list;

View File

@ -24,6 +24,9 @@
#include "display-channel-private.h"
#include "red-qxl.h"
// XXX this should go away with virtual on the right class (client!)
XXX_CAST(RedChannelClient, DisplayChannelClient, DISPLAY_CHANNEL_CLIENT)
typedef enum {
FILL_BITS_TYPE_INVALID,
FILL_BITS_TYPE_CACHE,

View File

@ -25,157 +25,50 @@
#include "main-channel-client.h"
#include <spice-server-enums.h>
G_DEFINE_TYPE(DisplayChannelClient, display_channel_client, TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT)
#define DISPLAY_CLIENT_SHORT_TIMEOUT 15000000000ULL //nano
#define DISPLAY_FREE_LIST_DEFAULT_SIZE 128
enum
{
PROP0,
PROP_IMAGE_COMPRESSION,
PROP_JPEG_STATE,
PROP_ZLIB_GLZ_STATE
};
static bool dcc_config_socket(RedChannelClient *rcc);
static void dcc_on_disconnect(RedChannelClient *rcc);
static void
display_channel_client_get_property(GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
DisplayChannelClient *self = DISPLAY_CHANNEL_CLIENT(object);
switch (property_id)
{
case PROP_IMAGE_COMPRESSION:
g_value_set_enum(value, self->priv->image_compression);
break;
case PROP_JPEG_STATE:
g_value_set_enum(value, self->priv->jpeg_state);
break;
case PROP_ZLIB_GLZ_STATE:
g_value_set_enum(value, self->priv->zlib_glz_state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
}
static void
display_channel_client_set_property(GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
DisplayChannelClient *self = DISPLAY_CHANNEL_CLIENT(object);
switch (property_id)
{
case PROP_IMAGE_COMPRESSION:
self->priv->image_compression = (SpiceImageCompression) g_value_get_enum(value);
break;
case PROP_JPEG_STATE:
self->priv->jpeg_state = (spice_wan_compression_t) g_value_get_enum(value);
break;
case PROP_ZLIB_GLZ_STATE:
self->priv->zlib_glz_state = (spice_wan_compression_t) g_value_get_enum(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
}
static void dcc_init_stream_agents(DisplayChannelClient *dcc);
static void
display_channel_client_constructed(GObject *object)
DisplayChannelClient::DisplayChannelClient(DisplayChannel *display,
RedClient *client, RedStream *stream,
RedChannelCapabilities *caps,
uint32_t id,
SpiceImageCompression image_compression,
spice_wan_compression_t jpeg_state,
spice_wan_compression_t zlib_glz_state):
CommonGraphicsChannelClient(RED_CHANNEL(display), client, stream, caps, true)
{
DisplayChannelClient *self = DISPLAY_CHANNEL_CLIENT(object);
priv = new DisplayChannelClientPrivate;
G_OBJECT_CLASS(display_channel_client_parent_class)->constructed(object);
dcc_init_stream_agents(self);
image_encoders_init(&self->priv->encoders, &DCC_TO_DC(self)->priv->encoder_shared_data);
}
static void
display_channel_client_finalize(GObject *object)
{
DisplayChannelClient *self = DISPLAY_CHANNEL_CLIENT(object);
g_clear_pointer(&self->priv->preferred_video_codecs, g_array_unref);
g_clear_pointer(&self->priv->client_preferred_video_codecs, g_array_unref);
g_free(self->priv);
G_OBJECT_CLASS(display_channel_client_parent_class)->finalize(object);
}
static void
display_channel_client_class_init(DisplayChannelClientClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
RedChannelClientClass *client_class = RED_CHANNEL_CLIENT_CLASS(klass);
object_class->get_property = display_channel_client_get_property;
object_class->set_property = display_channel_client_set_property;
object_class->constructed = display_channel_client_constructed;
object_class->finalize = display_channel_client_finalize;
client_class->config_socket = dcc_config_socket;
client_class->on_disconnect = dcc_on_disconnect;
g_object_class_install_property(object_class,
PROP_IMAGE_COMPRESSION,
g_param_spec_enum("image-compression",
"image compression",
"Image compression type",
SPICE_TYPE_SPICE_IMAGE_COMPRESSION_T,
SPICE_IMAGE_COMPRESSION_INVALID,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property(object_class,
PROP_JPEG_STATE,
g_param_spec_enum("jpeg-state",
"jpeg state",
"JPEG compression state",
SPICE_TYPE_SPICE_WAN_COMPRESSION_T,
SPICE_WAN_COMPRESSION_INVALID,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property(object_class,
PROP_ZLIB_GLZ_STATE,
g_param_spec_enum("zlib-glz-state",
"zlib glz state",
"zlib glz state",
SPICE_TYPE_SPICE_WAN_COMPRESSION_T,
SPICE_WAN_COMPRESSION_INVALID,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
}
static void display_channel_client_init(DisplayChannelClient *self)
{
/* we need to allocate the private data manually here since
* g_type_class_add_private() doesn't support private structs larger than
* 64k */
self->priv = g_new0(DisplayChannelClientPrivate, 1);
ring_init(&self->priv->palette_cache_lru);
self->priv->palette_cache_available = CLIENT_PALETTE_CACHE_SIZE;
// XXX from display_channel_client_init, put somewhere else
ring_init(&priv->palette_cache_lru);
// todo: tune quality according to bandwidth
self->priv->encoders.jpeg_quality = 85;
priv->encoders.jpeg_quality = 85;
self->priv->send_data.free_list.res = (SpiceResourceList*)
priv->send_data.free_list.res = (SpiceResourceList*)
g_malloc(sizeof(SpiceResourceList) +
DISPLAY_FREE_LIST_DEFAULT_SIZE * sizeof(SpiceResourceID));
self->priv->send_data.free_list.res_size = DISPLAY_FREE_LIST_DEFAULT_SIZE;
priv->send_data.free_list.res_size = DISPLAY_FREE_LIST_DEFAULT_SIZE;
priv->image_compression = image_compression;
priv->jpeg_state = jpeg_state;
priv->zlib_glz_state = zlib_glz_state;
priv->id = id;
image_encoders_init(&priv->encoders, &DCC_TO_DC(this)->priv->encoder_shared_data);
dcc_init_stream_agents(this);
}
DisplayChannelClient::~DisplayChannelClient()
{
g_clear_pointer(&priv->preferred_video_codecs, g_array_unref);
g_clear_pointer(&priv->client_preferred_video_codecs, g_array_unref);
g_free(priv);
}
static RedSurfaceCreateItem *red_surface_create_item_new(RedChannel* channel,
@ -491,25 +384,15 @@ DisplayChannelClient *dcc_new(DisplayChannel *display,
spice_wan_compression_t zlib_glz_state)
{
DisplayChannelClient *dcc;
dcc = (DisplayChannelClient*)
g_initable_new(TYPE_DISPLAY_CHANNEL_CLIENT,
NULL, NULL,
"channel", display,
"client", client,
"stream", stream,
"monitor-latency", TRUE,
"caps", caps,
"image-compression", image_compression,
"jpeg-state", jpeg_state,
"zlib-glz-state", zlib_glz_state,
NULL);
auto dcc =
new DisplayChannelClient(display, client, stream, caps, display->priv->qxl->id,
image_compression, jpeg_state, zlib_glz_state);
if (!dcc->init()) {
dcc->unref();
dcc = nullptr;
}
spice_debug("New display (client %p) dcc %p stream %p", client, dcc, stream);
common_graphics_channel_set_during_target_migrate(display, mig_target);
if (dcc) {
dcc->priv->id = display->priv->qxl->id;
}
return dcc;
}
@ -695,6 +578,8 @@ RedPipeItem *dcc_gl_scanout_item_new(RedChannelClient *rcc, void *data, int num)
return &item->base;
}
XXX_CAST(RedChannelClient, DisplayChannelClient, DISPLAY_CHANNEL_CLIENT);
RedPipeItem *dcc_gl_draw_item_new(RedChannelClient *rcc, void *data, int num)
{
DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc);
@ -1374,28 +1259,25 @@ void dcc_set_max_stream_bit_rate(DisplayChannelClient *dcc, uint64_t rate)
dcc->priv->streams_max_bit_rate = rate;
}
static bool dcc_config_socket(RedChannelClient *rcc)
bool DisplayChannelClient::config_socket()
{
RedClient *client = rcc->get_client();
RedClient *client = get_client();
MainChannelClient *mcc = red_client_get_main(client);
DISPLAY_CHANNEL_CLIENT(rcc)->is_low_bandwidth = main_channel_client_is_low_bandwidth(mcc);
is_low_bandwidth = main_channel_client_is_low_bandwidth(mcc);
return common_channel_client_config_socket(rcc);
return CommonGraphicsChannelClient::config_socket();
}
static void dcc_on_disconnect(RedChannelClient *rcc)
void DisplayChannelClient::on_disconnect()
{
DisplayChannel *display;
DisplayChannelClient *dcc;
spice_debug("trace");
spice_return_if_fail(rcc != NULL);
dcc = DISPLAY_CHANNEL_CLIENT(rcc);
display = DCC_TO_DC(dcc);
display = DCC_TO_DC(this);
dcc_stop(dcc); // TODO: start/stop -> connect/disconnect?
dcc_stop(this); // TODO: start/stop -> connect/disconnect?
display_channel_compress_stats_print(display);
// this was the last channel client

View File

@ -19,8 +19,6 @@
#ifndef DCC_H_
#define DCC_H_
#include <glib-object.h>
#include "image-encoders.h"
#include "image-cache.h"
#include "pixmap-cache.h"
@ -29,34 +27,30 @@
G_BEGIN_DECLS
#define TYPE_DISPLAY_CHANNEL_CLIENT display_channel_client_get_type()
#define DISPLAY_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DISPLAY_CHANNEL_CLIENT, DisplayChannelClient))
#define DISPLAY_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DISPLAY_CHANNEL_CLIENT, DisplayChannelClientClass))
#define IS_DISPLAY_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DISPLAY_CHANNEL_CLIENT))
#define IS_DISPLAY_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DISPLAY_CHANNEL_CLIENT))
#define DISPLAY_CHANNEL_CLIENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DISPLAY_CHANNEL_CLIENT, DisplayChannelClientClass))
struct DisplayChannel;
struct DisplayChannelClientPrivate;
struct DisplayChannelClient final: public CommonGraphicsChannelClient
class DisplayChannelClient final: public CommonGraphicsChannelClient
{
protected:
~DisplayChannelClient();
public:
DisplayChannelClient(DisplayChannel *display,
RedClient *client, RedStream *stream,
RedChannelCapabilities *caps,
uint32_t id,
SpiceImageCompression image_compression,
spice_wan_compression_t jpeg_state,
spice_wan_compression_t zlib_glz_state);
virtual bool config_socket() override;
virtual void on_disconnect() override;
DisplayChannelClientPrivate *priv = nullptr;
int is_low_bandwidth;
DisplayChannelClientPrivate *priv;
};
struct DisplayChannelClientClass {
CommonGraphicsChannelClientClass parent_class;
};
GType display_channel_client_get_type(void) G_GNUC_CONST;
#define PALETTE_CACHE_HASH_SHIFT 8
#define PALETTE_CACHE_HASH_SIZE (1 << PALETTE_CACHE_HASH_SHIFT)
#define PALETTE_CACHE_HASH_MASK (PALETTE_CACHE_HASH_SIZE - 1)

View File

@ -21,6 +21,8 @@
#include "display-channel-private.h"
#include "red-qxl.h"
XXX_CAST(RedChannelClient, DisplayChannelClient, DISPLAY_CHANNEL_CLIENT);
G_DEFINE_TYPE(DisplayChannel, display_channel, TYPE_COMMON_GRAPHICS_CHANNEL)
static void display_channel_connect(RedChannel *channel, RedClient *client,

View File

@ -20,68 +20,25 @@
#include "migration-protocol.h"
#include "red-channel-client.h"
// TODO: RECEIVE_BUF_SIZE used to be the same for inputs_channel and main_channel
// since it was defined once in reds.c which contained both.
// Now that they are split we can give a more fitting value for inputs - what
// should it be?
#define REDS_AGENT_WINDOW_SIZE 10
#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
XXX_CAST(RedChannelClient, InputsChannelClient, INPUTS_CHANNEL_CLIENT);
// approximate max receive message size
#define RECEIVE_BUF_SIZE \
(4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
struct InputsChannelClientPrivate
uint8_t *InputsChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
{
uint16_t motion_count;
uint8_t recv_buf[RECEIVE_BUF_SIZE];
};
G_DEFINE_TYPE_WITH_PRIVATE(InputsChannelClient, inputs_channel_client, RED_TYPE_CHANNEL_CLIENT)
static uint8_t *
inputs_channel_client_alloc_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type, uint32_t size)
{
InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc);
if (size > sizeof(icc->priv->recv_buf)) {
red_channel_warning(rcc->get_channel(),
"error: too large incoming message");
if (size > sizeof(recv_buf)) {
red_channel_warning(get_channel(), "error: too large incoming message");
return NULL;
}
return icc->priv->recv_buf;
return recv_buf;
}
static void
inputs_channel_client_release_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type, uint32_t size, uint8_t *msg)
void InputsChannelClient::release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)
{
}
static void inputs_channel_client_on_disconnect(RedChannelClient *rcc)
void InputsChannelClient::on_disconnect()
{
if (!rcc) {
return;
}
inputs_release_keys(INPUTS_CHANNEL(rcc->get_channel()));
}
static void
inputs_channel_client_class_init(InputsChannelClientClass *klass)
{
RedChannelClientClass *client_class = RED_CHANNEL_CLIENT_CLASS(klass);
client_class->alloc_recv_buf = inputs_channel_client_alloc_msg_rcv_buf;
client_class->release_recv_buf = inputs_channel_client_release_msg_rcv_buf;
client_class->on_disconnect = inputs_channel_client_on_disconnect;
}
static void
inputs_channel_client_init(InputsChannelClient *self)
{
self->priv = (InputsChannelClientPrivate *) inputs_channel_client_get_instance_private(self);
inputs_release_keys(INPUTS_CHANNEL(get_channel()));
}
RedChannelClient* inputs_channel_client_create(RedChannel *channel,
@ -89,17 +46,11 @@ RedChannelClient* inputs_channel_client_create(RedChannel *channel,
RedStream *stream,
RedChannelCapabilities *caps)
{
RedChannelClient *rcc;
rcc = (RedChannelClient *)
g_initable_new(TYPE_INPUTS_CHANNEL_CLIENT,
NULL, NULL,
"channel", channel,
"client", client,
"stream", stream,
"caps", caps,
NULL);
auto rcc = new InputsChannelClient(channel, client, stream, caps);
if (!rcc->init()) {
delete rcc;
rcc = nullptr;
}
return rcc;
}
@ -113,16 +64,16 @@ void inputs_channel_client_send_migrate_data(RedChannelClient *rcc,
spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_MAGIC);
spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_VERSION);
spice_marshaller_add_uint16(m, icc->priv->motion_count);
spice_marshaller_add_uint16(m, icc->motion_count);
}
void inputs_channel_client_handle_migrate_data(InputsChannelClient *icc,
uint16_t motion_count)
{
icc->priv->motion_count = motion_count;
icc->motion_count = motion_count;
for (; icc->priv->motion_count >= SPICE_INPUT_MOTION_ACK_BUNCH;
icc->priv->motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH) {
for (; icc->motion_count >= SPICE_INPUT_MOTION_ACK_BUNCH;
icc->motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH) {
icc->pipe_add_type(RED_PIPE_ITEM_MOUSE_MOTION_ACK);
}
}
@ -131,9 +82,9 @@ void inputs_channel_client_on_mouse_motion(InputsChannelClient *icc)
{
InputsChannel *inputs_channel = INPUTS_CHANNEL(icc->get_channel());
if (++icc->priv->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
!inputs_channel_is_src_during_migrate(inputs_channel)) {
icc->pipe_add_type(RED_PIPE_ITEM_MOUSE_MOTION_ACK);
icc->priv->motion_count = 0;
icc->motion_count = 0;
}
}

View File

@ -18,31 +18,34 @@
#ifndef INPUTS_CHANNEL_CLIENT_H_
#define INPUTS_CHANNEL_CLIENT_H_
#include <glib-object.h>
#include "red-channel-client.h"
#include "inputs-channel.h"
G_BEGIN_DECLS
#define TYPE_INPUTS_CHANNEL_CLIENT inputs_channel_client_get_type()
// TODO: RECEIVE_BUF_SIZE used to be the same for inputs_channel and main_channel
// since it was defined once in reds.c which contained both.
// Now that they are split we can give a more fitting value for inputs - what
// should it be?
#define REDS_AGENT_WINDOW_SIZE 10
#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
#define INPUTS_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_INPUTS_CHANNEL_CLIENT, InputsChannelClient))
// approximate max receive message size
#define RECEIVE_BUF_SIZE \
(4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
struct InputsChannelClientPrivate;
struct InputsChannelClient final: public RedChannelClient
class InputsChannelClient final: public RedChannelClient
{
InputsChannelClientPrivate *priv;
};
uint8_t recv_buf[RECEIVE_BUF_SIZE];
public:
uint16_t motion_count; // XXX private
struct InputsChannelClientClass
{
RedChannelClientClass parent_class;
};
using RedChannelClient::RedChannelClient;
GType inputs_channel_client_get_type(void) G_GNUC_CONST;
virtual uint8_t *alloc_recv_buf(uint16_t type, uint32_t size) override;
virtual void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg) override;
virtual void on_disconnect() override;
};
RedChannelClient* inputs_channel_client_create(RedChannel *channel,
RedClient *client,

View File

@ -40,6 +40,8 @@
#include "migration-protocol.h"
#include "utils.h"
XXX_CAST(RedChannelClient, InputsChannelClient, INPUTS_CHANNEL_CLIENT);
struct InputsChannel final: public RedChannel
{
VDAgentMouseState mouse_state;

View File

@ -43,24 +43,24 @@ typedef enum {
(4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
struct MainChannelClientPrivate {
SPICE_CXX_GLIB_ALLOCATOR
uint32_t connection_id;
uint32_t ping_id;
uint32_t net_test_id;
NetTestStage net_test_stage;
uint64_t latency;
uint64_t bitrate_per_sec;
int mig_wait_connect;
int mig_connect_ok;
int mig_wait_prev_complete;
int mig_wait_prev_try_seamless;
int init_sent;
int seamless_mig_dst;
bool initial_channels_list_sent;
uint32_t ping_id = 0;
uint32_t net_test_id = 0;
NetTestStage net_test_stage = NET_TEST_STAGE_INVALID;
uint64_t latency = 0;
uint64_t bitrate_per_sec = ~0;
int mig_wait_connect = 0;
int mig_connect_ok = 0;
int mig_wait_prev_complete = 0;
int mig_wait_prev_try_seamless = 0;
int init_sent = 0;
int seamless_mig_dst = 0;
bool initial_channels_list_sent = false;
uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE];
};
G_DEFINE_TYPE_WITH_PRIVATE(MainChannelClient, main_channel_client, RED_TYPE_CHANNEL_CLIENT)
typedef struct RedPingPipeItem {
RedPipeItem base;
int size;
@ -125,68 +125,23 @@ typedef struct RedRegisteredChannelPipeItem {
static const uint8_t zero_page[ZERO_BUF_SIZE] = {0};
enum {
PROP0,
PROP_CONNECTION_ID
};
static void main_channel_client_get_property(GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
uint8_t *MainChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
{
MainChannelClient *self = MAIN_CHANNEL_CLIENT(object);
switch (property_id)
{
case PROP_CONNECTION_ID:
g_value_set_uint(value, self->priv->connection_id);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
}
static void main_channel_client_set_property(GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
MainChannelClient *self = MAIN_CHANNEL_CLIENT(object);
switch (property_id)
{
case PROP_CONNECTION_ID:
self->priv->connection_id = g_value_get_uint(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
}
static uint8_t *
main_channel_client_alloc_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type, uint32_t size)
{
MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
RedChannel *channel = rcc->get_channel();
return reds_get_agent_data_buffer(channel->get_server(), mcc, size);
} else if (size > sizeof(mcc->priv->recv_buf)) {
RedChannel *channel = get_channel();
return reds_get_agent_data_buffer(channel->get_server(), this, size);
} else if (size > sizeof(priv->recv_buf)) {
/* message too large, caller will log a message and close the connection */
return NULL;
} else {
return mcc->priv->recv_buf;
return priv->recv_buf;
}
}
static void
main_channel_client_release_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type, uint32_t size, uint8_t *msg)
void MainChannelClient::release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)
{
if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
RedChannel *channel = rcc->get_channel();
RedChannel *channel = get_channel();
reds_release_agent_data_buffer(channel->get_server(), msg);
}
}
@ -194,42 +149,11 @@ main_channel_client_release_msg_rcv_buf(RedChannelClient *rcc,
/*
* When the main channel is disconnected, disconnect the entire client.
*/
static void main_channel_client_on_disconnect(RedChannelClient *rcc)
void MainChannelClient::on_disconnect()
{
RedsState *reds = rcc->get_channel()->get_server();
RedsState *reds = get_channel()->get_server();
main_dispatcher_client_disconnect(reds_get_main_dispatcher(reds),
rcc->get_client());
}
static void main_channel_client_class_init(MainChannelClientClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
RedChannelClientClass *client_class = RED_CHANNEL_CLIENT_CLASS(klass);
object_class->get_property = main_channel_client_get_property;
object_class->set_property = main_channel_client_set_property;
client_class->alloc_recv_buf = main_channel_client_alloc_msg_rcv_buf;
client_class->release_recv_buf = main_channel_client_release_msg_rcv_buf;
client_class->on_disconnect = main_channel_client_on_disconnect;
g_object_class_install_property(object_class,
PROP_CONNECTION_ID,
g_param_spec_uint("connection-id",
"Connection ID",
"Connection ID",
0,
G_MAXUINT,
0,
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
}
static void main_channel_client_init(MainChannelClient *self)
{
self->priv = (MainChannelClientPrivate *) main_channel_client_get_instance_private(self);
self->priv->bitrate_per_sec = ~0;
get_client());
}
static void main_channel_client_push_ping(MainChannelClient *mcc, int size);
@ -602,22 +526,32 @@ gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc,
return ret;
}
MainChannelClient::MainChannelClient(MainChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps,
uint32_t connection_id):
RedChannelClient(RED_CHANNEL(channel), client, stream, caps),
priv(new MainChannelClientPrivate())
{
priv->connection_id = connection_id;
}
MainChannelClient::~MainChannelClient()
{
delete priv;
}
MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
RedStream *stream, uint32_t connection_id,
RedChannelCapabilities *caps)
{
MainChannelClient *mcc;
mcc = (MainChannelClient *)
g_initable_new(TYPE_MAIN_CHANNEL_CLIENT,
NULL, NULL,
"channel", main_chan,
"client", client,
"stream", stream,
"caps", caps,
"connection-id", connection_id,
NULL);
auto mcc = new MainChannelClient(main_chan, client, stream, caps, connection_id);
if (!mcc->init()) {
mcc->unref();
mcc = nullptr;
}
return mcc;
}
@ -642,6 +576,8 @@ uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
return mcc->priv->latency / 1000;
}
XXX_CAST(RedChannelClient, MainChannelClient, MAIN_CHANNEL_CLIENT);
void main_channel_client_migrate(RedChannelClient *rcc)
{
RedChannel *channel = rcc->get_channel();

View File

@ -18,7 +18,6 @@
#ifndef MAIN_CHANNEL_CLIENT_H_
#define MAIN_CHANNEL_CLIENT_H_
#include <glib-object.h>
#include <common/messages.h>
#include "red-channel-client.h"
@ -26,33 +25,25 @@
G_BEGIN_DECLS
#define TYPE_MAIN_CHANNEL_CLIENT main_channel_client_get_type()
#define MAIN_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_MAIN_CHANNEL_CLIENT, MainChannelClient))
#define MAIN_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), TYPE_MAIN_CHANNEL_CLIENT, MainChannelClientClass))
#define IS_MAIN_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_MAIN_CHANNEL_CLIENT))
#define IS_MAIN_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_MAIN_CHANNEL_CLIENT))
#define MAIN_CHANNEL_CLIENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_MAIN_CHANNEL_CLIENT, MainChannelClientClass))
struct MainChannelClientPrivate;
struct MainChannelClient final: public RedChannelClient
class MainChannelClient final: public RedChannelClient
{
MainChannelClientPrivate *priv;
};
protected:
~MainChannelClient();
public:
MainChannelClient(MainChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps,
uint32_t connection_id);
struct MainChannelClientClass
{
RedChannelClientClass parent_class;
virtual uint8_t *alloc_recv_buf(uint16_t type, uint32_t size) override;
virtual void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg) override;
virtual void on_disconnect() override;
MainChannelClientPrivate *const priv = nullptr;
};
GType main_channel_client_get_type(void) G_GNUC_CONST;
MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
RedStream *stream, uint32_t connection_id,
RedChannelCapabilities *caps);

View File

@ -45,6 +45,8 @@ int main_channel_is_connected(MainChannel *main_chan)
return main_chan && main_chan->is_connected();
}
XXX_CAST(RedChannelClient, MainChannelClient, MAIN_CHANNEL_CLIENT);
RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
{
RedChannelClient *rcc;

View File

@ -115,6 +115,8 @@ typedef struct IncomingMessageBuffer {
struct RedChannelClientPrivate
{
SPICE_CXX_GLIB_ALLOCATOR
RedChannel *channel;
RedClient *client;
RedStream *stream;
@ -196,9 +198,7 @@ static const SpiceDataHeaderOpaque mini_header_wrapper = {NULL, sizeof(SpiceMini
mini_header_get_msg_size};
static void red_channel_client_clear_sent_item(RedChannelClient *rcc);
static void red_channel_client_initable_interface_init(GInitableIface *iface);
static void red_channel_client_set_message_serial(RedChannelClient *channel, uint64_t);
static bool red_channel_client_config_socket(RedChannelClient *rcc);
/*
* When an error occurs over a channel, we treat it as a warning
@ -210,24 +210,6 @@ static bool red_channel_client_config_socket(RedChannelClient *rcc);
rcc->shutdown(); \
} while (0)
G_DEFINE_TYPE_WITH_CODE(RedChannelClient, red_channel_client, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE,
red_channel_client_initable_interface_init);
G_ADD_PRIVATE(RedChannelClient));
static gboolean red_channel_client_initable_init(GInitable *initable,
GCancellable *cancellable,
GError **error);
enum {
PROP0,
PROP_STREAM,
PROP_CHANNEL,
PROP_CLIENT,
PROP_MONITOR_LATENCY,
PROP_CAPS
};
#define PING_TEST_TIMEOUT_MS (MSEC_PER_SEC * 15)
#define PING_TEST_LONG_TIMEOUT_MS (MSEC_PER_SEC * 60 * 5)
#define PING_TEST_IDLE_NET_TIMEOUT_MS (MSEC_PER_SEC / 10)
@ -284,112 +266,60 @@ static void red_channel_client_restart_ping_timer(RedChannelClient *rcc)
red_channel_client_start_ping_timer(rcc, timeout);
}
static void
red_channel_client_get_property(GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
RedChannelClient::~RedChannelClient()
{
RedChannelClient *self = RED_CHANNEL_CLIENT(object);
red_timer_remove(priv->latency_monitor.timer);
priv->latency_monitor.timer = NULL;
switch (property_id)
{
case PROP_STREAM:
g_value_set_pointer(value, self->priv->stream);
break;
case PROP_CHANNEL:
g_value_set_object(value, self->priv->channel);
break;
case PROP_CLIENT:
g_value_set_object(value, self->priv->client);
break;
case PROP_MONITOR_LATENCY:
g_value_set_boolean(value, self->priv->monitor_latency);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
red_timer_remove(priv->connectivity_monitor.timer);
priv->connectivity_monitor.timer = NULL;
red_stream_free(priv->stream);
priv->stream = NULL;
if (priv->send_data.main.marshaller) {
spice_marshaller_destroy(priv->send_data.main.marshaller);
}
if (priv->send_data.urgent.marshaller) {
spice_marshaller_destroy(priv->send_data.urgent.marshaller);
}
red_channel_capabilities_reset(&priv->remote_caps);
if (priv->channel) {
g_object_unref(priv->channel);
}
delete priv;
}
static void
red_channel_client_set_property(GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
RedChannelClient::RedChannelClient(RedChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps,
bool monitor_latency)
{
RedChannelClient *self = RED_CHANNEL_CLIENT(object);
RedChannelClient *self = this;
switch (property_id)
{
case PROP_STREAM:
self->priv->stream = (RedStream*) g_value_get_pointer(value);
break;
case PROP_CHANNEL:
if (self->priv->channel)
g_object_unref(self->priv->channel);
self->priv->channel = (RedChannel *) g_value_dup_object(value);
break;
case PROP_CLIENT:
self->priv->client = (RedClient *) g_value_get_object(value);
break;
case PROP_MONITOR_LATENCY:
self->priv->monitor_latency = g_value_get_boolean(value);
break;
case PROP_CAPS:
{
RedChannelCapabilities *caps = (RedChannelCapabilities *) g_value_get_boxed(value);
if (caps) {
red_channel_capabilities_reset(&self->priv->remote_caps);
red_channel_capabilities_init(&self->priv->remote_caps, caps);
}
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
}
// XXX initialize
priv = new RedChannelClientPrivate();
static void
red_channel_client_finalize(GObject *object)
{
RedChannelClient *self = RED_CHANNEL_CLIENT(object);
// blocks send message (maybe use send_data.blocked + block flags)
self->priv->ack_data.messages_window = ~0;
self->priv->ack_data.client_generation = ~0;
self->priv->ack_data.client_window = CLIENT_ACK_WINDOW;
self->priv->send_data.main.marshaller = spice_marshaller_new();
self->priv->send_data.urgent.marshaller = spice_marshaller_new();
red_timer_remove(self->priv->latency_monitor.timer);
self->priv->latency_monitor.timer = NULL;
self->priv->send_data.marshaller = self->priv->send_data.main.marshaller;
red_timer_remove(self->priv->connectivity_monitor.timer);
self->priv->connectivity_monitor.timer = NULL;
red_stream_free(self->priv->stream);
self->priv->stream = NULL;
if (self->priv->send_data.main.marshaller) {
spice_marshaller_destroy(self->priv->send_data.main.marshaller);
}
if (self->priv->send_data.urgent.marshaller) {
spice_marshaller_destroy(self->priv->send_data.urgent.marshaller);
}
g_queue_init(&self->priv->pipe);
red_channel_capabilities_reset(&self->priv->remote_caps);
if (self->priv->channel) {
g_object_unref(self->priv->channel);
}
red_channel_capabilities_init(&self->priv->remote_caps, caps);
G_OBJECT_CLASS(red_channel_client_parent_class)->finalize(object);
}
static void red_channel_client_initable_interface_init(GInitableIface *iface)
{
iface->init = red_channel_client_initable_init;
}
static void red_channel_client_constructed(GObject *object)
{
RedChannelClient *self = RED_CHANNEL_CLIENT(object);
RedChannelClientClass *klass = RED_CHANNEL_CLIENT_GET_CLASS(self);
spice_assert(klass->alloc_recv_buf && klass->release_recv_buf);
priv->channel = (RedChannel*) g_object_ref(channel);
priv->client = client;
priv->stream = stream;
self->priv->outgoing.pos = 0;
self->priv->outgoing.size = 0;
@ -405,81 +335,12 @@ static void red_channel_client_constructed(GObject *object)
}
self->priv->incoming.header.data = self->priv->incoming.header_buf;
RedChannel *channel = self->priv->channel;
RedsState* reds = channel->get_server();
const RedStatNode *node = channel->get_stat_node();
stat_init_counter(&self->priv->out_messages, reds, node, "out_messages", TRUE);
stat_init_counter(&self->priv->out_bytes, reds, node, "out_bytes", TRUE);
}
static void red_channel_client_class_init(RedChannelClientClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
GParamSpec *spec;
g_debug("%s", G_STRFUNC);
object_class->get_property = red_channel_client_get_property;
object_class->set_property = red_channel_client_set_property;
object_class->finalize = red_channel_client_finalize;
object_class->constructed = red_channel_client_constructed;
spec = g_param_spec_pointer("stream", "stream",
"Associated RedStream",
G_PARAM_STATIC_STRINGS
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property(object_class, PROP_STREAM, spec);
spec = g_param_spec_object("channel", "channel",
"Associated RedChannel",
RED_TYPE_CHANNEL,
G_PARAM_STATIC_STRINGS
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property(object_class, PROP_CHANNEL, spec);
spec = g_param_spec_object("client", "client",
"Associated RedClient",
RED_TYPE_CLIENT,
G_PARAM_STATIC_STRINGS
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property(object_class, PROP_CLIENT, spec);
spec = g_param_spec_boolean("monitor-latency", "monitor-latency",
"Whether to monitor latency for this client",
FALSE,
G_PARAM_STATIC_STRINGS
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property(object_class, PROP_MONITOR_LATENCY, spec);
spec = g_param_spec_boxed("caps", "caps",
"Capabilities",
RED_TYPE_CHANNEL_CAPABILITIES,
G_PARAM_STATIC_STRINGS
| G_PARAM_WRITABLE
| G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property(object_class, PROP_CAPS, spec);
}
static void
red_channel_client_init(RedChannelClient *self)
{
self->priv = (RedChannelClientPrivate *) red_channel_client_get_instance_private(self);
// blocks send message (maybe use send_data.blocked + block flags)
self->priv->ack_data.messages_window = ~0;
self->priv->ack_data.client_generation = ~0;
self->priv->ack_data.client_window = CLIENT_ACK_WINDOW;
self->priv->send_data.main.marshaller = spice_marshaller_new();
self->priv->send_data.urgent.marshaller = spice_marshaller_new();
self->priv->send_data.marshaller = self->priv->send_data.main.marshaller;
g_queue_init(&self->priv->pipe);
}
RedChannel* RedChannelClient::get_channel()
{
return priv->channel;
@ -912,13 +773,11 @@ static void mini_header_set_msg_sub_list(SpiceDataHeaderOpaque *header, uint32_t
spice_error("attempt to set header sub list on mini header");
}
static gboolean red_channel_client_initable_init(GInitable *initable,
GCancellable *cancellable,
GError **error)
bool RedChannelClient::init()
{
GError *local_error = NULL;
SpiceCoreInterfaceInternal *core;
RedChannelClient *self = RED_CHANNEL_CLIENT(initable);
RedChannelClient *self = this;
if (!self->priv->stream) {
g_set_error_literal(&local_error,
@ -928,7 +787,7 @@ static gboolean red_channel_client_initable_init(GInitable *initable,
goto cleanup;
}
if (!red_channel_client_config_socket(self)) {
if (!self->config_socket()) {
g_set_error_literal(&local_error,
SPICE_SERVER_ERROR,
SPICE_SERVER_ERROR_FAILED,
@ -967,7 +826,7 @@ cleanup:
red_channel_warning(self->get_channel(),
"Failed to create channel client: %s",
local_error->message);
g_propagate_error(error, local_error);
g_error_free(local_error);
}
return local_error == NULL;
}
@ -1048,34 +907,6 @@ void RedChannelClient::shutdown()
}
}
static bool red_channel_client_config_socket(RedChannelClient *rcc)
{
RedChannelClientClass *klass = RED_CHANNEL_CLIENT_GET_CLASS(rcc);
if (!klass->config_socket) {
return TRUE;
}
return klass->config_socket(rcc);
}
static uint8_t *red_channel_client_alloc_msg_buf(RedChannelClient *rcc,
uint16_t type, uint32_t size)
{
RedChannelClientClass *klass = RED_CHANNEL_CLIENT_GET_CLASS(rcc);
return klass->alloc_recv_buf(rcc, type, size);
}
static void red_channel_client_release_msg_buf(RedChannelClient *rcc,
uint16_t type, uint32_t size,
uint8_t *msg)
{
RedChannelClientClass *klass = RED_CHANNEL_CLIENT_GET_CLASS(rcc);
klass->release_recv_buf(rcc, type, size, msg);
}
static void red_channel_client_handle_outgoing(RedChannelClient *rcc)
{
RedStream *stream = rcc->priv->stream;
@ -1221,7 +1052,7 @@ static void red_channel_client_handle_incoming(RedChannelClient *rcc)
msg_type = buffer->header.get_msg_type(&buffer->header);
if (buffer->msg_pos < msg_size) {
if (!buffer->msg) {
buffer->msg = red_channel_client_alloc_msg_buf(rcc, msg_type, msg_size);
buffer->msg = rcc->alloc_recv_buf(msg_type, msg_size);
if (buffer->msg == NULL && rcc->priv->block_read) {
// if we are blocked by flow control just return, message will be read
// when data will be available
@ -1238,8 +1069,7 @@ static void red_channel_client_handle_incoming(RedChannelClient *rcc)
buffer->msg + buffer->msg_pos,
msg_size - buffer->msg_pos);
if (bytes_read == -1) {
red_channel_client_release_msg_buf(rcc, msg_type, msg_size,
buffer->msg);
rcc->release_recv_buf(msg_type, msg_size, buffer->msg);
buffer->msg = NULL;
rcc->disconnect();
return;
@ -1257,9 +1087,7 @@ static void red_channel_client_handle_incoming(RedChannelClient *rcc)
&parsed_size, &parsed_free);
if (parsed == NULL) {
red_channel_warning(channel, "failed to parse message type %d", msg_type);
red_channel_client_release_msg_buf(rcc,
msg_type, msg_size,
buffer->msg);
rcc->release_recv_buf(msg_type, msg_size, buffer->msg);
buffer->msg = NULL;
rcc->disconnect();
return;
@ -1270,9 +1098,7 @@ static void red_channel_client_handle_incoming(RedChannelClient *rcc)
parsed_free(parsed);
}
buffer->msg_pos = 0;
red_channel_client_release_msg_buf(rcc,
msg_type, msg_size,
buffer->msg);
rcc->release_recv_buf(msg_type, msg_size, buffer->msg);
buffer->msg = NULL;
buffer->header_pos = 0;
@ -1711,15 +1537,6 @@ void RedChannelClient::push_set_ack()
pipe_add_type(RED_PIPE_ITEM_TYPE_SET_ACK);
}
static void red_channel_client_on_disconnect(RedChannelClient *rcc)
{
RedChannelClientClass *klass = RED_CHANNEL_CLIENT_GET_CLASS(rcc);
if (klass->on_disconnect != NULL) {
klass->on_disconnect(rcc);
}
}
void RedChannelClient::disconnect()
{
RedChannel *channel = priv->channel;
@ -1738,7 +1555,7 @@ void RedChannelClient::disconnect()
priv->connectivity_monitor.timer = NULL;
channel->remove_client(this);
red_channel_client_on_disconnect(this);
on_disconnect();
// remove client from RedClient
// NOTE this may trigger the free of the object, if we are in a watch/timer
// we should make sure we keep a reference

View File

@ -18,8 +18,6 @@
#ifndef RED_CHANNEL_CLIENT_H_
#define RED_CHANNEL_CLIENT_H_
#include <glib-object.h>
#include <gio/gio.h>
#include <common/marshaller.h>
#include "red-pipe-item.h"
@ -28,12 +26,28 @@
G_BEGIN_DECLS
#define RED_TYPE_CHANNEL_CLIENT red_channel_client_get_type()
struct RedChannelClientPrivate;
SPICE_DECLARE_TYPE(RedChannelClient, red_channel_client, CHANNEL_CLIENT);
struct RedChannelClient: public GObject
class RedChannelClient
{
public:
SPICE_CXX_GLIB_ALLOCATOR
// This is made protected to avoid allocation on stack conflicting with
// reference counting
protected:
virtual ~RedChannelClient();
public:
RedChannelClient(RedChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps,
bool monitor_latency=false);
virtual bool init();
RedChannelClientPrivate *priv = nullptr;
bool is_connected() const;
static void default_migrate(RedChannelClient *rcc);
bool is_waiting_for_migrate_data() const;
@ -137,22 +151,19 @@ struct RedChannelClient: public GObject
void block_read();
void unblock_read();
void ref() { g_object_ref(this); }
void unref() { g_object_unref(this); }
RedChannelClientPrivate *priv;
};
struct RedChannelClientClass
{
GObjectClass parent_class;
void ref() { g_atomic_int_inc(&_ref); }
void unref() { if (g_atomic_int_dec_and_test(&_ref)) delete this; }
/* configure socket connected to the client */
bool (*config_socket)(RedChannelClient *rcc);
uint8_t *(*alloc_recv_buf)(RedChannelClient *channel, uint16_t type, uint32_t size);
void (*release_recv_buf)(RedChannelClient *channel, uint16_t type, uint32_t size, uint8_t *msg);
virtual bool config_socket() { return true; }
virtual uint8_t *alloc_recv_buf(uint16_t type, uint32_t size)=0;
virtual void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)=0;
void (*on_disconnect)(RedChannelClient *rcc);
virtual void on_disconnect() {};
/* Private data */
private:
gint _ref = 1;
};
#define SPICE_SERVER_ERROR spice_server_error_quark()

View File

@ -128,6 +128,20 @@ typedef struct GListIter {
{ return G_TYPE_INSTANCE_GET_CLASS(obj, \
module_obj_name ## _get_type(), ModuleObjName ## Class); }
/* This macro allows to use GLib for a class hieranrchy allocation.
* The aims are:
* - do not depend on C++ runtime, just C;
* - do not throw memory exception from a C library;
* - zero out the structure like GOject did, we are not still
* initializing automatically all members;
* - do not allow to allocate array of this type, do not mix fine
* with reference counting and inheritance.
*/
#define SPICE_CXX_GLIB_ALLOCATOR \
void *operator new(size_t size) { return g_malloc0(size); } \
void operator delete(void *p) { g_free(p); } \
void* operator new[](size_t count);
#ifdef __cplusplus
#include <glib-object.h>
@ -137,6 +151,12 @@ inline GParamFlags operator|(GParamFlags a, GParamFlags b)
}
#endif
// XXX todo remove, just for easy portability
#define XXX_CAST(from, to, name) \
static inline to* name(from *p) { \
return p ? static_cast<to*>(p) : nullptr; \
}
SPICE_END_DECLS
#endif /* RED_COMMON_H_ */

View File

@ -20,140 +20,111 @@
#include "smartcard-channel-client.h"
XXX_CAST(RedChannelClient, SmartCardChannelClient, SMARTCARD_CHANNEL_CLIENT)
struct SmartCardChannelClientPrivate
{
RedCharDeviceSmartcard *smartcard;
SPICE_CXX_GLIB_ALLOCATOR
RedCharDeviceSmartcard *smartcard = nullptr;
/* read_from_client/write_to_device buffer.
* The beginning of the buffer should always be VSCMsgHeader*/
RedCharDeviceWriteBuffer *write_buf;
int msg_in_write_buf; /* was the client msg received into a RedCharDeviceWriteBuffer
* or was it explicitly malloced */
RedCharDeviceWriteBuffer *write_buf = nullptr;
/* was the client msg received into a RedCharDeviceWriteBuffer
* or was it explicitly malloced */
bool msg_in_write_buf = false;
};
G_DEFINE_TYPE_WITH_PRIVATE(SmartCardChannelClient, smart_card_channel_client,
RED_TYPE_CHANNEL_CLIENT)
typedef struct RedErrorItem {
RedPipeItem base;
VSCMsgHeader vheader;
VSCMsgError error;
} RedErrorItem;
static uint8_t *
smartcard_channel_client_alloc_msg_rcv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size);
static void
smartcard_channel_client_release_msg_rcv_buf(RedChannelClient *rcc, uint16_t type,
uint32_t size, uint8_t *msg);
static void smartcard_channel_client_on_disconnect(RedChannelClient *rcc);
static void smart_card_channel_client_finalize(GObject *object)
SmartCardChannelClient::SmartCardChannelClient(RedChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps):
RedChannelClient(channel, client, stream, caps),
priv(new SmartCardChannelClientPrivate())
{
SmartCardChannelClient *self = SMARTCARD_CHANNEL_CLIENT(object);
if (self->priv->smartcard)
g_object_remove_weak_pointer(G_OBJECT(self->priv->smartcard),
(gpointer*)&self->priv->smartcard);
G_OBJECT_CLASS(smart_card_channel_client_parent_class)->finalize(object);
}
static void smart_card_channel_client_class_init(SmartCardChannelClientClass *klass)
SmartCardChannelClient::~SmartCardChannelClient()
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
RedChannelClientClass *client_class = RED_CHANNEL_CLIENT_CLASS(klass);
client_class->alloc_recv_buf = smartcard_channel_client_alloc_msg_rcv_buf;
client_class->release_recv_buf = smartcard_channel_client_release_msg_rcv_buf;
client_class->on_disconnect = smartcard_channel_client_on_disconnect;
object_class->finalize = smart_card_channel_client_finalize;
}
static void
smart_card_channel_client_init(SmartCardChannelClient *self)
{
self->priv = (SmartCardChannelClientPrivate*)
smart_card_channel_client_get_instance_private(self);
if (priv->smartcard) {
g_object_remove_weak_pointer(G_OBJECT(priv->smartcard),
(gpointer*)&priv->smartcard);
}
delete priv;
}
SmartCardChannelClient* smartcard_channel_client_create(RedChannel *channel,
RedClient *client, RedStream *stream,
RedChannelCapabilities *caps)
{
SmartCardChannelClient *rcc;
rcc = (SmartCardChannelClient *)
g_initable_new(TYPE_SMARTCARD_CHANNEL_CLIENT,
NULL, NULL,
"channel", channel,
"client", client,
"stream", stream,
"caps", caps,
NULL);
auto rcc = new SmartCardChannelClient(channel, client, stream, caps);
if (!rcc->init()) {
rcc->unref();
rcc = nullptr;
}
return rcc;
}
static uint8_t *
smartcard_channel_client_alloc_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type, uint32_t size)
uint8_t *
SmartCardChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
{
SmartCardChannelClient *scc = SMARTCARD_CHANNEL_CLIENT(rcc);
/* TODO: only one reader is actually supported. When we fix the code to support
* multiple readers, we will probably associate different devices to
* different channels */
if (!scc->priv->smartcard) {
scc->priv->msg_in_write_buf = FALSE;
return (uint8_t*) g_malloc(size);
if (!priv->smartcard) {
priv->msg_in_write_buf = FALSE;
return (uint8_t *) g_malloc(size);
} else {
RedCharDeviceSmartcard *smartcard;
spice_assert(smartcard_get_n_readers() == 1);
smartcard = scc->priv->smartcard;
spice_assert(smartcard_char_device_get_client(smartcard) || scc->priv->smartcard);
spice_assert(!scc->priv->write_buf);
scc->priv->write_buf =
red_char_device_write_buffer_get_client(RED_CHAR_DEVICE(smartcard), rcc, size);
smartcard = priv->smartcard;
spice_assert(smartcard_char_device_get_client(smartcard) || priv->smartcard);
spice_assert(!priv->write_buf);
priv->write_buf =
red_char_device_write_buffer_get_client(RED_CHAR_DEVICE(smartcard), this, size);
if (!scc->priv->write_buf) {
if (!priv->write_buf) {
spice_error("failed to allocate write buffer");
return NULL;
}
scc->priv->msg_in_write_buf = TRUE;
return scc->priv->write_buf->buf;
priv->msg_in_write_buf = TRUE;
return priv->write_buf->buf;
}
}
static void
smartcard_channel_client_release_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type, uint32_t size, uint8_t *msg)
void
SmartCardChannelClient::release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)
{
SmartCardChannelClient *scc = SMARTCARD_CHANNEL_CLIENT(rcc);
/* todo: only one reader is actually supported. When we fix the code to support
* multiple readers, we will porbably associate different devices to
* differenc channels */
if (!scc->priv->msg_in_write_buf) {
spice_assert(!scc->priv->write_buf);
if (!priv->msg_in_write_buf) {
spice_assert(!priv->write_buf);
g_free(msg);
} else {
if (scc->priv->write_buf) { /* msg hasn't been pushed to the guest */
spice_assert(scc->priv->write_buf->buf == msg);
red_char_device_write_buffer_release(RED_CHAR_DEVICE(scc->priv->smartcard),
&scc->priv->write_buf);
if (priv->write_buf) { /* msg hasn't been pushed to the guest */
spice_assert(priv->write_buf->buf == msg);
red_char_device_write_buffer_release(RED_CHAR_DEVICE(priv->smartcard),
&priv->write_buf);
}
}
}
static void smartcard_channel_client_on_disconnect(RedChannelClient *rcc)
void SmartCardChannelClient::on_disconnect()
{
SmartCardChannelClient *scc = SMARTCARD_CHANNEL_CLIENT(rcc);
RedCharDeviceSmartcard *device = scc->priv->smartcard;
RedCharDeviceSmartcard *device = priv->smartcard;
if (device) {
smartcard_char_device_detach_client(device, scc);
smartcard_char_device_detach_client(device, this);
smartcard_char_device_notify_reader_remove(device);
}
}

View File

@ -18,39 +18,29 @@
#ifndef SMARTCARD_CHANNEL_CLIENT_H_
#define SMARTCARD_CHANNEL_CLIENT_H_
#include <glib-object.h>
#include "smartcard.h"
G_BEGIN_DECLS
#define TYPE_SMARTCARD_CHANNEL_CLIENT smart_card_channel_client_get_type()
#define SMARTCARD_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_SMARTCARD_CHANNEL_CLIENT, SmartCardChannelClient))
#define SMARTCARD_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), TYPE_SMARTCARD_CHANNEL_CLIENT, SmartCardChannelClientClass))
#define IS_SMARTCARD_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_SMARTCARD_CHANNEL_CLIENT))
#define IS_SMARTCARD_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_SMARTCARD_CHANNEL_CLIENT))
#define SMARTCARD_CHANNEL_CLIENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_SMARTCARD_CHANNEL_CLIENT, SmartCardChannelClientClass))
struct SmartCardChannelClientPrivate;
struct SmartCardChannelClient final: public RedChannelClient
class SmartCardChannelClient final: public RedChannelClient
{
SmartCardChannelClientPrivate *priv;
};
protected:
~SmartCardChannelClient();
public:
SmartCardChannelClientPrivate *const priv = nullptr;
SmartCardChannelClient(RedChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps);
struct SmartCardChannelClientClass
{
RedChannelClientClass parent_class;
private:
virtual uint8_t *alloc_recv_buf(uint16_t type, uint32_t size) override;
virtual void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg) override;
virtual void on_disconnect() override;
};
GType smart_card_channel_client_get_type(void) G_GNUC_CONST;
SmartCardChannelClient* smartcard_channel_client_create(RedChannel *channel,
RedClient *client, RedStream *stream,
RedChannelCapabilities *caps);

View File

@ -30,6 +30,8 @@
#include "smartcard-channel-client.h"
#include "migration-protocol.h"
XXX_CAST(RedChannelClient, SmartCardChannelClient, SMARTCARD_CHANNEL_CLIENT)
/*
* TODO: the code doesn't really support multiple readers.
* For example: smartcard_char_device_add_to_readers calls smartcard_init,

View File

@ -79,19 +79,17 @@ typedef struct SpiceRecordState RecordChannel;
typedef void (*snd_channel_on_message_done_proc)(SndChannelClient *client);
#define TYPE_SND_CHANNEL_CLIENT snd_channel_client_get_type()
#define SND_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_SND_CHANNEL_CLIENT, SndChannelClient))
GType snd_channel_client_get_type(void) G_GNUC_CONST;
struct PersistentPipeItem: public RedPipeItem
{
SndChannelClient *client;
};
/* Connects an audio client to a Spice client */
struct SndChannelClient: public RedChannelClient
class SndChannelClient: public RedChannelClient
{
public:
using RedChannelClient::RedChannelClient;
bool active;
bool client_active;
@ -103,19 +101,13 @@ struct SndChannelClient: public RedChannelClient
PersistentPipeItem persistent_pipe_item;
snd_channel_on_message_done_proc on_message_done;
virtual bool config_socket() override;
virtual uint8_t *alloc_recv_buf(uint16_t type, uint32_t size) override;
virtual void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg) override;
};
typedef struct SndChannelClientClass {
RedChannelClientClass parent_class;
} SndChannelClientClass;
static void playback_channel_client_initable_interface_init(GInitableIface *iface);
static void record_channel_client_initable_interface_init(GInitableIface *iface);
static GInitableIface *playback_channel_client_parent_initable_iface;
static GInitableIface *record_channel_client_parent_initable_iface;
G_DEFINE_TYPE(SndChannelClient, snd_channel_client, RED_TYPE_CHANNEL_CLIENT)
static void snd_playback_alloc_frames(PlaybackChannelClient *playback);
enum {
RED_PIPE_ITEM_PERSISTENT = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
@ -138,31 +130,27 @@ struct AudioFrameContainer
AudioFrame items[NUM_AUDIO_FRAMES];
};
#define TYPE_PLAYBACK_CHANNEL_CLIENT playback_channel_client_get_type()
#define PLAYBACK_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_PLAYBACK_CHANNEL_CLIENT, PlaybackChannelClient))
GType playback_channel_client_get_type(void) G_GNUC_CONST;
struct PlaybackChannelClient final: public SndChannelClient
class PlaybackChannelClient final: public SndChannelClient
{
AudioFrameContainer *frames;
AudioFrame *free_frames;
AudioFrame *in_progress; /* Frame being sent to the client */
AudioFrame *pending_frame; /* Next frame to send to the client */
uint32_t mode;
uint32_t latency;
SndCodec codec;
protected:
~PlaybackChannelClient();
public:
PlaybackChannelClient(RedChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps);
virtual bool init() override;
AudioFrameContainer *frames = nullptr;
AudioFrame *free_frames = nullptr;
AudioFrame *in_progress = nullptr; /* Frame being sent to the client */
AudioFrame *pending_frame = nullptr; /* Next frame to send to the client */
uint32_t mode = SPICE_AUDIO_DATA_MODE_RAW;
uint32_t latency = 0;
SndCodec codec = nullptr;
uint8_t encode_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
};
typedef struct PlaybackChannelClientClass {
SndChannelClientClass parent_class;
} PlaybackChannelClientClass;
G_DEFINE_TYPE_WITH_CODE(PlaybackChannelClient, playback_channel_client, TYPE_SND_CHANNEL_CLIENT,
G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE,
playback_channel_client_initable_interface_init))
typedef struct SpiceVolumeState {
uint16_t *volume;
uint8_t volume_nchannels;
@ -219,31 +207,24 @@ typedef struct RecordChannelClass {
G_DEFINE_TYPE(RecordChannel, record_channel, TYPE_SND_CHANNEL)
#define TYPE_RECORD_CHANNEL_CLIENT record_channel_client_get_type()
#define RECORD_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_RECORD_CHANNEL_CLIENT, RecordChannelClient))
GType record_channel_client_get_type(void) G_GNUC_CONST;
struct RecordChannelClient final: public SndChannelClient
class RecordChannelClient final: public SndChannelClient
{
protected:
~RecordChannelClient();
public:
using SndChannelClient::SndChannelClient;
virtual bool init() override;
uint32_t samples[RECORD_SAMPLES_SIZE];
uint32_t write_pos;
uint32_t read_pos;
uint32_t mode;
uint32_t mode_time;
uint32_t start_time;
SndCodec codec;
uint32_t write_pos = 0;
uint32_t read_pos = 0;
uint32_t mode = SPICE_AUDIO_DATA_MODE_RAW;
uint32_t mode_time = 0;
uint32_t start_time = 0;
SndCodec codec = nullptr;
uint8_t decode_buf[SND_CODEC_MAX_FRAME_BYTES];
};
typedef struct RecordChannelClientClass {
SndChannelClientClass parent_class;
} RecordChannelClientClass;
G_DEFINE_TYPE_WITH_CODE(RecordChannelClient, record_channel_client, TYPE_SND_CHANNEL_CLIENT,
G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE,
record_channel_client_initable_interface_init))
/* A list of all Spice{Playback,Record}State objects */
static GList *snd_channels;
@ -350,6 +331,8 @@ const char* spice_audio_data_mode_to_string(gint mode)
return "unknown audio codec";
}
XXX_CAST(RedChannelClient, RecordChannelClient, RECORD_CHANNEL_CLIENT)
static bool
record_channel_handle_message(RedChannelClient *rcc, uint16_t type, uint32_t size, void *message)
{
@ -415,7 +398,7 @@ static bool snd_channel_send_migrate(SndChannelClient *client)
static bool snd_playback_send_migrate(PlaybackChannelClient *client)
{
return snd_channel_send_migrate(SND_CHANNEL_CLIENT(client));
return snd_channel_send_migrate(client);
}
static bool snd_send_volume(SndChannelClient *client, uint32_t cap, int msg)
@ -446,7 +429,7 @@ static bool snd_send_volume(SndChannelClient *client, uint32_t cap, int msg)
static bool snd_playback_send_volume(PlaybackChannelClient *playback_client)
{
return snd_send_volume(SND_CHANNEL_CLIENT(playback_client), SPICE_PLAYBACK_CAP_VOLUME,
return snd_send_volume(playback_client, SPICE_PLAYBACK_CAP_VOLUME,
SPICE_MSG_PLAYBACK_VOLUME);
}
@ -472,7 +455,7 @@ static bool snd_send_mute(SndChannelClient *client, uint32_t cap, int msg)
static bool snd_playback_send_mute(PlaybackChannelClient *playback_client)
{
return snd_send_mute(SND_CHANNEL_CLIENT(playback_client), SPICE_PLAYBACK_CAP_VOLUME,
return snd_send_mute(playback_client, SPICE_PLAYBACK_CAP_VOLUME,
SPICE_MSG_PLAYBACK_MUTE);
}
@ -521,7 +504,7 @@ static bool snd_playback_send_stop(PlaybackChannelClient *playback_client)
static int snd_playback_send_ctl(PlaybackChannelClient *playback_client)
{
SndChannelClient *client = SND_CHANNEL_CLIENT(playback_client);
SndChannelClient *client = playback_client;
if ((client->client_active = client->active)) {
return snd_playback_send_start(playback_client);
@ -560,7 +543,7 @@ static bool snd_record_send_stop(RecordChannelClient *record_client)
static int snd_record_send_ctl(RecordChannelClient *record_client)
{
SndChannelClient *client = SND_CHANNEL_CLIENT(record_client);
SndChannelClient *client = record_client;
if ((client->client_active = client->active)) {
return snd_record_send_start(record_client);
@ -571,13 +554,13 @@ static int snd_record_send_ctl(RecordChannelClient *record_client)
static bool snd_record_send_volume(RecordChannelClient *record_client)
{
return snd_send_volume(SND_CHANNEL_CLIENT(record_client), SPICE_RECORD_CAP_VOLUME,
return snd_send_volume(record_client, SPICE_RECORD_CAP_VOLUME,
SPICE_MSG_RECORD_VOLUME);
}
static bool snd_record_send_mute(RecordChannelClient *record_client)
{
return snd_send_mute(SND_CHANNEL_CLIENT(record_client), SPICE_RECORD_CAP_VOLUME,
return snd_send_mute(record_client, SPICE_RECORD_CAP_VOLUME,
SPICE_MSG_RECORD_MUTE);
}
@ -587,7 +570,7 @@ static bool snd_record_send_migrate(RecordChannelClient *record_client)
* the client receives RECORD_STOP from the src before the migration completion
* notification (when the vm is stopped).
* Afterwards, when the vm starts on the dest, the client receives RECORD_START. */
return snd_channel_send_migrate(SND_CHANNEL_CLIENT(record_client));
return snd_channel_send_migrate(record_client);
}
static bool snd_playback_send_write(PlaybackChannelClient *playback_client)
@ -596,7 +579,7 @@ static bool snd_playback_send_write(PlaybackChannelClient *playback_client)
SpiceMarshaller *m = rcc->get_marshaller();
AudioFrame *frame;
SpiceMsgPlaybackPacket msg;
RedPipeItem *pipe_item = &SND_CHANNEL_CLIENT(playback_client)->persistent_pipe_item;
RedPipeItem *pipe_item = &playback_client->persistent_pipe_item;
rcc->init_send_data(SPICE_MSG_PLAYBACK_DATA);
@ -666,25 +649,22 @@ static void snd_persistent_pipe_item_free(struct RedPipeItem *item)
static void snd_send(SndChannelClient * client)
{
RedChannelClient *rcc;
g_return_if_fail(RED_IS_CHANNEL_CLIENT(client));
rcc = client;
if (!rcc->pipe_is_empty() || !client->command) {
if (!client->pipe_is_empty()|| !client->command) {
return;
}
// just append a dummy item and push!
red_pipe_item_init_full(&client->persistent_pipe_item, RED_PIPE_ITEM_PERSISTENT,
snd_persistent_pipe_item_free);
client->persistent_pipe_item.client = client;
rcc->pipe_add_push(&client->persistent_pipe_item);
client->pipe_add_push(&client->persistent_pipe_item);
}
XXX_CAST(RedChannelClient, PlaybackChannelClient, PLAYBACK_CHANNEL_CLIENT)
static void playback_channel_send_item(RedChannelClient *rcc, G_GNUC_UNUSED RedPipeItem *item)
{
PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(rcc);
SndChannelClient *client = SND_CHANNEL_CLIENT(rcc);
SndChannelClient *client = playback_client;
client->command &= SND_PLAYBACK_MODE_MASK|SND_PLAYBACK_PCM_MASK|
SND_CTRL_MASK|SND_VOLUME_MUTE_MASK|
@ -744,7 +724,7 @@ static void playback_channel_send_item(RedChannelClient *rcc, G_GNUC_UNUSED RedP
static void record_channel_send_item(RedChannelClient *rcc, G_GNUC_UNUSED RedPipeItem *item)
{
RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(rcc);
SndChannelClient *client = SND_CHANNEL_CLIENT(rcc);
SndChannelClient *client = record_client;
client->command &= SND_CTRL_MASK|SND_VOLUME_MUTE_MASK|SND_MIGRATE_MASK;
while (client->command) {
@ -776,10 +756,10 @@ static void record_channel_send_item(RedChannelClient *rcc, G_GNUC_UNUSED RedPip
snd_send(client);
}
static bool snd_channel_client_config_socket(RedChannelClient *rcc)
bool SndChannelClient::config_socket()
{
RedStream *stream = rcc->get_stream();
RedClient *red_client = rcc->get_client();
RedStream *stream = get_stream();
RedClient *red_client = get_client();
MainChannelClient *mcc = red_client_get_main(red_client);
#ifdef SO_PRIORITY
@ -787,7 +767,7 @@ static bool snd_channel_client_config_socket(RedChannelClient *rcc)
if (setsockopt(stream->socket, SOL_SOCKET, SO_PRIORITY, (void*)&priority,
sizeof(priority)) == -1) {
if (errno != ENOTSUP) {
red_channel_warning(rcc->get_channel(),
red_channel_warning(get_channel(),
"setsockopt failed, %s", strerror(errno));
}
}
@ -797,7 +777,7 @@ static bool snd_channel_client_config_socket(RedChannelClient *rcc)
int tos = IPTOS_LOWDELAY;
if (setsockopt(stream->socket, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) == -1) {
if (errno != ENOTSUP) {
red_channel_warning(rcc->get_channel(),
red_channel_warning(get_channel(),
"setsockopt failed, %s",
strerror(errno));
}
@ -809,23 +789,20 @@ static bool snd_channel_client_config_socket(RedChannelClient *rcc)
return true;
}
static uint8_t*
snd_channel_client_alloc_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size)
uint8_t*
SndChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
{
SndChannelClient *client = SND_CHANNEL_CLIENT(rcc);
// If message is too big allocate one, this should never happen
if (size > sizeof(client->receive_buf)) {
if (size > sizeof(receive_buf)) {
return (uint8_t*) g_malloc(size);
}
return client->receive_buf;
return receive_buf;
}
static void
snd_channel_client_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size,
uint8_t *msg)
void
SndChannelClient::release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)
{
SndChannelClient *client = SND_CHANNEL_CLIENT(rcc);
if (msg != client->receive_buf) {
if (msg != receive_buf) {
g_free(msg);
}
}
@ -976,20 +953,20 @@ SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance
}
}
playback_client = frame->client;
if (!playback_client || snd_channel_get_client(sin->st) != SND_CHANNEL_CLIENT(playback_client)) {
if (!playback_client || snd_channel_get_client(sin->st) != playback_client) {
/* lost last reference, client has been destroyed previously */
spice_debug("audio samples belong to a disconnected client");
return;
}
spice_assert(SND_CHANNEL_CLIENT(playback_client)->active);
spice_assert(playback_client->active);
if (playback_client->pending_frame) {
snd_playback_free_frame(playback_client, playback_client->pending_frame);
}
frame->time = reds_get_mm_time();
playback_client->pending_frame = frame;
snd_set_command(SND_CHANNEL_CLIENT(playback_client), SND_PLAYBACK_PCM_MASK);
snd_send(SND_CHANNEL_CLIENT(playback_client));
snd_set_command(playback_client, SND_PLAYBACK_PCM_MASK);
snd_send(playback_client);
}
void snd_set_playback_latency(RedClient *client, uint32_t latency)
@ -1029,74 +1006,65 @@ static int snd_desired_audio_mode(bool playback_compression, int frequency,
return SPICE_AUDIO_DATA_MODE_RAW;
}
static void
playback_channel_client_finalize(GObject *object)
PlaybackChannelClient::~PlaybackChannelClient()
{
int i;
PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(object);
SndChannelClient *client = SND_CHANNEL_CLIENT(playback_client);
// free frames, unref them
for (i = 0; i < NUM_AUDIO_FRAMES; ++i) {
playback_client->frames->items[i].client = NULL;
frames->items[i].client = NULL;
}
if (--playback_client->frames->refs == 0) {
g_free(playback_client->frames);
if (--frames->refs == 0) {
g_free(frames);
}
if (client->active) {
reds_enable_mm_time(snd_channel_get_server(client));
if (active) {
reds_enable_mm_time(snd_channel_get_server(this));
}
snd_codec_destroy(&playback_client->codec);
G_OBJECT_CLASS(playback_channel_client_parent_class)->finalize(object);
snd_codec_destroy(&codec);
}
static void
playback_channel_client_constructed(GObject *object)
PlaybackChannelClient::PlaybackChannelClient(RedChannel *channel,
RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps):
SndChannelClient(channel, client, stream, caps)
{
PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(object);
RedChannelClient *rcc = playback_client;
RedChannel *red_channel = rcc->get_channel();
SndChannel *channel = SND_CHANNEL(red_channel);
SndChannelClient *scc = SND_CHANNEL_CLIENT(playback_client);
snd_playback_alloc_frames(this);
G_OBJECT_CLASS(playback_channel_client_parent_class)->constructed(object);
RedChannel *red_channel = get_channel();
SndChannel *snd_channel = SND_CHANNEL(red_channel);
scc->on_message_done = snd_playback_on_message_done;
on_message_done = snd_playback_on_message_done;
bool client_can_opus = rcc->test_remote_cap(SPICE_PLAYBACK_CAP_OPUS);
bool client_can_opus = test_remote_cap(SPICE_PLAYBACK_CAP_OPUS);
bool playback_compression =
reds_config_get_playback_compression(red_channel->get_server());
int desired_mode = snd_desired_audio_mode(playback_compression, channel->frequency, client_can_opus);
int desired_mode = snd_desired_audio_mode(playback_compression, snd_channel->frequency, client_can_opus);
if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW) {
if (snd_codec_create(&playback_client->codec, desired_mode, channel->frequency,
if (snd_codec_create(&codec, desired_mode, snd_channel->frequency,
SND_CODEC_ENCODE) == SND_CODEC_OK) {
playback_client->mode = desired_mode;
mode = desired_mode;
} else {
red_channel_warning(red_channel, "create encoder failed");
}
}
spice_debug("playback client %p using mode %s", playback_client,
spice_audio_data_mode_to_string(playback_client->mode));
spice_debug("playback client %p using mode %s", this,
spice_audio_data_mode_to_string(mode));
}
static gboolean playback_channel_client_initable_init(GInitable *initable,
GCancellable *cancellable,
GError **error)
bool PlaybackChannelClient::init()
{
RedChannelClient *rcc = PLAYBACK_CHANNEL_CLIENT(initable);
gboolean success;
RedClient *red_client = rcc->get_client();
SndChannelClient *scc = SND_CHANNEL_CLIENT(initable);
RedChannel *red_channel = rcc->get_channel();
RedClient *red_client = get_client();
SndChannelClient *scc = this;
RedChannel *red_channel = get_channel();
SndChannel *channel = SND_CHANNEL(red_channel);
success = playback_channel_client_parent_initable_iface->init(initable, cancellable, error);
if (!success) {
return FALSE;
if (!SndChannelClient::init()) {
return false;
}
if (!red_client_during_migrate_at_target(red_client)) {
@ -1111,19 +1079,10 @@ static gboolean playback_channel_client_initable_init(GInitable *initable,
}
snd_send(scc);
return TRUE;
return true;
}
static void playback_channel_client_initable_interface_init(GInitableIface *iface)
{
playback_channel_client_parent_initable_iface =
(GInitableIface *) g_type_interface_peek_parent(iface);
iface->init = playback_channel_client_initable_init;
}
static void snd_set_peer(RedChannel *red_channel, RedClient *client, RedStream *stream,
RedChannelCapabilities *caps, GType type)
static void snd_set_peer_common(RedChannel *red_channel)
{
SndChannel *channel = SND_CHANNEL(red_channel);
SndChannelClient *snd_client = snd_channel_get_client(channel);
@ -1132,26 +1091,22 @@ static void snd_set_peer(RedChannel *red_channel, RedClient *client, RedStream *
if (snd_client) {
snd_client->disconnect();
}
snd_client = (SndChannelClient *)
g_initable_new(type,
NULL, NULL,
"channel", channel,
"client", client,
"stream", stream,
"caps", caps,
NULL);
g_warn_if_fail(snd_client != NULL);
}
static void snd_set_playback_peer(RedChannel *red_channel, RedClient *client, RedStream *stream,
G_GNUC_UNUSED int migration,
RedChannelCapabilities *caps)
{
snd_set_peer(red_channel, client, stream, caps,
TYPE_PLAYBACK_CHANNEL_CLIENT);
snd_set_peer_common(red_channel);
auto peer = new PlaybackChannelClient(red_channel, client, stream, caps);
if (!peer->init()) {
peer->unref();
}
}
XXX_CAST(RedChannelClient, SndChannelClient, SND_CHANNEL_CLIENT)
static void snd_migrate_channel_client(RedChannelClient *rcc)
{
snd_set_command(SND_CHANNEL_CLIENT(rcc), SND_MIGRATE_MASK);
@ -1263,57 +1218,42 @@ SPICE_GNUC_VISIBLE void spice_server_set_record_rate(SpiceRecordInstance *sin, u
snd_set_rate(sin->st, frequency, SPICE_RECORD_CAP_OPUS);
}
static void
record_channel_client_finalize(GObject *object)
RecordChannelClient::~RecordChannelClient()
{
RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(object);
snd_codec_destroy(&record_client->codec);
G_OBJECT_CLASS(record_channel_client_parent_class)->finalize(object);
snd_codec_destroy(&codec);
}
static gboolean record_channel_client_initable_init(GInitable *initable,
GCancellable *cancellable,
GError **error)
bool RecordChannelClient::init()
{
gboolean success;
RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(initable);
RedChannel *red_channel = record_client->get_channel();
RedChannel *red_channel = get_channel();
SndChannel *channel = SND_CHANNEL(red_channel);
SndChannelClient *scc = SND_CHANNEL_CLIENT(record_client);
success = record_channel_client_parent_initable_iface->init(initable, cancellable, error);
if (!success) {
if (!SndChannelClient::init()) {
return FALSE;
}
if (channel->volume.volume_nchannels) {
snd_set_command(scc, SND_VOLUME_MUTE_MASK);
snd_set_command(this, SND_VOLUME_MUTE_MASK);
}
if (channel->active) {
record_channel_client_start(scc);
record_channel_client_start(this);
}
snd_send(scc);
snd_send(this);
return TRUE;
}
static void record_channel_client_initable_interface_init(GInitableIface *iface)
{
record_channel_client_parent_initable_iface =
(GInitableIface *) g_type_interface_peek_parent(iface);
iface->init = record_channel_client_initable_init;
}
static void snd_set_record_peer(RedChannel *red_channel, RedClient *client, RedStream *stream,
G_GNUC_UNUSED int migration,
RedChannelCapabilities *caps)
{
snd_set_peer(red_channel, client, stream, caps,
TYPE_RECORD_CHANNEL_CLIENT);
snd_set_peer_common(red_channel);
auto peer = new RecordChannelClient(red_channel, client, stream, caps);
if (!peer->init()) {
peer->unref();
}
}
static void add_channel(SndChannel *channel)
@ -1488,29 +1428,6 @@ void snd_set_playback_compression(bool on)
}
}
static void
snd_channel_client_class_init(SndChannelClientClass *klass)
{
RedChannelClientClass *client_class = RED_CHANNEL_CLIENT_CLASS(klass);
client_class->config_socket = snd_channel_client_config_socket;
client_class->alloc_recv_buf = snd_channel_client_alloc_recv_buf;
client_class->release_recv_buf = snd_channel_client_release_recv_buf;
}
static void
snd_channel_client_init(SndChannelClient *self)
{
}
static void
playback_channel_client_class_init(PlaybackChannelClientClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->constructed = playback_channel_client_constructed;
object_class->finalize = playback_channel_client_finalize;
}
static void snd_playback_alloc_frames(PlaybackChannelClient *playback)
{
int i;
@ -1522,23 +1439,3 @@ static void snd_playback_alloc_frames(PlaybackChannelClient *playback)
snd_playback_free_frame(playback, &playback->frames->items[i]);
}
}
static void
playback_channel_client_init(PlaybackChannelClient *playback)
{
playback->mode = SPICE_AUDIO_DATA_MODE_RAW;
snd_playback_alloc_frames(playback);
}
static void
record_channel_client_class_init(RecordChannelClientClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = record_channel_client_finalize;
}
static void
record_channel_client_init(RecordChannelClient *record)
{
record->mode = SPICE_AUDIO_DATA_MODE_RAW;
}

View File

@ -51,9 +51,6 @@ SPICE_DECLARE_TYPE(RedVmcChannel, red_vmc_channel, VMC_CHANNEL);
SPICE_DECLARE_TYPE(RedVmcChannelPort, red_vmc_channel_port, VMC_CHANNEL_PORT);
#define RED_TYPE_VMC_CHANNEL_PORT red_vmc_channel_port_get_type()
SPICE_DECLARE_TYPE(VmcChannelClient, vmc_channel_client, VMC_CHANNEL_CLIENT);
#define TYPE_VMC_CHANNEL_CLIENT vmc_channel_client_get_type()
SPICE_DECLARE_TYPE(RedVmcChannelUsbredir, red_vmc_channel_usbredir, VMC_CHANNEL_USBREDIR);
#define RED_TYPE_VMC_CHANNEL_USBREDIR red_vmc_channel_usbredir_get_type()
@ -156,16 +153,15 @@ static void red_vmc_channel_port_init(RedVmcChannelPort *self)
G_DEFINE_TYPE(RedVmcChannelPort, red_vmc_channel_port, RED_TYPE_VMC_CHANNEL)
struct VmcChannelClient final: public RedChannelClient
class VmcChannelClient final: public RedChannelClient
{
};
using RedChannelClient::RedChannelClient;
struct VmcChannelClientClass {
RedChannelClientClass parent_class;
virtual uint8_t *alloc_recv_buf(uint16_t type, uint32_t size) override;
virtual void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg) override;
virtual void on_disconnect() override;
};
G_DEFINE_TYPE(VmcChannelClient, vmc_channel_client, RED_TYPE_CHANNEL_CLIENT)
static RedChannelClient *
vmc_channel_client_create(RedChannel *channel, RedClient *client,
RedStream *stream,
@ -271,10 +267,6 @@ enum {
RED_PIPE_ITEM_TYPE_PORT_EVENT,
};
static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type,
uint32_t size,
uint8_t *msg);
/* n is the data size (uncompressed)
* msg_item -- the current pipe item with the uncompressed data
* This function returns:
@ -411,13 +403,13 @@ static void spicevmc_char_dev_remove_client(RedCharDevice *self,
channel->rcc->shutdown();
}
static void spicevmc_red_channel_client_on_disconnect(RedChannelClient *rcc)
void VmcChannelClient::on_disconnect()
{
RedVmcChannel *channel;
SpiceCharDeviceInterface *sif;
RedClient *client = rcc->get_client();
RedClient *client = get_client();
channel = RED_VMC_CHANNEL(rcc->get_channel());
channel = RED_VMC_CHANNEL(get_channel());
/* partial message which wasn't pushed to device */
red_char_device_write_buffer_release(channel->chardev, &channel->recv_from_client_buf);
@ -514,7 +506,7 @@ static bool spicevmc_red_channel_client_handle_message(RedChannelClient *rcc,
uint32_t size,
void *msg)
{
/* NOTE: *msg free by g_free() (when cb to spicevmc_red_channel_release_msg_rcv_buf
/* NOTE: *msg free by g_free() (when cb to VmcChannelClient::release_recv_buf
* with the compressed msg type) */
RedVmcChannel *channel;
SpiceCharDeviceInterface *sif;
@ -557,21 +549,19 @@ static void spicevmc_on_free_self_token(RedCharDevice *self)
channel->rcc->unblock_read();
}
static uint8_t *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type,
uint32_t size)
uint8_t *VmcChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
{
switch (type) {
case SPICE_MSGC_SPICEVMC_DATA: {
RedVmcChannel *channel = RED_VMC_CHANNEL(rcc->get_channel());
RedVmcChannel *channel = RED_VMC_CHANNEL(get_channel());
assert(!channel->recv_from_client_buf);
channel->recv_from_client_buf = red_char_device_write_buffer_get_server(channel->chardev,
size, true);
if (!channel->recv_from_client_buf) {
rcc->block_read();
block_read();
return NULL;
}
return channel->recv_from_client_buf->buf;
@ -583,15 +573,12 @@ static uint8_t *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
}
static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type,
uint32_t size,
uint8_t *msg)
void VmcChannelClient::release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)
{
switch (type) {
case SPICE_MSGC_SPICEVMC_DATA: {
RedVmcChannel *channel = RED_VMC_CHANNEL(rcc->get_channel());
RedVmcChannel *channel = RED_VMC_CHANNEL(get_channel());
/* buffer wasn't pushed to device */
red_char_device_write_buffer_release(channel->chardev, &channel->recv_from_client_buf);
break;
@ -929,36 +916,15 @@ red_char_device_spicevmc_new(SpiceCharDeviceInstance *sin,
NULL);
}
static void
vmc_channel_client_init(VmcChannelClient *self)
{
}
static void
vmc_channel_client_class_init(VmcChannelClientClass *klass)
{
RedChannelClientClass *client_class = RED_CHANNEL_CLIENT_CLASS(klass);
client_class->alloc_recv_buf = spicevmc_red_channel_alloc_msg_rcv_buf;
client_class->release_recv_buf = spicevmc_red_channel_release_msg_rcv_buf;
client_class->on_disconnect = spicevmc_red_channel_client_on_disconnect;
}
static RedChannelClient *
vmc_channel_client_create(RedChannel *channel, RedClient *client,
RedStream *stream,
RedChannelCapabilities *caps)
{
RedChannelClient *rcc;
rcc = (RedChannelClient *)
g_initable_new(TYPE_VMC_CHANNEL_CLIENT,
NULL, NULL,
"channel", channel,
"client", client,
"stream", stream,
"caps", caps,
NULL);
auto rcc = new VmcChannelClient(channel, client, stream, caps);
if (!rcc->init()) {
delete rcc;
rcc = nullptr;
}
return rcc;
}

View File

@ -28,42 +28,26 @@
#include "display-limits.h"
#include "video-stream.h" // TODO remove, put common stuff
#define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type()
#define STREAM_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClient))
#define STREAM_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass))
#define IS_STREAM_CHANNEL_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_STREAM_CHANNEL_CLIENT))
#define IS_STREAM_CHANNEL_CLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_STREAM_CHANNEL_CLIENT))
#define STREAM_CHANNEL_CLIENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass))
typedef struct StreamChannelClient StreamChannelClient;
typedef struct StreamChannelClientClass StreamChannelClientClass;
/* we need to inherit from CommonGraphicsChannelClient
* to get buffer handling */
struct StreamChannelClient final: public CommonGraphicsChannelClient
class StreamChannelClient final: public CommonGraphicsChannelClient
{
protected:
~StreamChannelClient();
public:
using CommonGraphicsChannelClient::CommonGraphicsChannelClient;
bool handle_preferred_video_codec_type(SpiceMsgcDisplayPreferredVideoCodecType *msg);
/* current video stream id, <0 if not initialized or
* we are not sending a stream */
int stream_id;
int stream_id = -1;
/* Array with SPICE_VIDEO_CODEC_TYPE_ENUM_END elements, with the client
* preference order (index) as value */
GArray *client_preferred_video_codecs;
virtual void on_disconnect() override;
};
struct StreamChannelClientClass {
CommonGraphicsChannelClientClass parent_class;
};
GType stream_channel_client_get_type(void) G_GNUC_CONST;
G_DEFINE_TYPE(StreamChannelClient, stream_channel_client, TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT)
struct StreamChannel final: public RedChannel
{
/* current video stream id, <0 if not initialized or
@ -114,37 +98,11 @@ typedef struct StreamDataItem {
#define PRIMARY_SURFACE_ID 0
static void stream_channel_client_on_disconnect(RedChannelClient *rcc);
static bool
stream_channel_handle_preferred_video_codec_type(RedChannelClient *rcc,
SpiceMsgcDisplayPreferredVideoCodecType *msg);
RECORDER(stream_channel_data, 32, "Stream channel data packet");
static void
stream_channel_client_finalize(GObject *object)
StreamChannelClient::~StreamChannelClient()
{
StreamChannelClient *self = STREAM_CHANNEL_CLIENT(object);
g_clear_pointer(&self->client_preferred_video_codecs, g_array_unref);
G_OBJECT_CLASS(stream_channel_client_parent_class)->finalize(object);
}
static void
stream_channel_client_class_init(StreamChannelClientClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
RedChannelClientClass *channel_class = RED_CHANNEL_CLIENT_CLASS(klass);
channel_class->on_disconnect = stream_channel_client_on_disconnect;
object_class->finalize = stream_channel_client_finalize;
}
static void
stream_channel_client_init(StreamChannelClient *client)
{
client->stream_id = -1;
g_clear_pointer(&client_preferred_video_codecs, g_array_unref);
}
static void
@ -155,10 +113,10 @@ request_new_stream(StreamChannel *channel, StreamMsgStartStop *start)
}
}
static void
stream_channel_client_on_disconnect(RedChannelClient *rcc)
void
StreamChannelClient::on_disconnect()
{
StreamChannel *channel = STREAM_CHANNEL(rcc->get_channel());
StreamChannel *channel = STREAM_CHANNEL(get_channel());
// if there are still some client connected keep streaming
// TODO, maybe would be worth sending new codecs if they are better
@ -179,17 +137,11 @@ static StreamChannelClient*
stream_channel_client_new(StreamChannel *channel, RedClient *client, RedStream *stream,
int mig_target, RedChannelCapabilities *caps)
{
StreamChannelClient *rcc;
rcc = (StreamChannelClient *)
g_initable_new(TYPE_STREAM_CHANNEL_CLIENT,
NULL, NULL,
"channel", channel,
"client", client,
"stream", stream,
"caps", caps,
NULL);
auto rcc = new StreamChannelClient(RED_CHANNEL(channel), client, stream, caps);
if (!rcc->init()) {
rcc->unref();
rcc = nullptr;
}
return rcc;
}
@ -227,6 +179,8 @@ marshall_monitors_config(RedChannelClient *rcc, StreamChannel *channel, SpiceMar
spice_marshall_msg_display_monitors_config(m, &msg.config);
}
XXX_CAST(RedChannelClient, StreamChannelClient, STREAM_CHANNEL_CLIENT)
static void
stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
{
@ -329,6 +283,8 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
static bool
handle_message(RedChannelClient *rcc, uint16_t type, uint32_t size, void *msg)
{
StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc);
switch (type) {
case SPICE_MSGC_DISPLAY_INIT:
case SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION:
@ -340,7 +296,7 @@ handle_message(RedChannelClient *rcc, uint16_t type, uint32_t size, void *msg)
/* client should not send this message */
return false;
case SPICE_MSGC_DISPLAY_PREFERRED_VIDEO_CODEC_TYPE:
return stream_channel_handle_preferred_video_codec_type(rcc,
return client->handle_preferred_video_codec_type(
(SpiceMsgcDisplayPreferredVideoCodecType *)msg);
default:
return RedChannelClient::handle_message(rcc, type, size, msg);
@ -407,18 +363,15 @@ stream_channel_get_supported_codecs(StreamChannel *channel, uint8_t *out_codecs)
return num;
}
static bool
stream_channel_handle_preferred_video_codec_type(RedChannelClient *rcc,
SpiceMsgcDisplayPreferredVideoCodecType *msg)
bool
StreamChannelClient::handle_preferred_video_codec_type(SpiceMsgcDisplayPreferredVideoCodecType *msg)
{
StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc);
if (msg->num_of_codecs == 0) {
return true;
}
g_clear_pointer(&client->client_preferred_video_codecs, g_array_unref);
client->client_preferred_video_codecs = video_stream_parse_preferred_codecs(msg);
g_clear_pointer(&client_preferred_video_codecs, g_array_unref);
client_preferred_video_codecs = video_stream_parse_preferred_codecs(msg);
return true;
}

View File

@ -47,30 +47,18 @@ struct RedTestChannelClass
G_DEFINE_TYPE(RedTestChannel, red_test_channel, RED_TYPE_CHANNEL)
SPICE_DECLARE_TYPE(RedTestChannelClient, red_test_channel_client, TEST_CHANNEL_CLIENT);
#define RED_TYPE_TEST_CHANNEL_CLIENT red_test_channel_client_get_type()
struct RedTestChannelClient final: public RedChannelClient
class RedTestChannelClient final: public RedChannelClient
{
using RedChannelClient::RedChannelClient;
virtual uint8_t * alloc_recv_buf(uint16_t type, uint32_t size) override;
virtual void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg) override;
};
struct RedTestChannelClientClass
{
RedChannelClientClass parent_class;
};
G_DEFINE_TYPE(RedTestChannelClient, red_test_channel_client, RED_TYPE_CHANNEL_CLIENT)
static void
red_test_channel_init(RedTestChannel *self)
{
}
static void
red_test_channel_client_init(RedTestChannelClient *self)
{
}
static void
test_channel_send_item(RedChannelClient *rcc, RedPipeItem *item)
{
@ -81,15 +69,9 @@ test_connect_client(RedChannel *channel, RedClient *client, RedStream *stream,
int migration, RedChannelCapabilities *caps)
{
RedChannelClient *rcc;
rcc = (RedChannelClient *)
g_initable_new(RED_TYPE_TEST_CHANNEL_CLIENT,
NULL, NULL,
"channel", channel,
"client", client,
"stream", stream,
"caps", caps,
NULL);
rcc = new RedTestChannelClient(channel, client, stream, caps);
g_assert_nonnull(rcc);
g_assert_true(rcc->init());
// requires an ACK after 10 messages
rcc->ack_set_client_window(10);
@ -115,27 +97,18 @@ red_test_channel_class_init(RedTestChannelClass *klass)
channel_class->connect = test_connect_client;
}
static uint8_t *
red_test_channel_client_alloc_msg_rcv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size)
uint8_t *
RedTestChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
{
return (uint8_t*) g_malloc(size);
}
static void
red_test_channel_client_release_msg_rcv_buf(RedChannelClient *rcc,
uint16_t type, uint32_t size, uint8_t *msg)
void
RedTestChannelClient::release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)
{
g_free(msg);
}
static void
red_test_channel_client_class_init(RedTestChannelClientClass *klass)
{
RedChannelClientClass *client_class = RED_CHANNEL_CLIENT_CLASS(klass);
client_class->alloc_recv_buf = red_test_channel_client_alloc_msg_rcv_buf;
client_class->release_recv_buf = red_test_channel_client_release_msg_rcv_buf;
}
/*
* Main test part