[PATCH 11/11] worker: painfully move display_channel_add_drawable

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Marc-André Lureau <marcandre.lureau@xxxxxxxxx>

---
 server/display-channel.c | 526 ++++++++++++++++++++++++++++++++++++++++++++
 server/display-channel.h |  15 +-
 server/red_worker.c      | 559 ++---------------------------------------------
 server/stream.c          |   7 +-
 4 files changed, 556 insertions(+), 551 deletions(-)

diff --git a/server/display-channel.c b/server/display-channel.c
index 1d5d8d3..baec460 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -20,6 +20,21 @@
 
 #include "display-channel.h"
 
+static stat_time_t display_channel_stat_now(DisplayChannel *display)
+{
+#ifdef RED_WORKER_STAT
+    RedWorker *worker = COMMON_CHANNEL(display)->worker;
+
+    return stat_now(red_worker_get_clockid(worker));
+
+#else
+    return 0;
+#endif
+}
+
+#define stat_start(display, var)                                        \
+    G_GNUC_UNUSED stat_time_t var = display_channel_stat_now((display));
+
 void display_channel_compress_stats_reset(DisplayChannel *display)
 {
     spice_return_if_fail(display);
@@ -406,3 +421,514 @@ bool display_channel_surface_has_canvas(DisplayChannel *display,
 {
     return display->surfaces[surface_id].context.canvas != NULL;
 }
+
+static void streams_update_visible_region(DisplayChannel *display, Drawable *drawable)
+{
+    Ring *ring;
+    RingItem *item;
+    RingItem *dcc_ring_item, *next;
+    DisplayChannelClient *dcc;
+
+    if (!red_channel_is_connected(RED_CHANNEL(display))) {
+        return;
+    }
+
+    if (!is_primary_surface(display, drawable->surface_id)) {
+        return;
+    }
+
+    ring = &display->streams;
+    item = ring_get_head(ring);
+
+    while (item) {
+        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
+        StreamAgent *agent;
+
+        item = ring_next(ring, item);
+
+        if (stream->current == drawable) {
+            continue;
+        }
+
+        FOREACH_DCC(display, dcc_ring_item, next, dcc) {
+            agent = &dcc->stream_agents[get_stream_id(display, stream)];
+
+            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);
+            }
+        }
+    }
+}
+
+
+static void current_add_drawable(DisplayChannel *display,
+                                 Drawable *drawable, RingItem *pos)
+{
+    RedSurface *surface;
+    uint32_t surface_id = drawable->surface_id;
+
+    surface = &display->surfaces[surface_id];
+    ring_add_after(&drawable->tree_item.base.siblings_link, pos);
+    ring_add(&display->current_list, &drawable->list_link);
+    ring_add(&surface->current_list, &drawable->surface_list_link);
+    display->current_size++;
+    drawable->refs++;
+}
+
+static int current_add_equal(DisplayChannel *display, DrawItem *item, TreeItem *other)
+{
+    DrawItem *other_draw_item;
+    Drawable *drawable;
+    Drawable *other_drawable;
+
+    if (other->type != TREE_ITEM_TYPE_DRAWABLE) {
+        return FALSE;
+    }
+    other_draw_item = (DrawItem *)other;
+
+    if (item->shadow || other_draw_item->shadow || item->effect != other_draw_item->effect) {
+        return FALSE;
+    }
+
+    drawable = SPICE_CONTAINEROF(item, Drawable, tree_item);
+    other_drawable = SPICE_CONTAINEROF(other_draw_item, Drawable, tree_item);
+
+    if (item->effect == QXL_EFFECT_OPAQUE) {
+        int add_after = !!other_drawable->stream &&
+                        is_drawable_independent_from_surfaces(drawable);
+        stream_maintenance(display, drawable, other_drawable);
+        current_add_drawable(display, drawable, &other->siblings_link);
+        other_drawable->refs++;
+        current_remove_drawable(display, other_drawable);
+        if (add_after) {
+            red_pipes_add_drawable_after(display, drawable, other_drawable);
+        } else {
+            red_pipes_add_drawable(display, drawable);
+        }
+        red_pipes_remove_drawable(other_drawable);
+        display_channel_drawable_unref(display, other_drawable);
+        return TRUE;
+    }
+
+    switch (item->effect) {
+    case QXL_EFFECT_REVERT_ON_DUP:
+        if (is_same_drawable(drawable, other_drawable)) {
+
+            DisplayChannelClient *dcc;
+            DrawablePipeItem *dpi;
+            RingItem *worker_ring_item, *dpi_ring_item;
+
+            other_drawable->refs++;
+            current_remove_drawable(display, other_drawable);
+
+            /* sending the drawable to clients that already received
+             * (or will receive) other_drawable */
+            worker_ring_item = ring_get_head(&RED_CHANNEL(display)->clients);
+            dpi_ring_item = ring_get_head(&other_drawable->pipes);
+            /* dpi contains a sublist of dcc's, ordered the same */
+            while (worker_ring_item) {
+                dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
+                                        common.base.channel_link);
+                dpi = SPICE_CONTAINEROF(dpi_ring_item, DrawablePipeItem, base);
+                while (worker_ring_item && (!dpi || dcc != dpi->dcc)) {
+                    dcc_add_drawable(dcc, drawable);
+                    worker_ring_item = ring_next(&RED_CHANNEL(display)->clients,
+                                                 worker_ring_item);
+                    dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
+                                            common.base.channel_link);
+                }
+
+                if (dpi_ring_item) {
+                    dpi_ring_item = ring_next(&other_drawable->pipes, dpi_ring_item);
+                }
+                if (worker_ring_item) {
+                    worker_ring_item = ring_next(&RED_CHANNEL(display)->clients,
+                                                 worker_ring_item);
+                }
+            }
+            /* not sending other_drawable where possible */
+            red_pipes_remove_drawable(other_drawable);
+
+            display_channel_drawable_unref(display, other_drawable);
+            return TRUE;
+        }
+        break;
+    case QXL_EFFECT_OPAQUE_BRUSH:
+        if (is_same_geometry(drawable, other_drawable)) {
+            current_add_drawable(display, drawable, &other->siblings_link);
+            red_pipes_remove_drawable(other_drawable);
+            current_remove_drawable(display, other_drawable);
+            red_pipes_add_drawable(display, drawable);
+            return TRUE;
+        }
+        break;
+    case QXL_EFFECT_NOP_ON_DUP:
+        if (is_same_drawable(drawable, other_drawable)) {
+            return TRUE;
+        }
+        break;
+    }
+    return FALSE;
+}
+
+static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem *item, QRegion *rgn,
+                             Ring **top_ring, Drawable *frame_candidate)
+{
+    QRegion and_rgn;
+    stat_start(display, start_time);
+
+    region_clone(&and_rgn, rgn);
+    region_and(&and_rgn, &item->rgn);
+    if (!region_is_empty(&and_rgn)) {
+        if (IS_DRAW_ITEM(item)) {
+            DrawItem *draw = (DrawItem *)item;
+
+            if (draw->effect == QXL_EFFECT_OPAQUE) {
+                region_exclude(rgn, &and_rgn);
+            }
+
+            if (draw->shadow) {
+                Shadow *shadow;
+                int32_t x = item->rgn.extents.x1;
+                int32_t y = item->rgn.extents.y1;
+
+                region_exclude(&draw->base.rgn, &and_rgn);
+                shadow = draw->shadow;
+                region_offset(&and_rgn, shadow->base.rgn.extents.x1 - x,
+                              shadow->base.rgn.extents.y1 - y);
+                region_exclude(&shadow->base.rgn, &and_rgn);
+                region_and(&and_rgn, &shadow->on_hold);
+                if (!region_is_empty(&and_rgn)) {
+                    region_exclude(&shadow->on_hold, &and_rgn);
+                    region_or(rgn, &and_rgn);
+                    // in flat representation of current, shadow is always his owner next
+                    if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) {
+                        *top_ring = tree_item_container_items((TreeItem*)shadow, ring);
+                    }
+                }
+            } else {
+                if (frame_candidate) {
+                    Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable, tree_item);
+                    stream_maintenance(display, frame_candidate, drawable);
+                }
+                region_exclude(&draw->base.rgn, &and_rgn);
+            }
+        } else if (item->type == TREE_ITEM_TYPE_CONTAINER) {
+            region_exclude(&item->rgn, &and_rgn);
+
+            if (region_is_empty(&item->rgn)) {  //assume container removal will follow
+                Shadow *shadow;
+
+                region_exclude(rgn, &and_rgn);
+                if ((shadow = tree_item_find_shadow(item))) {
+                    region_or(rgn, &shadow->on_hold);
+                    if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) {
+                        *top_ring = tree_item_container_items((TreeItem*)shadow, ring);
+                    }
+                }
+            }
+        } else {
+            Shadow *shadow;
+
+            spice_assert(item->type == TREE_ITEM_TYPE_SHADOW);
+            shadow = (Shadow *)item;
+            region_exclude(rgn, &and_rgn);
+            region_or(&shadow->on_hold, &and_rgn);
+        }
+    }
+    region_destroy(&and_rgn);
+    stat_add(&display->__exclude_stat, start_time);
+}
+
+static void exclude_region(DisplayChannel *display, Ring *ring, RingItem *ring_item,
+                           QRegion *rgn, TreeItem **last, Drawable *frame_candidate)
+{
+    Ring *top_ring;
+    stat_start(display, start_time);
+
+    if (!ring_item) {
+        return;
+    }
+
+    top_ring = ring;
+
+    for (;;) {
+        TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem, siblings_link);
+        Container *container = now->container;
+
+        spice_assert(!region_is_empty(&now->rgn));
+
+        if (region_intersects(rgn, &now->rgn)) {
+            __exclude_region(display, ring, now, rgn, &top_ring, frame_candidate);
+
+            if (region_is_empty(&now->rgn)) {
+                spice_assert(now->type != TREE_ITEM_TYPE_SHADOW);
+                ring_item = now->siblings_link.prev;
+                current_remove(display, now);
+                if (last && *last == now) {
+                    *last = (TreeItem *)ring_next(ring, ring_item);
+                }
+            } else if (now->type == TREE_ITEM_TYPE_CONTAINER) {
+                Container *container = (Container *)now;
+                if ((ring_item = ring_get_head(&container->items))) {
+                    ring = &container->items;
+                    spice_assert(((TreeItem *)ring_item)->container);
+                    continue;
+                }
+                ring_item = &now->siblings_link;
+            }
+
+            if (region_is_empty(rgn)) {
+                stat_add(&display->exclude_stat, start_time);
+                return;
+            }
+        }
+
+        while ((last && *last == (TreeItem *)ring_item) ||
+               !(ring_item = ring_next(ring, ring_item))) {
+            if (ring == top_ring) {
+                stat_add(&display->exclude_stat, start_time);
+                return;
+            }
+            ring_item = &container->base.siblings_link;
+            container = container->base.container;
+            ring = (container) ? &container->items : top_ring;
+        }
+    }
+}
+
+static int current_add_with_shadow(DisplayChannel *display, Ring *ring, Drawable *item)
+{
+    stat_start(display, start_time);
+    ++display->add_with_shadow_count;
+
+    RedDrawable *red_drawable = item->red_drawable;
+    SpicePoint delta = {
+        .x = red_drawable->u.copy_bits.src_pos.x - red_drawable->bbox.left,
+        .y = red_drawable->u.copy_bits.src_pos.y - red_drawable->bbox.top
+    };
+
+    Shadow *shadow = shadow_new(&item->tree_item, &delta);
+    if (!shadow) {
+        stat_add(&display->add_stat, start_time);
+        return FALSE;
+    }
+    // item and his shadow must initially be placed in the same container.
+    // for now putting them on root.
+
+    // only primary surface streams are supported
+    if (is_primary_surface(display, item->surface_id)) {
+        detach_streams_behind(display, &shadow->base.rgn, NULL);
+    }
+
+    ring_add(ring, &shadow->base.siblings_link);
+    current_add_drawable(display, item, ring);
+    if (item->tree_item.effect == QXL_EFFECT_OPAQUE) {
+        QRegion exclude_rgn;
+        region_clone(&exclude_rgn, &item->tree_item.base.rgn);
+        exclude_region(display, ring, &shadow->base.siblings_link, &exclude_rgn, NULL, NULL);
+        region_destroy(&exclude_rgn);
+        streams_update_visible_region(display, item);
+    } else {
+        if (is_primary_surface(display, item->surface_id)) {
+            detach_streams_behind(display, &item->tree_item.base.rgn, item);
+        }
+    }
+    stat_add(&display->add_stat, start_time);
+    return TRUE;
+}
+
+static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable)
+{
+    DrawItem *item = &drawable->tree_item;
+    RingItem *now;
+    QRegion exclude_rgn;
+    RingItem *exclude_base = NULL;
+    stat_start(display, start_time);
+
+    spice_assert(!region_is_empty(&item->base.rgn));
+    region_init(&exclude_rgn);
+    now = ring_next(ring, ring);
+
+    while (now) {
+        TreeItem *sibling = SPICE_CONTAINEROF(now, TreeItem, siblings_link);
+        int test_res;
+
+        if (!region_bounds_intersects(&item->base.rgn, &sibling->rgn)) {
+            now = ring_next(ring, now);
+            continue;
+        }
+        test_res = region_test(&item->base.rgn, &sibling->rgn, REGION_TEST_ALL);
+        if (!(test_res & REGION_TEST_SHARED)) {
+            now = ring_next(ring, now);
+            continue;
+        } else if (sibling->type != TREE_ITEM_TYPE_SHADOW) {
+            if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) &&
+                                                   !(test_res & REGION_TEST_LEFT_EXCLUSIVE) &&
+                                                   current_add_equal(display, item, sibling)) {
+                stat_add(&display->add_stat, start_time);
+                return FALSE;
+            }
+
+            if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect == QXL_EFFECT_OPAQUE) {
+                Shadow *shadow;
+                int skip = now == exclude_base;
+
+                if ((shadow = tree_item_find_shadow(sibling))) {
+                    if (exclude_base) {
+                        TreeItem *next = sibling;
+                        exclude_region(display, ring, exclude_base, &exclude_rgn, &next, NULL);
+                        if (next != sibling) {
+                            now = next ? &next->siblings_link : NULL;
+                            exclude_base = NULL;
+                            continue;
+                        }
+                    }
+                    region_or(&exclude_rgn, &shadow->on_hold);
+                }
+                now = now->prev;
+                current_remove(display, sibling);
+                now = ring_next(ring, now);
+                if (shadow || skip) {
+                    exclude_base = now;
+                }
+                continue;
+            }
+
+            if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) && is_opaque_item(sibling)) {
+                Container *container;
+
+                if (exclude_base) {
+                    exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, NULL);
+                    region_clear(&exclude_rgn);
+                    exclude_base = NULL;
+                }
+                if (sibling->type == TREE_ITEM_TYPE_CONTAINER) {
+                    container = (Container *)sibling;
+                    ring = &container->items;
+                    item->base.container = container;
+                    now = ring_next(ring, ring);
+                    continue;
+                }
+                spice_assert(IS_DRAW_ITEM(sibling));
+                if (!DRAW_ITEM(sibling)->container_root) {
+                    container = container_new(DRAW_ITEM(sibling));
+                    if (!container) {
+                        spice_warning("create new container failed");
+                        region_destroy(&exclude_rgn);
+                        return FALSE;
+                    }
+                    item->base.container = container;
+                    ring = &container->items;
+                }
+            }
+        }
+        if (!exclude_base) {
+            exclude_base = now;
+        }
+        break;
+    }
+    if (item->effect == QXL_EFFECT_OPAQUE) {
+        region_or(&exclude_rgn, &item->base.rgn);
+        exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, drawable);
+        stream_trace_update(display, drawable);
+        streams_update_visible_region(display, drawable);
+        /*
+         * Performing the insertion after exclude_region for
+         * safety (todo: Not sure if exclude_region can affect the drawable
+         * if it is added to the tree before calling exclude_region).
+         */
+        current_add_drawable(display, drawable, ring);
+    } else {
+        /*
+         * red_detach_streams_behind can affect the current tree since it may
+         * trigger calls to update_area. Thus, the drawable should be added to the tree
+         * before calling red_detach_streams_behind
+         */
+        current_add_drawable(display, drawable, ring);
+        if (is_primary_surface(display, drawable->surface_id)) {
+            detach_streams_behind(display, &drawable->tree_item.base.rgn, drawable);
+        }
+    }
+    region_destroy(&exclude_rgn);
+    stat_add(&display->add_stat, start_time);
+    return TRUE;
+}
+
+static bool drawable_can_stream(DisplayChannel *display, Drawable *drawable)
+{
+    RedDrawable *red_drawable = drawable->red_drawable;
+    SpiceImage *image;
+
+    if (display->stream_video == SPICE_STREAM_VIDEO_OFF)
+        return FALSE;
+
+    if (!is_primary_surface(display, drawable->surface_id))
+        return FALSE;
+
+    if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE ||
+        red_drawable->type != QXL_DRAW_COPY ||
+        red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT)
+        return FALSE;
+
+    image = red_drawable->u.copy.src_bitmap;
+    if (image == NULL ||
+        image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP)
+        return FALSE;
+
+    if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) {
+        SpiceRect* rect;
+        int size;
+
+        rect = &drawable->red_drawable->u.copy.src_area;
+        size = (rect->right - rect->left) * (rect->bottom - rect->top);
+        if (size < RED_STREAM_MIN_SIZE)
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+void display_channel_print_stats(DisplayChannel *display)
+{
+#ifdef RED_WORKER_STAT
+    stat_time_t total = display->add_stat.total;
+    spice_info("add with shadow count %u",
+               display->add_with_shadow_count);
+    display->add_with_shadow_count = 0;
+    spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f",
+               display->add_stat.count,
+               stat_cpu_time_to_sec(total),
+               display->exclude_stat.count,
+               stat_cpu_time_to_sec(display->exclude_stat.total),
+               display->__exclude_stat.count,
+               stat_cpu_time_to_sec(display->__exclude_stat.total));
+    spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%",
+               (double)(total - display->exclude_stat.total) / total * 100,
+               (double)(display->exclude_stat.total) / total * 100,
+               (double)(display->exclude_stat.total -
+                        display->__exclude_stat.total) / display->exclude_stat.total * 100,
+               (double)(display->__exclude_stat.total) / display->exclude_stat.total * 100);
+    stat_reset(&display->add_stat);
+    stat_reset(&display->exclude_stat);
+    stat_reset(&display->__exclude_stat);
+#endif
+}
+
+int display_channel_add_drawable(DisplayChannel *display, Drawable *drawable)
+{
+    int ret = FALSE, surface_id = drawable->surface_id;
+    RedDrawable *red_drawable = drawable->red_drawable;
+    Ring *ring = &display->surfaces[surface_id].current;
+
+    if (has_shadow(red_drawable)) {
+        ret = current_add_with_shadow(display, ring, drawable);
+    } else {
+        drawable->streamable = drawable_can_stream(display, drawable);
+        ret = current_add(display, ring, drawable);
+    }
+
+    return ret;
+}
diff --git a/server/display-channel.h b/server/display-channel.h
index 26b4e4e..a1e5947 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -364,12 +364,12 @@ struct DisplayChannel {
     RedCompressBuf *free_compress_bufs;
 
 /* TODO: some day unify this, make it more runtime.. */
+    uint32_t add_count;
+    uint32_t add_with_shadow_count;
 #ifdef RED_WORKER_STAT
     stat_info_t add_stat;
     stat_info_t exclude_stat;
     stat_info_t __exclude_stat;
-    uint32_t add_count;
-    uint32_t add_with_shadow_count;
 #endif
 #ifdef RED_STATISTICS
     uint64_t *cache_hits_counter;
@@ -423,6 +423,8 @@ void                       display_channel_surface_unref             (DisplayCha
 bool                       display_channel_surface_has_canvas        (DisplayChannel *display,
                                                                       uint32_t surface_id);
 void                       display_channel_show_tree                 (DisplayChannel *display);
+int                        display_channel_add_drawable              (DisplayChannel *display,
+                                                                      Drawable *drawable);
 
 static inline int is_equal_path(SpicePath *path1, SpicePath *path2)
 {
@@ -539,4 +541,13 @@ static inline void region_add_clip_rects(QRegion *rgn, SpiceClipRects *data)
     }
 }
 
+void red_pipes_add_drawable(DisplayChannel *display, Drawable *drawable);
+void current_remove_drawable(DisplayChannel *display, Drawable *item);
+void red_pipes_add_drawable_after(DisplayChannel *display,
+                                  Drawable *drawable, Drawable *pos_after);
+void red_pipes_remove_drawable(Drawable *drawable);
+void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable);
+void current_remove(DisplayChannel *display, TreeItem *item);
+void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable *drawable);
+
 #endif /* DISPLAY_CHANNEL_H_ */
diff --git a/server/red_worker.c b/server/red_worker.c
index 6a29e00..7ed195d 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -492,7 +492,7 @@ static int cursor_is_connected(RedWorker *worker)
         red_channel_is_connected(RED_CHANNEL(worker->cursor_channel));
 }
 
-static void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
+void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
 {
     DrawablePipeItem *dpi;
 
@@ -501,7 +501,7 @@ static void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
     red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &dpi->dpi_pipe_item);
 }
 
-static void red_pipes_add_drawable(DisplayChannel *display, Drawable *drawable)
+void red_pipes_add_drawable(DisplayChannel *display, Drawable *drawable)
 {
     DisplayChannelClient *dcc;
     RingItem *dcc_ring_item, *next;
@@ -524,8 +524,8 @@ static void dcc_add_drawable_to_tail(DisplayChannelClient *dcc, Drawable *drawab
     red_channel_client_pipe_add_tail(RED_CHANNEL_CLIENT(dcc), &dpi->dpi_pipe_item);
 }
 
-static inline void red_pipes_add_drawable_after(DisplayChannel *display,
-                                                Drawable *drawable, Drawable *pos_after)
+void red_pipes_add_drawable_after(DisplayChannel *display,
+                                  Drawable *drawable, Drawable *pos_after)
 {
     DrawablePipeItem *dpi, *dpi_pos_after;
     RingItem *dpi_link, *dpi_next;
@@ -571,7 +571,7 @@ static inline PipeItem *red_pipe_get_tail(DisplayChannelClient *dcc)
     return (PipeItem*)ring_get_tail(&RED_CHANNEL_CLIENT(dcc)->pipe);
 }
 
-static inline void red_pipes_remove_drawable(Drawable *drawable)
+void red_pipes_remove_drawable(Drawable *drawable)
 {
     DrawablePipeItem *dpi;
     RingItem *item, *next;
@@ -816,8 +816,9 @@ static void red_flush_source_surfaces(DisplayChannel *display, Drawable *drawabl
     }
 }
 
-static inline void current_remove_drawable(DisplayChannel *display, Drawable *item)
+void current_remove_drawable(DisplayChannel *display, Drawable *item)
 {
+    /* todo: move all to unref? */
     display_stream_trace_add_drawable(display, item);
     draw_item_remove_shadow(&item->tree_item);
     ring_remove(&item->tree_item.base.siblings_link);
@@ -827,13 +828,7 @@ static inline void current_remove_drawable(DisplayChannel *display, Drawable *it
     display->current_size--;
 }
 
-static void remove_drawable(DisplayChannel *display, Drawable *drawable)
-{
-    red_pipes_remove_drawable(drawable);
-    current_remove_drawable(display, drawable);
-}
-
-static inline void current_remove(DisplayChannel *display, TreeItem *item)
+void current_remove(DisplayChannel *display, TreeItem *item)
 {
     TreeItem *now = item;
 
@@ -843,8 +838,10 @@ static inline void current_remove(DisplayChannel *display, TreeItem *item)
         RingItem *ring_item;
 
         if (now->type == TREE_ITEM_TYPE_DRAWABLE) {
+            Drawable *drawable = SPICE_CONTAINEROF(now, Drawable, tree_item);
             ring_item = now->siblings_link.prev;
-            remove_drawable(display, SPICE_CONTAINEROF(now, Drawable, tree_item));
+            red_pipes_remove_drawable(drawable);
+            current_remove_drawable(display, drawable);
         } else {
             Container *container = (Container *)now;
 
@@ -975,152 +972,6 @@ static void red_clear_surface_drawables_from_pipes(DisplayChannel *display,
     }
 }
 
-static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem *item, QRegion *rgn,
-                             Ring **top_ring, Drawable *frame_candidate)
-{
-    QRegion and_rgn;
-#ifdef RED_WORKER_STAT
-    RedWorker *worker = COMMON_CHANNEL(display)->worker;
-    stat_time_t start_time = stat_now(worker->clockid);
-#endif
-
-    region_clone(&and_rgn, rgn);
-    region_and(&and_rgn, &item->rgn);
-    if (!region_is_empty(&and_rgn)) {
-        if (IS_DRAW_ITEM(item)) {
-            DrawItem *draw = (DrawItem *)item;
-
-            if (draw->effect == QXL_EFFECT_OPAQUE) {
-                region_exclude(rgn, &and_rgn);
-            }
-
-            if (draw->shadow) {
-                Shadow *shadow;
-                int32_t x = item->rgn.extents.x1;
-                int32_t y = item->rgn.extents.y1;
-
-                region_exclude(&draw->base.rgn, &and_rgn);
-                shadow = draw->shadow;
-                region_offset(&and_rgn, shadow->base.rgn.extents.x1 - x,
-                              shadow->base.rgn.extents.y1 - y);
-                region_exclude(&shadow->base.rgn, &and_rgn);
-                region_and(&and_rgn, &shadow->on_hold);
-                if (!region_is_empty(&and_rgn)) {
-                    region_exclude(&shadow->on_hold, &and_rgn);
-                    region_or(rgn, &and_rgn);
-                    // in flat representation of current, shadow is always his owner next
-                    if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) {
-                        *top_ring = tree_item_container_items((TreeItem*)shadow, ring);
-                    }
-                }
-            } else {
-                if (frame_candidate) {
-                    Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable, tree_item);
-                    stream_maintenance(display, frame_candidate, drawable);
-                }
-                region_exclude(&draw->base.rgn, &and_rgn);
-            }
-        } else if (item->type == TREE_ITEM_TYPE_CONTAINER) {
-            region_exclude(&item->rgn, &and_rgn);
-
-            if (region_is_empty(&item->rgn)) {  //assume container removal will follow
-                Shadow *shadow;
-
-                region_exclude(rgn, &and_rgn);
-                if ((shadow = tree_item_find_shadow(item))) {
-                    region_or(rgn, &shadow->on_hold);
-                    if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) {
-                        *top_ring = tree_item_container_items((TreeItem*)shadow, ring);
-                    }
-                }
-            }
-        } else {
-            Shadow *shadow;
-
-            spice_assert(item->type == TREE_ITEM_TYPE_SHADOW);
-            shadow = (Shadow *)item;
-            region_exclude(rgn, &and_rgn);
-            region_or(&shadow->on_hold, &and_rgn);
-        }
-    }
-    region_destroy(&and_rgn);
-    stat_add(&display->__exclude_stat, start_time);
-}
-
-static void exclude_region(DisplayChannel *display, Ring *ring, RingItem *ring_item,
-                           QRegion *rgn, TreeItem **last, Drawable *frame_candidate)
-{
-#ifdef RED_WORKER_STAT
-    RedWorker *worker = COMMON_CHANNEL(display)->worker;
-    stat_time_t start_time = stat_now(worker->clockid);
-#endif
-    Ring *top_ring;
-
-    if (!ring_item) {
-        return;
-    }
-
-    top_ring = ring;
-
-    for (;;) {
-        TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem, siblings_link);
-        Container *container = now->container;
-
-        spice_assert(!region_is_empty(&now->rgn));
-
-        if (region_intersects(rgn, &now->rgn)) {
-            __exclude_region(display, ring, now, rgn, &top_ring, frame_candidate);
-
-            if (region_is_empty(&now->rgn)) {
-                spice_assert(now->type != TREE_ITEM_TYPE_SHADOW);
-                ring_item = now->siblings_link.prev;
-                current_remove(display, now);
-                if (last && *last == now) {
-                    *last = (TreeItem *)ring_next(ring, ring_item);
-                }
-            } else if (now->type == TREE_ITEM_TYPE_CONTAINER) {
-                Container *container = (Container *)now;
-                if ((ring_item = ring_get_head(&container->items))) {
-                    ring = &container->items;
-                    spice_assert(((TreeItem *)ring_item)->container);
-                    continue;
-                }
-                ring_item = &now->siblings_link;
-            }
-
-            if (region_is_empty(rgn)) {
-                stat_add(&display->exclude_stat, start_time);
-                return;
-            }
-        }
-
-        while ((last && *last == (TreeItem *)ring_item) ||
-               !(ring_item = ring_next(ring, ring_item))) {
-            if (ring == top_ring) {
-                stat_add(&display->exclude_stat, start_time);
-                return;
-            }
-            ring_item = &container->base.siblings_link;
-            container = container->base.container;
-            ring = (container) ? &container->items : top_ring;
-        }
-    }
-}
-
-static void current_add_drawable(DisplayChannel *display,
-                                 Drawable *drawable, RingItem *pos)
-{
-    RedSurface *surface;
-    uint32_t surface_id = drawable->surface_id;
-
-    surface = &display->surfaces[surface_id];
-    ring_add_after(&drawable->tree_item.base.siblings_link, pos);
-    ring_add(&display->current_list, &drawable->list_link);
-    ring_add(&surface->current_list, &drawable->surface_list_link);
-    display->current_size++;
-    drawable->refs++;
-}
-
 void detach_stream(DisplayChannel *display, Stream *stream,
                    int detach_sized)
 {
@@ -1243,7 +1094,7 @@ static void detach_stream_gracefully(DisplayChannel *display, Stream *stream,
  *           involves sending an upgrade image to the client, this drawable won't be rendered
  *           (see dcc_detach_stream_gracefully).
  */
-static void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable *drawable)
+void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable *drawable)
 {
     Ring *ring = &display->streams;
     RingItem *item = ring_get_head(ring);
@@ -1276,46 +1127,6 @@ static void detach_streams_behind(DisplayChannel *display, QRegion *region, Draw
     }
 }
 
-static void streams_update_visible_region(DisplayChannel *display, Drawable *drawable)
-{
-    Ring *ring;
-    RingItem *item;
-    RingItem *dcc_ring_item, *next;
-    DisplayChannelClient *dcc;
-
-    if (!red_channel_is_connected(RED_CHANNEL(display))) {
-        return;
-    }
-
-    if (!is_primary_surface(display, drawable->surface_id)) {
-        return;
-    }
-
-    ring = &display->streams;
-    item = ring_get_head(ring);
-
-    while (item) {
-        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
-        StreamAgent *agent;
-
-        item = ring_next(ring, item);
-
-        if (stream->current == drawable) {
-            continue;
-        }
-
-        FOREACH_DCC(display, dcc_ring_item, next, dcc) {
-            agent = &dcc->stream_agents[get_stream_id(display, stream)];
-
-            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);
-            }
-        }
-    }
-}
-
 static void display_channel_streams_timeout(DisplayChannel *display)
 {
     Ring *ring = &display->streams;
@@ -1552,350 +1363,6 @@ static void dcc_destroy_stream_agents(DisplayChannelClient *dcc)
     }
 }
 
-static inline int red_current_add_equal(DisplayChannel *display, DrawItem *item, TreeItem *other)
-{
-    DrawItem *other_draw_item;
-    Drawable *drawable;
-    Drawable *other_drawable;
-
-    if (other->type != TREE_ITEM_TYPE_DRAWABLE) {
-        return FALSE;
-    }
-    other_draw_item = (DrawItem *)other;
-
-    if (item->shadow || other_draw_item->shadow || item->effect != other_draw_item->effect) {
-        return FALSE;
-    }
-
-    drawable = SPICE_CONTAINEROF(item, Drawable, tree_item);
-    other_drawable = SPICE_CONTAINEROF(other_draw_item, Drawable, tree_item);
-
-    if (item->effect == QXL_EFFECT_OPAQUE) {
-        int add_after = !!other_drawable->stream &&
-                        is_drawable_independent_from_surfaces(drawable);
-        stream_maintenance(display, drawable, other_drawable);
-        current_add_drawable(display, drawable, &other->siblings_link);
-        other_drawable->refs++;
-        current_remove_drawable(display, other_drawable);
-        if (add_after) {
-            red_pipes_add_drawable_after(display, drawable, other_drawable);
-        } else {
-            red_pipes_add_drawable(display, drawable);
-        }
-        red_pipes_remove_drawable(other_drawable);
-        display_channel_drawable_unref(display, other_drawable);
-        return TRUE;
-    }
-
-    switch (item->effect) {
-    case QXL_EFFECT_REVERT_ON_DUP:
-        if (is_same_drawable(drawable, other_drawable)) {
-
-            DisplayChannelClient *dcc;
-            DrawablePipeItem *dpi;
-            RingItem *worker_ring_item, *dpi_ring_item;
-
-            other_drawable->refs++;
-            current_remove_drawable(display, other_drawable);
-
-            /* sending the drawable to clients that already received
-             * (or will receive) other_drawable */
-            worker_ring_item = ring_get_head(&RED_CHANNEL(display)->clients);
-            dpi_ring_item = ring_get_head(&other_drawable->pipes);
-            /* dpi contains a sublist of dcc's, ordered the same */
-            while (worker_ring_item) {
-                dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
-                                        common.base.channel_link);
-                dpi = SPICE_CONTAINEROF(dpi_ring_item, DrawablePipeItem, base);
-                while (worker_ring_item && (!dpi || dcc != dpi->dcc)) {
-                    dcc_add_drawable(dcc, drawable);
-                    worker_ring_item = ring_next(&RED_CHANNEL(display)->clients,
-                                                 worker_ring_item);
-                    dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
-                                            common.base.channel_link);
-                }
-
-                if (dpi_ring_item) {
-                    dpi_ring_item = ring_next(&other_drawable->pipes, dpi_ring_item);
-                }
-                if (worker_ring_item) {
-                    worker_ring_item = ring_next(&RED_CHANNEL(display)->clients,
-                                                 worker_ring_item);
-                }
-            }
-            /* not sending other_drawable where possible */
-            red_pipes_remove_drawable(other_drawable);
-
-            display_channel_drawable_unref(display, other_drawable);
-            return TRUE;
-        }
-        break;
-    case QXL_EFFECT_OPAQUE_BRUSH:
-        if (is_same_geometry(drawable, other_drawable)) {
-            current_add_drawable(display, drawable, &other->siblings_link);
-            remove_drawable(display, other_drawable);
-            red_pipes_add_drawable(display, drawable);
-            return TRUE;
-        }
-        break;
-    case QXL_EFFECT_NOP_ON_DUP:
-        if (is_same_drawable(drawable, other_drawable)) {
-            return TRUE;
-        }
-        break;
-    }
-    return FALSE;
-}
-
-static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable)
-{
-    DrawItem *item = &drawable->tree_item;
-#ifdef RED_WORKER_STAT
-    RedWorker *worker = COMMON_CHANNEL(display)->worker;
-    stat_time_t start_time = stat_now(worker->clockid);
-#endif
-    RingItem *now;
-    QRegion exclude_rgn;
-    RingItem *exclude_base = NULL;
-
-    spice_assert(!region_is_empty(&item->base.rgn));
-    region_init(&exclude_rgn);
-    now = ring_next(ring, ring);
-
-    while (now) {
-        TreeItem *sibling = SPICE_CONTAINEROF(now, TreeItem, siblings_link);
-        int test_res;
-
-        if (!region_bounds_intersects(&item->base.rgn, &sibling->rgn)) {
-            now = ring_next(ring, now);
-            continue;
-        }
-        test_res = region_test(&item->base.rgn, &sibling->rgn, REGION_TEST_ALL);
-        if (!(test_res & REGION_TEST_SHARED)) {
-            now = ring_next(ring, now);
-            continue;
-        } else if (sibling->type != TREE_ITEM_TYPE_SHADOW) {
-            if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) &&
-                                                   !(test_res & REGION_TEST_LEFT_EXCLUSIVE) &&
-                                                   red_current_add_equal(display, item, sibling)) {
-                stat_add(&display->add_stat, start_time);
-                return FALSE;
-            }
-
-            if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect == QXL_EFFECT_OPAQUE) {
-                Shadow *shadow;
-                int skip = now == exclude_base;
-
-                if ((shadow = tree_item_find_shadow(sibling))) {
-                    if (exclude_base) {
-                        TreeItem *next = sibling;
-                        exclude_region(display, ring, exclude_base, &exclude_rgn, &next, NULL);
-                        if (next != sibling) {
-                            now = next ? &next->siblings_link : NULL;
-                            exclude_base = NULL;
-                            continue;
-                        }
-                    }
-                    region_or(&exclude_rgn, &shadow->on_hold);
-                }
-                now = now->prev;
-                current_remove(display, sibling);
-                now = ring_next(ring, now);
-                if (shadow || skip) {
-                    exclude_base = now;
-                }
-                continue;
-            }
-
-            if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) && is_opaque_item(sibling)) {
-                Container *container;
-
-                if (exclude_base) {
-                    exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, NULL);
-                    region_clear(&exclude_rgn);
-                    exclude_base = NULL;
-                }
-                if (sibling->type == TREE_ITEM_TYPE_CONTAINER) {
-                    container = (Container *)sibling;
-                    ring = &container->items;
-                    item->base.container = container;
-                    now = ring_next(ring, ring);
-                    continue;
-                }
-                spice_assert(IS_DRAW_ITEM(sibling));
-                if (!DRAW_ITEM(sibling)->container_root) {
-                    container = container_new(DRAW_ITEM(sibling));
-                    if (!container) {
-                        spice_warning("create new container failed");
-                        region_destroy(&exclude_rgn);
-                        return FALSE;
-                    }
-                    item->base.container = container;
-                    ring = &container->items;
-                }
-            }
-        }
-        if (!exclude_base) {
-            exclude_base = now;
-        }
-        break;
-    }
-    if (item->effect == QXL_EFFECT_OPAQUE) {
-        region_or(&exclude_rgn, &item->base.rgn);
-        exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, drawable);
-        stream_trace_update(display, drawable);
-        streams_update_visible_region(display, drawable);
-        /*
-         * Performing the insertion after exclude_region for
-         * safety (todo: Not sure if exclude_region can affect the drawable
-         * if it is added to the tree before calling exclude_region).
-         */
-        current_add_drawable(display, drawable, ring);
-    } else {
-        /*
-         * red_detach_streams_behind can affect the current tree since it may
-         * trigger calls to update_area. Thus, the drawable should be added to the tree
-         * before calling red_detach_streams_behind
-         */
-        current_add_drawable(display, drawable, ring);
-        if (is_primary_surface(display, drawable->surface_id)) {
-            detach_streams_behind(display, &drawable->tree_item.base.rgn, drawable);
-        }
-    }
-    region_destroy(&exclude_rgn);
-    stat_add(&display->add_stat, start_time);
-    return TRUE;
-}
-
-static int current_add_with_shadow(DisplayChannel *display, Ring *ring, Drawable *item)
-{
-#ifdef RED_WORKER_STAT
-    RedWorker *worker = COMMON_CHANNEL(display)->worker;
-    stat_time_t start_time = stat_now(worker->clockid);
-    ++display->add_with_shadow_count;
-#endif
-
-    RedDrawable *red_drawable = item->red_drawable;
-    SpicePoint delta = {
-        .x = red_drawable->u.copy_bits.src_pos.x - red_drawable->bbox.left,
-        .y = red_drawable->u.copy_bits.src_pos.y - red_drawable->bbox.top
-    };
-
-    Shadow *shadow = shadow_new(&item->tree_item, &delta);
-    if (!shadow) {
-        stat_add(&display->add_stat, start_time);
-        return FALSE;
-    }
-    // item and his shadow must initially be placed in the same container.
-    // for now putting them on root.
-
-    // only primary surface streams are supported
-    if (is_primary_surface(display, item->surface_id)) {
-        detach_streams_behind(display, &shadow->base.rgn, NULL);
-    }
-
-    ring_add(ring, &shadow->base.siblings_link);
-    current_add_drawable(display, item, ring);
-    if (item->tree_item.effect == QXL_EFFECT_OPAQUE) {
-        QRegion exclude_rgn;
-        region_clone(&exclude_rgn, &item->tree_item.base.rgn);
-        exclude_region(display, ring, &shadow->base.siblings_link, &exclude_rgn, NULL, NULL);
-        region_destroy(&exclude_rgn);
-        streams_update_visible_region(display, item);
-    } else {
-        if (is_primary_surface(display, item->surface_id)) {
-            detach_streams_behind(display, &item->tree_item.base.rgn, item);
-        }
-    }
-    stat_add(&display->add_stat, start_time);
-    return TRUE;
-}
-
-static void drawable_update_streamable(DisplayChannel *display, Drawable *drawable)
-{
-    RedDrawable *red_drawable = drawable->red_drawable;
-    SpiceImage *image;
-
-    if (display->stream_video == SPICE_STREAM_VIDEO_OFF) {
-        return;
-    }
-
-    if (!is_primary_surface(display, drawable->surface_id)) {
-        return;
-    }
-
-    if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE ||
-        red_drawable->type != QXL_DRAW_COPY ||
-        red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT) {
-        return;
-    }
-
-    image = red_drawable->u.copy.src_bitmap;
-    if (image == NULL ||
-        image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
-        return;
-    }
-
-    if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) {
-        SpiceRect* rect;
-        int size;
-
-        rect = &drawable->red_drawable->u.copy.src_area;
-        size = (rect->right - rect->left) * (rect->bottom - rect->top);
-        if (size < RED_STREAM_MIN_SIZE) {
-            return;
-        }
-    }
-
-    drawable->streamable = TRUE;
-}
-
-void print_stats(DisplayChannel *display)
-{
-#ifdef RED_WORKER_STAT
-    stat_time_t total = display->add_stat.total;
-    spice_info("add with shadow count %u",
-               display->add_with_shadow_count);
-    display->add_with_shadow_count = 0;
-    spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f",
-               display->add_stat.count,
-               stat_cpu_time_to_sec(total),
-               display->exclude_stat.count,
-               stat_cpu_time_to_sec(display->exclude_stat.total),
-               display->__exclude_stat.count,
-               stat_cpu_time_to_sec(display->__exclude_stat.total));
-    spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%",
-               (double)(total - display->exclude_stat.total) / total * 100,
-               (double)(display->exclude_stat.total) / total * 100,
-               (double)(display->exclude_stat.total -
-                        display->__exclude_stat.total) / display->exclude_stat.total * 100,
-               (double)(display->__exclude_stat.total) / display->exclude_stat.total * 100);
-    stat_reset(&display->add_stat);
-    stat_reset(&display->exclude_stat);
-    stat_reset(&display->__exclude_stat);
-#endif
-}
-
- static int red_add_drawable(DisplayChannel *display, Drawable *drawable)
-{
-    int ret = FALSE, surface_id = drawable->surface_id;
-    RedDrawable *red_drawable = drawable->red_drawable;
-    Ring *ring = &display->surfaces[surface_id].current;
-
-    if (has_shadow(red_drawable)) {
-        ret = current_add_with_shadow(display, ring, drawable);
-    } else {
-        drawable_update_streamable(display, drawable);
-        ret = current_add(display, ring, drawable);
-    }
-
-#ifdef RED_WORKER_STAT
-    if ((++display->add_count % 100) == 0)
-        print_stats(display);
-#endif
-    return ret;
-}
-
 static void red_get_area(DisplayChannel *display, int surface_id, const SpiceRect *area,
                          uint8_t *dest, int dest_stride, int update)
 {
@@ -2193,7 +1660,7 @@ static inline void red_process_draw(RedWorker *worker, RedDrawable *red_drawable
         goto cleanup;
     }
 
-    if (red_add_drawable(worker->display_channel, drawable)) {
+    if (display_channel_add_drawable(worker->display_channel, drawable)) {
         red_pipes_add_drawable(worker->display_channel, drawable);
     }
 cleanup:
diff --git a/server/stream.c b/server/stream.c
index 54f81a8..19ffa70 100644
--- a/server/stream.c
+++ b/server/stream.c
@@ -152,12 +152,13 @@ static int is_stream_start(Drawable *drawable)
              (RED_STREAM_GRADUAL_FRAMES_START_CONDITION * drawable->frames_count)));
 }
 
-static void update_copy_graduality(DisplayChannel *display, Drawable *drawable)
+static void update_copy_graduality(Drawable *drawable)
 {
     SpiceBitmap *bitmap;
     spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY);
 
-    if (display->stream_video != SPICE_STREAM_VIDEO_FILTER) {
+    /* TODO: global property -> per dc/dcc */
+    if (streaming_video != SPICE_STREAM_VIDEO_FILTER) {
         drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID;
         return;
     }
@@ -379,7 +380,7 @@ static int stream_add_frame(DisplayChannel *display,
                             int gradual_frames_count,
                             int last_gradual_frame)
 {
-    update_copy_graduality(display, frame_drawable);
+    update_copy_graduality(frame_drawable);
     frame_drawable->frames_count = frames_count + 1;
     frame_drawable->gradual_frames_count  = gradual_frames_count;
 
-- 
2.4.3

_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/spice-devel





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]