From: Marc-André Lureau <marcandre.lureau@xxxxxxxxx> Acked-by: Frediano Ziglio <fziglio@xxxxxxxxxx> --- server/Makefile.am | 1 + server/dcc-send.c | 2468 +++++++++++++++++++++++++++++++++++++++++++++++++++ server/dcc.h | 2 + server/red_worker.c | 2460 +------------------------------------------------- server/red_worker.h | 2 + 5 files changed, 2480 insertions(+), 2453 deletions(-) create mode 100644 server/dcc-send.c diff --git a/server/Makefile.am b/server/Makefile.am index db1cbc3..03ac757 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -140,6 +140,7 @@ libspice_server_la_SOURCES = \ stream.c \ stream.h \ dcc.c \ + dcc-send.c \ dcc.h \ display-limits.h \ dcc-encoders.c \ diff --git a/server/dcc-send.c b/server/dcc-send.c new file mode 100644 index 0000000..634084a --- /dev/null +++ b/server/dcc-send.c @@ -0,0 +1,2468 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2009 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "dcc.h" +#include "display-channel.h" + +#include "common/marshaller.h" +#include "common/generated_server_marshallers.h" + +typedef enum { + FILL_BITS_TYPE_INVALID, + FILL_BITS_TYPE_CACHE, + FILL_BITS_TYPE_SURFACE, + FILL_BITS_TYPE_COMPRESS_LOSSLESS, + FILL_BITS_TYPE_COMPRESS_LOSSY, + FILL_BITS_TYPE_BITMAP, +} FillBitsType; + +typedef enum { + BITMAP_DATA_TYPE_INVALID, + BITMAP_DATA_TYPE_CACHE, + BITMAP_DATA_TYPE_SURFACE, + BITMAP_DATA_TYPE_BITMAP, + BITMAP_DATA_TYPE_BITMAP_TO_CACHE, +} BitmapDataType; + +typedef struct BitmapData { + BitmapDataType type; + uint64_t id; // surface id or cache item id + SpiceRect lossy_rect; +} BitmapData; + +static int dcc_pixmap_cache_unlocked_hit(DisplayChannelClient *dcc, uint64_t id, int *lossy) +{ + PixmapCache *cache = dcc->pixmap_cache; + NewCacheItem *item; + uint64_t serial; + + serial = red_channel_client_get_message_serial(RED_CHANNEL_CLIENT(dcc)); + item = cache->hash_table[BITS_CACHE_HASH_KEY(id)]; + + while (item) { + if (item->id == id) { + ring_remove(&item->lru_link); + ring_add(&cache->lru, &item->lru_link); + spice_assert(dcc->common.id < MAX_CACHE_CLIENTS); + item->sync[dcc->common.id] = serial; + cache->sync[dcc->common.id] = serial; + *lossy = item->lossy; + break; + } + item = item->next; + } + + return !!item; +} + +static int dcc_pixmap_cache_hit(DisplayChannelClient *dcc, uint64_t id, int *lossy) +{ + int hit; + PixmapCache *cache = dcc->pixmap_cache; + + pthread_mutex_lock(&cache->lock); + hit = dcc_pixmap_cache_unlocked_hit(dcc, id, lossy); + pthread_mutex_unlock(&cache->lock); + return hit; +} + +/* set area=NULL for testing the whole surface */ +static int is_surface_area_lossy(DisplayChannelClient *dcc, uint32_t surface_id, + const SpiceRect *area, SpiceRect *out_lossy_area) +{ + RedSurface *surface; + QRegion *surface_lossy_region; + QRegion lossy_region; + DisplayChannel *display = DCC_TO_DC(dcc); + + spice_return_val_if_fail(validate_surface(display, surface_id), FALSE); + + surface = &display->surfaces[surface_id]; + surface_lossy_region = &dcc->surface_client_lossy_region[surface_id]; + + if (!area) { + if (region_is_empty(surface_lossy_region)) { + return FALSE; + } else { + out_lossy_area->top = 0; + out_lossy_area->left = 0; + out_lossy_area->bottom = surface->context.height; + out_lossy_area->right = surface->context.width; + return TRUE; + } + } + + region_init(&lossy_region); + region_add(&lossy_region, area); + region_and(&lossy_region, surface_lossy_region); + if (!region_is_empty(&lossy_region)) { + out_lossy_area->left = lossy_region.extents.x1; + out_lossy_area->top = lossy_region.extents.y1; + out_lossy_area->right = lossy_region.extents.x2; + out_lossy_area->bottom = lossy_region.extents.y2; + region_destroy(&lossy_region); + return TRUE; + } else { + return FALSE; + } +} + +/* returns if the bitmap was already sent lossy to the client. If the bitmap hasn't been sent yet + to the client, returns false. "area" is for surfaces. If area = NULL, + all the surface is considered. out_lossy_data will hold info about the bitmap, and its lossy + area in case it is lossy and part of a surface. */ +static int is_bitmap_lossy(RedChannelClient *rcc, SpiceImage *image, SpiceRect *area, + Drawable *drawable, BitmapData *out_data) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + + if (image == NULL) { + // self bitmap + out_data->type = BITMAP_DATA_TYPE_BITMAP; + return FALSE; + } + + if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { + int is_hit_lossy; + + out_data->id = image->descriptor.id; + if (dcc_pixmap_cache_hit(dcc, image->descriptor.id, &is_hit_lossy)) { + out_data->type = BITMAP_DATA_TYPE_CACHE; + if (is_hit_lossy) { + return TRUE; + } else { + return FALSE; + } + } else { + out_data->type = BITMAP_DATA_TYPE_BITMAP_TO_CACHE; + } + } else { + out_data->type = BITMAP_DATA_TYPE_BITMAP; + } + + if (image->descriptor.type != SPICE_IMAGE_TYPE_SURFACE) { + return FALSE; + } + + out_data->type = BITMAP_DATA_TYPE_SURFACE; + out_data->id = image->u.surface.surface_id; + + if (is_surface_area_lossy(dcc, out_data->id, + area, &out_data->lossy_rect)) + { + return TRUE; + } else { + return FALSE; + } +} + +static int is_brush_lossy(RedChannelClient *rcc, SpiceBrush *brush, + Drawable *drawable, BitmapData *out_data) +{ + if (brush->type == SPICE_BRUSH_TYPE_PATTERN) { + return is_bitmap_lossy(rcc, brush->u.pattern.pat, NULL, + drawable, out_data); + } else { + out_data->type = BITMAP_DATA_TYPE_INVALID; + return FALSE; + } +} + +static PipeItem *dcc_get_tail(DisplayChannelClient *dcc) +{ + return (PipeItem*)ring_get_tail(&RED_CHANNEL_CLIENT(dcc)->pipe); +} + +static inline void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc, + SpiceImage *image, SpiceImage *io_image, + int is_lossy) +{ + DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + + if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { + spice_assert(image->descriptor.width * image->descriptor.height > 0); + if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME)) { + if (dcc_pixmap_cache_unlocked_add(dcc, image->descriptor.id, + image->descriptor.width * image->descriptor.height, + is_lossy)) { + io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME; + dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] = + image->descriptor.id; + stat_inc_counter(display_channel->add_to_cache_counter, 1); + } + } + } + + if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { + stat_inc_counter(display_channel->non_cache_counter, 1); + } +} + +static void marshal_sub_msg_inval_list(SpiceMarshaller *m, + FreeList *free_list) +{ + /* type + size + submessage */ + spice_marshaller_add_uint16(m, SPICE_MSG_DISPLAY_INVAL_LIST); + spice_marshaller_add_uint32(m, sizeof(*free_list->res) + + free_list->res->count * sizeof(free_list->res->resources[0])); + spice_marshall_msg_display_inval_list(m, free_list->res); +} + +static void marshal_sub_msg_inval_list_wait(SpiceMarshaller *m, + FreeList *free_list) +{ + /* type + size + submessage */ + spice_marshaller_add_uint16(m, SPICE_MSG_WAIT_FOR_CHANNELS); + spice_marshaller_add_uint32(m, sizeof(free_list->wait.header) + + free_list->wait.header.wait_count * sizeof(free_list->wait.buf[0])); + spice_marshall_msg_wait_for_channels(m, &free_list->wait.header); +} + +/* use legacy SpiceDataHeader (with sub_list) */ +static void send_free_list_legacy(RedChannelClient *rcc) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + FreeList *free_list = &dcc->send_data.free_list; + SpiceMarshaller *marshaller; + int sub_list_len = 1; + SpiceMarshaller *wait_m = NULL; + SpiceMarshaller *inval_m; + SpiceMarshaller *sub_list_m; + + marshaller = red_channel_client_get_marshaller(rcc); + inval_m = spice_marshaller_get_submarshaller(marshaller); + + marshal_sub_msg_inval_list(inval_m, free_list); + + if (free_list->wait.header.wait_count) { + wait_m = spice_marshaller_get_submarshaller(marshaller); + marshal_sub_msg_inval_list_wait(wait_m, free_list); + sub_list_len++; + } + + sub_list_m = spice_marshaller_get_submarshaller(marshaller); + spice_marshaller_add_uint16(sub_list_m, sub_list_len); + if (wait_m) { + spice_marshaller_add_uint32(sub_list_m, spice_marshaller_get_offset(wait_m)); + } + spice_marshaller_add_uint32(sub_list_m, spice_marshaller_get_offset(inval_m)); + red_channel_client_set_header_sub_list(rcc, spice_marshaller_get_offset(sub_list_m)); +} + +/* use mini header and SPICE_MSG_LIST */ +static void send_free_list(RedChannelClient *rcc) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + FreeList *free_list = &dcc->send_data.free_list; + int sub_list_len = 1; + SpiceMarshaller *urgent_marshaller; + SpiceMarshaller *wait_m = NULL; + SpiceMarshaller *inval_m; + uint32_t sub_arr_offset; + uint32_t wait_offset = 0; + uint32_t inval_offset = 0; + int i; + + urgent_marshaller = red_channel_client_switch_to_urgent_sender(rcc); + for (i = 0; i < dcc->send_data.num_pixmap_cache_items; i++) { + int dummy; + /* When using the urgent marshaller, the serial number of the message that is + * going to be sent right after the SPICE_MSG_LIST, is increased by one. + * But all this message pixmaps cache references used its old serial. + * we use pixmap_cache_items to collect these pixmaps, and we update their serial + * by calling pixmap_cache_hit. */ + dcc_pixmap_cache_hit(dcc, dcc->send_data.pixmap_cache_items[i], &dummy); + } + + if (free_list->wait.header.wait_count) { + red_channel_client_init_send_data(rcc, SPICE_MSG_LIST, NULL); + } else { /* only one message, no need for a list */ + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_LIST, NULL); + spice_marshall_msg_display_inval_list(urgent_marshaller, free_list->res); + return; + } + + inval_m = spice_marshaller_get_submarshaller(urgent_marshaller); + marshal_sub_msg_inval_list(inval_m, free_list); + + if (free_list->wait.header.wait_count) { + wait_m = spice_marshaller_get_submarshaller(urgent_marshaller); + marshal_sub_msg_inval_list_wait(wait_m, free_list); + sub_list_len++; + } + + sub_arr_offset = sub_list_len * sizeof(uint32_t); + + spice_marshaller_add_uint16(urgent_marshaller, sub_list_len); + inval_offset = spice_marshaller_get_offset(inval_m); // calc the offset before + // adding the sub list + // offsets array to the marshaller + /* adding the array of offsets */ + if (wait_m) { + wait_offset = spice_marshaller_get_offset(wait_m); + spice_marshaller_add_uint32(urgent_marshaller, wait_offset + sub_arr_offset); + } + spice_marshaller_add_uint32(urgent_marshaller, inval_offset + sub_arr_offset); +} + +static void fill_base(SpiceMarshaller *base_marshaller, Drawable *drawable) +{ + SpiceMsgDisplayBase base; + + base.surface_id = drawable->surface_id; + base.box = drawable->red_drawable->bbox; + base.clip = drawable->red_drawable->clip; + + spice_marshall_DisplayBase(base_marshaller, &base); +} + +/* if the number of times fill_bits can be called per one qxl_drawable increases - + MAX_LZ_DRAWABLE_INSTANCES must be increased as well */ +static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, + SpiceImage *simage, Drawable *drawable, int can_lossy) +{ + RedChannelClient *rcc = RED_CHANNEL_CLIENT(dcc); + DisplayChannel *display = DCC_TO_DC(dcc); + SpiceImage image; + compress_send_data_t comp_send_data = {0}; + SpiceMarshaller *bitmap_palette_out, *lzplt_palette_out; + + if (simage == NULL) { + spice_assert(drawable->red_drawable->self_bitmap_image); + simage = drawable->red_drawable->self_bitmap_image; + } + + image.descriptor = simage->descriptor; + image.descriptor.flags = 0; + if (simage->descriptor.flags & SPICE_IMAGE_FLAGS_HIGH_BITS_SET) { + image.descriptor.flags = SPICE_IMAGE_FLAGS_HIGH_BITS_SET; + } + pthread_mutex_lock(&dcc->pixmap_cache->lock); + + if ((simage->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { + int lossy_cache_item; + if (dcc_pixmap_cache_unlocked_hit(dcc, image.descriptor.id, &lossy_cache_item)) { + dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] = + image.descriptor.id; + if (can_lossy || !lossy_cache_item) { + if (!display->enable_jpeg || lossy_cache_item) { + image.descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE; + } else { + // making sure, in multiple monitor scenario, that lossy items that + // should have been replaced with lossless data by one display channel, + // will be retrieved as lossless by another display channel. + image.descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS; + } + spice_marshall_Image(m, &image, + &bitmap_palette_out, &lzplt_palette_out); + spice_assert(bitmap_palette_out == NULL); + spice_assert(lzplt_palette_out == NULL); + stat_inc_counter(display->cache_hits_counter, 1); + pthread_mutex_unlock(&dcc->pixmap_cache->lock); + return FILL_BITS_TYPE_CACHE; + } else { + pixmap_cache_unlocked_set_lossy(dcc->pixmap_cache, simage->descriptor.id, + FALSE); + image.descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME; + } + } + } + + switch (simage->descriptor.type) { + case SPICE_IMAGE_TYPE_SURFACE: { + int surface_id; + RedSurface *surface; + + surface_id = simage->u.surface.surface_id; + if (!validate_surface(display, surface_id)) { + spice_warning("Invalid surface in SPICE_IMAGE_TYPE_SURFACE"); + pthread_mutex_unlock(&dcc->pixmap_cache->lock); + return FILL_BITS_TYPE_SURFACE; + } + + surface = &display->surfaces[surface_id]; + image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE; + image.descriptor.flags = 0; + image.descriptor.width = surface->context.width; + image.descriptor.height = surface->context.height; + + image.u.surface.surface_id = surface_id; + spice_marshall_Image(m, &image, + &bitmap_palette_out, &lzplt_palette_out); + spice_assert(bitmap_palette_out == NULL); + spice_assert(lzplt_palette_out == NULL); + pthread_mutex_unlock(&dcc->pixmap_cache->lock); + return FILL_BITS_TYPE_SURFACE; + } + case SPICE_IMAGE_TYPE_BITMAP: { + SpiceBitmap *bitmap = &image.u.bitmap; +#ifdef DUMP_BITMAP + dump_bitmap(&simage->u.bitmap); +#endif + /* Images must be added to the cache only after they are compressed + in order to prevent starvation in the client between pixmap_cache and + global dictionary (in cases of multiple monitors) */ + if (reds_stream_get_family(rcc->stream) == AF_UNIX || + !dcc_compress_image(dcc, &image, &simage->u.bitmap, + drawable, can_lossy, &comp_send_data)) { + SpicePalette *palette; + + red_display_add_image_to_pixmap_cache(rcc, simage, &image, FALSE); + + *bitmap = simage->u.bitmap; + bitmap->flags = bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN; + + palette = bitmap->palette; + dcc_palette_cache_palette(dcc, palette, &bitmap->flags); + spice_marshall_Image(m, &image, + &bitmap_palette_out, &lzplt_palette_out); + spice_assert(lzplt_palette_out == NULL); + + if (bitmap_palette_out && palette) { + spice_marshall_Palette(bitmap_palette_out, palette); + } + + spice_marshaller_add_ref_chunks(m, bitmap->data); + pthread_mutex_unlock(&dcc->pixmap_cache->lock); + return FILL_BITS_TYPE_BITMAP; + } else { + red_display_add_image_to_pixmap_cache(rcc, simage, &image, + comp_send_data.is_lossy); + + spice_marshall_Image(m, &image, + &bitmap_palette_out, &lzplt_palette_out); + spice_assert(bitmap_palette_out == NULL); + + marshaller_add_compressed(m, comp_send_data.comp_buf, + comp_send_data.comp_buf_size); + + if (lzplt_palette_out && comp_send_data.lzplt_palette) { + spice_marshall_Palette(lzplt_palette_out, comp_send_data.lzplt_palette); + } + + spice_assert(!comp_send_data.is_lossy || can_lossy); + pthread_mutex_unlock(&dcc->pixmap_cache->lock); + return (comp_send_data.is_lossy ? FILL_BITS_TYPE_COMPRESS_LOSSY : + FILL_BITS_TYPE_COMPRESS_LOSSLESS); + } + break; + } + case SPICE_IMAGE_TYPE_QUIC: + red_display_add_image_to_pixmap_cache(rcc, simage, &image, FALSE); + image.u.quic = simage->u.quic; + spice_marshall_Image(m, &image, + &bitmap_palette_out, &lzplt_palette_out); + spice_assert(bitmap_palette_out == NULL); + spice_assert(lzplt_palette_out == NULL); + spice_marshaller_add_ref_chunks(m, image.u.quic.data); + pthread_mutex_unlock(&dcc->pixmap_cache->lock); + return FILL_BITS_TYPE_COMPRESS_LOSSLESS; + default: + spice_error("invalid image type %u", image.descriptor.type); + } + pthread_mutex_unlock(&dcc->pixmap_cache->lock); + return FILL_BITS_TYPE_INVALID; +} + +static void fill_mask(RedChannelClient *rcc, SpiceMarshaller *m, + SpiceImage *mask_bitmap, Drawable *drawable) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + + if (mask_bitmap && m) { + if (dcc->image_compression != SPICE_IMAGE_COMPRESSION_OFF) { + /* todo: pass compression argument */ + SpiceImageCompression save_img_comp = dcc->image_compression; + dcc->image_compression = SPICE_IMAGE_COMPRESSION_OFF; + fill_bits(dcc, m, mask_bitmap, drawable, FALSE); + dcc->image_compression = save_img_comp; + } else { + fill_bits(dcc, m, mask_bitmap, drawable, FALSE); + } + } +} + +static void fill_attr(SpiceMarshaller *m, SpiceLineAttr *attr, uint32_t group_id) +{ + int i; + + if (m && attr->style_nseg) { + for (i = 0 ; i < attr->style_nseg; i++) { + spice_marshaller_add_uint32(m, attr->style[i]); + } + } +} + +static void marshall_qxl_draw_fill(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + SpiceMarshaller *brush_pat_out; + SpiceMarshaller *mask_bitmap_out; + SpiceFill fill; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + fill = drawable->u.fill; + spice_marshall_Fill(base_marshaller, + &fill, + &brush_pat_out, + &mask_bitmap_out); + + if (brush_pat_out) { + fill_bits(dcc, brush_pat_out, fill.brush.u.pattern.pat, item, FALSE); + } + + fill_mask(rcc, mask_bitmap_out, fill.mask.bitmap, item); +} + +static void surface_lossy_region_update(DisplayChannelClient *dcc, + Drawable *item, int has_mask, int lossy) +{ + QRegion *surface_lossy_region; + RedDrawable *drawable; + + if (has_mask && !lossy) { + return; + } + + surface_lossy_region = &dcc->surface_client_lossy_region[item->surface_id]; + drawable = item->red_drawable; + + if (drawable->clip.type == SPICE_CLIP_TYPE_RECTS ) { + QRegion clip_rgn; + QRegion draw_region; + region_init(&clip_rgn); + region_init(&draw_region); + region_add(&draw_region, &drawable->bbox); + region_add_clip_rects(&clip_rgn, drawable->clip.rects); + region_and(&draw_region, &clip_rgn); + if (lossy) { + region_or(surface_lossy_region, &draw_region); + } else { + region_exclude(surface_lossy_region, &draw_region); + } + + region_destroy(&clip_rgn); + region_destroy(&draw_region); + } else { /* no clip */ + if (!lossy) { + region_remove(surface_lossy_region, &drawable->bbox); + } else { + region_add(surface_lossy_region, &drawable->bbox); + } + } +} + +static int drawable_intersects_with_areas(Drawable *drawable, int surface_ids[], + SpiceRect *surface_areas[], + int num_surfaces) +{ + int i; + for (i = 0; i < num_surfaces; i++) { + if (surface_ids[i] == drawable->red_drawable->surface_id) { + if (rect_intersects(surface_areas[i], &drawable->red_drawable->bbox)) { + return TRUE; + } + } + } + return FALSE; +} + +static int pipe_rendered_drawables_intersect_with_areas(DisplayChannelClient *dcc, + int surface_ids[], + SpiceRect *surface_areas[], + int num_surfaces) +{ + PipeItem *pipe_item; + Ring *pipe; + + spice_assert(num_surfaces); + pipe = &RED_CHANNEL_CLIENT(dcc)->pipe; + + for (pipe_item = (PipeItem *)ring_get_head(pipe); + pipe_item; + pipe_item = (PipeItem *)ring_next(pipe, &pipe_item->link)) + { + Drawable *drawable; + + if (pipe_item->type != PIPE_ITEM_TYPE_DRAW) + continue; + drawable = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item)->drawable; + + if (ring_item_is_linked(&drawable->list_link)) + continue; // item hasn't been rendered + + if (drawable_intersects_with_areas(drawable, surface_ids, surface_areas, num_surfaces)) { + return TRUE; + } + } + + return FALSE; +} + +static int drawable_depends_on_areas(Drawable *drawable, int surface_ids[], + SpiceRect surface_areas[], int num_surfaces) +{ + int i; + RedDrawable *red_drawable; + int drawable_has_shadow; + SpiceRect shadow_rect = {0, 0, 0, 0}; + + red_drawable = drawable->red_drawable; + drawable_has_shadow = has_shadow(red_drawable); + + if (drawable_has_shadow) { + int delta_x = red_drawable->u.copy_bits.src_pos.x - red_drawable->bbox.left; + int delta_y = red_drawable->u.copy_bits.src_pos.y - red_drawable->bbox.top; + + shadow_rect.left = red_drawable->u.copy_bits.src_pos.x; + shadow_rect.top = red_drawable->u.copy_bits.src_pos.y; + shadow_rect.right = red_drawable->bbox.right + delta_x; + shadow_rect.bottom = red_drawable->bbox.bottom + delta_y; + } + + for (i = 0; i < num_surfaces; i++) { + int x; + int dep_surface_id; + + for (x = 0; x < 3; ++x) { + dep_surface_id = drawable->surface_deps[x]; + if (dep_surface_id == surface_ids[i]) { + if (rect_intersects(&surface_areas[i], &red_drawable->surfaces_rects[x])) { + return TRUE; + } + } + } + + if (surface_ids[i] == red_drawable->surface_id) { + if (drawable_has_shadow) { + if (rect_intersects(&surface_areas[i], &shadow_rect)) { + return TRUE; + } + } + + // not dependent on dest + if (red_drawable->effect == QXL_EFFECT_OPAQUE) { + continue; + } + + if (rect_intersects(&surface_areas[i], &red_drawable->bbox)) { + return TRUE; + } + } + + } + return FALSE; +} + +static void red_pipe_replace_rendered_drawables_with_images(DisplayChannelClient *dcc, + int first_surface_id, + SpiceRect *first_area) +{ + /* TODO: can't have those statics with multiple clients */ + static int resent_surface_ids[MAX_PIPE_SIZE]; + static SpiceRect resent_areas[MAX_PIPE_SIZE]; // not pointers since drawbales may be released + int num_resent; + PipeItem *pipe_item; + Ring *pipe; + + resent_surface_ids[0] = first_surface_id; + resent_areas[0] = *first_area; + num_resent = 1; + + pipe = &RED_CHANNEL_CLIENT(dcc)->pipe; + + // going from the oldest to the newest + for (pipe_item = (PipeItem *)ring_get_tail(pipe); + pipe_item; + pipe_item = (PipeItem *)ring_prev(pipe, &pipe_item->link)) { + Drawable *drawable; + DrawablePipeItem *dpi; + ImageItem *image; + + if (pipe_item->type != PIPE_ITEM_TYPE_DRAW) + continue; + dpi = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item); + drawable = dpi->drawable; + if (ring_item_is_linked(&drawable->list_link)) + continue; // item hasn't been rendered + + // When a drawable command, X, depends on bitmaps that were resent, + // these bitmaps state at the client might not be synchronized with X + // (i.e., the bitmaps can be more futuristic w.r.t X). Thus, X shouldn't + // be rendered at the client, and we replace it with an image as well. + if (!drawable_depends_on_areas(drawable, + resent_surface_ids, + resent_areas, + num_resent)) { + continue; + } + + image = dcc_add_surface_area_image(dcc, drawable->red_drawable->surface_id, + &drawable->red_drawable->bbox, pipe_item, TRUE); + resent_surface_ids[num_resent] = drawable->red_drawable->surface_id; + resent_areas[num_resent] = drawable->red_drawable->bbox; + num_resent++; + + spice_assert(image); + red_channel_client_pipe_remove_and_release(RED_CHANNEL_CLIENT(dcc), &dpi->dpi_pipe_item); + pipe_item = &image->link; + } +} + +static void red_add_lossless_drawable_dependencies(RedChannelClient *rcc, + Drawable *item, + int deps_surfaces_ids[], + SpiceRect *deps_areas[], + int num_deps) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannel *display = DCC_TO_DC(dcc); + RedDrawable *drawable = item->red_drawable; + int sync_rendered = FALSE; + int i; + + if (!ring_item_is_linked(&item->list_link)) { + /* drawable was already rendered, we may not be able to retrieve the lossless data + for the lossy areas */ + sync_rendered = TRUE; + + // checking if the drawable itself or one of the other commands + // that were rendered, affected the areas that need to be resent + if (!drawable_intersects_with_areas(item, deps_surfaces_ids, + deps_areas, num_deps)) { + if (pipe_rendered_drawables_intersect_with_areas(dcc, + deps_surfaces_ids, + deps_areas, + num_deps)) { + sync_rendered = TRUE; + } + } else { + sync_rendered = TRUE; + } + } else { + sync_rendered = FALSE; + for (i = 0; i < num_deps; i++) { + display_channel_draw_until(display, deps_areas[i], deps_surfaces_ids[i], item); + } + } + + if (!sync_rendered) { + // pushing the pipe item back to the pipe + dcc_append_drawable(dcc, item); + // the surfaces areas will be sent as DRAW_COPY commands, that + // will be executed before the current drawable + for (i = 0; i < num_deps; i++) { + dcc_add_surface_area_image(dcc, deps_surfaces_ids[i], deps_areas[i], + dcc_get_tail(dcc), FALSE); + + } + } else { + int drawable_surface_id[1]; + SpiceRect *drawable_bbox[1]; + + drawable_surface_id[0] = drawable->surface_id; + drawable_bbox[0] = &drawable->bbox; + + // check if the other rendered images in the pipe have updated the drawable bbox + if (pipe_rendered_drawables_intersect_with_areas(dcc, + drawable_surface_id, + drawable_bbox, + 1)) { + red_pipe_replace_rendered_drawables_with_images(dcc, + drawable->surface_id, + &drawable->bbox); + } + + dcc_add_surface_area_image(dcc, drawable->surface_id, &drawable->bbox, + dcc_get_tail(dcc), TRUE); + } +} + +static void red_lossy_marshall_qxl_draw_fill(RedChannelClient *rcc, + SpiceMarshaller *m, + DrawablePipeItem *dpi) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + + int dest_allowed_lossy = FALSE; + int dest_is_lossy = FALSE; + SpiceRect dest_lossy_area; + int brush_is_lossy; + BitmapData brush_bitmap_data; + uint16_t rop; + + rop = drawable->u.fill.rop_descriptor; + + dest_allowed_lossy = !((rop & SPICE_ROPD_OP_OR) || + (rop & SPICE_ROPD_OP_AND) || + (rop & SPICE_ROPD_OP_XOR)); + + brush_is_lossy = is_brush_lossy(rcc, &drawable->u.fill.brush, item, + &brush_bitmap_data); + if (!dest_allowed_lossy) { + dest_is_lossy = is_surface_area_lossy(dcc, item->surface_id, &drawable->bbox, + &dest_lossy_area); + } + + if (!dest_is_lossy && + !(brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE))) { + int has_mask = !!drawable->u.fill.mask.bitmap; + + marshall_qxl_draw_fill(rcc, m, dpi); + // either the brush operation is opaque, or the dest is not lossy + surface_lossy_region_update(dcc, item, has_mask, FALSE); + } else { + int resend_surface_ids[2]; + SpiceRect *resend_areas[2]; + int num_resend = 0; + + if (dest_is_lossy) { + resend_surface_ids[num_resend] = item->surface_id; + resend_areas[num_resend] = &dest_lossy_area; + num_resend++; + } + + if (brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = brush_bitmap_data.id; + resend_areas[num_resend] = &brush_bitmap_data.lossy_rect; + num_resend++; + } + + red_add_lossless_drawable_dependencies(rcc, item, + resend_surface_ids, resend_areas, num_resend); + } +} + +static FillBitsType red_marshall_qxl_draw_opaque(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi, int src_allowed_lossy) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + SpiceMarshaller *brush_pat_out; + SpiceMarshaller *src_bitmap_out; + SpiceMarshaller *mask_bitmap_out; + SpiceOpaque opaque; + FillBitsType src_send_type; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_OPAQUE, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + opaque = drawable->u.opaque; + spice_marshall_Opaque(base_marshaller, + &opaque, + &src_bitmap_out, + &brush_pat_out, + &mask_bitmap_out); + + src_send_type = fill_bits(dcc, src_bitmap_out, opaque.src_bitmap, item, + src_allowed_lossy); + + if (brush_pat_out) { + fill_bits(dcc, brush_pat_out, opaque.brush.u.pattern.pat, item, FALSE); + } + fill_mask(rcc, mask_bitmap_out, opaque.mask.bitmap, item); + + return src_send_type; +} + +static void red_lossy_marshall_qxl_draw_opaque(RedChannelClient *rcc, + SpiceMarshaller *m, + DrawablePipeItem *dpi) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + + int src_allowed_lossy; + int rop; + int src_is_lossy = FALSE; + int brush_is_lossy = FALSE; + BitmapData src_bitmap_data; + BitmapData brush_bitmap_data; + + rop = drawable->u.opaque.rop_descriptor; + src_allowed_lossy = !((rop & SPICE_ROPD_OP_OR) || + (rop & SPICE_ROPD_OP_AND) || + (rop & SPICE_ROPD_OP_XOR)); + + brush_is_lossy = is_brush_lossy(rcc, &drawable->u.opaque.brush, item, + &brush_bitmap_data); + + if (!src_allowed_lossy) { + src_is_lossy = is_bitmap_lossy(rcc, drawable->u.opaque.src_bitmap, + &drawable->u.opaque.src_area, + item, + &src_bitmap_data); + } + + if (!(brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) && + !(src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE))) { + FillBitsType src_send_type; + int has_mask = !!drawable->u.opaque.mask.bitmap; + + src_send_type = red_marshall_qxl_draw_opaque(rcc, m, dpi, src_allowed_lossy); + if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) { + src_is_lossy = TRUE; + } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) { + src_is_lossy = FALSE; + } + + surface_lossy_region_update(dcc, item, has_mask, src_is_lossy); + } else { + int resend_surface_ids[2]; + SpiceRect *resend_areas[2]; + int num_resend = 0; + + if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = src_bitmap_data.id; + resend_areas[num_resend] = &src_bitmap_data.lossy_rect; + num_resend++; + } + + if (brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = brush_bitmap_data.id; + resend_areas[num_resend] = &brush_bitmap_data.lossy_rect; + num_resend++; + } + + red_add_lossless_drawable_dependencies(rcc, item, + resend_surface_ids, resend_areas, num_resend); + } +} + +static FillBitsType red_marshall_qxl_draw_copy(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi, int src_allowed_lossy) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + SpiceMarshaller *src_bitmap_out; + SpiceMarshaller *mask_bitmap_out; + SpiceCopy copy; + FillBitsType src_send_type; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + copy = drawable->u.copy; + spice_marshall_Copy(base_marshaller, + ©, + &src_bitmap_out, + &mask_bitmap_out); + + src_send_type = fill_bits(dcc, src_bitmap_out, copy.src_bitmap, item, src_allowed_lossy); + fill_mask(rcc, mask_bitmap_out, copy.mask.bitmap, item); + + return src_send_type; +} + +static void red_lossy_marshall_qxl_draw_copy(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + int has_mask = !!drawable->u.copy.mask.bitmap; + int src_is_lossy; + BitmapData src_bitmap_data; + FillBitsType src_send_type; + + src_is_lossy = is_bitmap_lossy(rcc, drawable->u.copy.src_bitmap, + &drawable->u.copy.src_area, item, &src_bitmap_data); + + src_send_type = red_marshall_qxl_draw_copy(rcc, base_marshaller, dpi, TRUE); + if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) { + src_is_lossy = TRUE; + } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) { + src_is_lossy = FALSE; + } + surface_lossy_region_update(dcc, item, has_mask, + src_is_lossy); +} + +static void red_marshall_qxl_draw_transparent(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + SpiceMarshaller *src_bitmap_out; + SpiceTransparent transparent; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TRANSPARENT, + &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + transparent = drawable->u.transparent; + spice_marshall_Transparent(base_marshaller, + &transparent, + &src_bitmap_out); + fill_bits(dcc, src_bitmap_out, transparent.src_bitmap, item, FALSE); +} + +static void red_lossy_marshall_qxl_draw_transparent(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + int src_is_lossy; + BitmapData src_bitmap_data; + + src_is_lossy = is_bitmap_lossy(rcc, drawable->u.transparent.src_bitmap, + &drawable->u.transparent.src_area, item, &src_bitmap_data); + + if (!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) { + red_marshall_qxl_draw_transparent(rcc, base_marshaller, dpi); + // don't update surface lossy region since transperent areas might be lossy + } else { + int resend_surface_ids[1]; + SpiceRect *resend_areas[1]; + + resend_surface_ids[0] = src_bitmap_data.id; + resend_areas[0] = &src_bitmap_data.lossy_rect; + + red_add_lossless_drawable_dependencies(rcc, item, + resend_surface_ids, resend_areas, 1); + } +} + +static FillBitsType red_marshall_qxl_draw_alpha_blend(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi, + int src_allowed_lossy) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + SpiceMarshaller *src_bitmap_out; + SpiceAlphaBlend alpha_blend; + FillBitsType src_send_type; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND, + &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + alpha_blend = drawable->u.alpha_blend; + spice_marshall_AlphaBlend(base_marshaller, + &alpha_blend, + &src_bitmap_out); + src_send_type = fill_bits(dcc, src_bitmap_out, alpha_blend.src_bitmap, item, + src_allowed_lossy); + + return src_send_type; +} + +static void red_lossy_marshall_qxl_draw_alpha_blend(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + int src_is_lossy; + BitmapData src_bitmap_data; + FillBitsType src_send_type; + + src_is_lossy = is_bitmap_lossy(rcc, drawable->u.alpha_blend.src_bitmap, + &drawable->u.alpha_blend.src_area, item, &src_bitmap_data); + + src_send_type = red_marshall_qxl_draw_alpha_blend(rcc, base_marshaller, dpi, TRUE); + + if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) { + src_is_lossy = TRUE; + } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) { + src_is_lossy = FALSE; + } + + if (src_is_lossy) { + surface_lossy_region_update(dcc, item, FALSE, src_is_lossy); + } // else, the area stays lossy/lossless as the destination +} + +static void red_marshall_qxl_copy_bits(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + SpicePoint copy_bits; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_COPY_BITS, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + copy_bits = drawable->u.copy_bits.src_pos; + spice_marshall_Point(base_marshaller, + ©_bits); +} + +static void red_lossy_marshall_qxl_copy_bits(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + SpiceRect src_rect; + int horz_offset; + int vert_offset; + int src_is_lossy; + SpiceRect src_lossy_area; + + red_marshall_qxl_copy_bits(rcc, base_marshaller, dpi); + + horz_offset = drawable->u.copy_bits.src_pos.x - drawable->bbox.left; + vert_offset = drawable->u.copy_bits.src_pos.y - drawable->bbox.top; + + src_rect.left = drawable->u.copy_bits.src_pos.x; + src_rect.top = drawable->u.copy_bits.src_pos.y; + src_rect.right = drawable->bbox.right + horz_offset; + src_rect.bottom = drawable->bbox.bottom + vert_offset; + + src_is_lossy = is_surface_area_lossy(dcc, item->surface_id, + &src_rect, &src_lossy_area); + + surface_lossy_region_update(dcc, item, FALSE, src_is_lossy); +} + +static void red_marshall_qxl_draw_blend(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + SpiceMarshaller *src_bitmap_out; + SpiceMarshaller *mask_bitmap_out; + SpiceBlend blend; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLEND, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + blend = drawable->u.blend; + spice_marshall_Blend(base_marshaller, + &blend, + &src_bitmap_out, + &mask_bitmap_out); + + fill_bits(dcc, src_bitmap_out, blend.src_bitmap, item, FALSE); + + fill_mask(rcc, mask_bitmap_out, blend.mask.bitmap, item); +} + +static void red_lossy_marshall_qxl_draw_blend(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + int src_is_lossy; + BitmapData src_bitmap_data; + int dest_is_lossy; + SpiceRect dest_lossy_area; + + src_is_lossy = is_bitmap_lossy(rcc, drawable->u.blend.src_bitmap, + &drawable->u.blend.src_area, item, &src_bitmap_data); + dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, + &drawable->bbox, &dest_lossy_area); + + if (!dest_is_lossy && + (!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) { + red_marshall_qxl_draw_blend(rcc, base_marshaller, dpi); + } else { + int resend_surface_ids[2]; + SpiceRect *resend_areas[2]; + int num_resend = 0; + + if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = src_bitmap_data.id; + resend_areas[num_resend] = &src_bitmap_data.lossy_rect; + num_resend++; + } + + if (dest_is_lossy) { + resend_surface_ids[num_resend] = item->surface_id; + resend_areas[num_resend] = &dest_lossy_area; + num_resend++; + } + + red_add_lossless_drawable_dependencies(rcc, item, + resend_surface_ids, resend_areas, num_resend); + } +} + +static void red_marshall_qxl_draw_blackness(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + SpiceMarshaller *mask_bitmap_out; + SpiceBlackness blackness; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLACKNESS, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + blackness = drawable->u.blackness; + + spice_marshall_Blackness(base_marshaller, + &blackness, + &mask_bitmap_out); + + fill_mask(rcc, mask_bitmap_out, blackness.mask.bitmap, item); +} + +static void red_lossy_marshall_qxl_draw_blackness(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + int has_mask = !!drawable->u.blackness.mask.bitmap; + + red_marshall_qxl_draw_blackness(rcc, base_marshaller, dpi); + + surface_lossy_region_update(dcc, item, has_mask, FALSE); +} + +static void red_marshall_qxl_draw_whiteness(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + SpiceMarshaller *mask_bitmap_out; + SpiceWhiteness whiteness; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_WHITENESS, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + whiteness = drawable->u.whiteness; + + spice_marshall_Whiteness(base_marshaller, + &whiteness, + &mask_bitmap_out); + + fill_mask(rcc, mask_bitmap_out, whiteness.mask.bitmap, item); +} + +static void red_lossy_marshall_qxl_draw_whiteness(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + int has_mask = !!drawable->u.whiteness.mask.bitmap; + + red_marshall_qxl_draw_whiteness(rcc, base_marshaller, dpi); + + surface_lossy_region_update(dcc, item, has_mask, FALSE); +} + +static void red_marshall_qxl_draw_inverse(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + Drawable *item) +{ + RedDrawable *drawable = item->red_drawable; + SpiceMarshaller *mask_bitmap_out; + SpiceInvers inverse; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_INVERS, NULL); + fill_base(base_marshaller, item); + inverse = drawable->u.invers; + + spice_marshall_Invers(base_marshaller, + &inverse, + &mask_bitmap_out); + + fill_mask(rcc, mask_bitmap_out, inverse.mask.bitmap, item); +} + +static void red_lossy_marshall_qxl_draw_inverse(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + Drawable *item) +{ + red_marshall_qxl_draw_inverse(rcc, base_marshaller, item); +} + +static void red_marshall_qxl_draw_rop3(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + SpiceRop3 rop3; + SpiceMarshaller *src_bitmap_out; + SpiceMarshaller *brush_pat_out; + SpiceMarshaller *mask_bitmap_out; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ROP3, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + rop3 = drawable->u.rop3; + spice_marshall_Rop3(base_marshaller, + &rop3, + &src_bitmap_out, + &brush_pat_out, + &mask_bitmap_out); + + fill_bits(dcc, src_bitmap_out, rop3.src_bitmap, item, FALSE); + + if (brush_pat_out) { + fill_bits(dcc, brush_pat_out, rop3.brush.u.pattern.pat, item, FALSE); + } + fill_mask(rcc, mask_bitmap_out, rop3.mask.bitmap, item); +} + +static void red_lossy_marshall_qxl_draw_rop3(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + int src_is_lossy; + BitmapData src_bitmap_data; + int brush_is_lossy; + BitmapData brush_bitmap_data; + int dest_is_lossy; + SpiceRect dest_lossy_area; + + src_is_lossy = is_bitmap_lossy(rcc, drawable->u.rop3.src_bitmap, + &drawable->u.rop3.src_area, item, &src_bitmap_data); + brush_is_lossy = is_brush_lossy(rcc, &drawable->u.rop3.brush, item, + &brush_bitmap_data); + dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, + &drawable->bbox, &dest_lossy_area); + + if ((!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && + (!brush_is_lossy || (brush_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && + !dest_is_lossy) { + int has_mask = !!drawable->u.rop3.mask.bitmap; + red_marshall_qxl_draw_rop3(rcc, base_marshaller, dpi); + surface_lossy_region_update(dcc, item, has_mask, FALSE); + } else { + int resend_surface_ids[3]; + SpiceRect *resend_areas[3]; + int num_resend = 0; + + if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = src_bitmap_data.id; + resend_areas[num_resend] = &src_bitmap_data.lossy_rect; + num_resend++; + } + + if (brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = brush_bitmap_data.id; + resend_areas[num_resend] = &brush_bitmap_data.lossy_rect; + num_resend++; + } + + if (dest_is_lossy) { + resend_surface_ids[num_resend] = item->surface_id; + resend_areas[num_resend] = &dest_lossy_area; + num_resend++; + } + + red_add_lossless_drawable_dependencies(rcc, item, + resend_surface_ids, resend_areas, num_resend); + } +} + +static void red_marshall_qxl_draw_composite(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + SpiceMarshaller *src_bitmap_out; + SpiceMarshaller *mask_bitmap_out; + SpiceComposite composite; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COMPOSITE, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + composite = drawable->u.composite; + spice_marshall_Composite(base_marshaller, + &composite, + &src_bitmap_out, + &mask_bitmap_out); + + fill_bits(dcc, src_bitmap_out, composite.src_bitmap, item, FALSE); + if (mask_bitmap_out) { + fill_bits(dcc, mask_bitmap_out, composite.mask_bitmap, item, FALSE); + } +} + +static void red_lossy_marshall_qxl_draw_composite(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + int src_is_lossy; + BitmapData src_bitmap_data; + int mask_is_lossy; + BitmapData mask_bitmap_data; + int dest_is_lossy; + SpiceRect dest_lossy_area; + + src_is_lossy = is_bitmap_lossy(rcc, drawable->u.composite.src_bitmap, + NULL, item, &src_bitmap_data); + mask_is_lossy = drawable->u.composite.mask_bitmap && + is_bitmap_lossy(rcc, drawable->u.composite.mask_bitmap, NULL, item, &mask_bitmap_data); + + dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, + &drawable->bbox, &dest_lossy_area); + + if ((!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && + (!mask_is_lossy || (mask_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && + !dest_is_lossy) { + red_marshall_qxl_draw_composite(rcc, base_marshaller, dpi); + surface_lossy_region_update(dcc, item, FALSE, FALSE); + } + else { + int resend_surface_ids[3]; + SpiceRect *resend_areas[3]; + int num_resend = 0; + + if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = src_bitmap_data.id; + resend_areas[num_resend] = &src_bitmap_data.lossy_rect; + num_resend++; + } + + if (mask_is_lossy && (mask_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = mask_bitmap_data.id; + resend_areas[num_resend] = &mask_bitmap_data.lossy_rect; + num_resend++; + } + + if (dest_is_lossy) { + resend_surface_ids[num_resend] = item->surface_id; + resend_areas[num_resend] = &dest_lossy_area; + num_resend++; + } + + red_add_lossless_drawable_dependencies(rcc, item, + resend_surface_ids, resend_areas, num_resend); + } +} + +static void red_marshall_qxl_draw_stroke(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + SpiceStroke stroke; + SpiceMarshaller *brush_pat_out; + SpiceMarshaller *style_out; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_STROKE, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + stroke = drawable->u.stroke; + spice_marshall_Stroke(base_marshaller, + &stroke, + &style_out, + &brush_pat_out); + + fill_attr(style_out, &stroke.attr, item->group_id); + if (brush_pat_out) { + fill_bits(dcc, brush_pat_out, stroke.brush.u.pattern.pat, item, FALSE); + } +} + +static void red_lossy_marshall_qxl_draw_stroke(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + int brush_is_lossy; + BitmapData brush_bitmap_data; + int dest_is_lossy = FALSE; + SpiceRect dest_lossy_area; + int rop; + + brush_is_lossy = is_brush_lossy(rcc, &drawable->u.stroke.brush, item, + &brush_bitmap_data); + + // back_mode is not used at the client. Ignoring. + rop = drawable->u.stroke.fore_mode; + + // assuming that if the brush type is solid, the destination can + // be lossy, no matter what the rop is. + if (drawable->u.stroke.brush.type != SPICE_BRUSH_TYPE_SOLID && + ((rop & SPICE_ROPD_OP_OR) || (rop & SPICE_ROPD_OP_AND) || + (rop & SPICE_ROPD_OP_XOR))) { + dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, + &drawable->bbox, &dest_lossy_area); + } + + if (!dest_is_lossy && + (!brush_is_lossy || (brush_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) + { + red_marshall_qxl_draw_stroke(rcc, base_marshaller, dpi); + } else { + int resend_surface_ids[2]; + SpiceRect *resend_areas[2]; + int num_resend = 0; + + if (brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = brush_bitmap_data.id; + resend_areas[num_resend] = &brush_bitmap_data.lossy_rect; + num_resend++; + } + + // TODO: use the path in order to resend smaller areas + if (dest_is_lossy) { + resend_surface_ids[num_resend] = drawable->surface_id; + resend_areas[num_resend] = &dest_lossy_area; + num_resend++; + } + + red_add_lossless_drawable_dependencies(rcc, item, + resend_surface_ids, resend_areas, num_resend); + } +} + +static void red_marshall_qxl_draw_text(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + SpiceText text; + SpiceMarshaller *brush_pat_out; + SpiceMarshaller *back_brush_pat_out; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TEXT, &dpi->dpi_pipe_item); + fill_base(base_marshaller, item); + text = drawable->u.text; + spice_marshall_Text(base_marshaller, + &text, + &brush_pat_out, + &back_brush_pat_out); + + if (brush_pat_out) { + fill_bits(dcc, brush_pat_out, text.fore_brush.u.pattern.pat, item, FALSE); + } + if (back_brush_pat_out) { + fill_bits(dcc, back_brush_pat_out, text.back_brush.u.pattern.pat, item, FALSE); + } +} + +static void red_lossy_marshall_qxl_draw_text(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *drawable = item->red_drawable; + int fg_is_lossy; + BitmapData fg_bitmap_data; + int bg_is_lossy; + BitmapData bg_bitmap_data; + int dest_is_lossy = FALSE; + SpiceRect dest_lossy_area; + int rop = 0; + + fg_is_lossy = is_brush_lossy(rcc, &drawable->u.text.fore_brush, item, + &fg_bitmap_data); + bg_is_lossy = is_brush_lossy(rcc, &drawable->u.text.back_brush, item, + &bg_bitmap_data); + + // assuming that if the brush type is solid, the destination can + // be lossy, no matter what the rop is. + if (drawable->u.text.fore_brush.type != SPICE_BRUSH_TYPE_SOLID) { + rop = drawable->u.text.fore_mode; + } + + if (drawable->u.text.back_brush.type != SPICE_BRUSH_TYPE_SOLID) { + rop |= drawable->u.text.back_mode; + } + + if ((rop & SPICE_ROPD_OP_OR) || (rop & SPICE_ROPD_OP_AND) || + (rop & SPICE_ROPD_OP_XOR)) { + dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, + &drawable->bbox, &dest_lossy_area); + } + + if (!dest_is_lossy && + (!fg_is_lossy || (fg_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && + (!bg_is_lossy || (bg_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) { + red_marshall_qxl_draw_text(rcc, base_marshaller, dpi); + } else { + int resend_surface_ids[3]; + SpiceRect *resend_areas[3]; + int num_resend = 0; + + if (fg_is_lossy && (fg_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = fg_bitmap_data.id; + resend_areas[num_resend] = &fg_bitmap_data.lossy_rect; + num_resend++; + } + + if (bg_is_lossy && (bg_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { + resend_surface_ids[num_resend] = bg_bitmap_data.id; + resend_areas[num_resend] = &bg_bitmap_data.lossy_rect; + num_resend++; + } + + if (dest_is_lossy) { + resend_surface_ids[num_resend] = drawable->surface_id; + resend_areas[num_resend] = &dest_lossy_area; + num_resend++; + } + red_add_lossless_drawable_dependencies(rcc, item, + resend_surface_ids, resend_areas, num_resend); + } +} + +static inline int red_marshall_stream_data(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, Drawable *drawable) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannel *display = DCC_TO_DC(dcc); + Stream *stream = drawable->stream; + SpiceImage *image; + uint32_t frame_mm_time; + int n; + int width, height; + int ret; + + if (!stream) { + spice_assert(drawable->sized_stream); + stream = drawable->sized_stream; + } + spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY); + + image = drawable->red_drawable->u.copy.src_bitmap; + + if (image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) { + return FALSE; + } + + if (drawable->sized_stream) { + if (red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_SIZED_STREAM)) { + SpiceRect *src_rect = &drawable->red_drawable->u.copy.src_area; + + width = src_rect->right - src_rect->left; + height = src_rect->bottom - src_rect->top; + } else { + return FALSE; + } + } else { + width = stream->width; + height = stream->height; + } + + StreamAgent *agent = &dcc->stream_agents[get_stream_id(display, stream)]; + uint64_t time_now = red_get_monotonic_time(); + size_t outbuf_size; + + if (!dcc->use_mjpeg_encoder_rate_control) { + if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) { + agent->frames--; +#ifdef STREAM_STATS + agent->stats.num_drops_fps++; +#endif + return TRUE; + } + } + + /* workaround for vga streams */ + frame_mm_time = drawable->red_drawable->mm_time ? + drawable->red_drawable->mm_time : + reds_get_mm_time(); + outbuf_size = dcc->send_data.stream_outbuf_size; + ret = mjpeg_encoder_encode_frame(agent->mjpeg_encoder, + &image->u.bitmap, width, height, + &drawable->red_drawable->u.copy.src_area, + stream->top_down, frame_mm_time, + &dcc->send_data.stream_outbuf, + &outbuf_size, &n); + switch (ret) { + case MJPEG_ENCODER_FRAME_DROP: + spice_assert(dcc->use_mjpeg_encoder_rate_control); +#ifdef STREAM_STATS + agent->stats.num_drops_fps++; +#endif + return TRUE; + case MJPEG_ENCODER_FRAME_UNSUPPORTED: + return FALSE; + case MJPEG_ENCODER_FRAME_ENCODE_DONE: + break; + default: + spice_error("bad return value (%d) from mjpeg_encoder_encode_frame", ret); + return FALSE; + } + dcc->send_data.stream_outbuf_size = outbuf_size; + + if (!drawable->sized_stream) { + SpiceMsgDisplayStreamData stream_data; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL); + + stream_data.base.id = get_stream_id(display, stream); + stream_data.base.multi_media_time = frame_mm_time; + stream_data.data_size = n; + + spice_marshall_msg_display_stream_data(base_marshaller, &stream_data); + } else { + SpiceMsgDisplayStreamDataSized stream_data; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA_SIZED, NULL); + + stream_data.base.id = get_stream_id(display, stream); + stream_data.base.multi_media_time = frame_mm_time; + stream_data.data_size = n; + stream_data.width = width; + stream_data.height = height; + stream_data.dest = drawable->red_drawable->bbox; + + spice_debug("stream %d: sized frame: dest ==> ", stream_data.base.id); + rect_debug(&stream_data.dest); + spice_marshall_msg_display_stream_data_sized(base_marshaller, &stream_data); + } + spice_marshaller_add_ref(base_marshaller, + dcc->send_data.stream_outbuf, n); + agent->last_send_time = time_now; +#ifdef STREAM_STATS + agent->stats.num_frames_sent++; + agent->stats.size_sent += n; + agent->stats.end = frame_mm_time; +#endif + + return TRUE; +} + +static inline void marshall_inval_palette(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + CacheItem *cache_item) +{ + SpiceMsgDisplayInvalOne inval_one; + + red_channel_client_init_send_data(rcc, cache_item->inval_type, NULL); + inval_one.id = *(uint64_t *)&cache_item->id; + + spice_marshall_msg_display_inval_palette(base_marshaller, &inval_one); + +} + +static void display_channel_marshall_migrate_data_surfaces(DisplayChannelClient *dcc, + SpiceMarshaller *m, + int lossy) +{ + SpiceMarshaller *m2 = spice_marshaller_get_ptr_submarshaller(m, 0); + uint32_t *num_surfaces_created; + uint32_t i; + + num_surfaces_created = (uint32_t *)spice_marshaller_reserve_space(m2, sizeof(uint32_t)); + *num_surfaces_created = 0; + for (i = 0; i < NUM_SURFACES; i++) { + SpiceRect lossy_rect; + + if (!dcc->surface_client_created[i]) { + continue; + } + spice_marshaller_add_uint32(m2, i); + (*num_surfaces_created)++; + + if (!lossy) { + continue; + } + region_extents(&dcc->surface_client_lossy_region[i], &lossy_rect); + spice_marshaller_add_int32(m2, lossy_rect.left); + spice_marshaller_add_int32(m2, lossy_rect.top); + spice_marshaller_add_int32(m2, lossy_rect.right); + spice_marshaller_add_int32(m2, lossy_rect.bottom); + } +} + +static void display_channel_marshall_migrate_data(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller) +{ + DisplayChannel *display_channel; + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + SpiceMigrateDataDisplay display_data = {0,}; + + display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); + + red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, NULL); + spice_marshaller_add_uint32(base_marshaller, SPICE_MIGRATE_DATA_DISPLAY_MAGIC); + spice_marshaller_add_uint32(base_marshaller, SPICE_MIGRATE_DATA_DISPLAY_VERSION); + + spice_assert(dcc->pixmap_cache); + spice_assert(MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS == 4 && + MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS == MAX_CACHE_CLIENTS); + + display_data.message_serial = red_channel_client_get_message_serial(rcc); + display_data.low_bandwidth_setting = dcc->common.is_low_bandwidth; + + display_data.pixmap_cache_freezer = pixmap_cache_freeze(dcc->pixmap_cache); + display_data.pixmap_cache_id = dcc->pixmap_cache->id; + display_data.pixmap_cache_size = dcc->pixmap_cache->size; + memcpy(display_data.pixmap_cache_clients, dcc->pixmap_cache->sync, + sizeof(display_data.pixmap_cache_clients)); + + spice_assert(dcc->glz_dict); + dcc_freeze_glz(dcc); + display_data.glz_dict_id = dcc->glz_dict->id; + glz_enc_dictionary_get_restore_data(dcc->glz_dict->dict, + &display_data.glz_dict_data, + &dcc->glz_data.usr); + + /* all data besided the surfaces ref */ + spice_marshaller_add(base_marshaller, + (uint8_t *)&display_data, sizeof(display_data) - sizeof(uint32_t)); + display_channel_marshall_migrate_data_surfaces(dcc, base_marshaller, + display_channel->enable_jpeg); +} + +static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + SpiceMsgWaitForChannels wait; + PixmapCache *pixmap_cache; + + red_channel_client_init_send_data(rcc, SPICE_MSG_WAIT_FOR_CHANNELS, NULL); + pixmap_cache = dcc->pixmap_cache; + + pthread_mutex_lock(&pixmap_cache->lock); + + wait.wait_count = 1; + wait.wait_list[0].channel_type = SPICE_CHANNEL_DISPLAY; + wait.wait_list[0].channel_id = pixmap_cache->generation_initiator.client; + wait.wait_list[0].message_serial = pixmap_cache->generation_initiator.message; + dcc->pixmap_cache_generation = pixmap_cache->generation; + dcc->pending_pixmaps_sync = FALSE; + + pthread_mutex_unlock(&pixmap_cache->lock); + + spice_marshall_msg_wait_for_channels(base_marshaller, &wait); +} + +static void dcc_pixmap_cache_reset(DisplayChannelClient *dcc, SpiceMsgWaitForChannels* sync_data) +{ + PixmapCache *cache = dcc->pixmap_cache; + uint8_t wait_count; + uint64_t serial; + uint32_t i; + + serial = red_channel_client_get_message_serial(RED_CHANNEL_CLIENT(dcc)); + pthread_mutex_lock(&cache->lock); + pixmap_cache_clear(cache); + + dcc->pixmap_cache_generation = ++cache->generation; + cache->generation_initiator.client = dcc->common.id; + cache->generation_initiator.message = serial; + cache->sync[dcc->common.id] = serial; + + wait_count = 0; + for (i = 0; i < MAX_CACHE_CLIENTS; i++) { + if (cache->sync[i] && i != dcc->common.id) { + sync_data->wait_list[wait_count].channel_type = SPICE_CHANNEL_DISPLAY; + sync_data->wait_list[wait_count].channel_id = i; + sync_data->wait_list[wait_count++].message_serial = cache->sync[i]; + } + } + sync_data->wait_count = wait_count; + pthread_mutex_unlock(&cache->lock); +} + +static void display_channel_marshall_reset_cache(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + SpiceMsgWaitForChannels wait; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS, NULL); + dcc_pixmap_cache_reset(dcc, &wait); + + spice_marshall_msg_display_inval_all_pixmaps(base_marshaller, + &wait); +} + +static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageItem *item) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + DisplayChannel *display = DCC_TO_DC(dcc); + SpiceImage red_image; + RedWorker *worker; + SpiceBitmap bitmap; + SpiceChunks *chunks; + QRegion *surface_lossy_region; + int comp_succeeded = FALSE; + int lossy_comp = FALSE; + int quic_comp = FALSE; + SpiceImageCompression comp_mode; + SpiceMsgDisplayDrawCopy copy; + SpiceMarshaller *src_bitmap_out, *mask_bitmap_out; + SpiceMarshaller *bitmap_palette_out, *lzplt_palette_out; + + spice_assert(rcc && display && item); + worker = display->common.worker; + + QXL_SET_IMAGE_ID(&red_image, QXL_IMAGE_GROUP_RED, display_channel_generate_uid(display)); + red_image.descriptor.type = SPICE_IMAGE_TYPE_BITMAP; + red_image.descriptor.flags = item->image_flags; + red_image.descriptor.width = item->width; + red_image.descriptor.height = item->height; + + bitmap.format = item->image_format; + bitmap.flags = 0; + if (item->top_down) { + bitmap.flags |= SPICE_BITMAP_FLAGS_TOP_DOWN; + } + bitmap.x = item->width; + bitmap.y = item->height; + bitmap.stride = item->stride; + bitmap.palette = 0; + bitmap.palette_id = 0; + + chunks = spice_chunks_new_linear(item->data, bitmap.stride * bitmap.y); + bitmap.data = chunks; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->link); + + copy.base.surface_id = item->surface_id; + copy.base.box.left = item->pos.x; + copy.base.box.top = item->pos.y; + copy.base.box.right = item->pos.x + bitmap.x; + copy.base.box.bottom = item->pos.y + bitmap.y; + copy.base.clip.type = SPICE_CLIP_TYPE_NONE; + copy.data.rop_descriptor = SPICE_ROPD_OP_PUT; + copy.data.src_area.left = 0; + copy.data.src_area.top = 0; + copy.data.src_area.right = bitmap.x; + copy.data.src_area.bottom = bitmap.y; + copy.data.scale_mode = 0; + copy.data.src_bitmap = 0; + copy.data.mask.flags = 0; + copy.data.mask.flags = 0; + copy.data.mask.pos.x = 0; + copy.data.mask.pos.y = 0; + copy.data.mask.bitmap = 0; + + spice_marshall_msg_display_draw_copy(m, ©, + &src_bitmap_out, &mask_bitmap_out); + + compress_send_data_t comp_send_data = {0}; + + comp_mode = dcc->image_compression; + + if (((comp_mode == SPICE_IMAGE_COMPRESSION_AUTO_LZ) || + (comp_mode == SPICE_IMAGE_COMPRESSION_AUTO_GLZ)) && !bitmap_has_extra_stride(&bitmap)) { + + if (bitmap_fmt_has_graduality(item->image_format)) { + BitmapGradualType grad_level; + + grad_level = bitmap_get_graduality_level(&bitmap); + if (grad_level == BITMAP_GRADUAL_HIGH) { + // if we use lz for alpha, the stride can't be extra + lossy_comp = display->enable_jpeg && item->can_lossy; + quic_comp = TRUE; + } + } + } else if (comp_mode == SPICE_IMAGE_COMPRESSION_QUIC) { + quic_comp = TRUE; + } + + uint32_t groupid = red_worker_get_memslot(worker)->internal_groupslot_id; + + if (lossy_comp) { + comp_succeeded = dcc_compress_image_jpeg(dcc, &red_image, &bitmap, &comp_send_data, groupid); + } else if (quic_comp) { + comp_succeeded = dcc_compress_image_quic(dcc, &red_image, &bitmap, &comp_send_data, groupid); +#ifdef USE_LZ4 + } else if (comp_mode == SPICE_IMAGE_COMPRESSION_LZ4 && + bitmap_fmt_is_rgb(bitmap.format) && + red_channel_client_test_remote_cap(&dcc->common.base, + SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) { + comp_succeeded = dcc_compress_image_lz4(dcc, &red_image, &bitmap, + &comp_send_data, + groupid); +#endif + } else if (comp_mode != SPICE_IMAGE_COMPRESSION_OFF) { + comp_succeeded = dcc_compress_image_lz(dcc, &red_image, &bitmap, + &comp_send_data, + groupid); + } + + surface_lossy_region = &dcc->surface_client_lossy_region[item->surface_id]; + if (comp_succeeded) { + spice_marshall_Image(src_bitmap_out, &red_image, + &bitmap_palette_out, &lzplt_palette_out); + + marshaller_add_compressed(src_bitmap_out, + comp_send_data.comp_buf, comp_send_data.comp_buf_size); + + if (lzplt_palette_out && comp_send_data.lzplt_palette) { + spice_marshall_Palette(lzplt_palette_out, comp_send_data.lzplt_palette); + } + + if (lossy_comp) { + region_add(surface_lossy_region, ©.base.box); + } else { + region_remove(surface_lossy_region, ©.base.box); + } + } else { + red_image.descriptor.type = SPICE_IMAGE_TYPE_BITMAP; + red_image.u.bitmap = bitmap; + + spice_marshall_Image(src_bitmap_out, &red_image, + &bitmap_palette_out, &lzplt_palette_out); + spice_marshaller_add_ref(src_bitmap_out, item->data, + bitmap.y * bitmap.stride); + region_remove(surface_lossy_region, ©.base.box); + } + spice_chunks_destroy(chunks); +} + +static void marshall_lossy_qxl_drawable(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + switch (item->red_drawable->type) { + case QXL_DRAW_FILL: + red_lossy_marshall_qxl_draw_fill(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_OPAQUE: + red_lossy_marshall_qxl_draw_opaque(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_COPY: + red_lossy_marshall_qxl_draw_copy(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_TRANSPARENT: + red_lossy_marshall_qxl_draw_transparent(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_ALPHA_BLEND: + red_lossy_marshall_qxl_draw_alpha_blend(rcc, base_marshaller, dpi); + break; + case QXL_COPY_BITS: + red_lossy_marshall_qxl_copy_bits(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_BLEND: + red_lossy_marshall_qxl_draw_blend(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_BLACKNESS: + red_lossy_marshall_qxl_draw_blackness(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_WHITENESS: + red_lossy_marshall_qxl_draw_whiteness(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_INVERS: + red_lossy_marshall_qxl_draw_inverse(rcc, base_marshaller, item); + break; + case QXL_DRAW_ROP3: + red_lossy_marshall_qxl_draw_rop3(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_COMPOSITE: + red_lossy_marshall_qxl_draw_composite(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_STROKE: + red_lossy_marshall_qxl_draw_stroke(rcc, base_marshaller, dpi); + break; + case QXL_DRAW_TEXT: + red_lossy_marshall_qxl_draw_text(rcc, base_marshaller, dpi); + break; + default: + spice_warn_if_reached(); + } +} + +static void marshall_lossless_qxl_drawable(RedChannelClient *rcc, + SpiceMarshaller *m, DrawablePipeItem *dpi) +{ + Drawable *item = dpi->drawable; + RedDrawable *drawable = item->red_drawable; + + switch (drawable->type) { + case QXL_DRAW_FILL: + marshall_qxl_draw_fill(rcc, m, dpi); + break; + case QXL_DRAW_OPAQUE: + red_marshall_qxl_draw_opaque(rcc, m, dpi, FALSE); + break; + case QXL_DRAW_COPY: + red_marshall_qxl_draw_copy(rcc, m, dpi, FALSE); + break; + case QXL_DRAW_TRANSPARENT: + red_marshall_qxl_draw_transparent(rcc, m, dpi); + break; + case QXL_DRAW_ALPHA_BLEND: + red_marshall_qxl_draw_alpha_blend(rcc, m, dpi, FALSE); + break; + case QXL_COPY_BITS: + red_marshall_qxl_copy_bits(rcc, m, dpi); + break; + case QXL_DRAW_BLEND: + red_marshall_qxl_draw_blend(rcc, m, dpi); + break; + case QXL_DRAW_BLACKNESS: + red_marshall_qxl_draw_blackness(rcc, m, dpi); + break; + case QXL_DRAW_WHITENESS: + red_marshall_qxl_draw_whiteness(rcc, m, dpi); + break; + case QXL_DRAW_INVERS: + red_marshall_qxl_draw_inverse(rcc, m, item); + break; + case QXL_DRAW_ROP3: + red_marshall_qxl_draw_rop3(rcc, m, dpi); + break; + case QXL_DRAW_STROKE: + red_marshall_qxl_draw_stroke(rcc, m, dpi); + break; + case QXL_DRAW_COMPOSITE: + red_marshall_qxl_draw_composite(rcc, m, dpi); + break; + case QXL_DRAW_TEXT: + red_marshall_qxl_draw_text(rcc, m, dpi); + break; + default: + spice_warn_if_reached(); + } +} + +static void marshall_qxl_drawable(RedChannelClient *rcc, + SpiceMarshaller *m, DrawablePipeItem *dpi) +{ + spice_return_if_fail(rcc); + + Drawable *item = dpi->drawable; + DisplayChannel *display = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); + + spice_return_if_fail(display); + /* allow sized frames to be streamed, even if they where replaced by another frame, since + * newer frames might not cover sized frames completely if they are bigger */ + if ((item->stream || item->sized_stream) && red_marshall_stream_data(rcc, m, item)) { + return; + } + if (display->enable_jpeg) + marshall_lossy_qxl_drawable(rcc, m, dpi); + else + marshall_lossless_qxl_drawable(rcc, m, dpi); +} + +static void marshall_stream_start(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, StreamAgent *agent) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + Stream *stream = agent->stream; + + agent->last_send_time = 0; + spice_assert(stream); + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CREATE, NULL); + SpiceMsgDisplayStreamCreate stream_create; + SpiceClipRects clip_rects; + + stream_create.surface_id = 0; + stream_create.id = get_stream_id(DCC_TO_DC(dcc), stream); + stream_create.flags = stream->top_down ? SPICE_STREAM_FLAGS_TOP_DOWN : 0; + stream_create.codec_type = SPICE_VIDEO_CODEC_TYPE_MJPEG; + + stream_create.src_width = stream->width; + stream_create.src_height = stream->height; + stream_create.stream_width = stream_create.src_width; + stream_create.stream_height = stream_create.src_height; + stream_create.dest = stream->dest_area; + + if (stream->current) { + RedDrawable *red_drawable = stream->current->red_drawable; + stream_create.clip = red_drawable->clip; + } else { + stream_create.clip.type = SPICE_CLIP_TYPE_RECTS; + clip_rects.num_rects = 0; + stream_create.clip.rects = &clip_rects; + } + + stream_create.stamp = 0; + + spice_marshall_msg_display_stream_create(base_marshaller, &stream_create); +} + +static void marshall_stream_clip(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + StreamClipItem *item) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + StreamAgent *agent = item->stream_agent; + + spice_return_if_fail(agent->stream); + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CLIP, &item->base); + SpiceMsgDisplayStreamClip stream_clip; + + stream_clip.id = get_stream_id(DCC_TO_DC(dcc), agent->stream); + stream_clip.clip.type = item->clip_type; + stream_clip.clip.rects = item->rects; + + spice_marshall_msg_display_stream_clip(base_marshaller, &stream_clip); +} + +static void marshall_stream_end(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, StreamAgent* agent) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + SpiceMsgDisplayStreamDestroy destroy; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL); + destroy.id = get_stream_id(DCC_TO_DC(dcc), agent->stream); + stream_agent_stop(agent); + spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy); +} + +static void marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller *m, + UpgradeItem *item) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + RedDrawable *red_drawable; + SpiceMsgDisplayDrawCopy copy; + SpiceMarshaller *src_bitmap_out, *mask_bitmap_out; + + spice_assert(rcc && rcc->channel && item && item->drawable); + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->base); + + red_drawable = item->drawable->red_drawable; + spice_assert(red_drawable->type == QXL_DRAW_COPY); + spice_assert(red_drawable->u.copy.rop_descriptor == SPICE_ROPD_OP_PUT); + spice_assert(red_drawable->u.copy.mask.bitmap == 0); + + copy.base.surface_id = 0; + copy.base.box = red_drawable->bbox; + copy.base.clip.type = SPICE_CLIP_TYPE_RECTS; + copy.base.clip.rects = item->rects; + copy.data = red_drawable->u.copy; + + spice_marshall_msg_display_draw_copy(m, ©, + &src_bitmap_out, &mask_bitmap_out); + + fill_bits(dcc, src_bitmap_out, copy.data.src_bitmap, item->drawable, FALSE); +} + +static void marshall_surface_create(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + SpiceMsgSurfaceCreate *surface_create) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + + region_init(&dcc->surface_client_lossy_region[surface_create->surface_id]); + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE, NULL); + + spice_marshall_msg_display_surface_create(base_marshaller, surface_create); +} + +static void marshall_surface_destroy(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, uint32_t surface_id) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + SpiceMsgSurfaceDestroy surface_destroy; + + region_destroy(&dcc->surface_client_lossy_region[surface_id]); + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_DESTROY, NULL); + + surface_destroy.surface_id = surface_id; + + spice_marshall_msg_display_surface_destroy(base_marshaller, &surface_destroy); +} + +static void marshall_monitors_config(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, + MonitorsConfig *monitors_config) +{ + int heads_size = sizeof(SpiceHead) * monitors_config->count; + int i; + SpiceMsgDisplayMonitorsConfig *msg = spice_malloc0(sizeof(*msg) + heads_size); + int count = 0; // ignore monitors_config->count, it may contain zero width monitors, remove them now + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_MONITORS_CONFIG, NULL); + for (i = 0 ; i < monitors_config->count; ++i) { + if (monitors_config->heads[i].width == 0 || monitors_config->heads[i].height == 0) { + continue; + } + msg->heads[count].id = monitors_config->heads[i].id; + msg->heads[count].surface_id = monitors_config->heads[i].surface_id; + msg->heads[count].width = monitors_config->heads[i].width; + msg->heads[count].height = monitors_config->heads[i].height; + msg->heads[count].x = monitors_config->heads[i].x; + msg->heads[count].y = monitors_config->heads[i].y; + count++; + } + msg->count = count; + msg->max_allowed = monitors_config->max_allowed; + spice_marshall_msg_display_monitors_config(base_marshaller, msg); + free(msg); +} + +static void marshall_stream_activate_report(RedChannelClient *rcc, + SpiceMarshaller *base_marshaller, + uint32_t stream_id) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + StreamAgent *agent = &dcc->stream_agents[stream_id]; + SpiceMsgDisplayStreamActivateReport msg; + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT, NULL); + msg.stream_id = stream_id; + msg.unique_id = agent->report_id; + msg.max_window_size = RED_STREAM_CLIENT_REPORT_WINDOW; + msg.timeout_ms = RED_STREAM_CLIENT_REPORT_TIMEOUT; + spice_marshall_msg_display_stream_activate_report(base_marshaller, &msg); +} + +static void begin_send_message(RedChannelClient *rcc) +{ + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); + FreeList *free_list = &dcc->send_data.free_list; + + if (free_list->res->count) { + int sync_count = 0; + int i; + + for (i = 0; i < MAX_CACHE_CLIENTS; i++) { + if (i != dcc->common.id && free_list->sync[i] != 0) { + free_list->wait.header.wait_list[sync_count].channel_type = SPICE_CHANNEL_DISPLAY; + free_list->wait.header.wait_list[sync_count].channel_id = i; + free_list->wait.header.wait_list[sync_count++].message_serial = free_list->sync[i]; + } + } + free_list->wait.header.wait_count = sync_count; + + if (rcc->is_mini_header) { + send_free_list(rcc); + } else { + send_free_list_legacy(rcc); + } + } + red_channel_client_begin_send_message(rcc); +} + +static void reset_send_data(DisplayChannelClient *dcc) +{ + dcc->send_data.free_list.res->count = 0; + dcc->send_data.num_pixmap_cache_items = 0; + memset(dcc->send_data.free_list.sync, 0, sizeof(dcc->send_data.free_list.sync)); +} + +void dcc_send_item(DisplayChannelClient *dcc, PipeItem *pipe_item) +{ + RedChannelClient *rcc = RED_CHANNEL_CLIENT(dcc); + SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); + + reset_send_data(dcc); + switch (pipe_item->type) { + case PIPE_ITEM_TYPE_DRAW: { + DrawablePipeItem *dpi = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item); + marshall_qxl_drawable(rcc, m, dpi); + break; + } + case PIPE_ITEM_TYPE_INVAL_ONE: + marshall_inval_palette(rcc, m, (CacheItem *)pipe_item); + break; + case PIPE_ITEM_TYPE_STREAM_CREATE: { + StreamAgent *agent = SPICE_CONTAINEROF(pipe_item, StreamAgent, create_item); + marshall_stream_start(rcc, m, agent); + break; + } + case PIPE_ITEM_TYPE_STREAM_CLIP: { + StreamClipItem* clip_item = (StreamClipItem *)pipe_item; + marshall_stream_clip(rcc, m, clip_item); + break; + } + case PIPE_ITEM_TYPE_STREAM_DESTROY: { + StreamAgent *agent = SPICE_CONTAINEROF(pipe_item, StreamAgent, destroy_item); + marshall_stream_end(rcc, m, agent); + break; + } + case PIPE_ITEM_TYPE_UPGRADE: + marshall_upgrade(rcc, m, (UpgradeItem *)pipe_item); + break; + case PIPE_ITEM_TYPE_VERB: + red_marshall_verb(rcc, (VerbItem*)pipe_item); + break; + case PIPE_ITEM_TYPE_MIGRATE_DATA: + display_channel_marshall_migrate_data(rcc, m); + break; + case PIPE_ITEM_TYPE_IMAGE: + red_marshall_image(rcc, m, (ImageItem *)pipe_item); + break; + case PIPE_ITEM_TYPE_PIXMAP_SYNC: + display_channel_marshall_pixmap_sync(rcc, m); + break; + case PIPE_ITEM_TYPE_PIXMAP_RESET: + display_channel_marshall_reset_cache(rcc, m); + break; + case PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE: + dcc_palette_cache_reset(dcc); + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES, NULL); + break; + case PIPE_ITEM_TYPE_CREATE_SURFACE: { + SurfaceCreateItem *surface_create = SPICE_CONTAINEROF(pipe_item, SurfaceCreateItem, + pipe_item); + marshall_surface_create(rcc, m, &surface_create->surface_create); + break; + } + case PIPE_ITEM_TYPE_DESTROY_SURFACE: { + SurfaceDestroyItem *surface_destroy = SPICE_CONTAINEROF(pipe_item, SurfaceDestroyItem, + pipe_item); + marshall_surface_destroy(rcc, m, surface_destroy->surface_destroy.surface_id); + break; + } + case PIPE_ITEM_TYPE_MONITORS_CONFIG: { + MonitorsConfigItem *monconf_item = SPICE_CONTAINEROF(pipe_item, + MonitorsConfigItem, pipe_item); + marshall_monitors_config(rcc, m, monconf_item->monitors_config); + break; + } + case PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT: { + StreamActivateReportItem *report_item = SPICE_CONTAINEROF(pipe_item, + StreamActivateReportItem, + pipe_item); + marshall_stream_activate_report(rcc, m, report_item->stream_id); + break; + } + default: + spice_warn_if_reached(); + } + + dcc_release_item(dcc, pipe_item, FALSE); + + // a message is pending + if (red_channel_client_send_message_pending(rcc)) { + begin_send_message(rcc); + } +} diff --git a/server/dcc.h b/server/dcc.h index c5767c9..2351d55 100644 --- a/server/dcc.h +++ b/server/dcc.h @@ -41,6 +41,8 @@ #define WIDE_CLIENT_ACK_WINDOW 40 #define NARROW_CLIENT_ACK_WINDOW 20 +#define MAX_PIPE_SIZE 50 + typedef struct WaitForChannels { SpiceMsgWaitForChannels header; SpiceWaitForChannel buf[MAX_CACHE_CLIENTS]; diff --git a/server/red_worker.c b/server/red_worker.c index 63d6362..997184c 100644 --- a/server/red_worker.c +++ b/server/red_worker.c @@ -50,11 +50,9 @@ #include <spice/protocol.h> #include <spice/qxl_dev.h> #include "common/lz.h" -#include "common/marshaller.h" #include "common/rect.h" #include "common/region.h" #include "common/ring.h" -#include "common/generated_server_marshallers.h" #include "display-channel.h" #include "stream.h" @@ -81,8 +79,6 @@ struct SpiceWatch { void *watch_func_opaque; }; -#define MAX_PIPE_SIZE 50 - struct RedWorker { pthread_t thread; clockid_t clockid; @@ -117,22 +113,6 @@ struct RedWorker { FILE *record_fd; }; -typedef enum { - BITMAP_DATA_TYPE_INVALID, - BITMAP_DATA_TYPE_CACHE, - BITMAP_DATA_TYPE_SURFACE, - BITMAP_DATA_TYPE_BITMAP, - BITMAP_DATA_TYPE_BITMAP_TO_CACHE, -} BitmapDataType; - -typedef struct BitmapData { - BitmapDataType type; - uint64_t id; // surface id or cache item id - SpiceRect lossy_rect; -} BitmapData; - -static inline void display_begin_send_message(RedChannelClient *rcc); - QXLInstance* red_worker_get_qxl(RedWorker *worker) { spice_return_val_if_fail(worker != NULL, NULL); @@ -140,6 +120,13 @@ QXLInstance* red_worker_get_qxl(RedWorker *worker) return worker->qxl; } +RedMemSlotInfo* red_worker_get_memslot(RedWorker *worker) +{ + spice_return_val_if_fail(worker != NULL, NULL); + + return &worker->mem_slots; +} + static int display_is_connected(RedWorker *worker) { return (worker->display_channel && red_channel_is_connected( @@ -152,11 +139,6 @@ static int cursor_is_connected(RedWorker *worker) red_channel_is_connected(RED_CHANNEL(worker->cursor_channel)); } -static PipeItem *dcc_get_tail(DisplayChannelClient *dcc) -{ - return (PipeItem*)ring_get_tail(&RED_CHANNEL_CLIENT(dcc)->pipe); -} - void red_pipes_remove_drawable(Drawable *drawable) { DrawablePipeItem *dpi; @@ -702,2434 +684,6 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int * return n; } -static void fill_base(SpiceMarshaller *base_marshaller, Drawable *drawable) -{ - SpiceMsgDisplayBase base; - - base.surface_id = drawable->surface_id; - base.box = drawable->red_drawable->bbox; - base.clip = drawable->red_drawable->clip; - - spice_marshall_DisplayBase(base_marshaller, &base); -} - -static inline void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc, - SpiceImage *image, SpiceImage *io_image, - int is_lossy) -{ - DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - - if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { - spice_assert(image->descriptor.width * image->descriptor.height > 0); - if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME)) { - if (dcc_pixmap_cache_unlocked_add(dcc, image->descriptor.id, - image->descriptor.width * image->descriptor.height, - is_lossy)) { - io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME; - dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] = - image->descriptor.id; - stat_inc_counter(display_channel->add_to_cache_counter, 1); - } - } - } - - if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { - stat_inc_counter(display_channel->non_cache_counter, 1); - } -} - -static int dcc_pixmap_cache_unlocked_hit(DisplayChannelClient *dcc, uint64_t id, int *lossy) -{ - PixmapCache *cache = dcc->pixmap_cache; - NewCacheItem *item; - uint64_t serial; - - serial = red_channel_client_get_message_serial(RED_CHANNEL_CLIENT(dcc)); - item = cache->hash_table[BITS_CACHE_HASH_KEY(id)]; - - while (item) { - if (item->id == id) { - ring_remove(&item->lru_link); - ring_add(&cache->lru, &item->lru_link); - spice_assert(dcc->common.id < MAX_CACHE_CLIENTS); - item->sync[dcc->common.id] = serial; - cache->sync[dcc->common.id] = serial; - *lossy = item->lossy; - break; - } - item = item->next; - } - - return !!item; -} - -static int dcc_pixmap_cache_hit(DisplayChannelClient *dcc, uint64_t id, int *lossy) -{ - int hit; - PixmapCache *cache = dcc->pixmap_cache; - - pthread_mutex_lock(&cache->lock); - hit = dcc_pixmap_cache_unlocked_hit(dcc, id, lossy); - pthread_mutex_unlock(&cache->lock); - return hit; -} - - -typedef enum { - FILL_BITS_TYPE_INVALID, - FILL_BITS_TYPE_CACHE, - FILL_BITS_TYPE_SURFACE, - FILL_BITS_TYPE_COMPRESS_LOSSLESS, - FILL_BITS_TYPE_COMPRESS_LOSSY, - FILL_BITS_TYPE_BITMAP, -} FillBitsType; - -/* if the number of times fill_bits can be called per one qxl_drawable increases - - MAX_LZ_DRAWABLE_INSTANCES must be increased as well */ -static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m, - SpiceImage *simage, Drawable *drawable, int can_lossy) -{ - RedChannelClient *rcc = RED_CHANNEL_CLIENT(dcc); - DisplayChannel *display = DCC_TO_DC(dcc); - SpiceImage image; - compress_send_data_t comp_send_data = {0}; - SpiceMarshaller *bitmap_palette_out, *lzplt_palette_out; - - if (simage == NULL) { - spice_assert(drawable->red_drawable->self_bitmap_image); - simage = drawable->red_drawable->self_bitmap_image; - } - - image.descriptor = simage->descriptor; - image.descriptor.flags = 0; - if (simage->descriptor.flags & SPICE_IMAGE_FLAGS_HIGH_BITS_SET) { - image.descriptor.flags = SPICE_IMAGE_FLAGS_HIGH_BITS_SET; - } - pthread_mutex_lock(&dcc->pixmap_cache->lock); - - if ((simage->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { - int lossy_cache_item; - if (dcc_pixmap_cache_unlocked_hit(dcc, image.descriptor.id, &lossy_cache_item)) { - dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] = - image.descriptor.id; - if (can_lossy || !lossy_cache_item) { - if (!display->enable_jpeg || lossy_cache_item) { - image.descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE; - } else { - // making sure, in multiple monitor scenario, that lossy items that - // should have been replaced with lossless data by one display channel, - // will be retrieved as lossless by another display channel. - image.descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS; - } - spice_marshall_Image(m, &image, - &bitmap_palette_out, &lzplt_palette_out); - spice_assert(bitmap_palette_out == NULL); - spice_assert(lzplt_palette_out == NULL); - stat_inc_counter(display->cache_hits_counter, 1); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); - return FILL_BITS_TYPE_CACHE; - } else { - pixmap_cache_unlocked_set_lossy(dcc->pixmap_cache, simage->descriptor.id, - FALSE); - image.descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME; - } - } - } - - switch (simage->descriptor.type) { - case SPICE_IMAGE_TYPE_SURFACE: { - int surface_id; - RedSurface *surface; - - surface_id = simage->u.surface.surface_id; - if (!validate_surface(display, surface_id)) { - spice_warning("Invalid surface in SPICE_IMAGE_TYPE_SURFACE"); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); - return FILL_BITS_TYPE_SURFACE; - } - - surface = &display->surfaces[surface_id]; - image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE; - image.descriptor.flags = 0; - image.descriptor.width = surface->context.width; - image.descriptor.height = surface->context.height; - - image.u.surface.surface_id = surface_id; - spice_marshall_Image(m, &image, - &bitmap_palette_out, &lzplt_palette_out); - spice_assert(bitmap_palette_out == NULL); - spice_assert(lzplt_palette_out == NULL); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); - return FILL_BITS_TYPE_SURFACE; - } - case SPICE_IMAGE_TYPE_BITMAP: { - SpiceBitmap *bitmap = &image.u.bitmap; -#ifdef DUMP_BITMAP - dump_bitmap(&simage->u.bitmap); -#endif - /* Images must be added to the cache only after they are compressed - in order to prevent starvation in the client between pixmap_cache and - global dictionary (in cases of multiple monitors) */ - if (reds_stream_get_family(rcc->stream) == AF_UNIX || - !dcc_compress_image(dcc, &image, &simage->u.bitmap, - drawable, can_lossy, &comp_send_data)) { - SpicePalette *palette; - - red_display_add_image_to_pixmap_cache(rcc, simage, &image, FALSE); - - *bitmap = simage->u.bitmap; - bitmap->flags = bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN; - - palette = bitmap->palette; - dcc_palette_cache_palette(dcc, palette, &bitmap->flags); - spice_marshall_Image(m, &image, - &bitmap_palette_out, &lzplt_palette_out); - spice_assert(lzplt_palette_out == NULL); - - if (bitmap_palette_out && palette) { - spice_marshall_Palette(bitmap_palette_out, palette); - } - - spice_marshaller_add_ref_chunks(m, bitmap->data); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); - return FILL_BITS_TYPE_BITMAP; - } else { - red_display_add_image_to_pixmap_cache(rcc, simage, &image, - comp_send_data.is_lossy); - - spice_marshall_Image(m, &image, - &bitmap_palette_out, &lzplt_palette_out); - spice_assert(bitmap_palette_out == NULL); - - marshaller_add_compressed(m, comp_send_data.comp_buf, - comp_send_data.comp_buf_size); - - if (lzplt_palette_out && comp_send_data.lzplt_palette) { - spice_marshall_Palette(lzplt_palette_out, comp_send_data.lzplt_palette); - } - - spice_assert(!comp_send_data.is_lossy || can_lossy); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); - return (comp_send_data.is_lossy ? FILL_BITS_TYPE_COMPRESS_LOSSY : - FILL_BITS_TYPE_COMPRESS_LOSSLESS); - } - break; - } - case SPICE_IMAGE_TYPE_QUIC: - red_display_add_image_to_pixmap_cache(rcc, simage, &image, FALSE); - image.u.quic = simage->u.quic; - spice_marshall_Image(m, &image, - &bitmap_palette_out, &lzplt_palette_out); - spice_assert(bitmap_palette_out == NULL); - spice_assert(lzplt_palette_out == NULL); - spice_marshaller_add_ref_chunks(m, image.u.quic.data); - pthread_mutex_unlock(&dcc->pixmap_cache->lock); - return FILL_BITS_TYPE_COMPRESS_LOSSLESS; - default: - spice_error("invalid image type %u", image.descriptor.type); - } - pthread_mutex_unlock(&dcc->pixmap_cache->lock); - return FILL_BITS_TYPE_INVALID; -} - -static void fill_mask(RedChannelClient *rcc, SpiceMarshaller *m, - SpiceImage *mask_bitmap, Drawable *drawable) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - - if (mask_bitmap && m) { - if (dcc->image_compression != SPICE_IMAGE_COMPRESSION_OFF) { - /* todo: pass compression argument */ - SpiceImageCompression save_img_comp = dcc->image_compression; - dcc->image_compression = SPICE_IMAGE_COMPRESSION_OFF; - fill_bits(dcc, m, mask_bitmap, drawable, FALSE); - dcc->image_compression = save_img_comp; - } else { - fill_bits(dcc, m, mask_bitmap, drawable, FALSE); - } - } -} - -static void fill_attr(SpiceMarshaller *m, SpiceLineAttr *attr, uint32_t group_id) -{ - int i; - - if (m && attr->style_nseg) { - for (i = 0 ; i < attr->style_nseg; i++) { - spice_marshaller_add_uint32(m, attr->style[i]); - } - } -} - -/* set area=NULL for testing the whole surface */ -static int is_surface_area_lossy(DisplayChannelClient *dcc, uint32_t surface_id, - const SpiceRect *area, SpiceRect *out_lossy_area) -{ - RedSurface *surface; - QRegion *surface_lossy_region; - QRegion lossy_region; - DisplayChannel *display = DCC_TO_DC(dcc); - - spice_return_val_if_fail(validate_surface(display, surface_id), FALSE); - - surface = &display->surfaces[surface_id]; - surface_lossy_region = &dcc->surface_client_lossy_region[surface_id]; - - if (!area) { - if (region_is_empty(surface_lossy_region)) { - return FALSE; - } else { - out_lossy_area->top = 0; - out_lossy_area->left = 0; - out_lossy_area->bottom = surface->context.height; - out_lossy_area->right = surface->context.width; - return TRUE; - } - } - - region_init(&lossy_region); - region_add(&lossy_region, area); - region_and(&lossy_region, surface_lossy_region); - if (!region_is_empty(&lossy_region)) { - out_lossy_area->left = lossy_region.extents.x1; - out_lossy_area->top = lossy_region.extents.y1; - out_lossy_area->right = lossy_region.extents.x2; - out_lossy_area->bottom = lossy_region.extents.y2; - region_destroy(&lossy_region); - return TRUE; - } else { - return FALSE; - } -} -/* returns if the bitmap was already sent lossy to the client. If the bitmap hasn't been sent yet - to the client, returns false. "area" is for surfaces. If area = NULL, - all the surface is considered. out_lossy_data will hold info about the bitmap, and its lossy - area in case it is lossy and part of a surface. */ -static int is_bitmap_lossy(RedChannelClient *rcc, SpiceImage *image, SpiceRect *area, - Drawable *drawable, BitmapData *out_data) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - - if (image == NULL) { - // self bitmap - out_data->type = BITMAP_DATA_TYPE_BITMAP; - return FALSE; - } - - if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) { - int is_hit_lossy; - - out_data->id = image->descriptor.id; - if (dcc_pixmap_cache_hit(dcc, image->descriptor.id, &is_hit_lossy)) { - out_data->type = BITMAP_DATA_TYPE_CACHE; - if (is_hit_lossy) { - return TRUE; - } else { - return FALSE; - } - } else { - out_data->type = BITMAP_DATA_TYPE_BITMAP_TO_CACHE; - } - } else { - out_data->type = BITMAP_DATA_TYPE_BITMAP; - } - - if (image->descriptor.type != SPICE_IMAGE_TYPE_SURFACE) { - return FALSE; - } - - out_data->type = BITMAP_DATA_TYPE_SURFACE; - out_data->id = image->u.surface.surface_id; - - if (is_surface_area_lossy(dcc, out_data->id, - area, &out_data->lossy_rect)) - { - return TRUE; - } else { - return FALSE; - } -} - -static int is_brush_lossy(RedChannelClient *rcc, SpiceBrush *brush, - Drawable *drawable, BitmapData *out_data) -{ - if (brush->type == SPICE_BRUSH_TYPE_PATTERN) { - return is_bitmap_lossy(rcc, brush->u.pattern.pat, NULL, - drawable, out_data); - } else { - out_data->type = BITMAP_DATA_TYPE_INVALID; - return FALSE; - } -} - -static void surface_lossy_region_update(DisplayChannelClient *dcc, - Drawable *item, int has_mask, int lossy) -{ - QRegion *surface_lossy_region; - RedDrawable *drawable; - - if (has_mask && !lossy) { - return; - } - - surface_lossy_region = &dcc->surface_client_lossy_region[item->surface_id]; - drawable = item->red_drawable; - - if (drawable->clip.type == SPICE_CLIP_TYPE_RECTS ) { - QRegion clip_rgn; - QRegion draw_region; - region_init(&clip_rgn); - region_init(&draw_region); - region_add(&draw_region, &drawable->bbox); - region_add_clip_rects(&clip_rgn, drawable->clip.rects); - region_and(&draw_region, &clip_rgn); - if (lossy) { - region_or(surface_lossy_region, &draw_region); - } else { - region_exclude(surface_lossy_region, &draw_region); - } - - region_destroy(&clip_rgn); - region_destroy(&draw_region); - } else { /* no clip */ - if (!lossy) { - region_remove(surface_lossy_region, &drawable->bbox); - } else { - region_add(surface_lossy_region, &drawable->bbox); - } - } -} - -static inline int drawable_intersects_with_areas(Drawable *drawable, int surface_ids[], - SpiceRect *surface_areas[], - int num_surfaces) -{ - int i; - for (i = 0; i < num_surfaces; i++) { - if (surface_ids[i] == drawable->red_drawable->surface_id) { - if (rect_intersects(surface_areas[i], &drawable->red_drawable->bbox)) { - return TRUE; - } - } - } - return FALSE; -} - -static inline int drawable_depends_on_areas(Drawable *drawable, - int surface_ids[], - SpiceRect surface_areas[], - int num_surfaces) -{ - int i; - RedDrawable *red_drawable; - int drawable_has_shadow; - SpiceRect shadow_rect = {0, 0, 0, 0}; - - red_drawable = drawable->red_drawable; - drawable_has_shadow = has_shadow(red_drawable); - - if (drawable_has_shadow) { - int delta_x = red_drawable->u.copy_bits.src_pos.x - red_drawable->bbox.left; - int delta_y = red_drawable->u.copy_bits.src_pos.y - red_drawable->bbox.top; - - shadow_rect.left = red_drawable->u.copy_bits.src_pos.x; - shadow_rect.top = red_drawable->u.copy_bits.src_pos.y; - shadow_rect.right = red_drawable->bbox.right + delta_x; - shadow_rect.bottom = red_drawable->bbox.bottom + delta_y; - } - - for (i = 0; i < num_surfaces; i++) { - int x; - int dep_surface_id; - - for (x = 0; x < 3; ++x) { - dep_surface_id = drawable->surface_deps[x]; - if (dep_surface_id == surface_ids[i]) { - if (rect_intersects(&surface_areas[i], &red_drawable->surfaces_rects[x])) { - return TRUE; - } - } - } - - if (surface_ids[i] == red_drawable->surface_id) { - if (drawable_has_shadow) { - if (rect_intersects(&surface_areas[i], &shadow_rect)) { - return TRUE; - } - } - - // not dependent on dest - if (red_drawable->effect == QXL_EFFECT_OPAQUE) { - continue; - } - - if (rect_intersects(&surface_areas[i], &red_drawable->bbox)) { - return TRUE; - } - } - - } - return FALSE; -} - - -static int pipe_rendered_drawables_intersect_with_areas(DisplayChannelClient *dcc, - int surface_ids[], - SpiceRect *surface_areas[], - int num_surfaces) -{ - PipeItem *pipe_item; - Ring *pipe; - - spice_assert(num_surfaces); - pipe = &RED_CHANNEL_CLIENT(dcc)->pipe; - - for (pipe_item = (PipeItem *)ring_get_head(pipe); - pipe_item; - pipe_item = (PipeItem *)ring_next(pipe, &pipe_item->link)) - { - Drawable *drawable; - - if (pipe_item->type != PIPE_ITEM_TYPE_DRAW) - continue; - drawable = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item)->drawable; - - if (ring_item_is_linked(&drawable->list_link)) - continue; // item hasn't been rendered - - if (drawable_intersects_with_areas(drawable, surface_ids, surface_areas, num_surfaces)) { - return TRUE; - } - } - - return FALSE; -} - -static void red_pipe_replace_rendered_drawables_with_images(DisplayChannelClient *dcc, - int first_surface_id, - SpiceRect *first_area) -{ - /* TODO: can't have those statics with multiple clients */ - static int resent_surface_ids[MAX_PIPE_SIZE]; - static SpiceRect resent_areas[MAX_PIPE_SIZE]; // not pointers since drawbales may be released - int num_resent; - PipeItem *pipe_item; - Ring *pipe; - - resent_surface_ids[0] = first_surface_id; - resent_areas[0] = *first_area; - num_resent = 1; - - pipe = &RED_CHANNEL_CLIENT(dcc)->pipe; - - // going from the oldest to the newest - for (pipe_item = (PipeItem *)ring_get_tail(pipe); - pipe_item; - pipe_item = (PipeItem *)ring_prev(pipe, &pipe_item->link)) { - Drawable *drawable; - DrawablePipeItem *dpi; - ImageItem *image; - - if (pipe_item->type != PIPE_ITEM_TYPE_DRAW) - continue; - dpi = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item); - drawable = dpi->drawable; - if (ring_item_is_linked(&drawable->list_link)) - continue; // item hasn't been rendered - - // When a drawable command, X, depends on bitmaps that were resent, - // these bitmaps state at the client might not be synchronized with X - // (i.e., the bitmaps can be more futuristic w.r.t X). Thus, X shouldn't - // be rendered at the client, and we replace it with an image as well. - if (!drawable_depends_on_areas(drawable, - resent_surface_ids, - resent_areas, - num_resent)) { - continue; - } - - image = dcc_add_surface_area_image(dcc, drawable->red_drawable->surface_id, - &drawable->red_drawable->bbox, pipe_item, TRUE); - resent_surface_ids[num_resent] = drawable->red_drawable->surface_id; - resent_areas[num_resent] = drawable->red_drawable->bbox; - num_resent++; - - spice_assert(image); - red_channel_client_pipe_remove_and_release(RED_CHANNEL_CLIENT(dcc), &dpi->dpi_pipe_item); - pipe_item = &image->link; - } -} - -static void red_add_lossless_drawable_dependencies(RedChannelClient *rcc, - Drawable *item, - int deps_surfaces_ids[], - SpiceRect *deps_areas[], - int num_deps) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - DisplayChannel *display = DCC_TO_DC(dcc); - RedDrawable *drawable = item->red_drawable; - int sync_rendered = FALSE; - int i; - - if (!ring_item_is_linked(&item->list_link)) { - /* drawable was already rendered, we may not be able to retrieve the lossless data - for the lossy areas */ - sync_rendered = TRUE; - - // checking if the drawable itself or one of the other commands - // that were rendered, affected the areas that need to be resent - if (!drawable_intersects_with_areas(item, deps_surfaces_ids, - deps_areas, num_deps)) { - if (pipe_rendered_drawables_intersect_with_areas(dcc, - deps_surfaces_ids, - deps_areas, - num_deps)) { - sync_rendered = TRUE; - } - } else { - sync_rendered = TRUE; - } - } else { - sync_rendered = FALSE; - for (i = 0; i < num_deps; i++) { - display_channel_draw_until(display, deps_areas[i], deps_surfaces_ids[i], item); - } - } - - if (!sync_rendered) { - // pushing the pipe item back to the pipe - dcc_append_drawable(dcc, item); - // the surfaces areas will be sent as DRAW_COPY commands, that - // will be executed before the current drawable - for (i = 0; i < num_deps; i++) { - dcc_add_surface_area_image(dcc, deps_surfaces_ids[i], deps_areas[i], - dcc_get_tail(dcc), FALSE); - - } - } else { - int drawable_surface_id[1]; - SpiceRect *drawable_bbox[1]; - - drawable_surface_id[0] = drawable->surface_id; - drawable_bbox[0] = &drawable->bbox; - - // check if the other rendered images in the pipe have updated the drawable bbox - if (pipe_rendered_drawables_intersect_with_areas(dcc, - drawable_surface_id, - drawable_bbox, - 1)) { - red_pipe_replace_rendered_drawables_with_images(dcc, - drawable->surface_id, - &drawable->bbox); - } - - dcc_add_surface_area_image(dcc, drawable->surface_id, &drawable->bbox, - dcc_get_tail(dcc), TRUE); - } -} - -static void red_marshall_qxl_draw_fill(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - SpiceMarshaller *brush_pat_out; - SpiceMarshaller *mask_bitmap_out; - SpiceFill fill; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - fill = drawable->u.fill; - spice_marshall_Fill(base_marshaller, - &fill, - &brush_pat_out, - &mask_bitmap_out); - - if (brush_pat_out) { - fill_bits(dcc, brush_pat_out, fill.brush.u.pattern.pat, item, FALSE); - } - - fill_mask(rcc, mask_bitmap_out, fill.mask.bitmap, item); -} - - -static void red_lossy_marshall_qxl_draw_fill(RedChannelClient *rcc, - SpiceMarshaller *m, - DrawablePipeItem *dpi) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - - int dest_allowed_lossy = FALSE; - int dest_is_lossy = FALSE; - SpiceRect dest_lossy_area; - int brush_is_lossy; - BitmapData brush_bitmap_data; - uint16_t rop; - - rop = drawable->u.fill.rop_descriptor; - - dest_allowed_lossy = !((rop & SPICE_ROPD_OP_OR) || - (rop & SPICE_ROPD_OP_AND) || - (rop & SPICE_ROPD_OP_XOR)); - - brush_is_lossy = is_brush_lossy(rcc, &drawable->u.fill.brush, item, - &brush_bitmap_data); - if (!dest_allowed_lossy) { - dest_is_lossy = is_surface_area_lossy(dcc, item->surface_id, &drawable->bbox, - &dest_lossy_area); - } - - if (!dest_is_lossy && - !(brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE))) { - int has_mask = !!drawable->u.fill.mask.bitmap; - - red_marshall_qxl_draw_fill(rcc, m, dpi); - // either the brush operation is opaque, or the dest is not lossy - surface_lossy_region_update(dcc, item, has_mask, FALSE); - } else { - int resend_surface_ids[2]; - SpiceRect *resend_areas[2]; - int num_resend = 0; - - if (dest_is_lossy) { - resend_surface_ids[num_resend] = item->surface_id; - resend_areas[num_resend] = &dest_lossy_area; - num_resend++; - } - - if (brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = brush_bitmap_data.id; - resend_areas[num_resend] = &brush_bitmap_data.lossy_rect; - num_resend++; - } - - red_add_lossless_drawable_dependencies(rcc, item, - resend_surface_ids, resend_areas, num_resend); - } -} - -static FillBitsType red_marshall_qxl_draw_opaque(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi, int src_allowed_lossy) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - SpiceMarshaller *brush_pat_out; - SpiceMarshaller *src_bitmap_out; - SpiceMarshaller *mask_bitmap_out; - SpiceOpaque opaque; - FillBitsType src_send_type; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_OPAQUE, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - opaque = drawable->u.opaque; - spice_marshall_Opaque(base_marshaller, - &opaque, - &src_bitmap_out, - &brush_pat_out, - &mask_bitmap_out); - - src_send_type = fill_bits(dcc, src_bitmap_out, opaque.src_bitmap, item, - src_allowed_lossy); - - if (brush_pat_out) { - fill_bits(dcc, brush_pat_out, opaque.brush.u.pattern.pat, item, FALSE); - } - fill_mask(rcc, mask_bitmap_out, opaque.mask.bitmap, item); - - return src_send_type; -} - -static void red_lossy_marshall_qxl_draw_opaque(RedChannelClient *rcc, - SpiceMarshaller *m, - DrawablePipeItem *dpi) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - - int src_allowed_lossy; - int rop; - int src_is_lossy = FALSE; - int brush_is_lossy = FALSE; - BitmapData src_bitmap_data; - BitmapData brush_bitmap_data; - - rop = drawable->u.opaque.rop_descriptor; - src_allowed_lossy = !((rop & SPICE_ROPD_OP_OR) || - (rop & SPICE_ROPD_OP_AND) || - (rop & SPICE_ROPD_OP_XOR)); - - brush_is_lossy = is_brush_lossy(rcc, &drawable->u.opaque.brush, item, - &brush_bitmap_data); - - if (!src_allowed_lossy) { - src_is_lossy = is_bitmap_lossy(rcc, drawable->u.opaque.src_bitmap, - &drawable->u.opaque.src_area, - item, - &src_bitmap_data); - } - - if (!(brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) && - !(src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE))) { - FillBitsType src_send_type; - int has_mask = !!drawable->u.opaque.mask.bitmap; - - src_send_type = red_marshall_qxl_draw_opaque(rcc, m, dpi, src_allowed_lossy); - if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) { - src_is_lossy = TRUE; - } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) { - src_is_lossy = FALSE; - } - - surface_lossy_region_update(dcc, item, has_mask, src_is_lossy); - } else { - int resend_surface_ids[2]; - SpiceRect *resend_areas[2]; - int num_resend = 0; - - if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = src_bitmap_data.id; - resend_areas[num_resend] = &src_bitmap_data.lossy_rect; - num_resend++; - } - - if (brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = brush_bitmap_data.id; - resend_areas[num_resend] = &brush_bitmap_data.lossy_rect; - num_resend++; - } - - red_add_lossless_drawable_dependencies(rcc, item, - resend_surface_ids, resend_areas, num_resend); - } -} - -static FillBitsType red_marshall_qxl_draw_copy(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi, int src_allowed_lossy) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - SpiceMarshaller *src_bitmap_out; - SpiceMarshaller *mask_bitmap_out; - SpiceCopy copy; - FillBitsType src_send_type; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - copy = drawable->u.copy; - spice_marshall_Copy(base_marshaller, - ©, - &src_bitmap_out, - &mask_bitmap_out); - - src_send_type = fill_bits(dcc, src_bitmap_out, copy.src_bitmap, item, src_allowed_lossy); - fill_mask(rcc, mask_bitmap_out, copy.mask.bitmap, item); - - return src_send_type; -} - -static void red_lossy_marshall_qxl_draw_copy(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - int has_mask = !!drawable->u.copy.mask.bitmap; - int src_is_lossy; - BitmapData src_bitmap_data; - FillBitsType src_send_type; - - src_is_lossy = is_bitmap_lossy(rcc, drawable->u.copy.src_bitmap, - &drawable->u.copy.src_area, item, &src_bitmap_data); - - src_send_type = red_marshall_qxl_draw_copy(rcc, base_marshaller, dpi, TRUE); - if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) { - src_is_lossy = TRUE; - } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) { - src_is_lossy = FALSE; - } - surface_lossy_region_update(dcc, item, has_mask, - src_is_lossy); -} - -static void red_marshall_qxl_draw_transparent(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - SpiceMarshaller *src_bitmap_out; - SpiceTransparent transparent; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TRANSPARENT, - &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - transparent = drawable->u.transparent; - spice_marshall_Transparent(base_marshaller, - &transparent, - &src_bitmap_out); - fill_bits(dcc, src_bitmap_out, transparent.src_bitmap, item, FALSE); -} - -static void red_lossy_marshall_qxl_draw_transparent(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - int src_is_lossy; - BitmapData src_bitmap_data; - - src_is_lossy = is_bitmap_lossy(rcc, drawable->u.transparent.src_bitmap, - &drawable->u.transparent.src_area, item, &src_bitmap_data); - - if (!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) { - red_marshall_qxl_draw_transparent(rcc, base_marshaller, dpi); - // don't update surface lossy region since transperent areas might be lossy - } else { - int resend_surface_ids[1]; - SpiceRect *resend_areas[1]; - - resend_surface_ids[0] = src_bitmap_data.id; - resend_areas[0] = &src_bitmap_data.lossy_rect; - - red_add_lossless_drawable_dependencies(rcc, item, - resend_surface_ids, resend_areas, 1); - } -} - -static FillBitsType red_marshall_qxl_draw_alpha_blend(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi, - int src_allowed_lossy) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - SpiceMarshaller *src_bitmap_out; - SpiceAlphaBlend alpha_blend; - FillBitsType src_send_type; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND, - &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - alpha_blend = drawable->u.alpha_blend; - spice_marshall_AlphaBlend(base_marshaller, - &alpha_blend, - &src_bitmap_out); - src_send_type = fill_bits(dcc, src_bitmap_out, alpha_blend.src_bitmap, item, - src_allowed_lossy); - - return src_send_type; -} - -static void red_lossy_marshall_qxl_draw_alpha_blend(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - int src_is_lossy; - BitmapData src_bitmap_data; - FillBitsType src_send_type; - - src_is_lossy = is_bitmap_lossy(rcc, drawable->u.alpha_blend.src_bitmap, - &drawable->u.alpha_blend.src_area, item, &src_bitmap_data); - - src_send_type = red_marshall_qxl_draw_alpha_blend(rcc, base_marshaller, dpi, TRUE); - - if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) { - src_is_lossy = TRUE; - } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) { - src_is_lossy = FALSE; - } - - if (src_is_lossy) { - surface_lossy_region_update(dcc, item, FALSE, src_is_lossy); - } // else, the area stays lossy/lossless as the destination -} - -static void red_marshall_qxl_copy_bits(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - SpicePoint copy_bits; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_COPY_BITS, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - copy_bits = drawable->u.copy_bits.src_pos; - spice_marshall_Point(base_marshaller, - ©_bits); -} - -static void red_lossy_marshall_qxl_copy_bits(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - SpiceRect src_rect; - int horz_offset; - int vert_offset; - int src_is_lossy; - SpiceRect src_lossy_area; - - red_marshall_qxl_copy_bits(rcc, base_marshaller, dpi); - - horz_offset = drawable->u.copy_bits.src_pos.x - drawable->bbox.left; - vert_offset = drawable->u.copy_bits.src_pos.y - drawable->bbox.top; - - src_rect.left = drawable->u.copy_bits.src_pos.x; - src_rect.top = drawable->u.copy_bits.src_pos.y; - src_rect.right = drawable->bbox.right + horz_offset; - src_rect.bottom = drawable->bbox.bottom + vert_offset; - - src_is_lossy = is_surface_area_lossy(dcc, item->surface_id, - &src_rect, &src_lossy_area); - - surface_lossy_region_update(dcc, item, FALSE, src_is_lossy); -} - -static void red_marshall_qxl_draw_blend(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - SpiceMarshaller *src_bitmap_out; - SpiceMarshaller *mask_bitmap_out; - SpiceBlend blend; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLEND, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - blend = drawable->u.blend; - spice_marshall_Blend(base_marshaller, - &blend, - &src_bitmap_out, - &mask_bitmap_out); - - fill_bits(dcc, src_bitmap_out, blend.src_bitmap, item, FALSE); - - fill_mask(rcc, mask_bitmap_out, blend.mask.bitmap, item); -} - -static void red_lossy_marshall_qxl_draw_blend(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - int src_is_lossy; - BitmapData src_bitmap_data; - int dest_is_lossy; - SpiceRect dest_lossy_area; - - src_is_lossy = is_bitmap_lossy(rcc, drawable->u.blend.src_bitmap, - &drawable->u.blend.src_area, item, &src_bitmap_data); - dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, - &drawable->bbox, &dest_lossy_area); - - if (!dest_is_lossy && - (!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) { - red_marshall_qxl_draw_blend(rcc, base_marshaller, dpi); - } else { - int resend_surface_ids[2]; - SpiceRect *resend_areas[2]; - int num_resend = 0; - - if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = src_bitmap_data.id; - resend_areas[num_resend] = &src_bitmap_data.lossy_rect; - num_resend++; - } - - if (dest_is_lossy) { - resend_surface_ids[num_resend] = item->surface_id; - resend_areas[num_resend] = &dest_lossy_area; - num_resend++; - } - - red_add_lossless_drawable_dependencies(rcc, item, - resend_surface_ids, resend_areas, num_resend); - } -} - -static void red_marshall_qxl_draw_blackness(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - SpiceMarshaller *mask_bitmap_out; - SpiceBlackness blackness; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_BLACKNESS, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - blackness = drawable->u.blackness; - - spice_marshall_Blackness(base_marshaller, - &blackness, - &mask_bitmap_out); - - fill_mask(rcc, mask_bitmap_out, blackness.mask.bitmap, item); -} - -static void red_lossy_marshall_qxl_draw_blackness(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - int has_mask = !!drawable->u.blackness.mask.bitmap; - - red_marshall_qxl_draw_blackness(rcc, base_marshaller, dpi); - - surface_lossy_region_update(dcc, item, has_mask, FALSE); -} - -static void red_marshall_qxl_draw_whiteness(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - SpiceMarshaller *mask_bitmap_out; - SpiceWhiteness whiteness; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_WHITENESS, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - whiteness = drawable->u.whiteness; - - spice_marshall_Whiteness(base_marshaller, - &whiteness, - &mask_bitmap_out); - - fill_mask(rcc, mask_bitmap_out, whiteness.mask.bitmap, item); -} - -static void red_lossy_marshall_qxl_draw_whiteness(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - int has_mask = !!drawable->u.whiteness.mask.bitmap; - - red_marshall_qxl_draw_whiteness(rcc, base_marshaller, dpi); - - surface_lossy_region_update(dcc, item, has_mask, FALSE); -} - -static void red_marshall_qxl_draw_inverse(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - Drawable *item) -{ - RedDrawable *drawable = item->red_drawable; - SpiceMarshaller *mask_bitmap_out; - SpiceInvers inverse; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_INVERS, NULL); - fill_base(base_marshaller, item); - inverse = drawable->u.invers; - - spice_marshall_Invers(base_marshaller, - &inverse, - &mask_bitmap_out); - - fill_mask(rcc, mask_bitmap_out, inverse.mask.bitmap, item); -} - -static void red_lossy_marshall_qxl_draw_inverse(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - Drawable *item) -{ - red_marshall_qxl_draw_inverse(rcc, base_marshaller, item); -} - -static void red_marshall_qxl_draw_rop3(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - SpiceRop3 rop3; - SpiceMarshaller *src_bitmap_out; - SpiceMarshaller *brush_pat_out; - SpiceMarshaller *mask_bitmap_out; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_ROP3, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - rop3 = drawable->u.rop3; - spice_marshall_Rop3(base_marshaller, - &rop3, - &src_bitmap_out, - &brush_pat_out, - &mask_bitmap_out); - - fill_bits(dcc, src_bitmap_out, rop3.src_bitmap, item, FALSE); - - if (brush_pat_out) { - fill_bits(dcc, brush_pat_out, rop3.brush.u.pattern.pat, item, FALSE); - } - fill_mask(rcc, mask_bitmap_out, rop3.mask.bitmap, item); -} - -static void red_lossy_marshall_qxl_draw_rop3(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - int src_is_lossy; - BitmapData src_bitmap_data; - int brush_is_lossy; - BitmapData brush_bitmap_data; - int dest_is_lossy; - SpiceRect dest_lossy_area; - - src_is_lossy = is_bitmap_lossy(rcc, drawable->u.rop3.src_bitmap, - &drawable->u.rop3.src_area, item, &src_bitmap_data); - brush_is_lossy = is_brush_lossy(rcc, &drawable->u.rop3.brush, item, - &brush_bitmap_data); - dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, - &drawable->bbox, &dest_lossy_area); - - if ((!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && - (!brush_is_lossy || (brush_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && - !dest_is_lossy) { - int has_mask = !!drawable->u.rop3.mask.bitmap; - red_marshall_qxl_draw_rop3(rcc, base_marshaller, dpi); - surface_lossy_region_update(dcc, item, has_mask, FALSE); - } else { - int resend_surface_ids[3]; - SpiceRect *resend_areas[3]; - int num_resend = 0; - - if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = src_bitmap_data.id; - resend_areas[num_resend] = &src_bitmap_data.lossy_rect; - num_resend++; - } - - if (brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = brush_bitmap_data.id; - resend_areas[num_resend] = &brush_bitmap_data.lossy_rect; - num_resend++; - } - - if (dest_is_lossy) { - resend_surface_ids[num_resend] = item->surface_id; - resend_areas[num_resend] = &dest_lossy_area; - num_resend++; - } - - red_add_lossless_drawable_dependencies(rcc, item, - resend_surface_ids, resend_areas, num_resend); - } -} - -static void red_marshall_qxl_draw_composite(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - SpiceMarshaller *src_bitmap_out; - SpiceMarshaller *mask_bitmap_out; - SpiceComposite composite; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COMPOSITE, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - composite = drawable->u.composite; - spice_marshall_Composite(base_marshaller, - &composite, - &src_bitmap_out, - &mask_bitmap_out); - - fill_bits(dcc, src_bitmap_out, composite.src_bitmap, item, FALSE); - if (mask_bitmap_out) { - fill_bits(dcc, mask_bitmap_out, composite.mask_bitmap, item, FALSE); - } -} - -static void red_lossy_marshall_qxl_draw_composite(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - int src_is_lossy; - BitmapData src_bitmap_data; - int mask_is_lossy; - BitmapData mask_bitmap_data; - int dest_is_lossy; - SpiceRect dest_lossy_area; - - src_is_lossy = is_bitmap_lossy(rcc, drawable->u.composite.src_bitmap, - NULL, item, &src_bitmap_data); - mask_is_lossy = drawable->u.composite.mask_bitmap && - is_bitmap_lossy(rcc, drawable->u.composite.mask_bitmap, NULL, item, &mask_bitmap_data); - - dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, - &drawable->bbox, &dest_lossy_area); - - if ((!src_is_lossy || (src_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && - (!mask_is_lossy || (mask_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && - !dest_is_lossy) { - red_marshall_qxl_draw_composite(rcc, base_marshaller, dpi); - surface_lossy_region_update(dcc, item, FALSE, FALSE); - } - else { - int resend_surface_ids[3]; - SpiceRect *resend_areas[3]; - int num_resend = 0; - - if (src_is_lossy && (src_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = src_bitmap_data.id; - resend_areas[num_resend] = &src_bitmap_data.lossy_rect; - num_resend++; - } - - if (mask_is_lossy && (mask_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = mask_bitmap_data.id; - resend_areas[num_resend] = &mask_bitmap_data.lossy_rect; - num_resend++; - } - - if (dest_is_lossy) { - resend_surface_ids[num_resend] = item->surface_id; - resend_areas[num_resend] = &dest_lossy_area; - num_resend++; - } - - red_add_lossless_drawable_dependencies(rcc, item, - resend_surface_ids, resend_areas, num_resend); - } -} - -static void red_marshall_qxl_draw_stroke(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - SpiceStroke stroke; - SpiceMarshaller *brush_pat_out; - SpiceMarshaller *style_out; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_STROKE, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - stroke = drawable->u.stroke; - spice_marshall_Stroke(base_marshaller, - &stroke, - &style_out, - &brush_pat_out); - - fill_attr(style_out, &stroke.attr, item->group_id); - if (brush_pat_out) { - fill_bits(dcc, brush_pat_out, stroke.brush.u.pattern.pat, item, FALSE); - } -} - -static void red_lossy_marshall_qxl_draw_stroke(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - int brush_is_lossy; - BitmapData brush_bitmap_data; - int dest_is_lossy = FALSE; - SpiceRect dest_lossy_area; - int rop; - - brush_is_lossy = is_brush_lossy(rcc, &drawable->u.stroke.brush, item, - &brush_bitmap_data); - - // back_mode is not used at the client. Ignoring. - rop = drawable->u.stroke.fore_mode; - - // assuming that if the brush type is solid, the destination can - // be lossy, no matter what the rop is. - if (drawable->u.stroke.brush.type != SPICE_BRUSH_TYPE_SOLID && - ((rop & SPICE_ROPD_OP_OR) || (rop & SPICE_ROPD_OP_AND) || - (rop & SPICE_ROPD_OP_XOR))) { - dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, - &drawable->bbox, &dest_lossy_area); - } - - if (!dest_is_lossy && - (!brush_is_lossy || (brush_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) - { - red_marshall_qxl_draw_stroke(rcc, base_marshaller, dpi); - } else { - int resend_surface_ids[2]; - SpiceRect *resend_areas[2]; - int num_resend = 0; - - if (brush_is_lossy && (brush_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = brush_bitmap_data.id; - resend_areas[num_resend] = &brush_bitmap_data.lossy_rect; - num_resend++; - } - - // TODO: use the path in order to resend smaller areas - if (dest_is_lossy) { - resend_surface_ids[num_resend] = drawable->surface_id; - resend_areas[num_resend] = &dest_lossy_area; - num_resend++; - } - - red_add_lossless_drawable_dependencies(rcc, item, - resend_surface_ids, resend_areas, num_resend); - } -} - -static void red_marshall_qxl_draw_text(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - SpiceText text; - SpiceMarshaller *brush_pat_out; - SpiceMarshaller *back_brush_pat_out; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_TEXT, &dpi->dpi_pipe_item); - fill_base(base_marshaller, item); - text = drawable->u.text; - spice_marshall_Text(base_marshaller, - &text, - &brush_pat_out, - &back_brush_pat_out); - - if (brush_pat_out) { - fill_bits(dcc, brush_pat_out, text.fore_brush.u.pattern.pat, item, FALSE); - } - if (back_brush_pat_out) { - fill_bits(dcc, back_brush_pat_out, text.back_brush.u.pattern.pat, item, FALSE); - } -} - -static void red_lossy_marshall_qxl_draw_text(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *drawable = item->red_drawable; - int fg_is_lossy; - BitmapData fg_bitmap_data; - int bg_is_lossy; - BitmapData bg_bitmap_data; - int dest_is_lossy = FALSE; - SpiceRect dest_lossy_area; - int rop = 0; - - fg_is_lossy = is_brush_lossy(rcc, &drawable->u.text.fore_brush, item, - &fg_bitmap_data); - bg_is_lossy = is_brush_lossy(rcc, &drawable->u.text.back_brush, item, - &bg_bitmap_data); - - // assuming that if the brush type is solid, the destination can - // be lossy, no matter what the rop is. - if (drawable->u.text.fore_brush.type != SPICE_BRUSH_TYPE_SOLID) { - rop = drawable->u.text.fore_mode; - } - - if (drawable->u.text.back_brush.type != SPICE_BRUSH_TYPE_SOLID) { - rop |= drawable->u.text.back_mode; - } - - if ((rop & SPICE_ROPD_OP_OR) || (rop & SPICE_ROPD_OP_AND) || - (rop & SPICE_ROPD_OP_XOR)) { - dest_is_lossy = is_surface_area_lossy(dcc, drawable->surface_id, - &drawable->bbox, &dest_lossy_area); - } - - if (!dest_is_lossy && - (!fg_is_lossy || (fg_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE)) && - (!bg_is_lossy || (bg_bitmap_data.type != BITMAP_DATA_TYPE_SURFACE))) { - red_marshall_qxl_draw_text(rcc, base_marshaller, dpi); - } else { - int resend_surface_ids[3]; - SpiceRect *resend_areas[3]; - int num_resend = 0; - - if (fg_is_lossy && (fg_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = fg_bitmap_data.id; - resend_areas[num_resend] = &fg_bitmap_data.lossy_rect; - num_resend++; - } - - if (bg_is_lossy && (bg_bitmap_data.type == BITMAP_DATA_TYPE_SURFACE)) { - resend_surface_ids[num_resend] = bg_bitmap_data.id; - resend_areas[num_resend] = &bg_bitmap_data.lossy_rect; - num_resend++; - } - - if (dest_is_lossy) { - resend_surface_ids[num_resend] = drawable->surface_id; - resend_areas[num_resend] = &dest_lossy_area; - num_resend++; - } - red_add_lossless_drawable_dependencies(rcc, item, - resend_surface_ids, resend_areas, num_resend); - } -} - -static void red_lossy_marshall_qxl_drawable(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - switch (item->red_drawable->type) { - case QXL_DRAW_FILL: - red_lossy_marshall_qxl_draw_fill(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_OPAQUE: - red_lossy_marshall_qxl_draw_opaque(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_COPY: - red_lossy_marshall_qxl_draw_copy(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_TRANSPARENT: - red_lossy_marshall_qxl_draw_transparent(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_ALPHA_BLEND: - red_lossy_marshall_qxl_draw_alpha_blend(rcc, base_marshaller, dpi); - break; - case QXL_COPY_BITS: - red_lossy_marshall_qxl_copy_bits(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_BLEND: - red_lossy_marshall_qxl_draw_blend(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_BLACKNESS: - red_lossy_marshall_qxl_draw_blackness(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_WHITENESS: - red_lossy_marshall_qxl_draw_whiteness(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_INVERS: - red_lossy_marshall_qxl_draw_inverse(rcc, base_marshaller, item); - break; - case QXL_DRAW_ROP3: - red_lossy_marshall_qxl_draw_rop3(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_COMPOSITE: - red_lossy_marshall_qxl_draw_composite(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_STROKE: - red_lossy_marshall_qxl_draw_stroke(rcc, base_marshaller, dpi); - break; - case QXL_DRAW_TEXT: - red_lossy_marshall_qxl_draw_text(rcc, base_marshaller, dpi); - break; - default: - spice_error("invalid type"); - } -} - -static inline void red_marshall_qxl_drawable(RedChannelClient *rcc, - SpiceMarshaller *m, DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - RedDrawable *drawable = item->red_drawable; - - switch (drawable->type) { - case QXL_DRAW_FILL: - red_marshall_qxl_draw_fill(rcc, m, dpi); - break; - case QXL_DRAW_OPAQUE: - red_marshall_qxl_draw_opaque(rcc, m, dpi, FALSE); - break; - case QXL_DRAW_COPY: - red_marshall_qxl_draw_copy(rcc, m, dpi, FALSE); - break; - case QXL_DRAW_TRANSPARENT: - red_marshall_qxl_draw_transparent(rcc, m, dpi); - break; - case QXL_DRAW_ALPHA_BLEND: - red_marshall_qxl_draw_alpha_blend(rcc, m, dpi, FALSE); - break; - case QXL_COPY_BITS: - red_marshall_qxl_copy_bits(rcc, m, dpi); - break; - case QXL_DRAW_BLEND: - red_marshall_qxl_draw_blend(rcc, m, dpi); - break; - case QXL_DRAW_BLACKNESS: - red_marshall_qxl_draw_blackness(rcc, m, dpi); - break; - case QXL_DRAW_WHITENESS: - red_marshall_qxl_draw_whiteness(rcc, m, dpi); - break; - case QXL_DRAW_INVERS: - red_marshall_qxl_draw_inverse(rcc, m, item); - break; - case QXL_DRAW_ROP3: - red_marshall_qxl_draw_rop3(rcc, m, dpi); - break; - case QXL_DRAW_STROKE: - red_marshall_qxl_draw_stroke(rcc, m, dpi); - break; - case QXL_DRAW_COMPOSITE: - red_marshall_qxl_draw_composite(rcc, m, dpi); - break; - case QXL_DRAW_TEXT: - red_marshall_qxl_draw_text(rcc, m, dpi); - break; - default: - spice_error("invalid type"); - } -} - -static inline void display_marshal_sub_msg_inval_list(SpiceMarshaller *m, - FreeList *free_list) -{ - /* type + size + submessage */ - spice_marshaller_add_uint16(m, SPICE_MSG_DISPLAY_INVAL_LIST); - spice_marshaller_add_uint32(m, sizeof(*free_list->res) + - free_list->res->count * sizeof(free_list->res->resources[0])); - spice_marshall_msg_display_inval_list(m, free_list->res); -} - -static inline void display_marshal_sub_msg_inval_list_wait(SpiceMarshaller *m, - FreeList *free_list) - -{ - /* type + size + submessage */ - spice_marshaller_add_uint16(m, SPICE_MSG_WAIT_FOR_CHANNELS); - spice_marshaller_add_uint32(m, sizeof(free_list->wait.header) + - free_list->wait.header.wait_count * sizeof(free_list->wait.buf[0])); - spice_marshall_msg_wait_for_channels(m, &free_list->wait.header); -} - -/* use legacy SpiceDataHeader (with sub_list) */ -static inline void display_channel_send_free_list_legacy(RedChannelClient *rcc) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - FreeList *free_list = &dcc->send_data.free_list; - SpiceMarshaller *marshaller; - int sub_list_len = 1; - SpiceMarshaller *wait_m = NULL; - SpiceMarshaller *inval_m; - SpiceMarshaller *sub_list_m; - - marshaller = red_channel_client_get_marshaller(rcc); - inval_m = spice_marshaller_get_submarshaller(marshaller); - - display_marshal_sub_msg_inval_list(inval_m, free_list); - - if (free_list->wait.header.wait_count) { - wait_m = spice_marshaller_get_submarshaller(marshaller); - display_marshal_sub_msg_inval_list_wait(wait_m, free_list); - sub_list_len++; - } - - sub_list_m = spice_marshaller_get_submarshaller(marshaller); - spice_marshaller_add_uint16(sub_list_m, sub_list_len); - if (wait_m) { - spice_marshaller_add_uint32(sub_list_m, spice_marshaller_get_offset(wait_m)); - } - spice_marshaller_add_uint32(sub_list_m, spice_marshaller_get_offset(inval_m)); - red_channel_client_set_header_sub_list(rcc, spice_marshaller_get_offset(sub_list_m)); -} - -/* use mini header and SPICE_MSG_LIST */ -static inline void display_channel_send_free_list(RedChannelClient *rcc) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - FreeList *free_list = &dcc->send_data.free_list; - int sub_list_len = 1; - SpiceMarshaller *urgent_marshaller; - SpiceMarshaller *wait_m = NULL; - SpiceMarshaller *inval_m; - uint32_t sub_arr_offset; - uint32_t wait_offset = 0; - uint32_t inval_offset = 0; - int i; - - urgent_marshaller = red_channel_client_switch_to_urgent_sender(rcc); - for (i = 0; i < dcc->send_data.num_pixmap_cache_items; i++) { - int dummy; - /* When using the urgent marshaller, the serial number of the message that is - * going to be sent right after the SPICE_MSG_LIST, is increased by one. - * But all this message pixmaps cache references used its old serial. - * we use pixmap_cache_items to collect these pixmaps, and we update their serial - * by calling pixmap_cache_hit. */ - dcc_pixmap_cache_hit(dcc, dcc->send_data.pixmap_cache_items[i], &dummy); - } - - if (free_list->wait.header.wait_count) { - red_channel_client_init_send_data(rcc, SPICE_MSG_LIST, NULL); - } else { /* only one message, no need for a list */ - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_LIST, NULL); - spice_marshall_msg_display_inval_list(urgent_marshaller, free_list->res); - return; - } - - inval_m = spice_marshaller_get_submarshaller(urgent_marshaller); - display_marshal_sub_msg_inval_list(inval_m, free_list); - - if (free_list->wait.header.wait_count) { - wait_m = spice_marshaller_get_submarshaller(urgent_marshaller); - display_marshal_sub_msg_inval_list_wait(wait_m, free_list); - sub_list_len++; - } - - sub_arr_offset = sub_list_len * sizeof(uint32_t); - - spice_marshaller_add_uint16(urgent_marshaller, sub_list_len); - inval_offset = spice_marshaller_get_offset(inval_m); // calc the offset before - // adding the sub list - // offsets array to the marshaller - /* adding the array of offsets */ - if (wait_m) { - wait_offset = spice_marshaller_get_offset(wait_m); - spice_marshaller_add_uint32(urgent_marshaller, wait_offset + sub_arr_offset); - } - spice_marshaller_add_uint32(urgent_marshaller, inval_offset + sub_arr_offset); -} - -static inline void display_begin_send_message(RedChannelClient *rcc) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - FreeList *free_list = &dcc->send_data.free_list; - - if (free_list->res->count) { - int sync_count = 0; - int i; - - for (i = 0; i < MAX_CACHE_CLIENTS; i++) { - if (i != dcc->common.id && free_list->sync[i] != 0) { - free_list->wait.header.wait_list[sync_count].channel_type = SPICE_CHANNEL_DISPLAY; - free_list->wait.header.wait_list[sync_count].channel_id = i; - free_list->wait.header.wait_list[sync_count++].message_serial = free_list->sync[i]; - } - } - free_list->wait.header.wait_count = sync_count; - - if (rcc->is_mini_header) { - display_channel_send_free_list(rcc); - } else { - display_channel_send_free_list_legacy(rcc); - } - } - red_channel_client_begin_send_message(rcc); -} - -static inline int red_marshall_stream_data(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, Drawable *drawable) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - DisplayChannel *display = DCC_TO_DC(dcc); - Stream *stream = drawable->stream; - SpiceImage *image; - uint32_t frame_mm_time; - int n; - int width, height; - int ret; - - if (!stream) { - spice_assert(drawable->sized_stream); - stream = drawable->sized_stream; - } - spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY); - - image = drawable->red_drawable->u.copy.src_bitmap; - - if (image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) { - return FALSE; - } - - if (drawable->sized_stream) { - if (red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_SIZED_STREAM)) { - SpiceRect *src_rect = &drawable->red_drawable->u.copy.src_area; - - width = src_rect->right - src_rect->left; - height = src_rect->bottom - src_rect->top; - } else { - return FALSE; - } - } else { - width = stream->width; - height = stream->height; - } - - StreamAgent *agent = &dcc->stream_agents[get_stream_id(display, stream)]; - uint64_t time_now = red_get_monotonic_time(); - size_t outbuf_size; - - if (!dcc->use_mjpeg_encoder_rate_control) { - if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) { - agent->frames--; -#ifdef STREAM_STATS - agent->stats.num_drops_fps++; -#endif - return TRUE; - } - } - - /* workaround for vga streams */ - frame_mm_time = drawable->red_drawable->mm_time ? - drawable->red_drawable->mm_time : - reds_get_mm_time(); - - outbuf_size = dcc->send_data.stream_outbuf_size; - ret = mjpeg_encoder_encode_frame(agent->mjpeg_encoder, - &image->u.bitmap, width, height, - &drawable->red_drawable->u.copy.src_area, - stream->top_down, frame_mm_time, - &dcc->send_data.stream_outbuf, - &outbuf_size, &n); - switch (ret) { - case MJPEG_ENCODER_FRAME_DROP: - spice_assert(dcc->use_mjpeg_encoder_rate_control); -#ifdef STREAM_STATS - agent->stats.num_drops_fps++; -#endif - return TRUE; - case MJPEG_ENCODER_FRAME_UNSUPPORTED: - return FALSE; - case MJPEG_ENCODER_FRAME_ENCODE_DONE: - break; - default: - spice_error("bad return value (%d) from mjpeg_encoder_encode_frame", ret); - return FALSE; - } - dcc->send_data.stream_outbuf_size = outbuf_size; - - if (!drawable->sized_stream) { - SpiceMsgDisplayStreamData stream_data; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL); - - stream_data.base.id = get_stream_id(display, stream); - stream_data.base.multi_media_time = frame_mm_time; - stream_data.data_size = n; - - spice_marshall_msg_display_stream_data(base_marshaller, &stream_data); - } else { - SpiceMsgDisplayStreamDataSized stream_data; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA_SIZED, NULL); - - stream_data.base.id = get_stream_id(display, stream); - stream_data.base.multi_media_time = frame_mm_time; - stream_data.data_size = n; - stream_data.width = width; - stream_data.height = height; - stream_data.dest = drawable->red_drawable->bbox; - - spice_debug("stream %d: sized frame: dest ==> ", stream_data.base.id); - rect_debug(&stream_data.dest); - spice_marshall_msg_display_stream_data_sized(base_marshaller, &stream_data); - } - spice_marshaller_add_ref(base_marshaller, - dcc->send_data.stream_outbuf, n); - agent->last_send_time = time_now; -#ifdef STREAM_STATS - agent->stats.num_frames_sent++; - agent->stats.size_sent += n; - agent->stats.end = frame_mm_time; -#endif - - return TRUE; -} - -static inline void marshall_qxl_drawable(RedChannelClient *rcc, - SpiceMarshaller *m, DrawablePipeItem *dpi) -{ - Drawable *item = dpi->drawable; - DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); - - spice_assert(display_channel && rcc); - /* allow sized frames to be streamed, even if they where replaced by another frame, since - * newer frames might not cover sized frames completely if they are bigger */ - if ((item->stream || item->sized_stream) && red_marshall_stream_data(rcc, m, item)) { - return; - } - if (!display_channel->enable_jpeg) - red_marshall_qxl_drawable(rcc, m, dpi); - else - red_lossy_marshall_qxl_drawable(rcc, m, dpi); -} - -static inline void red_marshall_inval_palette(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - CacheItem *cache_item) -{ - SpiceMsgDisplayInvalOne inval_one; - - red_channel_client_init_send_data(rcc, cache_item->inval_type, NULL); - inval_one.id = *(uint64_t *)&cache_item->id; - - spice_marshall_msg_display_inval_palette(base_marshaller, &inval_one); - -} - -static void display_channel_marshall_migrate_data_surfaces(DisplayChannelClient *dcc, - SpiceMarshaller *m, - int lossy) -{ - SpiceMarshaller *m2 = spice_marshaller_get_ptr_submarshaller(m, 0); - uint32_t *num_surfaces_created; - uint32_t i; - - num_surfaces_created = (uint32_t *)spice_marshaller_reserve_space(m2, sizeof(uint32_t)); - *num_surfaces_created = 0; - for (i = 0; i < NUM_SURFACES; i++) { - SpiceRect lossy_rect; - - if (!dcc->surface_client_created[i]) { - continue; - } - spice_marshaller_add_uint32(m2, i); - (*num_surfaces_created)++; - - if (!lossy) { - continue; - } - region_extents(&dcc->surface_client_lossy_region[i], &lossy_rect); - spice_marshaller_add_int32(m2, lossy_rect.left); - spice_marshaller_add_int32(m2, lossy_rect.top); - spice_marshaller_add_int32(m2, lossy_rect.right); - spice_marshaller_add_int32(m2, lossy_rect.bottom); - } -} - -static void display_channel_marshall_migrate_data(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller) -{ - DisplayChannel *display_channel; - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - SpiceMigrateDataDisplay display_data = {0,}; - - display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base); - - red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, NULL); - spice_marshaller_add_uint32(base_marshaller, SPICE_MIGRATE_DATA_DISPLAY_MAGIC); - spice_marshaller_add_uint32(base_marshaller, SPICE_MIGRATE_DATA_DISPLAY_VERSION); - - spice_assert(dcc->pixmap_cache); - spice_assert(MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS == 4 && - MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS == MAX_CACHE_CLIENTS); - - display_data.message_serial = red_channel_client_get_message_serial(rcc); - display_data.low_bandwidth_setting = dcc->common.is_low_bandwidth; - - display_data.pixmap_cache_freezer = pixmap_cache_freeze(dcc->pixmap_cache); - display_data.pixmap_cache_id = dcc->pixmap_cache->id; - display_data.pixmap_cache_size = dcc->pixmap_cache->size; - memcpy(display_data.pixmap_cache_clients, dcc->pixmap_cache->sync, - sizeof(display_data.pixmap_cache_clients)); - - spice_assert(dcc->glz_dict); - dcc_freeze_glz(dcc); - display_data.glz_dict_id = dcc->glz_dict->id; - glz_enc_dictionary_get_restore_data(dcc->glz_dict->dict, - &display_data.glz_dict_data, - &dcc->glz_data.usr); - - /* all data besided the surfaces ref */ - spice_marshaller_add(base_marshaller, - (uint8_t *)&display_data, sizeof(display_data) - sizeof(uint32_t)); - display_channel_marshall_migrate_data_surfaces(dcc, base_marshaller, - display_channel->enable_jpeg); -} - -static void display_channel_marshall_pixmap_sync(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - SpiceMsgWaitForChannels wait; - PixmapCache *pixmap_cache; - - red_channel_client_init_send_data(rcc, SPICE_MSG_WAIT_FOR_CHANNELS, NULL); - pixmap_cache = dcc->pixmap_cache; - - pthread_mutex_lock(&pixmap_cache->lock); - - wait.wait_count = 1; - wait.wait_list[0].channel_type = SPICE_CHANNEL_DISPLAY; - wait.wait_list[0].channel_id = pixmap_cache->generation_initiator.client; - wait.wait_list[0].message_serial = pixmap_cache->generation_initiator.message; - dcc->pixmap_cache_generation = pixmap_cache->generation; - dcc->pending_pixmaps_sync = FALSE; - - pthread_mutex_unlock(&pixmap_cache->lock); - - spice_marshall_msg_wait_for_channels(base_marshaller, &wait); -} - -static void dcc_pixmap_cache_reset(DisplayChannelClient *dcc, SpiceMsgWaitForChannels* sync_data) -{ - PixmapCache *cache = dcc->pixmap_cache; - uint8_t wait_count; - uint64_t serial; - uint32_t i; - - serial = red_channel_client_get_message_serial(RED_CHANNEL_CLIENT(dcc)); - pthread_mutex_lock(&cache->lock); - pixmap_cache_clear(cache); - - dcc->pixmap_cache_generation = ++cache->generation; - cache->generation_initiator.client = dcc->common.id; - cache->generation_initiator.message = serial; - cache->sync[dcc->common.id] = serial; - - wait_count = 0; - for (i = 0; i < MAX_CACHE_CLIENTS; i++) { - if (cache->sync[i] && i != dcc->common.id) { - sync_data->wait_list[wait_count].channel_type = SPICE_CHANNEL_DISPLAY; - sync_data->wait_list[wait_count].channel_id = i; - sync_data->wait_list[wait_count++].message_serial = cache->sync[i]; - } - } - sync_data->wait_count = wait_count; - pthread_mutex_unlock(&cache->lock); -} - -static void display_channel_marshall_reset_cache(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - SpiceMsgWaitForChannels wait; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS, NULL); - dcc_pixmap_cache_reset(dcc, &wait); - - spice_marshall_msg_display_inval_all_pixmaps(base_marshaller, - &wait); -} - -static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageItem *item) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - DisplayChannel *display = DCC_TO_DC(dcc); - SpiceImage red_image; - RedWorker *worker; - SpiceBitmap bitmap; - SpiceChunks *chunks; - QRegion *surface_lossy_region; - int comp_succeeded = FALSE; - int lossy_comp = FALSE; - int quic_comp = FALSE; - SpiceImageCompression comp_mode; - SpiceMsgDisplayDrawCopy copy; - SpiceMarshaller *src_bitmap_out, *mask_bitmap_out; - SpiceMarshaller *bitmap_palette_out, *lzplt_palette_out; - - spice_assert(rcc && display && item); - worker = display->common.worker; - - QXL_SET_IMAGE_ID(&red_image, QXL_IMAGE_GROUP_RED, display_channel_generate_uid(display)); - red_image.descriptor.type = SPICE_IMAGE_TYPE_BITMAP; - red_image.descriptor.flags = item->image_flags; - red_image.descriptor.width = item->width; - red_image.descriptor.height = item->height; - - bitmap.format = item->image_format; - bitmap.flags = 0; - if (item->top_down) { - bitmap.flags |= SPICE_BITMAP_FLAGS_TOP_DOWN; - } - bitmap.x = item->width; - bitmap.y = item->height; - bitmap.stride = item->stride; - bitmap.palette = 0; - bitmap.palette_id = 0; - - chunks = spice_chunks_new_linear(item->data, bitmap.stride * bitmap.y); - bitmap.data = chunks; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->link); - - copy.base.surface_id = item->surface_id; - copy.base.box.left = item->pos.x; - copy.base.box.top = item->pos.y; - copy.base.box.right = item->pos.x + bitmap.x; - copy.base.box.bottom = item->pos.y + bitmap.y; - copy.base.clip.type = SPICE_CLIP_TYPE_NONE; - copy.data.rop_descriptor = SPICE_ROPD_OP_PUT; - copy.data.src_area.left = 0; - copy.data.src_area.top = 0; - copy.data.src_area.right = bitmap.x; - copy.data.src_area.bottom = bitmap.y; - copy.data.scale_mode = 0; - copy.data.src_bitmap = 0; - copy.data.mask.flags = 0; - copy.data.mask.flags = 0; - copy.data.mask.pos.x = 0; - copy.data.mask.pos.y = 0; - copy.data.mask.bitmap = 0; - - spice_marshall_msg_display_draw_copy(m, ©, - &src_bitmap_out, &mask_bitmap_out); - - compress_send_data_t comp_send_data = {0}; - - comp_mode = dcc->image_compression; - - if (((comp_mode == SPICE_IMAGE_COMPRESSION_AUTO_LZ) || - (comp_mode == SPICE_IMAGE_COMPRESSION_AUTO_GLZ)) && !bitmap_has_extra_stride(&bitmap)) { - - if (bitmap_fmt_has_graduality(item->image_format)) { - BitmapGradualType grad_level; - - grad_level = bitmap_get_graduality_level(&bitmap); - if (grad_level == BITMAP_GRADUAL_HIGH) { - // if we use lz for alpha, the stride can't be extra - lossy_comp = display->enable_jpeg && item->can_lossy; - quic_comp = TRUE; - } - } - } else if (comp_mode == SPICE_IMAGE_COMPRESSION_QUIC) { - quic_comp = TRUE; - } - - if (lossy_comp) { - comp_succeeded = dcc_compress_image_jpeg(dcc, &red_image, - &bitmap, &comp_send_data, - worker->mem_slots.internal_groupslot_id); - } else if (quic_comp) { - comp_succeeded = dcc_compress_image_quic(dcc, &red_image, &bitmap, - &comp_send_data, - worker->mem_slots.internal_groupslot_id); -#ifdef USE_LZ4 - } else if (comp_mode == SPICE_IMAGE_COMPRESSION_LZ4 && - bitmap_fmt_is_rgb(bitmap.format) && - red_channel_client_test_remote_cap(&dcc->common.base, - SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) { - comp_succeeded = dcc_compress_image_lz4(dcc, &red_image, &bitmap, - &comp_send_data, - worker->mem_slots.internal_groupslot_id); -#endif - } else if (comp_mode != SPICE_IMAGE_COMPRESSION_OFF) { - comp_succeeded = dcc_compress_image_lz(dcc, &red_image, &bitmap, - &comp_send_data, - worker->mem_slots.internal_groupslot_id); - } - - surface_lossy_region = &dcc->surface_client_lossy_region[item->surface_id]; - if (comp_succeeded) { - spice_marshall_Image(src_bitmap_out, &red_image, - &bitmap_palette_out, &lzplt_palette_out); - - marshaller_add_compressed(src_bitmap_out, - comp_send_data.comp_buf, comp_send_data.comp_buf_size); - - if (lzplt_palette_out && comp_send_data.lzplt_palette) { - spice_marshall_Palette(lzplt_palette_out, comp_send_data.lzplt_palette); - } - - if (lossy_comp) { - region_add(surface_lossy_region, ©.base.box); - } else { - region_remove(surface_lossy_region, ©.base.box); - } - } else { - red_image.descriptor.type = SPICE_IMAGE_TYPE_BITMAP; - red_image.u.bitmap = bitmap; - - spice_marshall_Image(src_bitmap_out, &red_image, - &bitmap_palette_out, &lzplt_palette_out); - spice_marshaller_add_ref(src_bitmap_out, item->data, - bitmap.y * bitmap.stride); - region_remove(surface_lossy_region, ©.base.box); - } - spice_chunks_destroy(chunks); -} - -static void red_display_marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller *m, - UpgradeItem *item) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - RedDrawable *red_drawable; - SpiceMsgDisplayDrawCopy copy; - SpiceMarshaller *src_bitmap_out, *mask_bitmap_out; - - spice_assert(rcc && rcc->channel && item && item->drawable); - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->base); - - red_drawable = item->drawable->red_drawable; - spice_assert(red_drawable->type == QXL_DRAW_COPY); - spice_assert(red_drawable->u.copy.rop_descriptor == SPICE_ROPD_OP_PUT); - spice_assert(red_drawable->u.copy.mask.bitmap == 0); - - copy.base.surface_id = 0; - copy.base.box = red_drawable->bbox; - copy.base.clip.type = SPICE_CLIP_TYPE_RECTS; - copy.base.clip.rects = item->rects; - copy.data = red_drawable->u.copy; - - spice_marshall_msg_display_draw_copy(m, ©, - &src_bitmap_out, &mask_bitmap_out); - - fill_bits(dcc, src_bitmap_out, copy.data.src_bitmap, item->drawable, FALSE); -} - -static void red_display_marshall_stream_start(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, StreamAgent *agent) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - Stream *stream = agent->stream; - - agent->last_send_time = 0; - spice_assert(stream); - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CREATE, NULL); - SpiceMsgDisplayStreamCreate stream_create; - SpiceClipRects clip_rects; - - stream_create.surface_id = 0; - stream_create.id = get_stream_id(DCC_TO_DC(dcc), stream); - stream_create.flags = stream->top_down ? SPICE_STREAM_FLAGS_TOP_DOWN : 0; - stream_create.codec_type = SPICE_VIDEO_CODEC_TYPE_MJPEG; - - stream_create.src_width = stream->width; - stream_create.src_height = stream->height; - stream_create.stream_width = stream_create.src_width; - stream_create.stream_height = stream_create.src_height; - stream_create.dest = stream->dest_area; - - if (stream->current) { - RedDrawable *red_drawable = stream->current->red_drawable; - stream_create.clip = red_drawable->clip; - } else { - stream_create.clip.type = SPICE_CLIP_TYPE_RECTS; - clip_rects.num_rects = 0; - stream_create.clip.rects = &clip_rects; - } - - stream_create.stamp = 0; - - spice_marshall_msg_display_stream_create(base_marshaller, &stream_create); -} - -static void red_display_marshall_stream_clip(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - StreamClipItem *item) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - StreamAgent *agent = item->stream_agent; - - spice_assert(agent->stream); - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CLIP, &item->base); - SpiceMsgDisplayStreamClip stream_clip; - - stream_clip.id = get_stream_id(DCC_TO_DC(dcc), agent->stream); - stream_clip.clip.type = item->clip_type; - stream_clip.clip.rects = item->rects; - - spice_marshall_msg_display_stream_clip(base_marshaller, &stream_clip); -} - -static void red_display_marshall_stream_end(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, StreamAgent* agent) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - SpiceMsgDisplayStreamDestroy destroy; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL); - destroy.id = get_stream_id(DCC_TO_DC(dcc), agent->stream); - stream_agent_stop(agent); - spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy); -} - -static void red_marshall_surface_create(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, SpiceMsgSurfaceCreate *surface_create) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - - region_init(&dcc->surface_client_lossy_region[surface_create->surface_id]); - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE, NULL); - - spice_marshall_msg_display_surface_create(base_marshaller, surface_create); -} - -static void red_marshall_surface_destroy(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, uint32_t surface_id) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - SpiceMsgSurfaceDestroy surface_destroy; - - region_destroy(&dcc->surface_client_lossy_region[surface_id]); - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_DESTROY, NULL); - - surface_destroy.surface_id = surface_id; - - spice_marshall_msg_display_surface_destroy(base_marshaller, &surface_destroy); -} - -static void red_marshall_monitors_config(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, - MonitorsConfig *monitors_config) -{ - int heads_size = sizeof(SpiceHead) * monitors_config->count; - int i; - SpiceMsgDisplayMonitorsConfig *msg = spice_malloc0(sizeof(*msg) + heads_size); - int count = 0; // ignore monitors_config->count, it may contain zero width monitors, remove them now - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_MONITORS_CONFIG, NULL); - for (i = 0 ; i < monitors_config->count; ++i) { - if (monitors_config->heads[i].width == 0 || monitors_config->heads[i].height == 0) { - continue; - } - msg->heads[count].id = monitors_config->heads[i].id; - msg->heads[count].surface_id = monitors_config->heads[i].surface_id; - msg->heads[count].width = monitors_config->heads[i].width; - msg->heads[count].height = monitors_config->heads[i].height; - msg->heads[count].x = monitors_config->heads[i].x; - msg->heads[count].y = monitors_config->heads[i].y; - count++; - } - msg->count = count; - msg->max_allowed = monitors_config->max_allowed; - spice_marshall_msg_display_monitors_config(base_marshaller, msg); - free(msg); -} - -static void red_marshall_stream_activate_report(RedChannelClient *rcc, - SpiceMarshaller *base_marshaller, - uint32_t stream_id) -{ - DisplayChannelClient *dcc = RCC_TO_DCC(rcc); - StreamAgent *agent = &dcc->stream_agents[stream_id]; - SpiceMsgDisplayStreamActivateReport msg; - - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT, NULL); - msg.stream_id = stream_id; - msg.unique_id = agent->report_id; - msg.max_window_size = RED_STREAM_CLIENT_REPORT_WINDOW; - msg.timeout_ms = RED_STREAM_CLIENT_REPORT_TIMEOUT; - spice_marshall_msg_display_stream_activate_report(base_marshaller, &msg); -} - -static void reset_send_data(DisplayChannelClient *dcc) -{ - dcc->send_data.free_list.res->count = 0; - dcc->send_data.num_pixmap_cache_items = 0; - memset(dcc->send_data.free_list.sync, 0, sizeof(dcc->send_data.free_list.sync)); -} - -void dcc_send_item(DisplayChannelClient *dcc, PipeItem *pipe_item) -{ - RedChannelClient *rcc = RED_CHANNEL_CLIENT(dcc); - SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); - - reset_send_data(dcc); - switch (pipe_item->type) { - case PIPE_ITEM_TYPE_DRAW: { - DrawablePipeItem *dpi = SPICE_CONTAINEROF(pipe_item, DrawablePipeItem, dpi_pipe_item); - marshall_qxl_drawable(rcc, m, dpi); - break; - } - case PIPE_ITEM_TYPE_INVAL_ONE: - red_marshall_inval_palette(rcc, m, (CacheItem *)pipe_item); - break; - case PIPE_ITEM_TYPE_STREAM_CREATE: { - StreamAgent *agent = SPICE_CONTAINEROF(pipe_item, StreamAgent, create_item); - red_display_marshall_stream_start(rcc, m, agent); - break; - } - case PIPE_ITEM_TYPE_STREAM_CLIP: { - StreamClipItem* clip_item = (StreamClipItem *)pipe_item; - red_display_marshall_stream_clip(rcc, m, clip_item); - break; - } - case PIPE_ITEM_TYPE_STREAM_DESTROY: { - StreamAgent *agent = SPICE_CONTAINEROF(pipe_item, StreamAgent, destroy_item); - red_display_marshall_stream_end(rcc, m, agent); - break; - } - case PIPE_ITEM_TYPE_UPGRADE: - red_display_marshall_upgrade(rcc, m, (UpgradeItem *)pipe_item); - break; - case PIPE_ITEM_TYPE_VERB: - red_marshall_verb(rcc, (VerbItem*)pipe_item); - break; - case PIPE_ITEM_TYPE_MIGRATE_DATA: - display_channel_marshall_migrate_data(rcc, m); - break; - case PIPE_ITEM_TYPE_IMAGE: - red_marshall_image(rcc, m, (ImageItem *)pipe_item); - break; - case PIPE_ITEM_TYPE_PIXMAP_SYNC: - display_channel_marshall_pixmap_sync(rcc, m); - break; - case PIPE_ITEM_TYPE_PIXMAP_RESET: - display_channel_marshall_reset_cache(rcc, m); - break; - case PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE: - dcc_palette_cache_reset(dcc); - red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES, NULL); - break; - case PIPE_ITEM_TYPE_CREATE_SURFACE: { - SurfaceCreateItem *surface_create = SPICE_CONTAINEROF(pipe_item, SurfaceCreateItem, - pipe_item); - red_marshall_surface_create(rcc, m, &surface_create->surface_create); - break; - } - case PIPE_ITEM_TYPE_DESTROY_SURFACE: { - SurfaceDestroyItem *surface_destroy = SPICE_CONTAINEROF(pipe_item, SurfaceDestroyItem, - pipe_item); - red_marshall_surface_destroy(rcc, m, surface_destroy->surface_destroy.surface_id); - break; - } - case PIPE_ITEM_TYPE_MONITORS_CONFIG: { - MonitorsConfigItem *monconf_item = SPICE_CONTAINEROF(pipe_item, - MonitorsConfigItem, pipe_item); - red_marshall_monitors_config(rcc, m, monconf_item->monitors_config); - break; - } - case PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT: { - StreamActivateReportItem *report_item = SPICE_CONTAINEROF(pipe_item, - StreamActivateReportItem, - pipe_item); - red_marshall_stream_activate_report(rcc, m, report_item->stream_id); - break; - } - default: - spice_error("invalid pipe item type"); - } - - dcc_release_item(dcc, pipe_item, FALSE); - - // a message is pending - if (red_channel_client_send_message_pending(rcc)) { - display_begin_send_message(rcc); - } -} - static inline void red_push(RedWorker *worker) { if (worker->cursor_channel) { diff --git a/server/red_worker.h b/server/red_worker.h index cbf91b9..bbbd523 100644 --- a/server/red_worker.h +++ b/server/red_worker.h @@ -110,6 +110,8 @@ QXLInstance* red_worker_get_qxl(RedWorker *worker); RedChannel* red_worker_get_cursor_channel(RedWorker *worker); RedChannel* red_worker_get_display_channel(RedWorker *worker); clockid_t red_worker_get_clockid(RedWorker *worker); +RedMemSlotInfo* red_worker_get_memslot(RedWorker *worker); + void red_drawable_unref(RedWorker *worker, RedDrawable *red_drawable, uint32_t group_id); -- 2.4.3 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel