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