On Fri, Nov 20, 2015 at 12:17 PM, Frediano Ziglio <fziglio@xxxxxxxxxx> wrote: > From: Marc-André Lureau <marcandre.lureau@xxxxxxxxx> > > --- > 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 > > _______________________________________________ > Spice-devel mailing list > Spice-devel@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel ACK! -- Fabiano Fidêncio _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel