Move thread/dispatching handling to RedChannel

Currently channel threading/handling is spread between RedQxl,
RedWorker and RedChannel.
Move more to RedChannel simplify RedQxl and RedWorker.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
This commit is contained in:
Frediano Ziglio 2019-03-19 19:05:13 +00:00
parent 46efdc1e09
commit 571dc645c5
10 changed files with 159 additions and 263 deletions

View File

@ -228,7 +228,8 @@ static void cursor_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_it
}
CursorChannel* cursor_channel_new(RedsState *server, int id,
const SpiceCoreInterfaceInternal *core)
const SpiceCoreInterfaceInternal *core,
Dispatcher *dispatcher)
{
spice_debug("create cursor channel");
return g_object_new(TYPE_CURSOR_CHANNEL,
@ -238,6 +239,7 @@ CursorChannel* cursor_channel_new(RedsState *server, int id,
"id", id,
"migration-flags", 0,
"handle-acks", TRUE,
"dispatcher", dispatcher,
NULL);
}

View File

@ -21,6 +21,7 @@
#include "common-graphics-channel.h"
#include "red-parse-qxl.h"
#include "dispatcher.h"
G_BEGIN_DECLS
@ -47,17 +48,12 @@ GType cursor_channel_get_type(void) G_GNUC_CONST;
/**
* Create CursorChannel.
* Since CursorChannel is intended to be run in a separate thread,
* it does not register its own client callbacks since they would
* be called from a different thread. Therefore users of this
* class are responsible for registering their own client callbacks
* for CursorChannel. These 'wrapper' client callbacks must forward
* execution on to the CursorChannel thread.
* cursor_channel_client_migrate() and cursor_channel_connect() are
* provided as helper functions and should only be called from the
* CursorChannel thread.
* the function accepts a dispatcher parameter to allows some
* operations to be executed in the channel thread.
*/
CursorChannel* cursor_channel_new(RedsState *server, int id,
const SpiceCoreInterfaceInternal *core);
const SpiceCoreInterfaceInternal *core,
Dispatcher *dispatcher);
void cursor_channel_reset (CursorChannel *cursor);
void cursor_channel_do_init (CursorChannel *cursor);
@ -66,8 +62,6 @@ void cursor_channel_set_mouse_mode(CursorChannel *cursor, uint32
/**
* Connect a new client to CursorChannel.
* This is the equivalent of RedChannel client connect callback.
* See comment on cursor_channel_new.
*/
void cursor_channel_connect (CursorChannel *cursor, RedClient *client,
RedStream *stream,

View File

@ -2227,6 +2227,7 @@ static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces, uint32_t su
DisplayChannel* display_channel_new(RedsState *reds,
QXLInstance *qxl,
const SpiceCoreInterfaceInternal *core,
Dispatcher *dispatcher,
int migrate, int stream_video,
GArray *video_codecs,
uint32_t n_surfaces)
@ -2246,6 +2247,7 @@ DisplayChannel* display_channel_new(RedsState *reds,
"n-surfaces", n_surfaces,
"video-codecs", video_codecs,
"handle-acks", TRUE,
"dispatcher", dispatcher,
NULL);
if (display) {
display_channel_set_stream_video(display, stream_video);

View File

@ -99,6 +99,7 @@ struct Drawable {
DisplayChannel* display_channel_new (RedsState *reds,
QXLInstance *qxl,
const SpiceCoreInterfaceInternal *core,
Dispatcher *dispatcher,
int migrate,
int stream_video,
GArray *video_codecs,

View File

@ -90,6 +90,12 @@ struct RedChannelPrivate
// TODO: when different channel_clients are in different threads
// from Channel -> need to protect!
pthread_t thread_id;
/* Setting dispatcher allows the channel to execute code in the right
* thread.
* thread_id will be used to check the channel thread and automatically
* use the dispatcher if the thread is different.
*/
Dispatcher *dispatcher;
RedsState *reds;
RedStatNode stat;
};
@ -103,7 +109,8 @@ enum {
PROP_TYPE,
PROP_ID,
PROP_HANDLE_ACKS,
PROP_MIGRATION_FLAGS
PROP_MIGRATION_FLAGS,
PROP_DISPATCHER,
};
static void
@ -134,6 +141,9 @@ red_channel_get_property(GObject *object,
case PROP_MIGRATION_FLAGS:
g_value_set_uint(value, self->priv->migration_flags);
break;
case PROP_DISPATCHER:
g_value_set_object(value, self->priv->dispatcher);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
@ -167,6 +177,10 @@ red_channel_set_property(GObject *object,
case PROP_MIGRATION_FLAGS:
self->priv->migration_flags = g_value_get_uint(value);
break;
case PROP_DISPATCHER:
g_clear_object(&self->priv->dispatcher);
self->priv->dispatcher = g_value_dup_object(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
@ -177,6 +191,7 @@ red_channel_finalize(GObject *object)
{
RedChannel *self = RED_CHANNEL(object);
g_clear_object(&self->priv->dispatcher);
red_channel_capabilities_reset(&self->priv->local_caps);
G_OBJECT_CLASS(red_channel_parent_class)->finalize(object);
@ -279,6 +294,14 @@ red_channel_class_init(RedChannelClass *klass)
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_property(object_class, PROP_MIGRATION_FLAGS, spec);
spec = g_param_spec_object("dispatcher", "dispatcher",
"Dispatcher bound to channel thread",
TYPE_DISPATCHER,
G_PARAM_STATIC_STRINGS
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property(object_class, PROP_DISPATCHER, spec);
}
static void
@ -483,11 +506,49 @@ void red_channel_disconnect(RedChannel *channel)
red_channel_foreach_client(channel, red_channel_client_disconnect);
}
typedef struct RedMessageConnect {
RedChannel *channel;
RedClient *client;
RedStream *stream;
RedChannelCapabilities caps;
int migration;
} RedMessageConnect;
static void handle_dispatcher_connect(void *opaque, void *payload)
{
RedMessageConnect *msg = payload;
RedChannel *channel = msg->channel;
channel->priv->client_cbs.connect(channel, msg->client, msg->stream,
msg->migration, &msg->caps);
g_object_unref(msg->client);
red_channel_capabilities_reset(&msg->caps);
}
void red_channel_connect(RedChannel *channel, RedClient *client,
RedStream *stream, int migration,
RedChannelCapabilities *caps)
{
channel->priv->client_cbs.connect(channel, client, stream, migration, caps);
if (channel->priv->dispatcher == NULL ||
pthread_equal(pthread_self(), channel->priv->thread_id)) {
channel->priv->client_cbs.connect(channel, client, stream, migration, caps);
return;
}
Dispatcher *dispatcher = channel->priv->dispatcher;
// get a reference potentially the main channel can be destroyed in
// the main thread causing RedClient to be destroyed before using it
RedMessageConnect payload = {
.channel = channel,
.client = g_object_ref(client),
.stream = stream,
.migration = migration
};
red_channel_capabilities_init(&payload.caps, caps);
dispatcher_send_message_custom(dispatcher, handle_dispatcher_connect,
&payload, sizeof(payload), false);
}
GList *red_channel_get_clients(RedChannel *channel)
@ -690,12 +751,55 @@ const RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *sel
return &self->priv->local_caps;
}
typedef struct RedMessageMigrate {
RedChannelClient *rcc;
} RedMessageMigrate;
static void handle_dispatcher_migrate(void *opaque, void *payload)
{
RedMessageMigrate *msg = payload;
RedChannel *channel = red_channel_client_get_channel(msg->rcc);
channel->priv->client_cbs.migrate(msg->rcc);
g_object_unref(msg->rcc);
}
void red_channel_migrate_client(RedChannel *channel, RedChannelClient *rcc)
{
channel->priv->client_cbs.migrate(rcc);
if (channel->priv->dispatcher == NULL ||
pthread_equal(pthread_self(), channel->priv->thread_id)) {
channel->priv->client_cbs.migrate(rcc);
return;
}
RedMessageMigrate payload = { .rcc = g_object_ref(rcc) };
dispatcher_send_message_custom(channel->priv->dispatcher, handle_dispatcher_migrate,
&payload, sizeof(payload), false);
}
typedef struct RedMessageDisconnect {
RedChannelClient *rcc;
} RedMessageDisconnect;
static void handle_dispatcher_disconnect(void *opaque, void *payload)
{
RedMessageDisconnect *msg = payload;
RedChannel *channel = red_channel_client_get_channel(msg->rcc);
channel->priv->client_cbs.disconnect(msg->rcc);
}
void red_channel_disconnect_client(RedChannel *channel, RedChannelClient *rcc)
{
channel->priv->client_cbs.disconnect(rcc);
if (channel->priv->dispatcher == NULL ||
pthread_equal(pthread_self(), channel->priv->thread_id)) {
channel->priv->client_cbs.disconnect(rcc);
return;
}
// TODO: we turned it to be sync, due to client_destroy . Should we support async? - for this we will need ref count
// for channels
RedMessageDisconnect payload = { .rcc = rcc };
dispatcher_send_message_custom(channel->priv->dispatcher, handle_dispatcher_disconnect,
&payload, sizeof(payload), true);
}

View File

@ -75,102 +75,6 @@ int red_qxl_check_qxl_version(QXLInstance *qxl, int major, int minor)
((qxl_major == major) && (qxl_minor >= minor)));
}
static void red_qxl_set_display_peer(RedChannel *channel, RedClient *client,
RedStream *stream, int migration,
RedChannelCapabilities *caps)
{
RedWorkerMessageDisplayConnect payload = {0,};
Dispatcher *dispatcher;
spice_debug("%s", "");
dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher");
// get a reference potentially the main channel can be destroyed in
// the main thread causing RedClient to be destroyed before using it
payload.client = g_object_ref(client);
payload.stream = stream;
payload.migration = migration;
red_channel_capabilities_init(&payload.caps, caps);
dispatcher_send_message(dispatcher,
RED_WORKER_MESSAGE_DISPLAY_CONNECT,
&payload);
}
static void red_qxl_disconnect_display_peer(RedChannelClient *rcc)
{
RedWorkerMessageDisplayDisconnect payload;
Dispatcher *dispatcher;
RedChannel *channel = red_channel_client_get_channel(rcc);
dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher");
payload.rcc = rcc;
// TODO: we turned it to be sync, due to client_destroy . Should we support async? - for this we will need ref count
// for channels
dispatcher_send_message(dispatcher,
RED_WORKER_MESSAGE_DISPLAY_DISCONNECT,
&payload);
}
static void red_qxl_display_migrate(RedChannelClient *rcc)
{
RedWorkerMessageDisplayMigrate payload;
Dispatcher *dispatcher;
RedChannel *channel = red_channel_client_get_channel(rcc);
dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher");
payload.rcc = g_object_ref(rcc);
dispatcher_send_message(dispatcher,
RED_WORKER_MESSAGE_DISPLAY_MIGRATE,
&payload);
}
static void red_qxl_set_cursor_peer(RedChannel *channel, RedClient *client, RedStream *stream,
int migration,
RedChannelCapabilities *caps)
{
RedWorkerMessageCursorConnect payload = {0,};
Dispatcher *dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher");
// get a reference potentially the main channel can be destroyed in
// the main thread causing RedClient to be destroyed before using it
payload.client = g_object_ref(client);
payload.stream = stream;
payload.migration = migration;
red_channel_capabilities_init(&payload.caps, caps);
dispatcher_send_message(dispatcher,
RED_WORKER_MESSAGE_CURSOR_CONNECT,
&payload);
}
static void red_qxl_disconnect_cursor_peer(RedChannelClient *rcc)
{
RedWorkerMessageCursorDisconnect payload;
Dispatcher *dispatcher;
RedChannel *channel = red_channel_client_get_channel(rcc);
dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher");
payload.rcc = rcc;
dispatcher_send_message(dispatcher,
RED_WORKER_MESSAGE_CURSOR_DISCONNECT,
&payload);
}
static void red_qxl_cursor_migrate(RedChannelClient *rcc)
{
RedWorkerMessageCursorMigrate payload;
Dispatcher *dispatcher;
RedChannel *channel = red_channel_client_get_channel(rcc);
dispatcher = (Dispatcher *)g_object_get_data(G_OBJECT(channel), "dispatcher");
payload.rcc = g_object_ref(rcc);
dispatcher_send_message(dispatcher,
RED_WORKER_MESSAGE_CURSOR_MIGRATE,
&payload);
}
static void red_qxl_update_area(QXLState *qxl_state, uint32_t surface_id,
QXLRect *qxl_area, QXLRect *qxl_dirty_rects,
uint32_t num_dirty_rects, uint32_t clear_dirty_region)
@ -914,8 +818,6 @@ size_t red_qxl_get_monitors_count(const QXLInstance *qxl)
void red_qxl_init(RedsState *reds, QXLInstance *qxl)
{
QXLState *qxl_state;
ClientCbs client_cursor_cbs = { NULL, };
ClientCbs client_display_cbs = { NULL, };
spice_return_if_fail(qxl != NULL);
@ -948,17 +850,7 @@ void red_qxl_init(RedsState *reds, QXLInstance *qxl)
qxl_state->max_monitors = UINT_MAX;
qxl->st = qxl_state;
// TODO: move to their respective channel files
client_cursor_cbs.connect = red_qxl_set_cursor_peer;
client_cursor_cbs.disconnect = red_qxl_disconnect_cursor_peer;
client_cursor_cbs.migrate = red_qxl_cursor_migrate;
client_display_cbs.connect = red_qxl_set_display_peer;
client_display_cbs.disconnect = red_qxl_disconnect_display_peer;
client_display_cbs.migrate = red_qxl_display_migrate;
qxl_state->worker = red_worker_new(qxl, &client_cursor_cbs,
&client_display_cbs);
qxl_state->worker = red_worker_new(qxl);
red_worker_run(qxl_state->worker);
}

View File

@ -1283,9 +1283,6 @@ static void replay_handle_dev_input(QXLWorker *worker, SpiceReplay *replay,
break;
case RED_WORKER_MESSAGE_UPDATE:
// XXX do anything? we record the correct bitmaps already.
case RED_WORKER_MESSAGE_DISPLAY_CONNECT:
// we want to ignore this one - it is sent on client connection, we
// shall have our own clients
case RED_WORKER_MESSAGE_WAKEUP:
// safe to ignore
break;

View File

@ -696,7 +696,7 @@ stream_device_create_channel(StreamDevice *dev)
StreamChannel *stream_channel = stream_channel_new(reds, id);
CursorChannel *cursor_channel = cursor_channel_new(reds, id, core);
CursorChannel *cursor_channel = cursor_channel_new(reds, id, core, NULL);
ClientCbs client_cbs = { NULL, };
client_cbs.connect = (channel_client_connect_proc) cursor_channel_connect;
client_cbs.migrate = cursor_channel_client_migrate;

View File

@ -695,20 +695,19 @@ static void handle_dev_create_primary_surface_async(void *opaque, void *payload)
red_qxl_async_complete(worker->qxl, msg->base.cookie);
}
static void handle_dev_display_connect(void *opaque, void *payload)
static void
handle_dev_display_connect(RedChannel *channel, RedClient *client,
RedStream *stream, int migration,
RedChannelCapabilities *caps)
{
RedWorkerMessageDisplayConnect *msg = payload;
RedWorker *worker = opaque;
DisplayChannel *display = worker->display_channel;
DisplayChannel *display = DISPLAY_CHANNEL(channel);
DisplayChannelClient *dcc;
RedWorker *worker = g_object_get_data(G_OBJECT(channel), "worker");
spice_debug("connect new client");
spice_return_if_fail(display);
dcc = dcc_new(display, msg->client, msg->stream, msg->migration, &msg->caps,
dcc = dcc_new(display, client, stream, migration, caps,
worker->image_compression, worker->jpeg_state, worker->zlib_glz_state);
g_object_unref(msg->client);
red_channel_capabilities_reset(&msg->caps);
if (!dcc) {
return;
}
@ -717,30 +716,23 @@ static void handle_dev_display_connect(void *opaque, void *payload)
dcc_start(dcc);
}
static void handle_dev_display_disconnect(void *opaque, void *payload)
static void
handle_dev_display_disconnect(RedChannelClient *rcc)
{
RedWorkerMessageDisplayDisconnect *msg = payload;
RedChannelClient *rcc = msg->rcc;
RedWorker *worker = opaque;
RedChannel *channel = red_channel_client_get_channel(rcc);
RedWorker *worker = g_object_get_data(G_OBJECT(channel), "worker");
spice_debug("disconnect display client");
spice_assert(rcc);
guest_set_client_capabilities(worker);
red_channel_client_disconnect(rcc);
}
static void handle_dev_display_migrate(void *opaque, void *payload)
static void handle_dev_display_migrate(RedChannelClient *rcc)
{
RedWorkerMessageDisplayMigrate *msg = payload;
RedWorker *worker = opaque;
RedChannelClient *rcc = msg->rcc;
spice_debug("migrate display client");
spice_assert(rcc);
red_migrate_display(worker->display_channel, rcc);
g_object_unref(rcc);
DisplayChannel *display = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
red_migrate_display(display, rcc);
}
static inline uint32_t qxl_monitors_config_size(uint32_t heads)
@ -792,40 +784,6 @@ async_complete:
red_qxl_async_complete(worker->qxl, msg->base.cookie);
}
/* TODO: special, perhaps use another dispatcher? */
static void handle_dev_cursor_connect(void *opaque, void *payload)
{
RedWorkerMessageCursorConnect *msg = payload;
RedWorker *worker = opaque;
spice_debug("cursor connect");
cursor_channel_connect(worker->cursor_channel,
msg->client, msg->stream, msg->migration,
&msg->caps);
g_object_unref(msg->client);
red_channel_capabilities_reset(&msg->caps);
}
static void handle_dev_cursor_disconnect(void *opaque, void *payload)
{
RedWorkerMessageCursorDisconnect *msg = payload;
RedChannelClient *rcc = msg->rcc;
spice_debug("disconnect cursor client");
spice_return_if_fail(rcc);
red_channel_client_disconnect(rcc);
}
static void handle_dev_cursor_migrate(void *opaque, void *payload)
{
RedWorkerMessageCursorMigrate *msg = payload;
RedChannelClient *rcc = msg->rcc;
spice_debug("migrate cursor client");
cursor_channel_client_migrate(rcc);
g_object_unref(rcc);
}
static void handle_dev_set_compression(void *opaque, void *payload)
{
RedWorkerMessageSetCompression *msg = payload;
@ -999,36 +957,6 @@ static void worker_dispatcher_record(void *opaque, uint32_t message_type, void *
static void register_callbacks(Dispatcher *dispatcher)
{
/* TODO: register cursor & display specific msg in respective channel files */
dispatcher_register_handler(dispatcher,
RED_WORKER_MESSAGE_DISPLAY_CONNECT,
handle_dev_display_connect,
sizeof(RedWorkerMessageDisplayConnect),
false);
dispatcher_register_handler(dispatcher,
RED_WORKER_MESSAGE_DISPLAY_DISCONNECT,
handle_dev_display_disconnect,
sizeof(RedWorkerMessageDisplayDisconnect),
true);
dispatcher_register_handler(dispatcher,
RED_WORKER_MESSAGE_DISPLAY_MIGRATE,
handle_dev_display_migrate,
sizeof(RedWorkerMessageDisplayMigrate),
false);
dispatcher_register_handler(dispatcher,
RED_WORKER_MESSAGE_CURSOR_CONNECT,
handle_dev_cursor_connect,
sizeof(RedWorkerMessageCursorConnect),
false);
dispatcher_register_handler(dispatcher,
RED_WORKER_MESSAGE_CURSOR_DISCONNECT,
handle_dev_cursor_disconnect,
sizeof(RedWorkerMessageCursorDisconnect),
true);
dispatcher_register_handler(dispatcher,
RED_WORKER_MESSAGE_CURSOR_MIGRATE,
handle_dev_cursor_migrate,
sizeof(RedWorkerMessageCursorMigrate),
false);
dispatcher_register_handler(dispatcher,
RED_WORKER_MESSAGE_UPDATE,
handle_dev_update,
@ -1260,9 +1188,7 @@ static GSourceFuncs worker_source_funcs = {
.dispatch = worker_source_dispatch,
};
RedWorker* red_worker_new(QXLInstance *qxl,
const ClientCbs *client_cursor_cbs,
const ClientCbs *client_display_cbs)
RedWorker* red_worker_new(QXLInstance *qxl)
{
QXLDevInitInfo init_info;
RedWorker *worker;
@ -1318,21 +1244,31 @@ RedWorker* red_worker_new(QXLInstance *qxl,
worker->event_timeout = INF_EVENT_WAIT;
worker->cursor_channel = cursor_channel_new(reds, qxl->id,
&worker->core);
&worker->core, dispatcher);
channel = RED_CHANNEL(worker->cursor_channel);
red_channel_init_stat_node(channel, &worker->stat, "cursor_channel");
red_channel_register_client_cbs(channel, client_cursor_cbs);
g_object_set_data(G_OBJECT(channel), "dispatcher", dispatcher);
ClientCbs client_cursor_cbs = { NULL, };
client_cursor_cbs.connect = (channel_client_connect_proc) cursor_channel_connect;
client_cursor_cbs.disconnect = NULL;
client_cursor_cbs.migrate = cursor_channel_client_migrate;
red_channel_register_client_cbs(channel, &client_cursor_cbs);
// TODO: handle seamless migration. Temp, setting migrate to FALSE
worker->display_channel = display_channel_new(reds, qxl, &worker->core, FALSE,
worker->display_channel = display_channel_new(reds, qxl, &worker->core, dispatcher,
FALSE,
reds_get_streaming_video(reds),
reds_get_video_codecs(reds),
init_info.n_surfaces);
channel = RED_CHANNEL(worker->display_channel);
red_channel_init_stat_node(channel, &worker->stat, "display_channel");
red_channel_register_client_cbs(channel, client_display_cbs);
g_object_set_data(G_OBJECT(channel), "dispatcher", dispatcher);
g_object_set_data(G_OBJECT(channel), "worker", worker);
ClientCbs client_display_cbs = { NULL, };
client_display_cbs.connect = handle_dev_display_connect;
client_display_cbs.disconnect = handle_dev_display_disconnect;
client_display_cbs.migrate = handle_dev_display_migrate;
red_channel_register_client_cbs(channel, &client_display_cbs);
return worker;
}

View File

@ -28,9 +28,7 @@
typedef struct RedWorker RedWorker;
RedWorker* red_worker_new(QXLInstance *qxl,
const ClientCbs *client_cursor_cbs,
const ClientCbs *client_display_cbs);
RedWorker* red_worker_new(QXLInstance *qxl);
bool red_worker_run(RedWorker *worker);
void red_worker_free(RedWorker *worker);
@ -51,14 +49,14 @@ enum {
RED_WORKER_MESSAGE_OOM,
RED_WORKER_MESSAGE_READY, /* unused */
RED_WORKER_MESSAGE_DISPLAY_CONNECT,
RED_WORKER_MESSAGE_DISPLAY_DISCONNECT,
RED_WORKER_MESSAGE_DISPLAY_MIGRATE,
RED_WORKER_MESSAGE_DISPLAY_CONNECT_DEPRECATED,
RED_WORKER_MESSAGE_DISPLAY_DISCONNECT_DEPRECATED,
RED_WORKER_MESSAGE_DISPLAY_MIGRATE_DEPRECATED,
RED_WORKER_MESSAGE_START,
RED_WORKER_MESSAGE_STOP,
RED_WORKER_MESSAGE_CURSOR_CONNECT,
RED_WORKER_MESSAGE_CURSOR_DISCONNECT,
RED_WORKER_MESSAGE_CURSOR_MIGRATE,
RED_WORKER_MESSAGE_CURSOR_CONNECT_DEPRECATED,
RED_WORKER_MESSAGE_CURSOR_DISCONNECT_DEPRECATED,
RED_WORKER_MESSAGE_CURSOR_MIGRATE_DEPRECATED,
RED_WORKER_MESSAGE_SET_COMPRESSION,
RED_WORKER_MESSAGE_SET_STREAMING_VIDEO,
RED_WORKER_MESSAGE_SET_MOUSE_MODE,
@ -97,36 +95,6 @@ enum {
RED_WORKER_MESSAGE_COUNT // LAST
};
typedef struct RedWorkerMessageDisplayConnect {
RedClient * client;
RedStream * stream;
RedChannelCapabilities caps; // red_worker should reset
int migration;
} RedWorkerMessageDisplayConnect;
typedef struct RedWorkerMessageDisplayDisconnect {
RedChannelClient *rcc;
} RedWorkerMessageDisplayDisconnect;
typedef struct RedWorkerMessageDisplayMigrate {
RedChannelClient *rcc;
} RedWorkerMessageDisplayMigrate;
typedef struct RedWorkerMessageCursorConnect {
RedClient *client;
RedStream *stream;
int migration;
RedChannelCapabilities caps; // red_worker should reset
} RedWorkerMessageCursorConnect;
typedef struct RedWorkerMessageCursorDisconnect {
RedChannelClient *rcc;
} RedWorkerMessageCursorDisconnect;
typedef struct RedWorkerMessageCursorMigrate {
RedChannelClient *rcc;
} RedWorkerMessageCursorMigrate;
typedef struct RedWorkerMessageUpdate {
uint32_t surface_id;
QXLRect * qxl_area;