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 <otte@redhat.com>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2012-02-02 01:07:27 +00:00
parent d95172858b
commit b454db4b13
2 changed files with 58 additions and 5 deletions

View file

@ -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

View file

@ -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;