[Spice-devel] [PATCH 09/19] worker: move dcc_send & marshallers to dcc-send.c
Fabiano Fidêncio
fabiano at fidencio.org
Thu Nov 26 00:59:22 PST 2015
This patch is freaking huge :-\
Is there some automated way to check whether a function move is just a
move or something else changed?
On Wed, Nov 25, 2015 at 4:27 PM, Frediano Ziglio <fziglio at redhat.com> wrote:
> From: Marc-André Lureau <marcandre.lureau at gmail.com>
>
> ---
> server/Makefile.am | 1 +
> server/dcc-send.c | 2450 ++++++++++++++++++++++++++++++++++++++++++++++++++
> server/dcc.h | 2 +
> server/red_worker.c | 2460 +--------------------------------------------------
> server/red_worker.h | 2 +
> 5 files changed, 2462 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..1dc9dcf
> --- /dev/null
> +++ b/server/dcc-send.c
> @@ -0,0 +1,2450 @@
> +#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;
> + }
> +}
> +
> +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);
> +}
> +
> +/* 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 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, 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)
> +{
> + Drawable *item = dpi->drawable;
> + DisplayChannel *display = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
> +
> + spice_return_if_fail(display);
> + spice_return_if_fail(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->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 2b5c162..ac52a8f 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;
> @@ -698,2434 +680,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, 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 at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
--
Fabiano Fidêncio
More information about the Spice-devel
mailing list