[Spice-devel] [PATCH] worker: move dcc_send & marshallers to dcc-send.c

Frediano Ziglio fziglio at redhat.com
Thu Nov 26 04:30:49 PST 2015


From: Marc-André Lureau <marcandre.lureau at gmail.com>

Acked-by: Frediano Ziglio <fziglio at redhat.com>
---
 server/Makefile.am  |    1 +
 server/dcc-send.c   | 2468 +++++++++++++++++++++++++++++++++++++++++++++++++++
 server/dcc.h        |    2 +
 server/red_worker.c | 2460 +-------------------------------------------------
 server/red_worker.h |    2 +
 5 files changed, 2480 insertions(+), 2453 deletions(-)
 create mode 100644 server/dcc-send.c

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



More information about the Spice-devel mailing list