[Spice-commits] 10 commits - server/cursor-channel.c server/cursor-channel-client.h server/cursor-channel.h server/dispatcher.c server/dispatcher.h server/display-channel.c server/display-channel.h server/display-channel-private.h server/inputs-channel.c server/main-channel.c server/red-channel.c server/red-channel.h server/red-qxl.c server/red-replay-qxl.c server/reds.c server/red-stream-device.c server/red-worker.c server/red-worker.h server/smartcard.c server/sound.c server/spicevmc.c server/stream-channel.c server/tests

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Thu Mar 28 20:22:07 UTC 2019


 server/cursor-channel-client.h   |    6 
 server/cursor-channel.c          |   17 ++
 server/cursor-channel.h          |   31 ----
 server/dispatcher.c              |   79 ++++++++----
 server/dispatcher.h              |   15 ++
 server/display-channel-private.h |    1 
 server/display-channel.c         |  120 ++++++++++++++++++
 server/display-channel.h         |    5 
 server/inputs-channel.c          |    9 -
 server/main-channel.c            |    7 -
 server/red-channel.c             |  140 ++++++++++++++++++---
 server/red-channel.h             |   19 +-
 server/red-qxl.c                 |  126 ++-----------------
 server/red-replay-qxl.c          |    3 
 server/red-stream-device.c       |    7 -
 server/red-worker.c              |  252 ++-------------------------------------
 server/red-worker.h              |   48 +------
 server/reds.c                    |   34 ++++-
 server/smartcard.c               |    6 
 server/sound.c                   |   18 +-
 server/spicevmc.c                |    7 -
 server/stream-channel.c          |    5 
 server/tests/test-channel.c      |   13 --
 23 files changed, 443 insertions(+), 525 deletions(-)

New commits:
commit 62bc14d81d248a75faa20c398bf362ce30cc3175
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Thu Mar 21 00:04:43 2019 +0000

    display-channel: Inline red_migrate_display function
    
    The only caller only called that function.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/display-channel.c b/server/display-channel.c
index 7b833e4c..9f3555e5 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -2633,8 +2633,10 @@ static void display_channel_disconnect(RedChannelClient *rcc)
     red_channel_client_disconnect(rcc);
 }
 
-static void red_migrate_display(DisplayChannel *display, RedChannelClient *rcc)
+static void display_channel_migrate(RedChannelClient *rcc)
 {
+    DisplayChannel *display = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
+
     /* We need to stop the streams, and to send upgrade_items to the client.
      * Otherwise, (1) the client might display lossy regions that we don't track
      * (streams are not part of the migration data) (2) streams_timeout may occur
@@ -2651,12 +2653,6 @@ static void red_migrate_display(DisplayChannel *display, RedChannelClient *rcc)
     }
 }
 
-static void display_channel_migrate(RedChannelClient *rcc)
-{
-    DisplayChannel *display = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
-    red_migrate_display(display, rcc);
-}
-
 void display_channel_set_image_compression(DisplayChannel *display,
                                            SpiceImageCompression image_compression)
 {
commit ee5cdb7e2f03d832dc6ecc8e1333c2419978328c
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Wed Mar 20 23:34:48 2019 +0000

    Move image_compression field from RedWorker to DisplayChannel
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/display-channel-private.h b/server/display-channel-private.h
index 58179531..4cdae8dc 100644
--- a/server/display-channel-private.h
+++ b/server/display-channel-private.h
@@ -87,6 +87,7 @@ struct DisplayChannelPrivate
     MonitorsConfig *monitors_config;
 
     uint32_t renderer;
+    SpiceImageCompression image_compression;
     int enable_jpeg;
     int enable_zlib_glz_wrap;
 
diff --git a/server/display-channel.c b/server/display-channel.c
index c2129c71..7b833e4c 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -2273,6 +2273,7 @@ display_channel_init(DisplayChannel *self)
     /* must be manually allocated here since g_type_class_add_private() only
      * supports structs smaller than 64k */
     self->priv = g_new0(DisplayChannelPrivate, 1);
+    self->priv->image_compression = SPICE_IMAGE_COMPRESSION_AUTO_GLZ;
     self->priv->pub = self;
 
     image_encoder_shared_init(&self->priv->encoder_shared_data);
@@ -2611,10 +2612,9 @@ display_channel_connect(RedChannel *channel, RedClient *client,
 
     spice_debug("connect new client");
 
-    // FIXME not sure how safe is reading directly from reds
     SpiceServer *reds = red_channel_get_server(channel);
     dcc = dcc_new(display, client, stream, migration, caps,
-                  spice_server_get_image_compression(reds), reds_get_jpeg_state(reds),
+                  display->priv->image_compression, reds_get_jpeg_state(reds),
                   reds_get_zlib_glz_state(reds));
     if (!dcc) {
         return;
@@ -2656,3 +2656,9 @@ static void display_channel_migrate(RedChannelClient *rcc)
     DisplayChannel *display = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
     red_migrate_display(display, rcc);
 }
+
+void display_channel_set_image_compression(DisplayChannel *display,
+                                           SpiceImageCompression image_compression)
+{
+    display->priv->image_compression = image_compression;
+}
diff --git a/server/display-channel.h b/server/display-channel.h
index 8dfdf523..6af9e129 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -159,6 +159,8 @@ void display_channel_reset_image_cache(DisplayChannel *self);
 void display_channel_debug_oom(DisplayChannel *display, const char *msg);
 
 void display_channel_update_qxl_running(DisplayChannel *display, bool running);
+void display_channel_set_image_compression(DisplayChannel *display,
+                                           SpiceImageCompression image_compression);
 
 G_END_DECLS
 
diff --git a/server/red-worker.c b/server/red-worker.c
index 7382e24d..bc63fc34 100644
--- a/server/red-worker.c
+++ b/server/red-worker.c
@@ -76,8 +76,6 @@ struct RedWorker {
 
     RedMemSlotInfo mem_slots;
 
-    SpiceImageCompression image_compression;
-
     uint32_t process_display_generation;
     RedStatNode stat;
     RedStatCounter wakeup_counter;
@@ -684,7 +682,7 @@ static void handle_dev_set_compression(void *opaque, void *payload)
     RedWorker *worker = opaque;
     SpiceImageCompression image_compression = msg->image_compression;
 
-    worker->image_compression = image_compression;
+    display_channel_set_image_compression(worker->display_channel, image_compression);
 
     display_channel_compress_stats_print(worker->display_channel);
     display_channel_compress_stats_reset(worker->display_channel);
@@ -1078,7 +1076,6 @@ RedWorker* red_worker_new(QXLInstance *qxl)
         dispatcher_register_universal_handler(dispatcher, worker_dispatcher_record);
     }
 
-    worker->image_compression = spice_server_get_image_compression(reds);
     worker->driver_cap_monitors_config = 0;
     char worker_str[SPICE_STAT_NODE_NAME_MAX];
     snprintf(worker_str, sizeof(worker_str), "display[%d]", worker->qxl->id & 0xff);
@@ -1120,6 +1117,8 @@ RedWorker* red_worker_new(QXLInstance *qxl)
                                                   init_info.n_surfaces);
     channel = RED_CHANNEL(worker->display_channel);
     red_channel_init_stat_node(channel, &worker->stat, "display_channel");
+    display_channel_set_image_compression(worker->display_channel,
+                                          spice_server_get_image_compression(reds));
 
     return worker;
 }
commit 163b3c39b607547e224ad2ceb209c1664e11e349
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Wed Mar 20 21:50:08 2019 +0000

    Check image compression value earlier
    
    Do not check it after assigning to reds->config->image_compression,
    check the value as soon as possible.
    This prevent potential invalid settings.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/red-worker.c b/server/red-worker.c
index d883f419..7382e24d 100644
--- a/server/red-worker.c
+++ b/server/red-worker.c
@@ -684,34 +684,6 @@ static void handle_dev_set_compression(void *opaque, void *payload)
     RedWorker *worker = opaque;
     SpiceImageCompression image_compression = msg->image_compression;
 
-    switch (image_compression) {
-    case SPICE_IMAGE_COMPRESSION_AUTO_LZ:
-        spice_debug("ic auto_lz");
-        break;
-    case SPICE_IMAGE_COMPRESSION_AUTO_GLZ:
-        spice_debug("ic auto_glz");
-        break;
-    case SPICE_IMAGE_COMPRESSION_QUIC:
-        spice_debug("ic quic");
-        break;
-#ifdef USE_LZ4
-    case SPICE_IMAGE_COMPRESSION_LZ4:
-        spice_debug("ic lz4");
-        break;
-#endif
-    case SPICE_IMAGE_COMPRESSION_LZ:
-        spice_debug("ic lz");
-        break;
-    case SPICE_IMAGE_COMPRESSION_GLZ:
-        spice_debug("ic glz");
-        break;
-    case SPICE_IMAGE_COMPRESSION_OFF:
-        spice_debug("ic off");
-        break;
-    default:
-        spice_warning("ic invalid");
-        image_compression = worker->image_compression;
-    }
     worker->image_compression = image_compression;
 
     display_channel_compress_stats_print(worker->display_channel);
diff --git a/server/reds.c b/server/reds.c
index 14e5728b..28542bd0 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3010,12 +3010,40 @@ static inline void on_activating_ticketing(RedsState *reds)
     }
 }
 
-static void reds_config_set_image_compression(RedsState *reds, SpiceImageCompression val)
+static void reds_config_set_image_compression(RedsState *reds, SpiceImageCompression image_compression)
 {
-    if (val == reds->config->image_compression) {
+    if (image_compression == reds->config->image_compression) {
         return;
     }
-    reds->config->image_compression = val;
+    switch (image_compression) {
+    case SPICE_IMAGE_COMPRESSION_AUTO_LZ:
+        spice_debug("ic auto_lz");
+        break;
+    case SPICE_IMAGE_COMPRESSION_AUTO_GLZ:
+        spice_debug("ic auto_glz");
+        break;
+    case SPICE_IMAGE_COMPRESSION_QUIC:
+        spice_debug("ic quic");
+        break;
+#ifdef USE_LZ4
+    case SPICE_IMAGE_COMPRESSION_LZ4:
+        spice_debug("ic lz4");
+        break;
+#endif
+    case SPICE_IMAGE_COMPRESSION_LZ:
+        spice_debug("ic lz");
+        break;
+    case SPICE_IMAGE_COMPRESSION_GLZ:
+        spice_debug("ic glz");
+        break;
+    case SPICE_IMAGE_COMPRESSION_OFF:
+        spice_debug("ic off");
+        break;
+    default:
+        spice_warning("ic invalid");
+        return;
+    }
+    reds->config->image_compression = image_compression;
     reds_on_ic_change(reds);
 }
 
commit bd55e74f64d008ed26d123192c51a0f01b365495
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Wed Mar 20 21:52:42 2019 +0000

    red-worker: Remove only assigned fields
    
    DisplayChannelClient get them directly from reds (they are changed
    only during initialisation so they can be read freely from any
    thread).
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/red-worker.c b/server/red-worker.c
index 129f9f93..d883f419 100644
--- a/server/red-worker.c
+++ b/server/red-worker.c
@@ -77,8 +77,6 @@ struct RedWorker {
     RedMemSlotInfo mem_slots;
 
     SpiceImageCompression image_compression;
-    spice_wan_compression_t jpeg_state;
-    spice_wan_compression_t zlib_glz_state;
 
     uint32_t process_display_generation;
     RedStatNode stat;
@@ -1109,8 +1107,6 @@ RedWorker* red_worker_new(QXLInstance *qxl)
     }
 
     worker->image_compression = spice_server_get_image_compression(reds);
-    worker->jpeg_state = reds_get_jpeg_state(reds);
-    worker->zlib_glz_state = reds_get_zlib_glz_state(reds);
     worker->driver_cap_monitors_config = 0;
     char worker_str[SPICE_STAT_NODE_NAME_MAX];
     snprintf(worker_str, sizeof(worker_str), "display[%d]", worker->qxl->id & 0xff);
commit 14403117b5ed4cb9d6dec1dda0cd2c1bbd0ad348
Author: Jonathon Jongsma <jjongsma at redhat.com>
Date:   Wed Mar 20 09:28:34 2019 +0000

    Make channel client callbacks virtual functions
    
    Rather than having an API to register client callbacks for each channel
    type, make them vfuncs.
    
    Since the client callbacks are registered identically for each channel
    of the same type, it doesn't make sense for to require these to be
    registered separately for each object.  It's cleaner to have these be
    per-class properties, so they've been converted to virtual functions.
    
    Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/cursor-channel-client.h b/server/cursor-channel-client.h
index 56b3b312..4deae535 100644
--- a/server/cursor-channel-client.h
+++ b/server/cursor-channel-client.h
@@ -75,6 +75,12 @@ enum {
     RED_PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE,
 };
 
+/**
+ * Migrate a client channel from a CursorChannel.
+ * This is the equivalent of RedChannel client migrate callback.
+ */
+void                 cursor_channel_client_migrate(RedChannelClient *client);
+
 G_END_DECLS
 
 #endif /* CURSOR_CHANNEL_CLIENT_H_ */
diff --git a/server/cursor-channel.c b/server/cursor-channel.c
index e8af01b0..d936b791 100644
--- a/server/cursor-channel.c
+++ b/server/cursor-channel.c
@@ -335,9 +335,12 @@ void cursor_channel_set_mouse_mode(CursorChannel *cursor, uint32_t mode)
     cursor->mouse_mode = mode;
 }
 
-void cursor_channel_connect(CursorChannel *cursor, RedClient *client, RedStream *stream,
-                            int migrate,
-                            RedChannelCapabilities *caps)
+/**
+ * Connect a new client to CursorChannel.
+ */
+static void
+cursor_channel_connect(CursorChannel *cursor, RedClient *client, RedStream *stream,
+                       int migrate, RedChannelCapabilities *caps)
 {
     CursorChannelClient *ccc;
 
@@ -392,6 +395,10 @@ cursor_channel_class_init(CursorChannelClass *klass)
     channel_class->handle_message = red_channel_client_handle_message;
 
     channel_class->send_item = cursor_channel_send_item;
+
+    // client callbacks
+    channel_class->connect = (channel_client_connect_proc) cursor_channel_connect;
+    channel_class->migrate = cursor_channel_client_migrate;
 }
 
 static void
diff --git a/server/cursor-channel.h b/server/cursor-channel.h
index 332aa987..dc48279a 100644
--- a/server/cursor-channel.h
+++ b/server/cursor-channel.h
@@ -60,21 +60,6 @@ void                 cursor_channel_do_init     (CursorChannel *cursor);
 void                 cursor_channel_process_cmd (CursorChannel *cursor, RedCursorCmd *cursor_cmd);
 void                 cursor_channel_set_mouse_mode(CursorChannel *cursor, uint32_t mode);
 
-/**
- * Connect a new client to CursorChannel.
- */
-void                 cursor_channel_connect     (CursorChannel *cursor, RedClient *client,
-                                                 RedStream *stream,
-                                                 int migrate,
-                                                 RedChannelCapabilities *caps);
-
-/**
- * Migrate a client channel from a CursorChannel.
- * This is the equivalent of RedChannel client migrate callback.
- * See comment on cursor_channel_new.
- */
-void                 cursor_channel_client_migrate(RedChannelClient *client);
-
 G_END_DECLS
 
 #endif /* CURSOR_CHANNEL_H_ */
diff --git a/server/display-channel.c b/server/display-channel.c
index e9368668..c2129c71 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -26,6 +26,12 @@
 
 G_DEFINE_TYPE(DisplayChannel, display_channel, TYPE_COMMON_GRAPHICS_CHANNEL)
 
+static void display_channel_connect(RedChannel *channel, RedClient *client,
+                                    RedStream *stream, int migration,
+                                    RedChannelCapabilities *caps);
+static void display_channel_disconnect(RedChannelClient *rcc);
+static void display_channel_migrate(RedChannelClient *rcc);
+
 enum {
     PROP0,
     PROP_N_SURFACES,
@@ -2500,6 +2506,11 @@ display_channel_class_init(DisplayChannelClass *klass)
     channel_class->handle_migrate_data = handle_migrate_data;
     channel_class->handle_migrate_data_get_serial = handle_migrate_data_get_serial;
 
+    // client callbacks
+    channel_class->connect = display_channel_connect;
+    channel_class->disconnect = display_channel_disconnect;
+    channel_class->migrate = display_channel_migrate;
+
     g_object_class_install_property(object_class,
                                     PROP_N_SURFACES,
                                     g_param_spec_uint("n-surfaces",
@@ -2590,7 +2601,7 @@ void display_channel_update_qxl_running(DisplayChannel *display, bool running)
     }
 }
 
-void
+static void
 display_channel_connect(RedChannel *channel, RedClient *client,
                         RedStream *stream, int migration,
                         RedChannelCapabilities *caps)
@@ -2613,7 +2624,7 @@ display_channel_connect(RedChannel *channel, RedClient *client,
     dcc_start(dcc);
 }
 
-void display_channel_disconnect(RedChannelClient *rcc)
+static void display_channel_disconnect(RedChannelClient *rcc)
 {
     DisplayChannel *display = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
 
@@ -2640,7 +2651,7 @@ static void red_migrate_display(DisplayChannel *display, RedChannelClient *rcc)
     }
 }
 
-void display_channel_migrate(RedChannelClient *rcc)
+static void display_channel_migrate(RedChannelClient *rcc)
 {
     DisplayChannel *display = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
     red_migrate_display(display, rcc);
diff --git a/server/display-channel.h b/server/display-channel.h
index 7dfedd75..8dfdf523 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -159,11 +159,6 @@ void display_channel_reset_image_cache(DisplayChannel *self);
 void display_channel_debug_oom(DisplayChannel *display, const char *msg);
 
 void display_channel_update_qxl_running(DisplayChannel *display, bool running);
-void display_channel_connect(RedChannel *channel, RedClient *client,
-                             RedStream *stream, int migration,
-                             RedChannelCapabilities *caps);
-void display_channel_disconnect(RedChannelClient *rcc);
-void display_channel_migrate(RedChannelClient *rcc);
 
 G_END_DECLS
 
diff --git a/server/inputs-channel.c b/server/inputs-channel.c
index a7df62e2..eff5421a 100644
--- a/server/inputs-channel.c
+++ b/server/inputs-channel.c
@@ -542,17 +542,12 @@ InputsChannel* inputs_channel_new(RedsState *reds)
 static void
 inputs_channel_constructed(GObject *object)
 {
-    ClientCbs client_cbs = { NULL, };
     InputsChannel *self = INPUTS_CHANNEL(object);
     RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
     SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(RED_CHANNEL(self));
 
     G_OBJECT_CLASS(inputs_channel_parent_class)->constructed(object);
 
-    client_cbs.connect = inputs_connect;
-    client_cbs.migrate = inputs_migrate;
-    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs);
-
     red_channel_set_cap(RED_CHANNEL(self), SPICE_INPUTS_CAP_KEY_SCANCODE);
     reds_register_channel(reds, RED_CHANNEL(self));
 
@@ -596,6 +591,10 @@ inputs_channel_class_init(InputsChannelClass *klass)
     channel_class->send_item = inputs_channel_send_item;
     channel_class->handle_migrate_data = inputs_channel_handle_migrate_data;
     channel_class->handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark;
+
+    // client callbacks
+    channel_class->connect = inputs_connect;
+    channel_class->migrate = inputs_migrate;
 }
 
 static SpiceKbdInstance* inputs_channel_get_keyboard(InputsChannel *inputs)
diff --git a/server/main-channel.c b/server/main-channel.c
index f866fb4a..a48e74be 100644
--- a/server/main-channel.c
+++ b/server/main-channel.c
@@ -264,15 +264,11 @@ static void
 main_channel_constructed(GObject *object)
 {
     MainChannel *self = MAIN_CHANNEL(object);
-    ClientCbs client_cbs = { NULL, };
 
     G_OBJECT_CLASS(main_channel_parent_class)->constructed(object);
 
     red_channel_set_cap(RED_CHANNEL(self), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
     red_channel_set_cap(RED_CHANNEL(self), SPICE_MAIN_CAP_SEAMLESS_MIGRATE);
-
-    client_cbs.migrate = main_channel_client_migrate;
-    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs);
 }
 
 static void
@@ -295,6 +291,9 @@ main_channel_class_init(MainChannelClass *klass)
     channel_class->send_item = main_channel_client_send_item;
     channel_class->handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark;
     channel_class->handle_migrate_data = main_channel_handle_migrate_data;
+
+    // client callbacks
+    channel_class->migrate = main_channel_client_migrate;
 }
 
 static int main_channel_connect_semi_seamless(MainChannel *main_channel)
diff --git a/server/red-channel.c b/server/red-channel.c
index 8d4992fa..121c7e47 100644
--- a/server/red-channel.c
+++ b/server/red-channel.c
@@ -86,7 +86,6 @@ struct RedChannelPrivate
     RedChannelCapabilities local_caps;
     uint32_t migration_flags;
 
-    ClientCbs client_cbs;
     // TODO: when different channel_clients are in different threads
     // from Channel -> need to protect!
     pthread_t thread_id;
@@ -302,6 +301,10 @@ red_channel_class_init(RedChannelClass *klass)
                                | G_PARAM_READWRITE
                                | G_PARAM_CONSTRUCT_ONLY);
     g_object_class_install_property(object_class, PROP_DISPATCHER, spec);
+
+    klass->connect = red_channel_client_default_connect;
+    klass->disconnect = red_channel_client_default_disconnect;
+    klass->migrate = red_channel_client_default_migrate;
 }
 
 static void
@@ -311,10 +314,6 @@ red_channel_init(RedChannel *self)
 
     red_channel_set_common_cap(self, SPICE_COMMON_CAP_MINI_HEADER);
     self->priv->thread_id = pthread_self();
-
-    self->priv->client_cbs.connect = red_channel_client_default_connect;
-    self->priv->client_cbs.disconnect = red_channel_client_default_disconnect;
-    self->priv->client_cbs.migrate = red_channel_client_default_migrate;
 }
 
 // utility to avoid possible invalid function cast
@@ -380,20 +379,6 @@ const RedStatNode *red_channel_get_stat_node(RedChannel *channel)
     return &channel->priv->stat;
 }
 
-void red_channel_register_client_cbs(RedChannel *channel, const ClientCbs *client_cbs)
-{
-    spice_assert(client_cbs->connect || channel->priv->type == SPICE_CHANNEL_MAIN);
-    channel->priv->client_cbs.connect = client_cbs->connect;
-
-    if (client_cbs->disconnect) {
-        channel->priv->client_cbs.disconnect = client_cbs->disconnect;
-    }
-
-    if (client_cbs->migrate) {
-        channel->priv->client_cbs.migrate = client_cbs->migrate;
-    }
-}
-
 static void add_capability(uint32_t **caps, int *num_caps, uint32_t cap)
 {
     int nbefore, n;
@@ -518,9 +503,9 @@ static void handle_dispatcher_connect(void *opaque, void *payload)
 {
     RedMessageConnect *msg = payload;
     RedChannel *channel = msg->channel;
+    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel);
 
-    channel->priv->client_cbs.connect(channel, msg->client, msg->stream,
-                                      msg->migration, &msg->caps);
+    klass->connect(channel, msg->client, msg->stream, msg->migration, &msg->caps);
     g_object_unref(msg->client);
     red_channel_capabilities_reset(&msg->caps);
 }
@@ -531,7 +516,8 @@ void red_channel_connect(RedChannel *channel, RedClient *client,
 {
     if (channel->priv->dispatcher == NULL ||
         pthread_equal(pthread_self(), channel->priv->thread_id)) {
-        channel->priv->client_cbs.connect(channel, client, stream, migration, caps);
+        RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel);
+        klass->connect(channel, client, stream, migration, caps);
         return;
     }
 
@@ -759,8 +745,9 @@ static void handle_dispatcher_migrate(void *opaque, void *payload)
 {
     RedMessageMigrate *msg = payload;
     RedChannel *channel = red_channel_client_get_channel(msg->rcc);
+    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel);
 
-    channel->priv->client_cbs.migrate(msg->rcc);
+    klass->migrate(msg->rcc);
     g_object_unref(msg->rcc);
 }
 
@@ -768,7 +755,8 @@ void red_channel_migrate_client(RedChannel *channel, RedChannelClient *rcc)
 {
     if (channel->priv->dispatcher == NULL ||
         pthread_equal(pthread_self(), channel->priv->thread_id)) {
-        channel->priv->client_cbs.migrate(rcc);
+        RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel);
+        klass->migrate(rcc);
         return;
     }
 
@@ -785,15 +773,17 @@ static void handle_dispatcher_disconnect(void *opaque, void *payload)
 {
     RedMessageDisconnect *msg = payload;
     RedChannel *channel = red_channel_client_get_channel(msg->rcc);
+    RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel);
 
-    channel->priv->client_cbs.disconnect(msg->rcc);
+    klass->disconnect(msg->rcc);
 }
 
 void red_channel_disconnect_client(RedChannel *channel, RedChannelClient *rcc)
 {
     if (channel->priv->dispatcher == NULL ||
         pthread_equal(pthread_self(), channel->priv->thread_id)) {
-        channel->priv->client_cbs.disconnect(rcc);
+        RedChannelClass *klass = RED_CHANNEL_GET_CLASS(channel);
+        klass->disconnect(rcc);
         return;
     }
 
diff --git a/server/red-channel.h b/server/red-channel.h
index bb3a95e8..4bfd81ee 100644
--- a/server/red-channel.h
+++ b/server/red-channel.h
@@ -63,16 +63,6 @@ typedef void (*channel_client_disconnect_proc)(RedChannelClient *base);
 typedef void (*channel_client_migrate_proc)(RedChannelClient *base);
 
 
-/*
- * callbacks that are triggered from client events.
- * They should be called from the thread that handles the RedClient
- */
-typedef struct {
-    channel_client_connect_proc connect;
-    channel_client_disconnect_proc disconnect;
-    channel_client_migrate_proc migrate;
-} ClientCbs;
-
 static inline gboolean test_capability(const uint32_t *caps, int num_caps, uint32_t cap)
 {
     return VD_AGENT_HAS_CAPABILITY(caps, num_caps, cap);
@@ -107,6 +97,14 @@ struct RedChannelClass
     channel_handle_migrate_flush_mark_proc handle_migrate_flush_mark;
     channel_handle_migrate_data_proc handle_migrate_data;
     channel_handle_migrate_data_get_serial_proc handle_migrate_data_get_serial;
+
+    /*
+     * callbacks that are triggered from client events.
+     * They should be called from the thread that handles the RedClient
+     */
+    channel_client_connect_proc connect;
+    channel_client_disconnect_proc disconnect;
+    channel_client_migrate_proc migrate;
 };
 
 #define FOREACH_CLIENT(_channel, _data) \
@@ -122,7 +120,6 @@ void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc);
 
 void red_channel_init_stat_node(RedChannel *channel, const RedStatNode *parent, const char *name);
 
-void red_channel_register_client_cbs(RedChannel *channel, const ClientCbs *client_cbs);
 // caps are freed when the channel is destroyed
 void red_channel_set_common_cap(RedChannel *channel, uint32_t cap);
 void red_channel_set_cap(RedChannel *channel, uint32_t cap);
diff --git a/server/red-stream-device.c b/server/red-stream-device.c
index cf5a6e9a..b4c32f71 100644
--- a/server/red-stream-device.c
+++ b/server/red-stream-device.c
@@ -695,12 +695,7 @@ stream_device_create_channel(StreamDevice *dev)
     g_return_if_fail(id >= 0);
 
     StreamChannel *stream_channel = stream_channel_new(reds, id);
-
     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;
-    red_channel_register_client_cbs(RED_CHANNEL(cursor_channel), &client_cbs);
 
     dev->stream_channel = stream_channel;
     dev->cursor_channel = cursor_channel;
diff --git a/server/red-worker.c b/server/red-worker.c
index d6b69f29..129f9f93 100644
--- a/server/red-worker.c
+++ b/server/red-worker.c
@@ -1144,12 +1144,6 @@ RedWorker* red_worker_new(QXLInstance *qxl)
     channel = RED_CHANNEL(worker->cursor_channel);
     red_channel_init_stat_node(channel, &worker->stat, "cursor_channel");
 
-    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, dispatcher,
                                                   FALSE,
@@ -1159,12 +1153,6 @@ RedWorker* red_worker_new(QXLInstance *qxl)
     channel = RED_CHANNEL(worker->display_channel);
     red_channel_init_stat_node(channel, &worker->stat, "display_channel");
 
-    ClientCbs client_display_cbs = { NULL, };
-    client_display_cbs.connect = display_channel_connect;
-    client_display_cbs.disconnect = display_channel_disconnect;
-    client_display_cbs.migrate = display_channel_migrate;
-    red_channel_register_client_cbs(channel, &client_display_cbs);
-
     return worker;
 }
 
diff --git a/server/smartcard.c b/server/smartcard.c
index 9ccfbc6b..e3e746ca 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -535,13 +535,9 @@ red_smartcard_channel_constructed(GObject *object)
 {
     RedSmartcardChannel *self = RED_SMARTCARD_CHANNEL(object);
     RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
-    ClientCbs client_cbs = { NULL, };
 
     G_OBJECT_CLASS(red_smartcard_channel_parent_class)->constructed(object);
 
-    client_cbs.connect = smartcard_connect_client;
-    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs);
-
     reds_register_channel(reds, RED_CHANNEL(self));
 }
 
@@ -559,6 +555,8 @@ red_smartcard_channel_class_init(RedSmartcardChannelClass *klass)
     channel_class->handle_migrate_flush_mark = smartcard_channel_client_handle_migrate_flush_mark;
     channel_class->handle_migrate_data = smartcard_channel_client_handle_migrate_data;
 
+    // client callbacks
+    channel_class->connect = smartcard_connect_client;
 }
 
 static void smartcard_init(RedsState *reds)
diff --git a/server/sound.c b/server/sound.c
index 44b27dec..167f68c5 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -1377,16 +1377,11 @@ playback_channel_init(PlaybackChannel *self)
 static void
 playback_channel_constructed(GObject *object)
 {
-    ClientCbs client_cbs = { NULL, };
     SndChannel *self = SND_CHANNEL(object);
     RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
 
     G_OBJECT_CLASS(playback_channel_parent_class)->constructed(object);
 
-    client_cbs.connect = snd_set_playback_peer;
-    client_cbs.migrate = snd_migrate_channel_client;
-    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs);
-
     if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY)) {
         red_channel_set_cap(RED_CHANNEL(self), SPICE_PLAYBACK_CAP_CELT_0_5_1);
     }
@@ -1407,6 +1402,10 @@ playback_channel_class_init(PlaybackChannelClass *klass)
     channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_PLAYBACK, NULL);
     channel_class->handle_message = red_channel_client_handle_message;
     channel_class->send_item = playback_channel_send_item;
+
+    // client callbacks
+    channel_class->connect = snd_set_playback_peer;
+    channel_class->migrate = snd_migrate_channel_client;
 }
 
 void snd_attach_playback(RedsState *reds, SpicePlaybackInstance *sin)
@@ -1427,16 +1426,11 @@ record_channel_init(RecordChannel *self)
 static void
 record_channel_constructed(GObject *object)
 {
-    ClientCbs client_cbs = { NULL, };
     SndChannel *self = SND_CHANNEL(object);
     RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
 
     G_OBJECT_CLASS(record_channel_parent_class)->constructed(object);
 
-    client_cbs.connect = snd_set_record_peer;
-    client_cbs.migrate = snd_migrate_channel_client;
-    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs);
-
     if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY)) {
         red_channel_set_cap(RED_CHANNEL(self), SPICE_RECORD_CAP_CELT_0_5_1);
     }
@@ -1457,6 +1451,10 @@ record_channel_class_init(RecordChannelClass *klass)
     channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_RECORD, NULL);
     channel_class->handle_message = record_channel_handle_message;
     channel_class->send_item = record_channel_send_item;
+
+    // client callbacks
+    channel_class->connect = snd_set_record_peer;
+    channel_class->migrate = snd_migrate_channel_client;
 }
 
 void snd_attach_record(RedsState *reds, SpiceRecordInstance *sin)
diff --git a/server/spicevmc.c b/server/spicevmc.c
index 51550c1a..d094166e 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -185,14 +185,10 @@ static void
 red_vmc_channel_constructed(GObject *object)
 {
     RedVmcChannel *self = RED_VMC_CHANNEL(object);
-    ClientCbs client_cbs = { NULL, };
     RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
 
     G_OBJECT_CLASS(red_vmc_channel_parent_class)->constructed(object);
 
-    client_cbs.connect = spicevmc_connect;
-    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs);
-
     red_channel_init_stat_node(RED_CHANNEL(self), NULL, "spicevmc");
     const RedStatNode *stat = red_channel_get_stat_node(RED_CHANNEL(self));
     stat_init_counter(&self->in_data, reds, stat, "in_data", TRUE);
@@ -723,6 +719,9 @@ red_vmc_channel_class_init(RedVmcChannelClass *klass)
     channel_class->send_item = spicevmc_red_channel_send_item;
     channel_class->handle_migrate_flush_mark = spicevmc_channel_client_handle_migrate_flush_mark;
     channel_class->handle_migrate_data = spicevmc_channel_client_handle_migrate_data;
+
+    // client callbacks
+    channel_class->connect = spicevmc_connect;
 }
 
 static void
diff --git a/server/stream-channel.c b/server/stream-channel.c
index c38ee043..ed844d6d 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -443,15 +443,11 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedStream
 static void
 stream_channel_constructed(GObject *object)
 {
-    ClientCbs client_cbs = { NULL, };
     RedChannel *red_channel = RED_CHANNEL(object);
     RedsState *reds = red_channel_get_server(red_channel);
 
     G_OBJECT_CLASS(stream_channel_parent_class)->constructed(object);
 
-    client_cbs.connect = stream_channel_connect;
-    red_channel_register_client_cbs(red_channel, &client_cbs);
-
     red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG);
     red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_STREAM_REPORT);
 
@@ -470,6 +466,7 @@ stream_channel_class_init(StreamChannelClass *klass)
     channel_class->handle_message = handle_message;
 
     channel_class->send_item = stream_channel_send_item;
+    channel_class->connect = stream_channel_connect;
 }
 
 static void
diff --git a/server/tests/test-channel.c b/server/tests/test-channel.c
index 1c9148df..9700e31c 100644
--- a/server/tests/test-channel.c
+++ b/server/tests/test-channel.c
@@ -106,24 +106,13 @@ test_connect_client(RedChannel *channel, RedClient *client, RedStream *stream,
 }
 
 static void
-red_test_channel_constructed(GObject *object)
-{
-    G_OBJECT_CLASS(red_test_channel_parent_class)->constructed(object);
-
-    ClientCbs client_cbs = { .connect = test_connect_client, };
-    red_channel_register_client_cbs(RED_CHANNEL(object), &client_cbs);
-}
-
-static void
 red_test_channel_class_init(RedTestChannelClass *klass)
 {
-    GObjectClass *object_class = G_OBJECT_CLASS(klass);
-    object_class->constructed = red_test_channel_constructed;
-
     RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
     channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_PORT, NULL);
     channel_class->handle_message = red_channel_client_handle_message;
     channel_class->send_item = test_channel_send_item;
+    channel_class->connect = test_connect_client;
 }
 
 static uint8_t *
commit fe522642042058d8de78416ac18e320b8652128f
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Fri Mar 22 09:35:20 2019 +0000

    Check running state in red_qxl_set_client_capabilities
    
    No reasons to expose red_qxl_is_running, this was used to not
    send capability is the state was not running.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/display-channel.c b/server/display-channel.c
index 1af87ba4..e9368668 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -2566,9 +2566,6 @@ static void guest_set_client_capabilities(DisplayChannel *display)
 #define CLEAR_CAP(a,c)                                                  \
         ((a)[(c) / 8] &= ~(1 << ((c) % 8)))
 
-    if (!red_qxl_is_running(display->priv->qxl)) {
-        return;
-    }
     if ((red_channel_get_n_clients(RED_CHANNEL(display)) == 0)) {
         red_qxl_set_client_capabilities(display->priv->qxl, FALSE, caps);
     } else {
diff --git a/server/red-qxl.c b/server/red-qxl.c
index e3fbf7b7..6dbd224c 100644
--- a/server/red-qxl.c
+++ b/server/red-qxl.c
@@ -68,6 +68,7 @@ struct QXLState {
 
 #define GL_DRAW_COOKIE_INVALID (~((uint64_t) 0))
 
+/* used by RedWorker */
 bool red_qxl_is_running(QXLInstance *qxl)
 {
     return qxl->st->running;
@@ -1048,7 +1049,9 @@ void red_qxl_set_client_capabilities(QXLInstance *qxl,
 {
     QXLInterface *interface = qxl_get_interface(qxl);
 
-    interface->set_client_capabilities(qxl, client_present, caps);
+    if (qxl->st->running) {
+        interface->set_client_capabilities(qxl, client_present, caps);
+    }
 }
 
 void red_qxl_async_complete(QXLInstance *qxl, uint64_t cookie)
diff --git a/server/red-qxl.h b/server/red-qxl.h
index 521f3659..94753948 100644
--- a/server/red-qxl.h
+++ b/server/red-qxl.h
@@ -44,9 +44,6 @@ const char* red_qxl_get_device_address(const QXLInstance *qxl);
 const uint32_t* red_qxl_get_device_display_ids(const QXLInstance *qxl);
 size_t red_qxl_get_monitors_count(const QXLInstance *qxl);
 
-/* check if QXL is running, should be used inside the worker thread */
-bool red_qxl_is_running(QXLInstance *qxl);
-
 /* Wrappers around QXLInterface vfuncs */
 void red_qxl_get_init_info(QXLInstance *qxl, QXLDevInitInfo *info);
 int red_qxl_get_command(QXLInstance *qxl, struct QXLCommandExt *cmd);
diff --git a/server/red-worker.h b/server/red-worker.h
index 54ab4da8..34c5b4af 100644
--- a/server/red-worker.h
+++ b/server/red-worker.h
@@ -35,6 +35,7 @@ void red_worker_free(RedWorker *worker);
 struct Dispatcher *red_qxl_get_dispatcher(QXLInstance *qxl);
 void red_qxl_destroy_primary_surface_complete(QXLState *qxl_state);
 void red_qxl_create_primary_surface_complete(QXLState *qxl_state, const QXLDevSurfaceCreate* surface);
+bool red_qxl_is_running(QXLInstance *qxl);
 void red_qxl_set_running(QXLInstance *qxl, bool running);
 
 typedef uint32_t RedWorkerMessage;
commit 0399cd2f904892acd6367901cf79803bbdfdd695
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Wed Mar 20 08:43:41 2019 +0000

    Move DisplayChannel callbacks from RedWorker to DisplayChannel
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/display-channel.c b/server/display-channel.c
index cb052bfc..1af87ba4 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -2540,3 +2540,111 @@ void display_channel_debug_oom(DisplayChannel *display, const char *msg)
                 ring_get_length(&display->priv->current_list),
                 red_channel_sum_pipes_size(channel));
 }
+
+static void guest_set_client_capabilities(DisplayChannel *display)
+{
+    int i;
+    RedChannelClient *rcc;
+    uint8_t caps[SPICE_CAPABILITIES_SIZE] = { 0 };
+    int caps_available[] = {
+        SPICE_DISPLAY_CAP_SIZED_STREAM,
+        SPICE_DISPLAY_CAP_MONITORS_CONFIG,
+        SPICE_DISPLAY_CAP_COMPOSITE,
+        SPICE_DISPLAY_CAP_A8_SURFACE,
+    };
+    QXLInterface *qif = qxl_get_interface(display->priv->qxl);
+
+    if (!red_qxl_check_qxl_version(display->priv->qxl, 3, 2)) {
+        return;
+    }
+    if (!qif->set_client_capabilities) {
+        return;
+    }
+#define SET_CAP(a,c)                                                    \
+        ((a)[(c) / 8] |= (1 << ((c) % 8)))
+
+#define CLEAR_CAP(a,c)                                                  \
+        ((a)[(c) / 8] &= ~(1 << ((c) % 8)))
+
+    if (!red_qxl_is_running(display->priv->qxl)) {
+        return;
+    }
+    if ((red_channel_get_n_clients(RED_CHANNEL(display)) == 0)) {
+        red_qxl_set_client_capabilities(display->priv->qxl, FALSE, caps);
+    } else {
+        // Take least common denominator
+        for (i = 0 ; i < SPICE_N_ELEMENTS(caps_available); ++i) {
+            SET_CAP(caps, caps_available[i]);
+        }
+        FOREACH_CLIENT(display, rcc) {
+            for (i = 0 ; i < SPICE_N_ELEMENTS(caps_available); ++i) {
+                if (!red_channel_client_test_remote_cap(rcc, caps_available[i]))
+                    CLEAR_CAP(caps, caps_available[i]);
+            }
+        }
+        red_qxl_set_client_capabilities(display->priv->qxl, TRUE, caps);
+    }
+}
+
+void display_channel_update_qxl_running(DisplayChannel *display, bool running)
+{
+    if (running) {
+        guest_set_client_capabilities(display);
+    }
+}
+
+void
+display_channel_connect(RedChannel *channel, RedClient *client,
+                        RedStream *stream, int migration,
+                        RedChannelCapabilities *caps)
+{
+    DisplayChannel *display = DISPLAY_CHANNEL(channel);
+    DisplayChannelClient *dcc;
+
+    spice_debug("connect new client");
+
+    // FIXME not sure how safe is reading directly from reds
+    SpiceServer *reds = red_channel_get_server(channel);
+    dcc = dcc_new(display, client, stream, migration, caps,
+                  spice_server_get_image_compression(reds), reds_get_jpeg_state(reds),
+                  reds_get_zlib_glz_state(reds));
+    if (!dcc) {
+        return;
+    }
+    display_channel_update_compression(display, dcc);
+    guest_set_client_capabilities(display);
+    dcc_start(dcc);
+}
+
+void display_channel_disconnect(RedChannelClient *rcc)
+{
+    DisplayChannel *display = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
+
+    guest_set_client_capabilities(display);
+
+    red_channel_client_disconnect(rcc);
+}
+
+static void red_migrate_display(DisplayChannel *display, RedChannelClient *rcc)
+{
+    /* We need to stop the streams, and to send upgrade_items to the client.
+     * Otherwise, (1) the client might display lossy regions that we don't track
+     * (streams are not part of the migration data) (2) streams_timeout may occur
+     * after the MIGRATE message has been sent. This can result in messages
+     * being sent to the client after MSG_MIGRATE and before MSG_MIGRATE_DATA (e.g.,
+     * STREAM_CLIP, STREAM_DESTROY, DRAW_COPY)
+     * No message besides MSG_MIGRATE_DATA should be sent after MSG_MIGRATE.
+     * Notice that detach_and_stop_streams won't lead to any dev ram changes, since
+     * handle_dev_stop already took care of releasing all the dev ram resources.
+     */
+    video_stream_detach_and_stop(display);
+    if (red_channel_client_is_connected(rcc)) {
+        red_channel_client_default_migrate(rcc);
+    }
+}
+
+void display_channel_migrate(RedChannelClient *rcc)
+{
+    DisplayChannel *display = DISPLAY_CHANNEL(red_channel_client_get_channel(rcc));
+    red_migrate_display(display, rcc);
+}
diff --git a/server/display-channel.h b/server/display-channel.h
index f64da0bd..7dfedd75 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -158,6 +158,13 @@ void display_channel_reset_image_cache(DisplayChannel *self);
 
 void display_channel_debug_oom(DisplayChannel *display, const char *msg);
 
+void display_channel_update_qxl_running(DisplayChannel *display, bool running);
+void display_channel_connect(RedChannel *channel, RedClient *client,
+                             RedStream *stream, int migration,
+                             RedChannelCapabilities *caps);
+void display_channel_disconnect(RedChannelClient *rcc);
+void display_channel_migrate(RedChannelClient *rcc);
+
 G_END_DECLS
 
 #endif /* DISPLAY_CHANNEL_H_ */
diff --git a/server/red-worker.c b/server/red-worker.c
index b80fefab..d6b69f29 100644
--- a/server/red-worker.c
+++ b/server/red-worker.c
@@ -270,24 +270,6 @@ static bool red_process_is_blocked(RedWorker *worker)
            red_channel_max_pipe_size(RED_CHANNEL(worker->display_channel)) > MAX_PIPE_SIZE;
 }
 
-static void red_migrate_display(DisplayChannel *display, RedChannelClient *rcc)
-{
-    /* We need to stop the streams, and to send upgrade_items to the client.
-     * Otherwise, (1) the client might display lossy regions that we don't track
-     * (streams are not part of the migration data) (2) streams_timeout may occur
-     * after the MIGRATE message has been sent. This can result in messages
-     * being sent to the client after MSG_MIGRATE and before MSG_MIGRATE_DATA (e.g.,
-     * STREAM_CLIP, STREAM_DESTROY, DRAW_COPY)
-     * No message besides MSG_MIGRATE_DATA should be sent after MSG_MIGRATE.
-     * Notice that detach_and_stop_streams won't lead to any dev ram changes, since
-     * handle_dev_stop already took care of releasing all the dev ram resources.
-     */
-    video_stream_detach_and_stop(display);
-    if (red_channel_client_is_connected(rcc)) {
-        red_channel_client_default_migrate(rcc);
-    }
-}
-
 typedef int (*red_process_t)(RedWorker *worker, int *ring_is_empty);
 static void flush_commands(RedWorker *worker, RedChannel *red_channel,
                            red_process_t process)
@@ -352,52 +334,6 @@ static void flush_all_qxl_commands(RedWorker *worker)
     flush_cursor_commands(worker);
 }
 
-static void guest_set_client_capabilities(RedWorker *worker)
-{
-    int i;
-    RedChannelClient *rcc;
-    uint8_t caps[SPICE_CAPABILITIES_SIZE] = { 0 };
-    int caps_available[] = {
-        SPICE_DISPLAY_CAP_SIZED_STREAM,
-        SPICE_DISPLAY_CAP_MONITORS_CONFIG,
-        SPICE_DISPLAY_CAP_COMPOSITE,
-        SPICE_DISPLAY_CAP_A8_SURFACE,
-    };
-    QXLInterface *qif = qxl_get_interface(worker->qxl);
-
-    if (!red_qxl_check_qxl_version(worker->qxl, 3, 2)) {
-        return;
-    }
-    if (!qif->set_client_capabilities) {
-        return;
-    }
-#define SET_CAP(a,c)                                                    \
-        ((a)[(c) / 8] |= (1 << ((c) % 8)))
-
-#define CLEAR_CAP(a,c)                                                  \
-        ((a)[(c) / 8] &= ~(1 << ((c) % 8)))
-
-    if (!red_qxl_is_running(worker->qxl)) {
-        return;
-    }
-    if ((worker->display_channel == NULL) ||
-        (red_channel_get_n_clients(RED_CHANNEL(worker->display_channel)) == 0)) {
-        red_qxl_set_client_capabilities(worker->qxl, FALSE, caps);
-    } else {
-        // Take least common denominator
-        for (i = 0 ; i < SPICE_N_ELEMENTS(caps_available); ++i) {
-            SET_CAP(caps, caps_available[i]);
-        }
-        FOREACH_CLIENT(worker->display_channel, rcc) {
-            for (i = 0 ; i < SPICE_N_ELEMENTS(caps_available); ++i) {
-                if (!red_channel_client_test_remote_cap(rcc, caps_available[i]))
-                    CLEAR_CAP(caps, caps_available[i]);
-            }
-        }
-        red_qxl_set_client_capabilities(worker->qxl, TRUE, caps);
-    }
-}
-
 static void handle_dev_update_async(void *opaque, void *payload)
 {
     RedWorker *worker = opaque;
@@ -588,6 +524,7 @@ static void handle_dev_stop(void *opaque, void *payload)
     spice_assert(red_qxl_is_running(worker->qxl));
 
     red_qxl_set_running(worker->qxl, false);
+    display_channel_update_qxl_running(worker->display_channel, false);
 
     display_channel_free_glz_drawables(worker->display_channel);
     display_channel_flush_all_surfaces(worker->display_channel);
@@ -616,8 +553,8 @@ static void handle_dev_start(void *opaque, void *payload)
         display_channel_wait_for_migrate_data(worker->display_channel);
     }
     red_qxl_set_running(worker->qxl, true);
+    display_channel_update_qxl_running(worker->display_channel, true);
     worker->event_timeout = 0;
-    guest_set_client_capabilities(worker);
 }
 
 static void handle_dev_wakeup(void *opaque, void *payload)
@@ -694,46 +631,6 @@ 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(RedChannel *channel, RedClient *client,
-                           RedStream *stream, int migration,
-                           RedChannelCapabilities *caps)
-{
-    DisplayChannel *display = DISPLAY_CHANNEL(channel);
-    DisplayChannelClient *dcc;
-    RedWorker *worker = g_object_get_data(G_OBJECT(channel), "worker");
-
-    spice_debug("connect new client");
-
-    dcc = dcc_new(display, client, stream, migration, caps,
-                  worker->image_compression, worker->jpeg_state, worker->zlib_glz_state);
-    if (!dcc) {
-        return;
-    }
-    display_channel_update_compression(display, dcc);
-    guest_set_client_capabilities(worker);
-    dcc_start(dcc);
-}
-
-static void
-handle_dev_display_disconnect(RedChannelClient *rcc)
-{
-    RedChannel *channel = red_channel_client_get_channel(rcc);
-    RedWorker *worker = g_object_get_data(G_OBJECT(channel), "worker");
-
-    spice_debug("disconnect display client");
-
-    guest_set_client_capabilities(worker);
-
-    red_channel_client_disconnect(rcc);
-}
-
-static void handle_dev_display_migrate(RedChannelClient *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)
 {
     return sizeof(QXLMonitorsConfig) + sizeof(QXLHead) * heads;
@@ -1261,12 +1158,11 @@ RedWorker* red_worker_new(QXLInstance *qxl)
                                                   init_info.n_surfaces);
     channel = RED_CHANNEL(worker->display_channel);
     red_channel_init_stat_node(channel, &worker->stat, "display_channel");
-    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;
+    client_display_cbs.connect = display_channel_connect;
+    client_display_cbs.disconnect = display_channel_disconnect;
+    client_display_cbs.migrate = display_channel_migrate;
     red_channel_register_client_cbs(channel, &client_display_cbs);
 
     return worker;
commit b0eab081be9d7142bb44492b2e351255439ded00
Author: Jonathon Jongsma <jjongsma at redhat.com>
Date:   Thu Mar 21 15:52:01 2019 -0500

    Save running property in QXLState
    
    This is a preparatory patch that states the running property in QXLState
    and provides accessor functions that allows us to check whether the QXL
    device is running from different threads.
    
    Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/red-qxl.c b/server/red-qxl.c
index 8274be56..e3fbf7b7 100644
--- a/server/red-qxl.c
+++ b/server/red-qxl.c
@@ -59,6 +59,8 @@ struct QXLState {
     uint32_t device_display_ids[MAX_MONITORS_COUNT];
     size_t monitors_count;  // length of ^^^
 
+    bool running;
+
     pthread_mutex_t scanout_mutex;
     SpiceMsgDisplayGlScanoutUnix scanout;
     uint64_t gl_draw_cookie;
@@ -66,6 +68,17 @@ struct QXLState {
 
 #define GL_DRAW_COOKIE_INVALID (~((uint64_t) 0))
 
+bool red_qxl_is_running(QXLInstance *qxl)
+{
+    return qxl->st->running;
+}
+
+/* used by RedWorker */
+void red_qxl_set_running(QXLInstance *qxl, bool running)
+{
+    qxl->st->running = running;
+}
+
 int red_qxl_check_qxl_version(QXLInstance *qxl, int major, int minor)
 {
     int qxl_major = qxl_get_interface(qxl)->base.major_version;
diff --git a/server/red-qxl.h b/server/red-qxl.h
index 94753948..521f3659 100644
--- a/server/red-qxl.h
+++ b/server/red-qxl.h
@@ -44,6 +44,9 @@ const char* red_qxl_get_device_address(const QXLInstance *qxl);
 const uint32_t* red_qxl_get_device_display_ids(const QXLInstance *qxl);
 size_t red_qxl_get_monitors_count(const QXLInstance *qxl);
 
+/* check if QXL is running, should be used inside the worker thread */
+bool red_qxl_is_running(QXLInstance *qxl);
+
 /* Wrappers around QXLInterface vfuncs */
 void red_qxl_get_init_info(QXLInstance *qxl, QXLDevInitInfo *info);
 int red_qxl_get_command(QXLInstance *qxl, struct QXLCommandExt *cmd);
diff --git a/server/red-worker.c b/server/red-worker.c
index 27fe04cc..b80fefab 100644
--- a/server/red-worker.c
+++ b/server/red-worker.c
@@ -63,7 +63,6 @@ struct RedWorker {
     pthread_t thread;
     QXLInstance *qxl;
     SpiceWatch *dispatch_watch;
-    int running;
     SpiceCoreInterfaceInternal core;
 
     unsigned int event_timeout;
@@ -115,7 +114,7 @@ static int red_process_cursor(RedWorker *worker, int *ring_is_empty)
     QXLCommandExt ext_cmd;
     int n = 0;
 
-    if (!worker->running) {
+    if (!red_qxl_is_running(worker->qxl)) {
         *ring_is_empty = TRUE;
         return n;
     }
@@ -173,7 +172,7 @@ static int red_process_display(RedWorker *worker, int *ring_is_empty)
     int n = 0;
     uint64_t start = spice_get_monotonic_time_ns();
 
-    if (!worker->running) {
+    if (!red_qxl_is_running(worker->qxl)) {
         *ring_is_empty = TRUE;
         return n;
     }
@@ -378,7 +377,7 @@ static void guest_set_client_capabilities(RedWorker *worker)
 #define CLEAR_CAP(a,c)                                                  \
         ((a)[(c) / 8] &= ~(1 << ((c) % 8)))
 
-    if (!worker->running) {
+    if (!red_qxl_is_running(worker->qxl)) {
         return;
     }
     if ((worker->display_channel == NULL) ||
@@ -406,7 +405,7 @@ static void handle_dev_update_async(void *opaque, void *payload)
     QXLRect *qxl_dirty_rects = NULL;
     uint32_t num_dirty_rects = 0;
 
-    spice_return_if_fail(worker->running);
+    spice_return_if_fail(red_qxl_is_running(worker->qxl));
     spice_return_if_fail(qxl_get_interface(worker->qxl)->update_area_complete);
 
     flush_display_commands(worker);
@@ -426,7 +425,7 @@ static void handle_dev_update(void *opaque, void *payload)
     RedWorkerMessageUpdate *msg = payload;
     QXLRect *qxl_dirty_rects = msg->qxl_dirty_rects;
 
-    spice_return_if_fail(worker->running);
+    spice_return_if_fail(red_qxl_is_running(worker->qxl));
 
     flush_display_commands(worker);
     display_channel_update(worker->display_channel,
@@ -586,9 +585,9 @@ static void handle_dev_stop(void *opaque, void *payload)
     RedWorker *worker = opaque;
 
     spice_debug("stop");
-    spice_assert(worker->running);
+    spice_assert(red_qxl_is_running(worker->qxl));
 
-    worker->running = FALSE;
+    red_qxl_set_running(worker->qxl, false);
 
     display_channel_free_glz_drawables(worker->display_channel);
     display_channel_flush_all_surfaces(worker->display_channel);
@@ -606,7 +605,7 @@ static void handle_dev_start(void *opaque, void *payload)
 {
     RedWorker *worker = opaque;
 
-    spice_assert(!worker->running);
+    spice_assert(!red_qxl_is_running(worker->qxl));
     if (worker->cursor_channel) {
         CommonGraphicsChannel *common = COMMON_GRAPHICS_CHANNEL(worker->cursor_channel);
         common_graphics_channel_set_during_target_migrate(common, FALSE);
@@ -616,7 +615,7 @@ static void handle_dev_start(void *opaque, void *payload)
         common_graphics_channel_set_during_target_migrate(common, FALSE);
         display_channel_wait_for_migrate_data(worker->display_channel);
     }
-    worker->running = TRUE;
+    red_qxl_set_running(worker->qxl, true);
     worker->event_timeout = 0;
     guest_set_client_capabilities(worker);
 }
@@ -637,7 +636,7 @@ static void handle_dev_oom(void *opaque, void *payload)
     RedChannel *display_red_channel = RED_CHANNEL(display);
     int ring_is_empty;
 
-    spice_return_if_fail(worker->running);
+    spice_return_if_fail(red_qxl_is_running(worker->qxl));
     // streams? but without streams also leak
     display_channel_debug_oom(display, "OOM1");
     while (red_process_display(worker, &ring_is_empty)) {
@@ -1153,7 +1152,7 @@ static gboolean worker_source_check(GSource *source)
     RedWorkerSource *wsource = SPICE_CONTAINEROF(source, RedWorkerSource, source);
     RedWorker *worker = wsource->worker;
 
-    return worker->running /* TODO && worker->pending_process */;
+    return red_qxl_is_running(worker->qxl) /* TODO && worker->pending_process */;
 }
 
 static gboolean worker_source_dispatch(GSource *source, GSourceFunc callback,
diff --git a/server/red-worker.h b/server/red-worker.h
index 3d2841ba..54ab4da8 100644
--- a/server/red-worker.h
+++ b/server/red-worker.h
@@ -35,6 +35,7 @@ void red_worker_free(RedWorker *worker);
 struct Dispatcher *red_qxl_get_dispatcher(QXLInstance *qxl);
 void red_qxl_destroy_primary_surface_complete(QXLState *qxl_state);
 void red_qxl_create_primary_surface_complete(QXLState *qxl_state, const QXLDevSurfaceCreate* surface);
+void red_qxl_set_running(QXLInstance *qxl, bool running);
 
 typedef uint32_t RedWorkerMessage;
 
commit 571dc645c54088ecaa177be61363ee9dd0d14f56
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Tue Mar 19 19:05:13 2019 +0000

    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 at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/cursor-channel.c b/server/cursor-channel.c
index 4220084f..e8af01b0 100644
--- a/server/cursor-channel.c
+++ b/server/cursor-channel.c
@@ -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);
 }
 
diff --git a/server/cursor-channel.h b/server/cursor-channel.h
index 603c2c0a..332aa987 100644
--- a/server/cursor-channel.h
+++ b/server/cursor-channel.h
@@ -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,
diff --git a/server/display-channel.c b/server/display-channel.c
index e179abfd..cb052bfc 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -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);
diff --git a/server/display-channel.h b/server/display-channel.h
index 948018cf..f64da0bd 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -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,
diff --git a/server/red-channel.c b/server/red-channel.c
index 1d88739e..8d4992fa 100644
--- a/server/red-channel.c
+++ b/server/red-channel.c
@@ -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);
 }
diff --git a/server/red-qxl.c b/server/red-qxl.c
index 789817f6..8274be56 100644
--- a/server/red-qxl.c
+++ b/server/red-qxl.c
@@ -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);
 }
diff --git a/server/red-replay-qxl.c b/server/red-replay-qxl.c
index 4884e97e..8124c1fd 100644
--- a/server/red-replay-qxl.c
+++ b/server/red-replay-qxl.c
@@ -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;
diff --git a/server/red-stream-device.c b/server/red-stream-device.c
index 78a8c908..cf5a6e9a 100644
--- a/server/red-stream-device.c
+++ b/server/red-stream-device.c
@@ -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;
diff --git a/server/red-worker.c b/server/red-worker.c
index 99369a0c..27fe04cc 100644
--- a/server/red-worker.c
+++ b/server/red-worker.c
@@ -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;
@@ -1000,36 +958,6 @@ 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,
                                 sizeof(RedWorkerMessageUpdate),
@@ -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;
 }
diff --git a/server/red-worker.h b/server/red-worker.h
index 767329da..3d2841ba 100644
--- a/server/red-worker.h
+++ b/server/red-worker.h
@@ -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;
commit 46efdc1e0914aeebe25486866341e824d043ad48
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Tue Mar 19 21:45:07 2019 +0000

    dispatcher: Allows to manage messages without registering them
    
    The only way to add new message to Dispatcher was to register
    using a number. These numbers corresponded to array indexes.
    This is good if the list of messages is allocated statically
    and contiguously, on the contrary this method is not that
    flexible.
    Writing a header of 4 or 16 bytes using system call does not
    make much difference so pass all message information in the
    payload header.
    A new dispatcher_send_message_custom function allows to send
    a message passing all message information, including the
    pointer to the handler.
    This will allow for instance a Dispatcher associate to a given
    thread to be reused by different classes.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/dispatcher.c b/server/dispatcher.c
index 5f839ec4..bae73f7d 100644
--- a/server/dispatcher.c
+++ b/server/dispatcher.c
@@ -38,10 +38,19 @@
 static void setup_dummy_signal_handler(void);
 #endif
 
+#define DISPATCHER_MESSAGE_TYPE_CUSTOM 0x7fffffffu
+
+/* structure to store message header information.
+ * That structure is sent through a socketpair so it's optimized
+ * to be transfered via sockets.
+ * Is also packaged to not leave holes in both 32 and 64 environments
+ * so memory instrumentation tools should not find uninitialised bytes.
+ */
 typedef struct DispatcherMessage {
-    size_t size;
-    bool ack;
     dispatcher_handle_message handler;
+    uint32_t size;
+    uint32_t type:31;
+    uint32_t ack:1;
 } DispatcherMessage;
 
 struct DispatcherPrivate {
@@ -249,12 +258,11 @@ static int write_safe(int fd, uint8_t *buf, size_t size)
 static int dispatcher_handle_single_read(Dispatcher *dispatcher)
 {
     int ret;
-    uint32_t type;
-    DispatcherMessage *msg = NULL;
-    uint8_t *payload = dispatcher->priv->payload;
+    DispatcherMessage msg[1];
+    uint8_t *payload;
     uint32_t ack = ACK;
 
-    if ((ret = read_safe(dispatcher->priv->recv_fd, (uint8_t*)&type, sizeof(type), 0)) == -1) {
+    if ((ret = read_safe(dispatcher->priv->recv_fd, (uint8_t*)msg, sizeof(msg), 0)) == -1) {
         g_warning("error reading from dispatcher: %d", errno);
         return 0;
     }
@@ -262,28 +270,28 @@ static int dispatcher_handle_single_read(Dispatcher *dispatcher)
         /* no message */
         return 0;
     }
-    if (type >= dispatcher->priv->max_message_type) {
-        spice_error("Invalid message type for this dispatcher: %u", type);
-        return 0;
+    if (G_UNLIKELY(msg->size > dispatcher->priv->payload_size)) {
+        dispatcher->priv->payload = g_realloc(dispatcher->priv->payload, msg->size);
+        dispatcher->priv->payload_size = msg->size;
     }
-    msg = &dispatcher->priv->messages[type];
+    payload = dispatcher->priv->payload;
     if (read_safe(dispatcher->priv->recv_fd, payload, msg->size, 1) == -1) {
         g_warning("error reading from dispatcher: %d", errno);
         /* TODO: close socketpair? */
         return 0;
     }
-    if (dispatcher->priv->any_handler) {
-        dispatcher->priv->any_handler(dispatcher->priv->opaque, type, payload);
+    if (dispatcher->priv->any_handler && msg->type != DISPATCHER_MESSAGE_TYPE_CUSTOM) {
+        dispatcher->priv->any_handler(dispatcher->priv->opaque, msg->type, payload);
     }
     if (msg->handler) {
         msg->handler(dispatcher->priv->opaque, payload);
     } else {
-        g_warning("error: no handler for message type %d", type);
+        g_warning("error: no handler for message type %d", msg->type);
     }
     if (msg->ack) {
         if (write_safe(dispatcher->priv->recv_fd,
                        (uint8_t*)&ack, sizeof(ack)) == -1) {
-            g_warning("error writing ack for message %d", type);
+            g_warning("error writing ack for message %d", msg->type);
             /* TODO: close socketpair? */
         }
     }
@@ -300,25 +308,22 @@ void dispatcher_handle_recv_read(Dispatcher *dispatcher)
     }
 }
 
-void dispatcher_send_message(Dispatcher *dispatcher, uint32_t message_type,
-                             void *payload)
+static void
+dispatcher_send_message_internal(Dispatcher *dispatcher, const DispatcherMessage*msg,
+                                 void *payload)
 {
-    DispatcherMessage *msg;
     uint32_t ack;
     int send_fd = dispatcher->priv->send_fd;
 
-    assert(dispatcher->priv->max_message_type > message_type);
-    assert(dispatcher->priv->messages[message_type].handler);
-    msg = &dispatcher->priv->messages[message_type];
     pthread_mutex_lock(&dispatcher->priv->lock);
-    if (write_safe(send_fd, (uint8_t*)&message_type, sizeof(message_type)) == -1) {
-        g_warning("error: failed to send message type for message %d",
-                  message_type);
+    if (write_safe(send_fd, (uint8_t*)msg, sizeof(*msg)) == -1) {
+        g_warning("error: failed to send message header for message %d",
+                  msg->type);
         goto unlock;
     }
     if (write_safe(send_fd, payload, msg->size) == -1) {
         g_warning("error: failed to send message body for message %d",
-                  message_type);
+                  msg->type);
         goto unlock;
     }
     if (msg->ack) {
@@ -326,7 +331,7 @@ void dispatcher_send_message(Dispatcher *dispatcher, uint32_t message_type,
             g_warning("error: failed to read ack");
         } else if (ack != ACK) {
             g_warning("error: got wrong ack value in dispatcher "
-                      "for message %d\n", message_type);
+                      "for message %d\n", msg->type);
             /* TODO handling error? */
         }
     }
@@ -334,6 +339,29 @@ unlock:
     pthread_mutex_unlock(&dispatcher->priv->lock);
 }
 
+void dispatcher_send_message(Dispatcher *dispatcher, uint32_t message_type,
+                             void *payload)
+{
+    DispatcherMessage *msg;
+
+    assert(dispatcher->priv->max_message_type > message_type);
+    assert(dispatcher->priv->messages[message_type].handler);
+    msg = &dispatcher->priv->messages[message_type];
+    dispatcher_send_message_internal(dispatcher, msg, payload);
+}
+
+void dispatcher_send_message_custom(Dispatcher *dispatcher, dispatcher_handle_message handler,
+                                    void *payload, uint32_t payload_size, bool ack)
+{
+    DispatcherMessage msg = {
+        .handler = handler,
+        .size = payload_size,
+        .type = DISPATCHER_MESSAGE_TYPE_CUSTOM,
+        .ack = ack,
+    };
+    dispatcher_send_message_internal(dispatcher, &msg, payload);
+}
+
 void dispatcher_register_handler(Dispatcher *dispatcher, uint32_t message_type,
                                  dispatcher_handle_message handler,
                                  size_t size, bool ack)
@@ -345,6 +373,7 @@ void dispatcher_register_handler(Dispatcher *dispatcher, uint32_t message_type,
     msg = &dispatcher->priv->messages[message_type];
     msg->handler = handler;
     msg->size = size;
+    msg->type = message_type;
     msg->ack = ack;
     if (msg->size > dispatcher->priv->payload_size) {
         dispatcher->priv->payload = g_realloc(dispatcher->priv->payload, msg->size);
diff --git a/server/dispatcher.h b/server/dispatcher.h
index bb968e56..49215782 100644
--- a/server/dispatcher.h
+++ b/server/dispatcher.h
@@ -99,6 +99,21 @@ typedef void (*dispatcher_handle_any_message)(void *opaque,
 void dispatcher_send_message(Dispatcher *dispatcher, uint32_t message_type,
                              void *payload);
 
+/* dispatcher_send_message_custom
+ *
+ * Sends a message to the receiving thread.
+ *
+ * If the sent message requires an ACK, this function will block until it
+ * receives an ACK from the receiving thread.
+ *
+ * @handler:      callback to handle message
+ * @payload:      payload
+ * @payload_size: size of payload
+ * @ack:          acknowledge required. Make message synchronous
+ */
+void dispatcher_send_message_custom(Dispatcher *dispatcher, dispatcher_handle_message handler,
+                                    void *payload, uint32_t payload_size, bool ack);
+
 /* dispatcher_register_handler
  *
  * This function registers a message type with the dispatcher, and registers


More information about the Spice-commits mailing list