From b454db4b138872121ac003d4ba953f44e1a1dc60 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 2 Feb 2012 01:07:27 +0000 Subject: [PATCH] xlib: Set IncludeInferiors when using CopyArea cairo-xlib semantics state that we copy the contents of a Window's children when we use a Window as a source in a cairo operation. This requires that we set the IncludeInferiors SubwindowMode on the GC. However, we can only set one SubwindowMode for an operation and our semantics are that drawing performed by cairo onto a Window are clipped by its children (the ClipByChildren SubwindowMode). Therefore if we have to copy between two Window, we can not use CopyArea. Furthermore, we cannot tell if an external Drawable is a Window or a Pixmap, therefore we treat all foriegn Drawables as Window. Failure here means falling back to a render path, where we can independently control the subwindow mode on the source and destination, or to a GetImage/PutImage if the xserver does not support render. Reported-by: Benjamin Otte Signed-off-by: Chris Wilson --- src/cairo-xlib-core-compositor.c | 37 ++++++++++++++++++++++++++---- src/cairo-xlib-render-compositor.c | 26 +++++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/cairo-xlib-core-compositor.c b/src/cairo-xlib-core-compositor.c index 426a79c39..f306640a8 100644 --- a/src/cairo-xlib-core-compositor.c +++ b/src/cairo-xlib-core-compositor.c @@ -359,18 +359,30 @@ copy_boxes (cairo_xlib_surface_t *dst, if (source->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_UNSUPPORTED; + /* XXX subsurface */ + pattern = (const cairo_surface_pattern_t *) source; - if (pattern->surface->type != CAIRO_SURFACE_TYPE_XLIB) + if (pattern->surface->backend->type != CAIRO_SURFACE_TYPE_XLIB) return CAIRO_INT_STATUS_UNSUPPORTED; src = (cairo_xlib_surface_t *) pattern->surface; if (src->depth != dst->depth) return CAIRO_INT_STATUS_UNSUPPORTED; - if (! _cairo_xlib_surface_same_screen (dst, src)) + /* We can only have a single control for subwindow_mode on the + * GC. If we have a Window destination, we need to set ClipByChildren, + * but if we have a Window source, we need IncludeInferiors. If we have + * both a Window destination and source, we must fallback. There is + * no convenient way to detect if a drawable is a Pixmap or Window, + * therefore we can only rely on those surfaces that we created + * ourselves to be Pixmaps, and treat everything else as a potential + * Window. + */ + if (! src->owns_pixmap && ! dst->owns_pixmap) return CAIRO_INT_STATUS_UNSUPPORTED; - /* XXX subsurface */ + if (! _cairo_xlib_surface_same_screen (dst, src)) + return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->matrix, &cb.tx, &cb.ty)) @@ -390,12 +402,27 @@ copy_boxes (cairo_xlib_surface_t *dst, if (unlikely (status)) return status; + if (! src->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = IncludeInferiors; + XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv); + } + + status = CAIRO_STATUS_SUCCESS; if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb)) - return CAIRO_INT_STATUS_UNSUPPORTED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (! src->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = ClipByChildren; + XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv); + } _cairo_xlib_surface_put_gc (dst->display, dst, cb.gc); - return CAIRO_STATUS_SUCCESS; + return status; } static cairo_status_t diff --git a/src/cairo-xlib-render-compositor.c b/src/cairo-xlib-render-compositor.c index 6ee69e1a7..0faa55766 100644 --- a/src/cairo-xlib-render-compositor.c +++ b/src/cairo-xlib-render-compositor.c @@ -164,6 +164,18 @@ copy_boxes (void *_dst, GC gc; int i, j; + /* We can only have a single control for subwindow_mode on the + * GC. If we have a Window destination, we need to set ClipByChildren, + * but if we have a Window source, we need IncludeInferiors. If we have + * both a Window destination and source, we must fallback. There is + * no convenient way to detect if a drawable is a Pixmap or Window, + * therefore we can only rely on those surfaces that we created + * ourselves to be Pixmaps, and treat everything else as a potential + * Window. + */ + if (! src->owns_pixmap && ! dst->owns_pixmap) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (! _cairo_xlib_surface_same_screen (dst, src)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -180,6 +192,13 @@ copy_boxes (void *_dst, return status; } + if (! src->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = IncludeInferiors; + XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv); + } + if (boxes->num_boxes == 1) { int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); @@ -230,6 +249,13 @@ copy_boxes (void *_dst, free (rects); } + if (! src->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = ClipByChildren; + XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv); + } + _cairo_xlib_surface_put_gc (dst->display, dst, gc); release (dst); return CAIRO_STATUS_SUCCESS;