[Spice-devel] [PATCH 11/18] worker: move dcc_handle_message
Frediano Ziglio
fziglio at redhat.com
Fri Nov 20 03:17:35 PST 2015
From: Marc-André Lureau <marcandre.lureau at gmail.com>
---
server/dcc-encoders.c | 131 +++++++++++++++++++++++++-
server/dcc-encoders.h | 7 ++
server/dcc.c | 82 +++++++++++++++++
server/dcc.h | 3 +
server/red_worker.c | 251 ++------------------------------------------------
5 files changed, 229 insertions(+), 245 deletions(-)
diff --git a/server/dcc-encoders.c b/server/dcc-encoders.c
index e39697a..4d8eab1 100644
--- a/server/dcc-encoders.c
+++ b/server/dcc-encoders.c
@@ -548,8 +548,133 @@ void dcc_free_glz_drawables(DisplayChannelClient *dcc)
void dcc_freeze_glz(DisplayChannelClient *dcc)
{
pthread_rwlock_wrlock(&dcc->glz_dict->encode_lock);
- if (!dcc->glz_dict->migrate_freeze) {
- dcc->glz_dict->migrate_freeze = TRUE;
- }
+ dcc->glz_dict->migrate_freeze = TRUE;
pthread_rwlock_unlock(&dcc->glz_dict->encode_lock);
}
+
+static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client, uint8_t id,
+ GlzEncDictContext *dict)
+{
+ spice_return_val_if_fail(dict != NULL, NULL);
+
+ GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
+
+ shared_dict->dict = dict;
+ shared_dict->id = id;
+ shared_dict->refs = 1;
+ shared_dict->migrate_freeze = FALSE;
+ shared_dict->client = client;
+ ring_item_init(&shared_dict->base);
+ pthread_rwlock_init(&shared_dict->encode_lock, NULL);
+
+ return shared_dict;
+}
+
+static pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static Ring glz_dictionary_list = {&glz_dictionary_list, &glz_dictionary_list};
+
+static GlzSharedDictionary *find_glz_dictionary(RedClient *client, uint8_t dict_id)
+{
+ RingItem *now;
+ GlzSharedDictionary *ret = NULL;
+
+ now = &glz_dictionary_list;
+ while ((now = ring_next(&glz_dictionary_list, now))) {
+ GlzSharedDictionary *dict = (GlzSharedDictionary *)now;
+ if ((dict->client == client) && (dict->id == dict_id)) {
+ ret = dict;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
+
+static GlzSharedDictionary *create_glz_dictionary(DisplayChannelClient *dcc,
+ uint8_t id, int window_size)
+{
+ spice_info("Lz Window %d Size=%d", id, window_size);
+
+ GlzEncDictContext *glz_dict =
+ glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &dcc->glz_data.usr);
+
+ return glz_shared_dictionary_new(RED_CHANNEL_CLIENT(dcc)->client, id, glz_dict);
+}
+
+GlzSharedDictionary *dcc_get_glz_dictionary(DisplayChannelClient *dcc,
+ uint8_t id, int window_size)
+{
+ GlzSharedDictionary *shared_dict;
+
+ pthread_mutex_lock(&glz_dictionary_list_lock);
+
+ shared_dict = find_glz_dictionary(RED_CHANNEL_CLIENT(dcc)->client, id);
+ if (shared_dict) {
+ shared_dict->refs++;
+ } else {
+ shared_dict = create_glz_dictionary(dcc, id, window_size);
+ ring_add(&glz_dictionary_list, &shared_dict->base);
+ }
+
+ pthread_mutex_unlock(&glz_dictionary_list_lock);
+ return shared_dict;
+}
+
+static GlzSharedDictionary *restore_glz_dictionary(DisplayChannelClient *dcc,
+ uint8_t id,
+ GlzEncDictRestoreData *restore_data)
+{
+ GlzEncDictContext *glz_dict =
+ glz_enc_dictionary_restore(restore_data, &dcc->glz_data.usr);
+
+ return glz_shared_dictionary_new(RED_CHANNEL_CLIENT(dcc)->client, id, glz_dict);
+}
+
+GlzSharedDictionary *dcc_restore_glz_dictionary(DisplayChannelClient *dcc,
+ uint8_t id,
+ GlzEncDictRestoreData *restore_data)
+{
+ GlzSharedDictionary *shared_dict = NULL;
+
+ pthread_mutex_lock(&glz_dictionary_list_lock);
+
+ shared_dict = find_glz_dictionary(RED_CHANNEL_CLIENT(dcc)->client, id);
+
+ if (shared_dict) {
+ shared_dict->refs++;
+ } else {
+ shared_dict = restore_glz_dictionary(dcc, id, restore_data);
+ ring_add(&glz_dictionary_list, &shared_dict->base);
+ }
+
+ pthread_mutex_unlock(&glz_dictionary_list_lock);
+ return shared_dict;
+}
+
+/* destroy encoder, and dictionary if no one uses it*/
+void dcc_release_glz(DisplayChannelClient *dcc)
+{
+ GlzSharedDictionary *shared_dict;
+
+ dcc_free_glz_drawables(dcc);
+
+ glz_encoder_destroy(dcc->glz);
+ dcc->glz = NULL;
+
+ if (!(shared_dict = dcc->glz_dict)) {
+ return;
+ }
+
+ dcc->glz_dict = NULL;
+ pthread_mutex_lock(&glz_dictionary_list_lock);
+ if (--shared_dict->refs != 0) {
+ pthread_mutex_unlock(&glz_dictionary_list_lock);
+ return;
+ }
+ ring_remove(&shared_dict->base);
+ pthread_mutex_unlock(&glz_dictionary_list_lock);
+ glz_enc_dictionary_destroy(shared_dict->dict, &dcc->glz_data.usr);
+ free(shared_dict);
+}
diff --git a/server/dcc-encoders.h b/server/dcc-encoders.h
index 8c769df..231d37f 100644
--- a/server/dcc-encoders.h
+++ b/server/dcc-encoders.h
@@ -45,6 +45,7 @@ void dcc_free_glz_drawable (DisplayChannelClie
void dcc_free_glz_drawables (DisplayChannelClient *dcc);
void dcc_free_glz_drawables_to_free (DisplayChannelClient* dcc);
void dcc_freeze_glz (DisplayChannelClient *dcc);
+void dcc_release_glz (DisplayChannelClient *dcc);
void marshaller_add_compressed (SpiceMarshaller *m,
RedCompressBuf *comp_buf,
@@ -76,6 +77,12 @@ typedef struct GlzSharedDictionary {
RedClient *client; // channel clients of the same client share the dict
} GlzSharedDictionary;
+GlzSharedDictionary* dcc_get_glz_dictionary (DisplayChannelClient *dcc,
+ uint8_t id, int window_size);
+GlzSharedDictionary* dcc_restore_glz_dictionary (DisplayChannelClient *dcc,
+ uint8_t id,
+ GlzEncDictRestoreData *restore_data);
+
typedef struct {
DisplayChannelClient *dcc;
RedCompressBuf *bufs_head;
diff --git a/server/dcc.c b/server/dcc.c
index b59029d..a20e27e 100644
--- a/server/dcc.c
+++ b/server/dcc.c
@@ -1173,3 +1173,85 @@ int dcc_pixmap_cache_unlocked_add(DisplayChannelClient *dcc, uint64_t id,
cache->sync[dcc->common.id] = serial;
return TRUE;
}
+
+static int dcc_handle_init(DisplayChannelClient *dcc, SpiceMsgcDisplayInit *init)
+{
+ spice_return_val_if_fail(dcc->expect_init, FALSE);
+ dcc->expect_init = FALSE;
+
+ spice_return_val_if_fail(!dcc->pixmap_cache, FALSE);
+ dcc->pixmap_cache = pixmap_cache_get(RED_CHANNEL_CLIENT(dcc)->client,
+ init->pixmap_cache_id,
+ init->pixmap_cache_size);
+ spice_return_val_if_fail(dcc->pixmap_cache, FALSE);
+
+ spice_return_val_if_fail(!dcc->glz_dict, FALSE);
+ ring_init(&dcc->glz_drawables);
+ ring_init(&dcc->glz_drawables_inst_to_free);
+ pthread_mutex_init(&dcc->glz_drawables_inst_to_free_lock, NULL);
+ dcc->glz_dict = dcc_get_glz_dictionary(dcc,
+ init->glz_dictionary_id,
+ init->glz_dictionary_window_size);
+ spice_return_val_if_fail(dcc->glz_dict, FALSE);
+
+ return TRUE;
+}
+
+static int dcc_handle_stream_report(DisplayChannelClient *dcc,
+ SpiceMsgcDisplayStreamReport *report)
+{
+ StreamAgent *agent;
+
+ spice_return_val_if_fail(report->stream_id < NUM_STREAMS, FALSE);
+ agent = &dcc->stream_agents[report->stream_id];
+ spice_return_val_if_fail(agent->mjpeg_encoder, TRUE);
+ spice_return_val_if_fail(report->unique_id == agent->report_id, TRUE);
+
+ mjpeg_encoder_client_stream_report(agent->mjpeg_encoder,
+ report->num_frames,
+ report->num_drops,
+ report->start_frame_mm_time,
+ report->end_frame_mm_time,
+ report->last_frame_delay,
+ report->audio_delay);
+ return TRUE;
+}
+
+static int dcc_handle_preferred_compression(DisplayChannelClient *dcc,
+ SpiceMsgcDisplayPreferredCompression *pc)
+{
+ switch (pc->image_compression) {
+ case SPICE_IMAGE_COMPRESSION_AUTO_LZ:
+ case SPICE_IMAGE_COMPRESSION_AUTO_GLZ:
+ case SPICE_IMAGE_COMPRESSION_QUIC:
+#ifdef USE_LZ4
+ case SPICE_IMAGE_COMPRESSION_LZ4:
+#endif
+ case SPICE_IMAGE_COMPRESSION_LZ:
+ case SPICE_IMAGE_COMPRESSION_GLZ:
+ case SPICE_IMAGE_COMPRESSION_OFF:
+ /* XXX change red_worker one too ? */
+ dcc->image_compression = pc->image_compression;
+ return TRUE;
+ default:
+ spice_warning("preferred-compression: unsupported image compression setting");
+ return FALSE;
+ }
+}
+
+int dcc_handle_message(RedChannelClient *rcc, uint32_t size, uint16_t type, void *msg)
+{
+ DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
+
+ switch (type) {
+ case SPICE_MSGC_DISPLAY_INIT:
+ return dcc_handle_init(dcc, (SpiceMsgcDisplayInit *)msg);
+ case SPICE_MSGC_DISPLAY_STREAM_REPORT:
+ return dcc_handle_stream_report(dcc, (SpiceMsgcDisplayStreamReport *)msg);
+ case SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION:
+ return dcc_handle_preferred_compression(dcc,
+ (SpiceMsgcDisplayPreferredCompression *)msg);
+ default:
+ return red_channel_client_handle_message(rcc, size, type, msg);
+ }
+}
diff --git a/server/dcc.h b/server/dcc.h
index f7c01a1..2757d16 100644
--- a/server/dcc.h
+++ b/server/dcc.h
@@ -158,6 +158,9 @@ DisplayChannelClient* dcc_new (DisplayCha
spice_wan_compression_t jpeg_state,
spice_wan_compression_t zlib_glz_state);
void dcc_start (DisplayChannelClient *dcc);
+int dcc_handle_message (RedChannelClient *rcc,
+ uint32_t size,
+ uint16_t type, void *msg);
void dcc_push_monitors_config (DisplayChannelClient *dcc);
void dcc_destroy_surface (DisplayChannelClient *dcc,
uint32_t surface_id);
diff --git a/server/red_worker.c b/server/red_worker.c
index 337748d..adc9cb3 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -83,16 +83,11 @@ struct SpiceWatch {
void *watch_func_opaque;
};
-#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
-
#define MAX_PIPE_SIZE 50
#define WIDE_CLIENT_ACK_WINDOW 40
#define NARROW_CLIENT_ACK_WINDOW 20
-pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
-Ring glz_dictionary_list = {&glz_dictionary_list, &glz_dictionary_list};
-
typedef struct UpgradeItem {
PipeItem base;
int refs;
@@ -158,7 +153,6 @@ static void red_update_area(DisplayChannel *display, const SpiceRect *area, int
static void red_update_area_till(DisplayChannel *display, const SpiceRect *area, int surface_id,
Drawable *last);
static inline void display_begin_send_message(RedChannelClient *rcc);
-static void dcc_release_glz(DisplayChannelClient *dcc);
static int red_display_free_some_independent_glz_drawables(DisplayChannelClient *dcc);
static void display_channel_client_release_item_before_push(DisplayChannelClient *dcc,
PipeItem *item);
@@ -4344,167 +4338,18 @@ static inline void flush_all_qxl_commands(RedWorker *worker)
flush_cursor_commands(worker);
}
-static GlzSharedDictionary *_red_find_glz_dictionary(RedClient *client, uint8_t dict_id)
-{
- RingItem *now;
- GlzSharedDictionary *ret = NULL;
-
- now = &glz_dictionary_list;
- while ((now = ring_next(&glz_dictionary_list, now))) {
- GlzSharedDictionary *dict = (GlzSharedDictionary *)now;
- if ((dict->client == client) && (dict->id == dict_id)) {
- ret = dict;
- break;
- }
- }
-
- return ret;
-}
-
-static GlzSharedDictionary *_red_create_glz_dictionary(RedClient *client, uint8_t id,
- GlzEncDictContext *opaque_dict)
-{
- GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
- shared_dict->dict = opaque_dict;
- shared_dict->id = id;
- shared_dict->refs = 1;
- shared_dict->migrate_freeze = FALSE;
- shared_dict->client = client;
- ring_item_init(&shared_dict->base);
- pthread_rwlock_init(&shared_dict->encode_lock, NULL);
- return shared_dict;
-}
-
-static GlzSharedDictionary *red_create_glz_dictionary(DisplayChannelClient *dcc,
- uint8_t id, int window_size)
-{
- GlzEncDictContext *glz_dict = glz_enc_dictionary_create(window_size,
- MAX_LZ_ENCODERS,
- &dcc->glz_data.usr);
-#ifdef COMPRESS_DEBUG
- spice_info("Lz Window %d Size=%d", id, window_size);
-#endif
- if (!glz_dict) {
- spice_critical("failed creating lz dictionary");
- return NULL;
- }
- return _red_create_glz_dictionary(RED_CHANNEL_CLIENT(dcc)->client, id, glz_dict);
-}
-
-static GlzSharedDictionary *red_create_restored_glz_dictionary(DisplayChannelClient *dcc,
- uint8_t id,
- GlzEncDictRestoreData *restore_data)
-{
- GlzEncDictContext *glz_dict = glz_enc_dictionary_restore(restore_data,
- &dcc->glz_data.usr);
- if (!glz_dict) {
- spice_critical("failed creating lz dictionary");
- return NULL;
- }
- return _red_create_glz_dictionary(RED_CHANNEL_CLIENT(dcc)->client, id, glz_dict);
-}
-
-static GlzSharedDictionary *red_get_glz_dictionary(DisplayChannelClient *dcc,
- uint8_t id, int window_size)
-{
- GlzSharedDictionary *shared_dict = NULL;
-
- pthread_mutex_lock(&glz_dictionary_list_lock);
-
- shared_dict = _red_find_glz_dictionary(RED_CHANNEL_CLIENT(dcc)->client, id);
-
- if (!shared_dict) {
- shared_dict = red_create_glz_dictionary(dcc, id, window_size);
- ring_add(&glz_dictionary_list, &shared_dict->base);
- } else {
- shared_dict->refs++;
- }
- pthread_mutex_unlock(&glz_dictionary_list_lock);
- return shared_dict;
-}
-
-static GlzSharedDictionary *red_restore_glz_dictionary(DisplayChannelClient *dcc,
- uint8_t id,
- GlzEncDictRestoreData *restore_data)
-{
- GlzSharedDictionary *shared_dict = NULL;
-
- pthread_mutex_lock(&glz_dictionary_list_lock);
-
- shared_dict = _red_find_glz_dictionary(RED_CHANNEL_CLIENT(dcc)->client, id);
-
- if (!shared_dict) {
- shared_dict = red_create_restored_glz_dictionary(dcc, id, restore_data);
- ring_add(&glz_dictionary_list, &shared_dict->base);
- } else {
- shared_dict->refs++;
- }
- pthread_mutex_unlock(&glz_dictionary_list_lock);
- return shared_dict;
-}
-
-/* destroy encoder, and dictionary if no one uses it*/
-static void dcc_release_glz(DisplayChannelClient *dcc)
-{
- GlzSharedDictionary *shared_dict;
-
- dcc_free_glz_drawables(dcc);
-
- glz_encoder_destroy(dcc->glz);
- dcc->glz = NULL;
-
- if (!(shared_dict = dcc->glz_dict)) {
- return;
- }
-
- dcc->glz_dict = NULL;
- pthread_mutex_lock(&glz_dictionary_list_lock);
- if (--shared_dict->refs) {
- pthread_mutex_unlock(&glz_dictionary_list_lock);
- return;
- }
- ring_remove(&shared_dict->base);
- pthread_mutex_unlock(&glz_dictionary_list_lock);
- glz_enc_dictionary_destroy(shared_dict->dict, &dcc->glz_data.usr);
- free(shared_dict);
-}
-
-static int display_channel_init_cache(DisplayChannelClient *dcc, SpiceMsgcDisplayInit *init_info)
+static int dcc_handle_migrate_glz_dictionary(DisplayChannelClient *dcc,
+ SpiceMigrateDataDisplay *migrate)
{
- spice_assert(!dcc->pixmap_cache);
- return !!(dcc->pixmap_cache = pixmap_cache_get(RED_CHANNEL_CLIENT(dcc)->client,
- init_info->pixmap_cache_id,
- init_info->pixmap_cache_size));
-}
+ spice_return_val_if_fail(!dcc->glz_dict, FALSE);
-static int display_channel_init_glz_dictionary(DisplayChannelClient *dcc,
- SpiceMsgcDisplayInit *init_info)
-{
- spice_assert(!dcc->glz_dict);
ring_init(&dcc->glz_drawables);
ring_init(&dcc->glz_drawables_inst_to_free);
pthread_mutex_init(&dcc->glz_drawables_inst_to_free_lock, NULL);
- return !!(dcc->glz_dict = red_get_glz_dictionary(dcc,
- init_info->glz_dictionary_id,
- init_info->glz_dictionary_window_size));
-}
-
-static int display_channel_init(DisplayChannelClient *dcc, SpiceMsgcDisplayInit *init_info)
-{
- return (display_channel_init_cache(dcc, init_info) &&
- display_channel_init_glz_dictionary(dcc, init_info));
-}
-
-static int display_channel_handle_migrate_glz_dictionary(DisplayChannelClient *dcc,
- SpiceMigrateDataDisplay *migrate_info)
-{
- spice_assert(!dcc->glz_dict);
- ring_init(&dcc->glz_drawables);
- ring_init(&dcc->glz_drawables_inst_to_free);
- pthread_mutex_init(&dcc->glz_drawables_inst_to_free_lock, NULL);
- return !!(dcc->glz_dict = red_restore_glz_dictionary(dcc,
- migrate_info->glz_dict_id,
- &migrate_info->glz_dict_data));
+ dcc->glz_dict = dcc_restore_glz_dictionary(dcc,
+ migrate->glz_dict_id,
+ &migrate->glz_dict_data);
+ return dcc->glz_dict != NULL;
}
static int display_channel_handle_migrate_mark(RedChannelClient *rcc)
@@ -4628,7 +4473,7 @@ static int display_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t s
PIPE_ITEM_TYPE_PIXMAP_RESET);
}
- if (display_channel_handle_migrate_glz_dictionary(dcc, migrate_data)) {
+ if (dcc_handle_migrate_glz_dictionary(dcc, migrate_data)) {
dcc->glz = glz_encoder_create(dcc->common.id,
dcc->glz_dict->dict, &dcc->glz_data.usr);
if (!dcc->glz) {
@@ -4668,84 +4513,6 @@ static int display_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t s
return TRUE;
}
-static int display_channel_handle_stream_report(DisplayChannelClient *dcc,
- SpiceMsgcDisplayStreamReport *stream_report)
-{
- StreamAgent *stream_agent;
-
- if (stream_report->stream_id >= NUM_STREAMS) {
- spice_warning("stream_report: invalid stream id %u", stream_report->stream_id);
- return FALSE;
- }
- stream_agent = &dcc->stream_agents[stream_report->stream_id];
- if (!stream_agent->mjpeg_encoder) {
- spice_info("stream_report: no encoder for stream id %u."
- "Probably the stream has been destroyed", stream_report->stream_id);
- return TRUE;
- }
-
- if (stream_report->unique_id != stream_agent->report_id) {
- spice_warning("local reoprt-id (%u) != msg report-id (%u)",
- stream_agent->report_id, stream_report->unique_id);
- return TRUE;
- }
- mjpeg_encoder_client_stream_report(stream_agent->mjpeg_encoder,
- stream_report->num_frames,
- stream_report->num_drops,
- stream_report->start_frame_mm_time,
- stream_report->end_frame_mm_time,
- stream_report->last_frame_delay,
- stream_report->audio_delay);
- return TRUE;
-}
-
-static int display_channel_handle_preferred_compression(DisplayChannelClient *dcc,
- SpiceMsgcDisplayPreferredCompression *pc) {
- DisplayChannel *display_channel = DCC_TO_DC(dcc);
- switch (pc->image_compression) {
- case SPICE_IMAGE_COMPRESSION_AUTO_LZ:
- case SPICE_IMAGE_COMPRESSION_AUTO_GLZ:
- case SPICE_IMAGE_COMPRESSION_QUIC:
-#ifdef USE_LZ4
- case SPICE_IMAGE_COMPRESSION_LZ4:
-#endif
- case SPICE_IMAGE_COMPRESSION_LZ:
- case SPICE_IMAGE_COMPRESSION_GLZ:
- case SPICE_IMAGE_COMPRESSION_OFF:
- display_channel->common.worker->image_compression = pc->image_compression;
- dcc->image_compression = pc->image_compression;
- return TRUE;
- default:
- spice_warning("preferred-compression: unsupported image compression setting");
- return FALSE;
- }
-}
-
-static int display_channel_handle_message(RedChannelClient *rcc, uint32_t size, uint16_t type,
- void *message)
-{
- DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
-
- switch (type) {
- case SPICE_MSGC_DISPLAY_INIT:
- if (!dcc->expect_init) {
- spice_warning("unexpected SPICE_MSGC_DISPLAY_INIT");
- return FALSE;
- }
- dcc->expect_init = FALSE;
- return display_channel_init(dcc, (SpiceMsgcDisplayInit *)message);
- case SPICE_MSGC_DISPLAY_STREAM_REPORT:
- return display_channel_handle_stream_report(dcc,
- (SpiceMsgcDisplayStreamReport *)message);
- case SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION:
- return display_channel_handle_preferred_compression(dcc,
- (SpiceMsgcDisplayPreferredCompression *)message);
-
- default:
- return red_channel_client_handle_message(rcc, size, type, message);
- }
-}
-
static int common_channel_config_socket(RedChannelClient *rcc)
{
RedClient *client = red_channel_client_get_client(rcc);
@@ -5091,7 +4858,7 @@ static void display_channel_create(RedWorker *worker, int migrate, int stream_vi
worker, sizeof(*display_channel), "display_channel",
SPICE_CHANNEL_DISPLAY,
SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER,
- &cbs, display_channel_handle_message))) {
+ &cbs, dcc_handle_message))) {
spice_warning("failed to create display channel");
return;
}
--
2.4.3
More information about the Spice-devel
mailing list