diff options
Diffstat (limited to 'server/display-channel.c')
-rw-r--r-- | server/display-channel.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/server/display-channel.c b/server/display-channel.c index 5e750193..d54f7256 100644 --- a/server/display-channel.c +++ b/server/display-channel.c @@ -1261,3 +1261,159 @@ void drawable_draw(DisplayChannel *display, Drawable *drawable) spice_warning("invalid type"); } } + +static void surface_update_dest(RedSurface *surface, const SpiceRect *area) +{ + SpiceCanvas *canvas = surface->context.canvas; + int stride = surface->context.stride; + uint8_t *line_0 = surface->context.line_0; + + if (surface->context.canvas_draws_on_surface) + return; + + int h = area->bottom - area->top; + if (h == 0) + return; + + spice_return_if_fail(stride < 0); + + uint8_t *dest = line_0 + (area->top * stride) + area->left * sizeof(uint32_t); + dest += (h - 1) * stride; + canvas->ops->read_bits(canvas, dest, -stride, area); +} + +/* + * Renders drawables for updating the requested area, but only drawables that are older + * than 'last' (exclusive). + * FIXME: merge with display_channel_draw()? + */ +void display_channel_draw_till(DisplayChannel *display, const SpiceRect *area, int surface_id, + Drawable *last) +{ + RedSurface *surface; + Drawable *surface_last = NULL; + Ring *ring; + RingItem *ring_item; + Drawable *now; + QRegion rgn; + + spice_assert(last); + spice_assert(ring_item_is_linked(&last->list_link)); + + surface = &display->surfaces[surface_id]; + + if (surface_id != last->surface_id) { + // find the nearest older drawable from the appropriate surface + ring = &display->current_list; + ring_item = &last->list_link; + while ((ring_item = ring_next(ring, ring_item))) { + now = SPICE_CONTAINEROF(ring_item, Drawable, list_link); + if (now->surface_id == surface_id) { + surface_last = now; + break; + } + } + } else { + ring_item = ring_next(&surface->current_list, &last->surface_list_link); + if (ring_item) { + surface_last = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link); + } + } + + if (!surface_last) { + return; + } + + ring = &surface->current_list; + ring_item = &surface_last->surface_list_link; + + region_init(&rgn); + region_add(&rgn, area); + + // find the first older drawable that intersects with the area + do { + now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link); + if (region_intersects(&rgn, &now->tree_item.base.rgn)) { + surface_last = now; + break; + } + } while ((ring_item = ring_next(ring, ring_item))); + + region_destroy(&rgn); + + if (!surface_last) { + return; + } + + do { + Container *container; + + ring_item = ring_get_tail(&surface->current_list); + now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link); + now->refs++; + container = now->tree_item.base.container; + current_remove_drawable(display, now); + container_cleanup(container); + /* drawable_draw may call display_channel_draw for the surfaces 'now' depends on. Notice, + that it is valid to call display_channel_draw in this case and not display_channel_draw_till: + It is impossible that there was newer item then 'last' in one of the surfaces + that display_channel_draw is called for, Otherwise, 'now' would have already been rendered. + See the call for red_handle_depends_on_target_surface in red_process_draw */ + drawable_draw(display, now); + display_channel_drawable_unref(display, now); + } while (now != surface_last); + surface_update_dest(surface, area); +} + +void display_channel_draw(DisplayChannel *display, const SpiceRect *area, int surface_id) +{ + RedSurface *surface; + Ring *ring; + RingItem *ring_item; + QRegion rgn; + Drawable *last; + Drawable *now; + spice_debug("surface %d: area ==>", surface_id); + rect_debug(area); + + spice_return_if_fail(surface_id >= 0 && surface_id < NUM_SURFACES); + spice_return_if_fail(area); + spice_return_if_fail(area->left >= 0 && area->top >= 0 && + area->left < area->right && area->top < area->bottom); + + surface = &display->surfaces[surface_id]; + + last = NULL; + ring = &surface->current_list; + ring_item = ring; + + region_init(&rgn); + region_add(&rgn, area); + while ((ring_item = ring_next(ring, ring_item))) { + now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link); + if (region_intersects(&rgn, &now->tree_item.base.rgn)) { + last = now; + break; + } + } + region_destroy(&rgn); + + if (!last) { + surface_update_dest(surface, area); + return; + } + + do { + Container *container; + + ring_item = ring_get_tail(&surface->current_list); + now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link); + now->refs++; + container = now->tree_item.base.container; + current_remove_drawable(display, now); + container_cleanup(container); + drawable_draw(display, now); + display_channel_drawable_unref(display, now); + } while (now != last); + surface_update_dest(surface, area); +} |