--- server/Makefile.am | 3 ++ server/dcc-encoders.c | 1 - server/dcc-encoders.h | 1 + server/dcc.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++ server/dcc.h | 134 +++++++++++++++++++++++++++++++++++++++++++++ server/display-channel.c | 127 +------------------------------------------ server/display-channel.h | 112 +------------------------------------- server/display-limits.h | 27 ++++++++++ server/red_worker.c | 4 +- 9 files changed, 308 insertions(+), 238 deletions(-) create mode 100644 server/dcc.c create mode 100644 server/dcc.h create mode 100644 server/display-limits.h Changes: - add a new display-limits.h header file to keep configuration limits instead of moving define where compiler is happy. diff --git a/server/Makefile.am b/server/Makefile.am index 5907dd2..669664a 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -138,6 +138,9 @@ libspice_server_la_SOURCES = \ utils.h \ stream.c \ stream.h \ + dcc.c \ + dcc.h \ + display-limits.h \ dcc-encoders.c \ dcc-encoders.h \ $(NULL) diff --git a/server/dcc-encoders.c b/server/dcc-encoders.c index 45db96f..0a0997a 100644 --- a/server/dcc-encoders.c +++ b/server/dcc-encoders.c @@ -20,7 +20,6 @@ #endif #include <glib.h> -#include <setjmp.h> #include "dcc-encoders.h" #include "display-channel.h" diff --git a/server/dcc-encoders.h b/server/dcc-encoders.h index c9e06e7..3c1fda1 100644 --- a/server/dcc-encoders.h +++ b/server/dcc-encoders.h @@ -18,6 +18,7 @@ #ifndef DCC_ENCODERS_H_ #define DCC_ENCODERS_H_ +#include <setjmp.h> #include "common/marshaller.h" #include "common/quic.h" #include "red_channel.h" diff --git a/server/dcc.c b/server/dcc.c new file mode 100644 index 0000000..5e35708 --- /dev/null +++ b/server/dcc.c @@ -0,0 +1,137 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2009-2015 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/>. +*/ +#include "dcc.h" +#include "display-channel.h" + +DisplayChannelClient *dcc_new(DisplayChannel *display, + RedClient *client, RedsStream *stream, + int mig_target, + uint32_t *common_caps, int num_common_caps, + uint32_t *caps, int num_caps, + SpiceImageCompression image_compression, + spice_wan_compression_t jpeg_state, + spice_wan_compression_t zlib_glz_state) + +{ + DisplayChannelClient *dcc; + + dcc = (DisplayChannelClient*)common_channel_new_client( + COMMON_CHANNEL(display), sizeof(DisplayChannelClient), + client, stream, mig_target, TRUE, + common_caps, num_common_caps, + caps, num_caps); + spice_return_val_if_fail(dcc, NULL); + + ring_init(&dcc->palette_cache_lru); + dcc->palette_cache_available = CLIENT_PALETTE_CACHE_SIZE; + dcc->image_compression = image_compression; + dcc->jpeg_state = jpeg_state; + dcc->zlib_glz_state = zlib_glz_state; + // todo: tune quality according to bandwidth + dcc->jpeg_quality = 85; + + dcc_encoders_init(dcc); + + return dcc; +} + +void dcc_stream_agent_clip(DisplayChannelClient* dcc, StreamAgent *agent) +{ + StreamClipItem *item = stream_clip_item_new(dcc, agent); + int n_rects; + + item->clip_type = SPICE_CLIP_TYPE_RECTS; + + n_rects = pixman_region32_n_rects(&agent->clip); + item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects)); + item->rects->num_rects = n_rects; + region_ret_rects(&agent->clip, item->rects->rects, n_rects); + + red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), (PipeItem *)item); +} + +static MonitorsConfigItem *monitors_config_item_new(RedChannel* channel, + MonitorsConfig *monitors_config) +{ + MonitorsConfigItem *mci; + + mci = (MonitorsConfigItem *)spice_malloc(sizeof(*mci)); + mci->monitors_config = monitors_config; + + red_channel_pipe_item_init(channel, + &mci->pipe_item, PIPE_ITEM_TYPE_MONITORS_CONFIG); + return mci; +} + +void dcc_push_monitors_config(DisplayChannelClient *dcc) +{ + DisplayChannel *dc = DCC_TO_DC(dcc); + MonitorsConfig *monitors_config = dc->monitors_config; + MonitorsConfigItem *mci; + + if (monitors_config == NULL) { + spice_warning("monitors_config is NULL"); + return; + } + + if (!red_channel_client_test_remote_cap(&dcc->common.base, + SPICE_DISPLAY_CAP_MONITORS_CONFIG)) { + return; + } + + mci = monitors_config_item_new(dcc->common.base.channel, + monitors_config_ref(dc->monitors_config)); + red_channel_client_pipe_add(&dcc->common.base, &mci->pipe_item); + red_channel_client_push(&dcc->common.base); +} + +static SurfaceDestroyItem *surface_destroy_item_new(RedChannel *channel, + uint32_t surface_id) +{ + SurfaceDestroyItem *destroy; + + destroy = spice_malloc(sizeof(SurfaceDestroyItem)); + destroy->surface_destroy.surface_id = surface_id; + red_channel_pipe_item_init(channel, &destroy->pipe_item, + PIPE_ITEM_TYPE_DESTROY_SURFACE); + + return destroy; +} + +void dcc_destroy_surface(DisplayChannelClient *dcc, uint32_t surface_id) +{ + DisplayChannel *display; + RedChannel *channel; + SurfaceDestroyItem *destroy; + + if (!dcc) { + return; + } + + display = DCC_TO_DC(dcc); + channel = RED_CHANNEL(display); + + if (COMMON_CHANNEL(display)->during_target_migrate || + !dcc->surface_client_created[surface_id]) { + return; + } + + dcc->surface_client_created[surface_id] = FALSE; + destroy = surface_destroy_item_new(channel, surface_id); + red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &destroy->pipe_item); +} diff --git a/server/dcc.h b/server/dcc.h new file mode 100644 index 0000000..ee2e75f --- /dev/null +++ b/server/dcc.h @@ -0,0 +1,134 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2009-2015 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/>. +*/ +#ifndef DCC_H_ +# define DCC_H_ + +#include "red_worker.h" +#include "pixmap-cache.h" +#include "cache-item.h" +#include "dcc-encoders.h" +#include "stream.h" +#include "display-limits.h" + +#define PALETTE_CACHE_HASH_SHIFT 8 +#define PALETTE_CACHE_HASH_SIZE (1 << PALETTE_CACHE_HASH_SHIFT) +#define PALETTE_CACHE_HASH_MASK (PALETTE_CACHE_HASH_SIZE - 1) +#define PALETTE_CACHE_HASH_KEY(id) ((id) & PALETTE_CACHE_HASH_MASK) +#define CLIENT_PALETTE_CACHE_SIZE 128 + +/* Each drawable can refer to at most 3 images: src, brush and mask */ +#define MAX_DRAWABLE_PIXMAP_CACHE_ITEMS 3 + +typedef struct WaitForChannels { + SpiceMsgWaitForChannels header; + SpiceWaitForChannel buf[MAX_CACHE_CLIENTS]; +} WaitForChannels; + +typedef struct FreeList { + int res_size; + SpiceResourceList *res; + uint64_t sync[MAX_CACHE_CLIENTS]; + WaitForChannels wait; +} FreeList; + +struct DisplayChannelClient { + CommonChannelClient common; + SpiceImageCompression image_compression; + spice_wan_compression_t jpeg_state; + spice_wan_compression_t zlib_glz_state; + int jpeg_quality; + int zlib_level; + + QuicData quic_data; + QuicContext *quic; + LzData lz_data; + LzContext *lz; + JpegData jpeg_data; + JpegEncoderContext *jpeg; +#ifdef USE_LZ4 + Lz4Data lz4_data; + Lz4EncoderContext *lz4; +#endif + ZlibData zlib_data; + ZlibEncoder *zlib; + + int expect_init; + + PixmapCache *pixmap_cache; + uint32_t pixmap_cache_generation; + int pending_pixmaps_sync; + + CacheItem *palette_cache[PALETTE_CACHE_HASH_SIZE]; + Ring palette_cache_lru; + long palette_cache_available; + uint32_t palette_cache_items; + + struct { + uint32_t stream_outbuf_size; + uint8_t *stream_outbuf; // caution stream buffer is also used as compress bufs!!! + + FreeList free_list; + uint64_t pixmap_cache_items[MAX_DRAWABLE_PIXMAP_CACHE_ITEMS]; + int num_pixmap_cache_items; + } send_data; + + /* global lz encoding entities */ + GlzSharedDictionary *glz_dict; + GlzEncoderContext *glz; + GlzData glz_data; + + Ring glz_drawables; // all the living lz drawable, ordered by encoding time + Ring glz_drawables_inst_to_free; // list of instances to be freed + pthread_mutex_t glz_drawables_inst_to_free_lock; + + uint8_t surface_client_created[NUM_SURFACES]; + QRegion surface_client_lossy_region[NUM_SURFACES]; + + StreamAgent stream_agents[NUM_STREAMS]; + int use_mjpeg_encoder_rate_control; + uint32_t streams_max_latency; + uint64_t streams_max_bit_rate; +}; + +#define DCC_TO_WORKER(dcc) \ + (SPICE_CONTAINEROF((dcc)->common.base.channel, CommonChannel, base)->worker) +#define DCC_TO_DC(dcc) \ + SPICE_CONTAINEROF((dcc)->common.base.channel, DisplayChannel, common.base) +#define RCC_TO_DCC(rcc) SPICE_CONTAINEROF((rcc), DisplayChannelClient, common.base) + + +DisplayChannelClient* dcc_new (DisplayChannel *display, + RedClient *client, + RedsStream *stream, + int mig_target, + uint32_t *common_caps, + int num_common_caps, + uint32_t *caps, + int num_caps, + SpiceImageCompression image_compression, + spice_wan_compression_t jpeg_state, + spice_wan_compression_t zlib_glz_state); +void dcc_push_monitors_config (DisplayChannelClient *dcc); +void dcc_destroy_surface (DisplayChannelClient *dcc, + uint32_t surface_id); +void dcc_stream_agent_clip (DisplayChannelClient* dcc, + StreamAgent *agent); +void dcc_create_stream (DisplayChannelClient *dcc, + Stream *stream); + +#endif /* DCC_H_ */ diff --git a/server/display-channel.c b/server/display-channel.c index d4fcc7e..9990c71 100644 --- a/server/display-channel.c +++ b/server/display-channel.c @@ -135,53 +135,6 @@ void display_channel_compress_stats_print(const DisplayChannel *display_channel) #endif } -DisplayChannelClient *dcc_new(DisplayChannel *display, - RedClient *client, RedsStream *stream, - int mig_target, - uint32_t *common_caps, int num_common_caps, - uint32_t *caps, int num_caps, - SpiceImageCompression image_compression, - spice_wan_compression_t jpeg_state, - spice_wan_compression_t zlib_glz_state) - -{ - DisplayChannelClient *dcc; - - dcc = (DisplayChannelClient*)common_channel_new_client( - (CommonChannel *)display, sizeof(DisplayChannelClient), - client, stream, mig_target, TRUE, - common_caps, num_common_caps, - caps, num_caps); - spice_return_val_if_fail(dcc, NULL); - - ring_init(&dcc->palette_cache_lru); - dcc->palette_cache_available = CLIENT_PALETTE_CACHE_SIZE; - dcc->image_compression = image_compression; - dcc->jpeg_state = jpeg_state; - dcc->zlib_glz_state = zlib_glz_state; - // todo: tune quality according to bandwidth - dcc->jpeg_quality = 85; - - dcc_encoders_init(dcc); - - return dcc; -} - -void dcc_add_stream_agent_clip(DisplayChannelClient* dcc, StreamAgent *agent) -{ - StreamClipItem *item = stream_clip_item_new(dcc, agent); - int n_rects; - - item->clip_type = SPICE_CLIP_TYPE_RECTS; - - n_rects = pixman_region32_n_rects(&agent->clip); - item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects)); - item->rects->num_rects = n_rects; - region_ret_rects(&agent->clip, item->rects->rects, n_rects); - - red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), (PipeItem *)item); -} - MonitorsConfig* monitors_config_ref(MonitorsConfig *monitors_config) { monitors_config->refs++; @@ -227,82 +180,6 @@ MonitorsConfig* monitors_config_new(QXLHead *heads, ssize_t nheads, ssize_t max) return mc; } -static MonitorsConfigItem *monitors_config_item_new(RedChannel* channel, - MonitorsConfig *monitors_config) -{ - MonitorsConfigItem *mci; - - mci = (MonitorsConfigItem *)spice_malloc(sizeof(*mci)); - mci->monitors_config = monitors_config; - - red_channel_pipe_item_init(channel, - &mci->pipe_item, PIPE_ITEM_TYPE_MONITORS_CONFIG); - return mci; -} - -static void red_monitors_config_item_add(DisplayChannelClient *dcc) -{ - DisplayChannel *dc = DCC_TO_DC(dcc); - MonitorsConfigItem *mci; - - mci = monitors_config_item_new(dcc->common.base.channel, - monitors_config_ref(dc->monitors_config)); - red_channel_client_pipe_add(&dcc->common.base, &mci->pipe_item); -} - -void dcc_push_monitors_config(DisplayChannelClient *dcc) -{ - MonitorsConfig *monitors_config = DCC_TO_DC(dcc)->monitors_config; - - if (monitors_config == NULL) { - spice_warning("monitors_config is NULL"); - return; - } - - if (!red_channel_client_test_remote_cap(&dcc->common.base, - SPICE_DISPLAY_CAP_MONITORS_CONFIG)) { - return; - } - red_monitors_config_item_add(dcc); - red_channel_client_push(&dcc->common.base); -} - -static SurfaceDestroyItem *surface_destroy_item_new(RedChannel *channel, - uint32_t surface_id) -{ - SurfaceDestroyItem *destroy; - - destroy = spice_malloc(sizeof(SurfaceDestroyItem)); - destroy->surface_destroy.surface_id = surface_id; - red_channel_pipe_item_init(channel, &destroy->pipe_item, - PIPE_ITEM_TYPE_DESTROY_SURFACE); - - return destroy; -} - -void dcc_push_destroy_surface(DisplayChannelClient *dcc, uint32_t surface_id) -{ - DisplayChannel *display; - RedChannel *channel; - SurfaceDestroyItem *destroy; - - if (!dcc) { - return; - } - - display = DCC_TO_DC(dcc); - channel = RED_CHANNEL(display); - - if (COMMON_CHANNEL(display)->during_target_migrate || - !dcc->surface_client_created[surface_id]) { - return; - } - - dcc->surface_client_created[surface_id] = FALSE; - destroy = surface_destroy_item_new(channel, surface_id); - red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &destroy->pipe_item); -} - int display_channel_get_streams_timeout(DisplayChannel *display) { int timeout = INT_MAX; @@ -395,7 +272,7 @@ void display_channel_surface_unref(DisplayChannel *display, uint32_t surface_id) region_destroy(&surface->draw_dirty_region); surface->context.canvas = NULL; FOREACH_DCC(display, link, next, dcc) { - dcc_push_destroy_surface(dcc, surface_id); + dcc_destroy_surface(dcc, surface_id); } spice_warn_if(!ring_is_empty(&surface->depend_on_me)); @@ -442,7 +319,7 @@ static void streams_update_visible_region(DisplayChannel *display, Drawable *dra if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) { region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn); region_exclude(&agent->clip, &drawable->tree_item.base.rgn); - dcc_add_stream_agent_clip(dcc, agent); + dcc_stream_agent_clip(dcc, agent); } } } diff --git a/server/display-channel.h b/server/display-channel.h index 90f8455..aba7dce 100644 --- a/server/display-channel.h +++ b/server/display-channel.h @@ -49,32 +49,9 @@ #include "utils.h" #include "tree.h" #include "stream.h" -#include "dcc-encoders.h" +#include "dcc.h" +#include "display-limits.h" -#define PALETTE_CACHE_HASH_SHIFT 8 -#define PALETTE_CACHE_HASH_SIZE (1 << PALETTE_CACHE_HASH_SHIFT) -#define PALETTE_CACHE_HASH_MASK (PALETTE_CACHE_HASH_SIZE - 1) -#define PALETTE_CACHE_HASH_KEY(id) ((id) & PALETTE_CACHE_HASH_MASK) - -#define CLIENT_PALETTE_CACHE_SIZE 128 - -/* Each drawable can refer to at most 3 images: src, brush and mask */ -#define MAX_DRAWABLE_PIXMAP_CACHE_ITEMS 3 - -#define NUM_STREAMS 50 -#define NUM_SURFACES 10000 - -typedef struct WaitForChannels { - SpiceMsgWaitForChannels header; - SpiceWaitForChannel buf[MAX_CACHE_CLIENTS]; -} WaitForChannels; - -typedef struct FreeList { - int res_size; - SpiceResourceList *res; - uint64_t sync[MAX_CACHE_CLIENTS]; - WaitForChannels wait; -} FreeList; typedef struct DependItem { Drawable *drawable; @@ -115,72 +92,6 @@ struct Drawable { SAFE_FOREACH(link, next, drawable, &(drawable)->pipes, dpi, LINK_TO_DPI(link)) -struct DisplayChannelClient { - CommonChannelClient common; - SpiceImageCompression image_compression; - spice_wan_compression_t jpeg_state; - spice_wan_compression_t zlib_glz_state; - int jpeg_quality; - int zlib_level; - - QuicData quic_data; - QuicContext *quic; - LzData lz_data; - LzContext *lz; - JpegData jpeg_data; - JpegEncoderContext *jpeg; -#ifdef USE_LZ4 - Lz4Data lz4_data; - Lz4EncoderContext *lz4; -#endif - ZlibData zlib_data; - ZlibEncoder *zlib; - - int expect_init; - - PixmapCache *pixmap_cache; - uint32_t pixmap_cache_generation; - int pending_pixmaps_sync; - - CacheItem *palette_cache[PALETTE_CACHE_HASH_SIZE]; - Ring palette_cache_lru; - long palette_cache_available; - uint32_t palette_cache_items; - - struct { - uint32_t stream_outbuf_size; - uint8_t *stream_outbuf; // caution stream buffer is also used as compress bufs!!! - - FreeList free_list; - uint64_t pixmap_cache_items[MAX_DRAWABLE_PIXMAP_CACHE_ITEMS]; - int num_pixmap_cache_items; - } send_data; - - /* global lz encoding entities */ - GlzSharedDictionary *glz_dict; - GlzEncoderContext *glz; - GlzData glz_data; - - Ring glz_drawables; // all the living lz drawable, ordered by encoding time - Ring glz_drawables_inst_to_free; // list of instances to be freed - pthread_mutex_t glz_drawables_inst_to_free_lock; - - uint8_t surface_client_created[NUM_SURFACES]; - QRegion surface_client_lossy_region[NUM_SURFACES]; - - StreamAgent stream_agents[NUM_STREAMS]; - int use_mjpeg_encoder_rate_control; - uint32_t streams_max_latency; - uint64_t streams_max_bit_rate; -}; - -#define DCC_TO_WORKER(dcc) \ - (SPICE_CONTAINEROF((dcc)->common.base.channel, CommonChannel, base)->worker) -#define DCC_TO_DC(dcc) \ - SPICE_CONTAINEROF((dcc)->common.base.channel, DisplayChannel, common.base) -#define RCC_TO_DCC(rcc) SPICE_CONTAINEROF((rcc), DisplayChannelClient, common.base) - - enum { PIPE_ITEM_TYPE_DRAW = PIPE_ITEM_TYPE_COMMON_LAST, PIPE_ITEM_TYPE_IMAGE, @@ -198,25 +109,6 @@ enum { PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT, }; -DisplayChannelClient* dcc_new (DisplayChannel *display, - RedClient *client, - RedsStream *stream, - int mig_target, - uint32_t *common_caps, - int num_common_caps, - uint32_t *caps, - int num_caps, - SpiceImageCompression image_compression, - spice_wan_compression_t jpeg_state, - spice_wan_compression_t zlib_glz_state); -void dcc_push_monitors_config (DisplayChannelClient *dcc); -void dcc_push_destroy_surface (DisplayChannelClient *dcc, - uint32_t surface_id); -void dcc_add_stream_agent_clip (DisplayChannelClient* dcc, - StreamAgent *agent); -void dcc_create_stream (DisplayChannelClient *dcc, - Stream *stream); - typedef struct DrawablePipeItem { RingItem base; /* link for a list of pipe items held by Drawable */ PipeItem dpi_pipe_item; /* link for the client's pipe itself */ diff --git a/server/display-limits.h b/server/display-limits.h new file mode 100644 index 0000000..8d2b41d --- /dev/null +++ b/server/display-limits.h @@ -0,0 +1,27 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2009-2015 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/>. +*/ +#ifndef DISPLAY_LIMITS_H_ +#define DISPLAY_LIMITS_H_ + +/*** Maximum number of surfaces a guest can create */ +#define NUM_SURFACES 10000 + +/*** Maximum number of streams created by spice-server */ +#define NUM_STREAMS 50 + +#endif /* DISPLAY_LIMITS_H_ */ diff --git a/server/red_worker.c b/server/red_worker.c index 5609b47..07dcf1c 100644 --- a/server/red_worker.c +++ b/server/red_worker.c @@ -274,7 +274,7 @@ void attach_stream(DisplayChannel *display, Drawable *drawable, Stream *stream) if (!region_is_equal(&clip_in_draw_dest, &drawable->tree_item.base.rgn)) { region_remove(&agent->clip, &drawable->red_drawable->bbox); region_or(&agent->clip, &drawable->tree_item.base.rgn); - dcc_add_stream_agent_clip(dcc, agent); + dcc_stream_agent_clip(dcc, agent); } #ifdef STREAM_STATS agent->stats.num_input_frames++; @@ -933,7 +933,7 @@ static void dcc_detach_stream_gracefully(DisplayChannelClient *dcc, /* stopping the client from playing older frames at once*/ region_clear(&agent->clip); - dcc_add_stream_agent_clip(dcc, agent); + dcc_stream_agent_clip(dcc, agent); if (region_is_empty(&agent->vis_region)) { spice_debug("stream %d: vis region empty", stream_id); -- 2.4.3 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel