mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-25 22:18:58 +00:00
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:
parent
38cd152952
commit
176970f3f1
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
216
server/dcc.cpp
216
server/dcc.cpp
@ -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
|
||||
|
||||
42
server/dcc.h
42
server/dcc.h
@ -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)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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_ */
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
353
server/sound.cpp
353
server/sound.cpp
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user