summaryrefslogtreecommitdiff
path: root/src/cairo-xlib-surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-xlib-surface.c')
-rw-r--r--src/cairo-xlib-surface.c2383
1 files changed, 2383 insertions, 0 deletions
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
new file mode 100644
index 000000000..029a542b9
--- /dev/null
+++ b/src/cairo-xlib-surface.c
@@ -0,0 +1,2383 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Behdad Esfahbod <behdad@behdad.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+/* Heed well the words of Owen Taylor:
+ * "Any patch that works around a render bug, or claims to, without a
+ * specific reference to the bug filed in bugzilla.freedesktop.org will
+ * never pass approval."
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-damage-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-pattern-private.h"
+#include "cairo-pixman-private.h"
+#include "cairo-region-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+#include <X11/Xutil.h> /* for XDestroyImage */
+
+#include <X11/extensions/XShm.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#define XLIB_COORD_MAX 32767
+
+#define DEBUG 0
+
+#if DEBUG
+#define UNSUPPORTED(reason) \
+ fprintf (stderr, \
+ "cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \
+ __FUNCTION__, __LINE__, reason), \
+ CAIRO_INT_STATUS_UNSUPPORTED
+#else
+#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
+#endif
+
+#if DEBUG
+#include <X11/Xlibint.h>
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_x_bread_crumb (Display *dpy,
+ const char *fmt,
+ ...)
+{
+ xReq *req;
+ char buf[2048];
+ unsigned int len, len_dwords;
+ va_list ap;
+
+ va_start (ap, fmt);
+ len = vsnprintf (buf, sizeof (buf), fmt, ap);
+ va_end (ap);
+
+ buf[len++] = '\0';
+ while (len & 3)
+ buf[len++] = '\0';
+
+ LockDisplay (dpy);
+ GetEmptyReq (NoOperation, req);
+
+ len_dwords = len >> 2;
+ SetReqLen (req, len_dwords, len_dwords);
+ Data (dpy, buf, len);
+
+ UnlockDisplay (dpy);
+ SyncHandle ();
+}
+#define X_DEBUG(x) _x_bread_crumb x
+#else
+#define X_DEBUG(x)
+#endif
+
+/**
+ * SECTION:cairo-xlib
+ * @Title: XLib Surfaces
+ * @Short_Description: X Window System rendering using XLib
+ * @See_Also: #cairo_surface_t
+ *
+ * The XLib surface is used to render cairo graphics to X Window System
+ * windows and pixmaps using the XLib library.
+ *
+ * Note that the XLib surface automatically takes advantage of X render extension
+ * if it is available.
+ **/
+
+/**
+ * CAIRO_HAS_XLIB_SURFACE:
+ *
+ * Defined if the Xlib surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.0
+ **/
+
+/**
+ * SECTION:cairo-xlib-xrender
+ * @Title: XLib-XRender Backend
+ * @Short_Description: X Window System rendering using XLib and the X Render extension
+ * @See_Also: #cairo_surface_t
+ *
+ * The XLib surface is used to render cairo graphics to X Window System
+ * windows and pixmaps using the XLib and Xrender libraries.
+ *
+ * Note that the XLib surface automatically takes advantage of X Render extension
+ * if it is available.
+ **/
+
+/**
+ * CAIRO_HAS_XLIB_XRENDER_SURFACE:
+ *
+ * Defined if the XLib/XRender surface functions are available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.6
+ **/
+
+/* Xlib doesn't define a typedef, so define one ourselves */
+typedef int (*cairo_xlib_error_func_t) (Display *display,
+ XErrorEvent *event);
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen,
+ Drawable drawable,
+ Visual *visual,
+ XRenderPictFormat *xrender_format,
+ int width,
+ int height,
+ int depth);
+
+static cairo_bool_t
+_cairo_surface_is_xlib (cairo_surface_t *surface);
+
+/*
+ * Instead of taking two round trips for each blending request,
+ * assume that if a particular drawable fails GetImage that it will
+ * fail for a "while"; use temporary pixmaps to avoid the errors
+ */
+
+#define CAIRO_ASSUME_PIXMAP 20
+
+static const XTransform identity = { {
+ { 1 << 16, 0x00000, 0x00000 },
+ { 0x00000, 1 << 16, 0x00000 },
+ { 0x00000, 0x00000, 1 << 16 },
+} };
+
+static Visual *
+_visual_for_xrender_format(Screen *screen,
+ XRenderPictFormat *xrender_format)
+{
+ int d, v;
+
+ /* XXX Consider searching through the list of known cairo_visual_t for
+ * the reverse mapping.
+ */
+
+ for (d = 0; d < screen->ndepths; d++) {
+ Depth *d_info = &screen->depths[d];
+
+ if (d_info->depth != xrender_format->depth)
+ continue;
+
+ for (v = 0; v < d_info->nvisuals; v++) {
+ Visual *visual = &d_info->visuals[v];
+
+ switch (visual->class) {
+ case TrueColor:
+ if (xrender_format->type != PictTypeDirect)
+ continue;
+ break;
+
+ case DirectColor:
+ /* Prefer TrueColor to DirectColor.
+ * (XRenderFindVisualFormat considers both TrueColor and DirectColor
+ * Visuals to match the same PictFormat.)
+ */
+ continue;
+
+ case StaticGray:
+ case GrayScale:
+ case StaticColor:
+ case PseudoColor:
+ if (xrender_format->type != PictTypeIndexed)
+ continue;
+ break;
+ }
+
+ if (xrender_format ==
+ XRenderFindVisualFormat (DisplayOfScreen(screen), visual))
+ return visual;
+ }
+ }
+
+ return NULL;
+}
+
+static cairo_content_t
+_xrender_format_to_content (XRenderPictFormat *xrender_format)
+{
+ cairo_content_t content;
+
+ /* This only happens when using a non-Render server. Let's punt
+ * and say there's no alpha here. */
+ if (xrender_format == NULL)
+ return CAIRO_CONTENT_COLOR;
+
+ content = 0;
+ if (xrender_format->direct.alphaMask)
+ content |= CAIRO_CONTENT_ALPHA;
+ if (xrender_format->direct.redMask |
+ xrender_format->direct.greenMask |
+ xrender_format->direct.blueMask)
+ content |= CAIRO_CONTENT_COLOR;
+
+ return content;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_xlib_surface_t *src = abstract_src;
+ XRenderPictFormat *xrender_format;
+ cairo_xlib_surface_t *surface;
+ cairo_xlib_display_t *display;
+ Pixmap pix;
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return NULL;
+
+ if (width == 0 || height == 0)
+ return NULL;
+
+ if (_cairo_xlib_display_acquire (src->base.device, &display))
+ return NULL;
+
+ /* If we never found an XRenderFormat or if it isn't compatible
+ * with the content being requested, then we fallback to just
+ * constructing a cairo_format_t instead, (which will fairly
+ * arbitrarily pick a visual/depth for the similar surface.
+ */
+ xrender_format = NULL;
+ if (src->xrender_format &&
+ _xrender_format_to_content (src->xrender_format) == content)
+ {
+ xrender_format = src->xrender_format;
+ }
+ if (xrender_format == NULL) {
+ xrender_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ _cairo_format_from_content (content));
+ }
+ if (xrender_format) {
+ Visual *visual;
+
+ /* We've got a compatible XRenderFormat now, which means the
+ * similar surface will match the existing surface as closely in
+ * visual/depth etc. as possible. */
+ pix = XCreatePixmap (display->display, src->drawable,
+ width, height, xrender_format->depth);
+
+ if (xrender_format == src->xrender_format)
+ visual = src->visual;
+ else
+ visual = _visual_for_xrender_format(src->screen->screen,
+ xrender_format);
+
+ surface = (cairo_xlib_surface_t *)
+ _cairo_xlib_surface_create_internal (src->screen, pix, visual,
+ xrender_format,
+ width, height,
+ xrender_format->depth);
+ }
+ else
+ {
+ Screen *screen = src->screen->screen;
+ int depth;
+
+ /* No compatible XRenderFormat, see if we can make an ordinary pixmap,
+ * so that we can still accelerate blits with XCopyArea(). */
+ if (content != CAIRO_CONTENT_COLOR) {
+ cairo_device_release (&display->base);
+ return NULL;
+ }
+
+ depth = DefaultDepthOfScreen (screen);
+
+ pix = XCreatePixmap (display->display, RootWindowOfScreen (screen),
+ width <= 0 ? 1 : width, height <= 0 ? 1 : height,
+ depth);
+
+ surface = (cairo_xlib_surface_t *)
+ _cairo_xlib_surface_create_internal (src->screen, pix,
+ DefaultVisualOfScreen (screen),
+ NULL,
+ width, height, depth);
+ }
+
+ if (likely (surface->base.status == CAIRO_STATUS_SUCCESS))
+ surface->owns_pixmap = TRUE;
+ else
+ XFreePixmap (display->display, pix);
+
+ cairo_device_release (&display->base);
+
+ return &surface->base;
+}
+
+static void
+_cairo_xlib_surface_discard_shm (cairo_xlib_surface_t *surface)
+{
+ if (surface->shm == NULL)
+ return;
+
+ /* Force the flush for an external surface */
+ if (!surface->owns_pixmap)
+ cairo_surface_flush (surface->shm);
+
+ cairo_surface_finish (surface->shm);
+ cairo_surface_destroy (surface->shm);
+ surface->shm = NULL;
+
+ _cairo_damage_destroy (surface->base.damage);
+ surface->base.damage = NULL;
+
+ surface->fallback = 0;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_finish (void *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+ cairo_xlib_display_t *display;
+
+ cairo_list_del (&surface->link);
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable));
+
+ if (surface->embedded_source.picture)
+ XRenderFreePicture (display->display, surface->embedded_source.picture);
+ if (surface->picture)
+ XRenderFreePicture (display->display, surface->picture);
+
+ _cairo_xlib_surface_discard_shm (surface);
+
+ if (surface->owns_pixmap)
+ XFreePixmap (display->display, surface->drawable);
+
+ cairo_device_release (&display->base);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ GC *gc)
+{
+ *gc = _cairo_xlib_screen_get_gc (display,
+ surface->screen,
+ surface->depth,
+ surface->drawable);
+ if (unlikely (*gc == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+_noop_error_handler (Display *display,
+ XErrorEvent *event)
+{
+ return False; /* return value is ignored */
+}
+
+static void
+_swap_ximage_2bytes (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+
+ for (j = ximage->height; j; j--) {
+ uint16_t *p = (uint16_t *) line;
+ for (i = ximage->width; i; i--) {
+ *p = bswap_16 (*p);
+ p++;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_3bytes (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+
+ for (j = ximage->height; j; j--) {
+ uint8_t *p = (uint8_t *) line;
+ for (i = ximage->width; i; i--) {
+ uint8_t tmp;
+ tmp = p[2];
+ p[2] = p[0];
+ p[0] = tmp;
+ p += 3;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_4bytes (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+
+ for (j = ximage->height; j; j--) {
+ uint32_t *p = (uint32_t *) line;
+ for (i = ximage->width; i; i--) {
+ *p = bswap_32 (*p);
+ p++;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_nibbles (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+
+ for (j = ximage->height; j; j--) {
+ uint8_t *p = (uint8_t *) line;
+ for (i = (ximage->width + 1) / 2; i; i--) {
+ *p = ((*p >> 4) & 0xf) | ((*p << 4) & ~0xf);
+ p++;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_bits (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+ int unit = ximage->bitmap_unit;
+ int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8;
+
+ for (j = ximage->height; j; j--) {
+ char *p = line;
+
+ for (i = line_bytes; i; i--) {
+ char b = *p;
+ b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
+ b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
+ b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
+ *p = b;
+
+ p++;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_to_native (XImage *ximage)
+{
+ int unit_bytes = 0;
+ int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
+
+ if (ximage->bits_per_pixel == 1 &&
+ ximage->bitmap_bit_order != native_byte_order)
+ {
+ _swap_ximage_bits (ximage);
+ if (ximage->bitmap_bit_order == ximage->byte_order)
+ return;
+ }
+
+ if (ximage->byte_order == native_byte_order)
+ return;
+
+ switch (ximage->bits_per_pixel) {
+ case 1:
+ unit_bytes = ximage->bitmap_unit / 8;
+ break;
+ case 4:
+ _swap_ximage_nibbles (ximage);
+ /* fall-through */
+ case 8:
+ case 16:
+ case 20:
+ case 24:
+ case 28:
+ case 30:
+ case 32:
+ unit_bytes = (ximage->bits_per_pixel + 7) / 8;
+ break;
+ default:
+ /* This could be hit on some rare but possible cases. */
+ ASSERT_NOT_REACHED;
+ }
+
+ switch (unit_bytes) {
+ case 1:
+ break;
+ case 2:
+ _swap_ximage_2bytes (ximage);
+ break;
+ case 3:
+ _swap_ximage_3bytes (ximage);
+ break;
+ case 4:
+ _swap_ximage_4bytes (ximage);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+}
+
+
+/* Given a mask, (with a single sequence of contiguous 1 bits), return
+ * the number of 1 bits in 'width' and the number of 0 bits to its
+ * right in 'shift'. */
+static void
+_characterize_field (uint32_t mask, int *width, int *shift)
+{
+ *width = _cairo_popcount (mask);
+ /* The final '& 31' is to force a 0 mask to result in 0 shift. */
+ *shift = _cairo_popcount ((mask - 1) & ~mask) & 31;
+}
+
+/* Convert a field of 'width' bits to 'new_width' bits with correct
+ * rounding. */
+static inline uint32_t
+_resize_field (uint32_t field, int width, int new_width)
+{
+ if (width == 0)
+ return 0;
+
+ if (width >= new_width) {
+ return field >> (width - new_width);
+ } else {
+ uint32_t result = field << (new_width - width);
+
+ while (width < new_width) {
+ result |= result >> width;
+ width <<= 1;
+ }
+ return result;
+ }
+}
+
+static inline uint32_t
+_adjust_field (uint32_t field, int adjustment)
+{
+ return MIN (255, MAX(0, (int)field + adjustment));
+}
+
+/* Given a shifted field value, (described by 'width' and 'shift),
+ * resize it 8-bits and return that value.
+ *
+ * Note that the original field value must not have any non-field bits
+ * set.
+ */
+static inline uint32_t
+_field_to_8 (uint32_t field, int width, int shift)
+{
+ return _resize_field (field >> shift, width, 8);
+}
+
+static inline uint32_t
+_field_to_8_undither (uint32_t field, int width, int shift,
+ int dither_adjustment)
+{
+ return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width);
+}
+
+/* Given an 8-bit value, convert it to a field of 'width', shift it up
+ * to 'shift, and return it. */
+static inline uint32_t
+_field_from_8 (uint32_t field, int width, int shift)
+{
+ return _resize_field (field, 8, width) << shift;
+}
+
+static inline uint32_t
+_field_from_8_dither (uint32_t field, int width, int shift,
+ int8_t dither_adjustment)
+{
+ return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift);
+}
+
+static inline uint32_t
+_pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info,
+ uint32_t r, uint32_t g, uint32_t b,
+ int8_t dither_adjustment)
+{
+ if (r == g && g == b) {
+ dither_adjustment /= RAMP_SIZE;
+ return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)];
+ } else {
+ dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128];
+ return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]]
+ [visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]]
+ [visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]];
+ }
+}
+
+static inline uint32_t
+_pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info,
+ uint32_t pixel)
+{
+ uint32_t r, g, b;
+ pixel &= 0xff;
+ r = visual_info->colors[pixel].r;
+ g = visual_info->colors[pixel].g;
+ b = visual_info->colors[pixel].b;
+ return (r << 16) |
+ (g << 8) |
+ (b );
+}
+
+/* should range from -128 to 127 */
+#define X 16
+static const int8_t dither_pattern[4][4] = {
+ {-8*X, +0*X, -6*X, +2*X},
+ {+4*X, -4*X, +6*X, -2*X},
+ {-5*X, +4*X, -7*X, +1*X},
+ {+7*X, -1*X, +5*X, -3*X}
+};
+#undef X
+
+static int bits_per_pixel(cairo_xlib_surface_t *surface)
+{
+ if (surface->depth > 16)
+ return 32;
+ else if (surface->depth > 8)
+ return 16;
+ else if (surface->depth > 1)
+ return 8;
+ else
+ return 1;
+}
+
+pixman_format_code_t
+_pixman_format_for_xlib_surface (cairo_xlib_surface_t *surface)
+{
+ cairo_format_masks_t masks;
+ pixman_format_code_t format;
+
+ masks.bpp = bits_per_pixel (surface);
+ masks.alpha_mask = surface->a_mask;
+ masks.red_mask = surface->r_mask;
+ masks.green_mask = surface->g_mask;
+ masks.blue_mask = surface->b_mask;
+ if (! _pixman_format_from_masks (&masks, &format))
+ return 0;
+
+ return format;
+}
+
+static cairo_surface_t *
+_get_image_surface (cairo_xlib_surface_t *surface,
+ const cairo_rectangle_int_t *extents,
+ int try_shm)
+{
+ cairo_int_status_t status;
+ cairo_image_surface_t *image = NULL;
+ XImage *ximage;
+ pixman_format_code_t pixman_format;
+ cairo_xlib_display_t *display;
+
+ assert (extents->x >= 0);
+ assert (extents->y >= 0);
+ assert (extents->x + extents->width <= surface->width);
+ assert (extents->y + extents->height <= surface->height);
+
+ if (surface->base.is_clear ||
+ (surface->base.serial == 0 && surface->owns_pixmap))
+ {
+ pixman_format = _pixman_format_for_xlib_surface (surface);
+ if (pixman_format)
+ {
+ return _cairo_image_surface_create_with_pixman_format (NULL,
+ pixman_format,
+ extents->width,
+ extents->height,
+ 0);
+ }
+ }
+
+ if (surface->shm) {
+ cairo_image_surface_t *src = (cairo_image_surface_t *) surface->shm;
+ cairo_surface_t *dst;
+ cairo_surface_pattern_t pattern;
+
+ dst = cairo_image_surface_create (src->format,
+ extents->width, extents->height);
+ if (unlikely (dst->status))
+ return dst;
+
+ _cairo_pattern_init_for_surface (&pattern, &src->base);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ extents->x, extents->y);
+ status = _cairo_surface_paint (dst, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL);
+ _cairo_pattern_fini (&pattern.base);
+ if (unlikely (status)) {
+ cairo_surface_destroy (dst);
+ dst = _cairo_surface_create_in_error (status);
+ }
+
+ return dst;
+ }
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (status)
+ return _cairo_surface_create_in_error (status);
+
+ pixman_format = _pixman_format_for_xlib_surface (surface);
+ if (try_shm && pixman_format) {
+ image = (cairo_image_surface_t *)
+ _cairo_xlib_surface_create_shm__image (surface, pixman_format,
+ extents->width, extents->height);
+ if (image && image->base.status == CAIRO_STATUS_SUCCESS) {
+ cairo_xlib_error_func_t old_handler;
+ XImage shm_image;
+ Bool success;
+
+ _cairo_xlib_shm_surface_get_ximage (&image->base, &shm_image);
+
+ old_handler = XSetErrorHandler (_noop_error_handler);
+ success = XShmGetImage (display->display,
+ surface->drawable,
+ &shm_image,
+ extents->x, extents->y,
+ AllPlanes);
+ XSetErrorHandler (old_handler);
+
+ if (success) {
+ cairo_device_release (&display->base);
+ return &image->base;
+ }
+
+ cairo_surface_destroy (&image->base);
+ }
+ }
+
+ if (surface->use_pixmap == 0) {
+ cairo_xlib_error_func_t old_handler;
+
+ old_handler = XSetErrorHandler (_noop_error_handler);
+
+ ximage = XGetImage (display->display,
+ surface->drawable,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ AllPlanes, ZPixmap);
+
+ XSetErrorHandler (old_handler);
+
+ /* If we get an error, the surface must have been a window,
+ * so retry with the safe code path.
+ */
+ if (!ximage)
+ surface->use_pixmap = CAIRO_ASSUME_PIXMAP;
+ } else {
+ surface->use_pixmap--;
+ ximage = NULL;
+ }
+
+ if (ximage == NULL) {
+ /* XGetImage from a window is dangerous because it can
+ * produce errors if the window is unmapped or partially
+ * outside the screen. We could check for errors and
+ * retry, but to keep things simple, we just create a
+ * temporary pixmap
+ */
+ Pixmap pixmap;
+ GC gc;
+
+ status = _cairo_xlib_surface_get_gc (display, surface, &gc);
+ if (unlikely (status))
+ goto BAIL;
+
+ pixmap = XCreatePixmap (display->display,
+ surface->drawable,
+ extents->width, extents->height,
+ surface->depth);
+ if (pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = IncludeInferiors;
+ XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
+
+ XCopyArea (display->display, surface->drawable, pixmap, gc,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ 0, 0);
+
+ gcv.subwindow_mode = ClipByChildren;
+ XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
+
+ ximage = XGetImage (display->display,
+ pixmap,
+ 0, 0,
+ extents->width, extents->height,
+ AllPlanes, ZPixmap);
+
+ XFreePixmap (display->display, pixmap);
+ }
+
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+
+ if (ximage == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+ }
+
+ _swap_ximage_to_native (ximage);
+
+ /* We can't use pixman to simply write to image if:
+ * (a) the pixels are not appropriately aligned,
+ * (b) pixman does not the pixel format, or
+ * (c) if the image is palettized and we need to convert.
+ */
+ if (pixman_format &&
+ ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 &&
+ (surface->visual == NULL || surface->visual->class == TrueColor))
+ {
+ image = (cairo_image_surface_t*)
+ _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data,
+ pixman_format,
+ ximage->width,
+ ximage->height,
+ ximage->bytes_per_line);
+ status = image->base.status;
+ if (unlikely (status))
+ goto BAIL;
+
+ /* Let the surface take ownership of the data */
+ _cairo_image_surface_assume_ownership_of_data (image);
+ ximage->data = NULL;
+ } else {
+ /* The visual we are dealing with is not supported by the
+ * standard pixman formats. So we must first convert the data
+ * to a supported format. */
+
+ cairo_format_t format;
+ unsigned char *data;
+ uint32_t *row;
+ uint32_t in_pixel, out_pixel;
+ unsigned int rowstride;
+ uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0;
+ int a_width=0, r_width=0, g_width=0, b_width=0;
+ int a_shift=0, r_shift=0, g_shift=0, b_shift=0;
+ int x, y, x0, y0, x_off, y_off;
+ cairo_xlib_visual_info_t *visual_info = NULL;
+
+ if (surface->visual == NULL || surface->visual->class == TrueColor) {
+ cairo_bool_t has_alpha;
+ cairo_bool_t has_color;
+
+ has_alpha = surface->a_mask;
+ has_color = (surface->r_mask ||
+ surface->g_mask ||
+ surface->b_mask);
+
+ if (has_color) {
+ if (has_alpha) {
+ format = CAIRO_FORMAT_ARGB32;
+ } else {
+ format = CAIRO_FORMAT_RGB24;
+ }
+ } else {
+ /* XXX: Using CAIRO_FORMAT_A8 here would be more
+ * efficient, but would require slightly different code in
+ * the image conversion to put the alpha channel values
+ * into the right place. */
+ format = CAIRO_FORMAT_ARGB32;
+ }
+
+ a_mask = surface->a_mask;
+ r_mask = surface->r_mask;
+ g_mask = surface->g_mask;
+ b_mask = surface->b_mask;
+
+ _characterize_field (a_mask, &a_width, &a_shift);
+ _characterize_field (r_mask, &r_width, &r_shift);
+ _characterize_field (g_mask, &g_width, &g_shift);
+ _characterize_field (b_mask, &b_width, &b_shift);
+
+ } else {
+ format = CAIRO_FORMAT_RGB24;
+
+ status = _cairo_xlib_screen_get_visual_info (display,
+ surface->screen,
+ surface->visual,
+ &visual_info);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ image = (cairo_image_surface_t *) cairo_image_surface_create
+ (format, ximage->width, ximage->height);
+ status = image->base.status;
+ if (unlikely (status))
+ goto BAIL;
+
+ data = cairo_image_surface_get_data (&image->base);
+ rowstride = cairo_image_surface_get_stride (&image->base) >> 2;
+ row = (uint32_t *) data;
+ x0 = extents->x + surface->base.device_transform.x0;
+ y0 = extents->y + surface->base.device_transform.y0;
+ for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern);
+ y < ximage->height;
+ y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) {
+ const int8_t *dither_row = dither_pattern[y_off];
+ for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]);
+ x < ximage->width;
+ x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) {
+ int dither_adjustment = dither_row[x_off];
+
+ in_pixel = XGetPixel (ximage, x, y);
+ if (visual_info == NULL) {
+ out_pixel = (
+ _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 |
+ _field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 |
+ _field_to_8_undither (in_pixel & g_mask, g_width, g_shift, dither_adjustment) << 8 |
+ _field_to_8_undither (in_pixel & b_mask, b_width, b_shift, dither_adjustment));
+ } else {
+ /* Undithering pseudocolor does not look better */
+ out_pixel = _pseudocolor_to_rgb888 (visual_info, in_pixel);
+ }
+ row[x] = out_pixel;
+ }
+ row += rowstride;
+ }
+ cairo_surface_mark_dirty (&image->base);
+ }
+
+ BAIL:
+ if (ximage)
+ XDestroyImage (ximage);
+
+ cairo_device_release (&display->base);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (&image->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return &image->base;
+}
+
+void
+_cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface,
+ cairo_antialias_t antialias)
+{
+ cairo_xlib_display_t *display = surface->display;
+ int precision;
+
+ if (display->force_precision != -1)
+ precision = display->force_precision;
+ else switch (antialias) {
+ default:
+ case CAIRO_ANTIALIAS_DEFAULT:
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_NONE:
+ case CAIRO_ANTIALIAS_FAST:
+ case CAIRO_ANTIALIAS_GOOD:
+ precision = PolyModeImprecise;
+ break;
+ case CAIRO_ANTIALIAS_BEST:
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ precision = PolyModePrecise;
+ break;
+ }
+
+ if (surface->precision != precision) {
+ XRenderPictureAttributes pa;
+
+ pa.poly_mode = precision;
+ XRenderChangePicture (display->display, surface->picture,
+ CPPolyMode, &pa);
+
+ surface->precision = precision;
+ }
+}
+
+void
+_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_display_t *display = surface->display;
+ XRenderPictureAttributes pa;
+ int mask = 0;
+
+ if (surface->picture)
+ return;
+
+ if (display->force_precision != -1)
+ pa.poly_mode = display->force_precision;
+ else
+ pa.poly_mode = PolyModeImprecise;
+ if (pa.poly_mode)
+ mask |= CPPolyMode;
+
+ surface->precision = pa.poly_mode;
+ surface->picture = XRenderCreatePicture (display->display,
+ surface->drawable,
+ surface->xrender_format,
+ mask, &pa);
+}
+
+cairo_status_t
+_cairo_xlib_surface_draw_image (cairo_xlib_surface_t *surface,
+ cairo_image_surface_t *image,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int dst_x,
+ int dst_y)
+{
+ cairo_xlib_display_t *display;
+ XImage ximage;
+ cairo_format_masks_t image_masks;
+ int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
+ cairo_surface_t *shm_image = NULL;
+ pixman_image_t *pixman_image = NULL;
+ cairo_status_t status;
+ cairo_bool_t own_data = FALSE;
+ cairo_bool_t is_rgb_image;
+ GC gc;
+
+ ximage.width = image->width;
+ ximage.height = image->height;
+ ximage.format = ZPixmap;
+ ximage.byte_order = native_byte_order;
+ ximage.bitmap_unit = 32; /* always for libpixman */
+ ximage.bitmap_bit_order = native_byte_order;
+ ximage.bitmap_pad = 32; /* always for libpixman */
+ ximage.depth = surface->depth;
+ ximage.red_mask = surface->r_mask;
+ ximage.green_mask = surface->g_mask;
+ ximage.blue_mask = surface->b_mask;
+ ximage.xoffset = 0;
+ ximage.obdata = NULL;
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ is_rgb_image = _pixman_format_to_masks (image->pixman_format, &image_masks);
+
+ if (is_rgb_image &&
+ (image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) &&
+ (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) &&
+ (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) &&
+ (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0))
+ {
+ int ret;
+
+ ximage.bits_per_pixel = image_masks.bpp;
+ ximage.bytes_per_line = image->stride;
+ ximage.data = (char *)image->data;
+ if (image->base.device != surface->base.device) {
+ /* If PutImage will break the image up into chunks, prefer to
+ * send it all in one pass with ShmPutImage. For larger images,
+ * it is further advantageous to reduce the number of copies,
+ * albeit at the expense of more SHM bookkeeping.
+ */
+ int max_request_size = XExtendedMaxRequestSize (display->display);
+ if (max_request_size == 0)
+ max_request_size = XMaxRequestSize (display->display);
+ if (max_request_size > 8192)
+ max_request_size = 8192;
+ if (width * height * 4 > max_request_size) {
+ shm_image = _cairo_xlib_surface_create_shm__image (surface,
+ image->pixman_format,
+ width, height);
+ if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) {
+ cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image;
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image, NULL, clone->pixman_image,
+ src_x, src_y,
+ 0, 0,
+ 0, 0,
+ width, height);
+ ximage.obdata = _cairo_xlib_shm_surface_get_obdata (shm_image);
+ ximage.data = (char *)clone->data;
+ ximage.bytes_per_line = clone->stride;
+ ximage.width = width;
+ ximage.height = height;
+ src_x = src_y = 0;
+ }
+ }
+ } else
+ ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&image->base);
+
+ ret = XInitImage (&ximage);
+ assert (ret != 0);
+ }
+ else if (surface->visual == NULL || surface->visual->class == TrueColor)
+ {
+ pixman_format_code_t intermediate_format;
+ int ret;
+
+ image_masks.alpha_mask = surface->a_mask;
+ image_masks.red_mask = surface->r_mask;
+ image_masks.green_mask = surface->g_mask;
+ image_masks.blue_mask = surface->b_mask;
+ image_masks.bpp = bits_per_pixel (surface);
+ ret = _pixman_format_from_masks (&image_masks, &intermediate_format);
+ assert (ret);
+
+ shm_image = _cairo_xlib_surface_create_shm__image (surface,
+ intermediate_format,
+ width, height);
+ if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) {
+ cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image;
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image,
+ NULL,
+ clone->pixman_image,
+ src_x, src_y,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ ximage.data = (char *) clone->data;
+ ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&clone->base);
+ ximage.bytes_per_line = clone->stride;
+ } else {
+ pixman_image = pixman_image_create_bits (intermediate_format,
+ width, height, NULL, 0);
+ if (pixman_image == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image,
+ NULL,
+ pixman_image,
+ src_x, src_y,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ ximage.data = (char *) pixman_image_get_data (pixman_image);
+ ximage.bytes_per_line = pixman_image_get_stride (pixman_image);
+ }
+
+ ximage.width = width;
+ ximage.height = height;
+ ximage.bits_per_pixel = image_masks.bpp;
+
+ ret = XInitImage (&ximage);
+ assert (ret != 0);
+
+ src_x = src_y = 0;
+ }
+ else
+ {
+ unsigned int stride, rowstride;
+ int x, y, x0, y0, x_off, y_off;
+ uint32_t in_pixel, out_pixel, *row;
+ int i_a_width=0, i_r_width=0, i_g_width=0, i_b_width=0;
+ int i_a_shift=0, i_r_shift=0, i_g_shift=0, i_b_shift=0;
+ int o_a_width=0, o_r_width=0, o_g_width=0, o_b_width=0;
+ int o_a_shift=0, o_r_shift=0, o_g_shift=0, o_b_shift=0;
+ cairo_xlib_visual_info_t *visual_info = NULL;
+ cairo_bool_t true_color;
+ int ret;
+
+ ximage.bits_per_pixel = bits_per_pixel(surface);
+ stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width,
+ ximage.bits_per_pixel);
+ ximage.bytes_per_line = stride;
+ ximage.data = _cairo_malloc_ab (stride, ximage.height);
+ if (unlikely (ximage.data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ own_data = TRUE;
+
+ ret = XInitImage (&ximage);
+ assert (ret != 0);
+
+ _characterize_field (image_masks.alpha_mask, &i_a_width, &i_a_shift);
+ _characterize_field (image_masks.red_mask , &i_r_width, &i_r_shift);
+ _characterize_field (image_masks.green_mask, &i_g_width, &i_g_shift);
+ _characterize_field (image_masks.blue_mask , &i_b_width, &i_b_shift);
+
+ true_color = surface->visual == NULL ||
+ surface->visual->class == TrueColor;
+ if (true_color) {
+ _characterize_field (surface->a_mask, &o_a_width, &o_a_shift);
+ _characterize_field (surface->r_mask, &o_r_width, &o_r_shift);
+ _characterize_field (surface->g_mask, &o_g_width, &o_g_shift);
+ _characterize_field (surface->b_mask, &o_b_width, &o_b_shift);
+ } else {
+ status = _cairo_xlib_screen_get_visual_info (display,
+ surface->screen,
+ surface->visual,
+ &visual_info);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ rowstride = image->stride >> 2;
+ row = (uint32_t *) image->data;
+ x0 = dst_x + surface->base.device_transform.x0;
+ y0 = dst_y + surface->base.device_transform.y0;
+ for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern);
+ y < ximage.height;
+ y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern))
+ {
+ const int8_t *dither_row = dither_pattern[y_off];
+
+ for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]);
+ x < ximage.width;
+ x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0]))
+ {
+ int dither_adjustment = dither_row[x_off];
+ int a, r, g, b;
+
+ if (image_masks.bpp == 1)
+ in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7)));
+ else if (image_masks.bpp <= 8)
+ in_pixel = ((uint8_t*)row)[x];
+ else if (image_masks.bpp <= 16)
+ in_pixel = ((uint16_t*)row)[x];
+ else if (image_masks.bpp <= 24)
+#ifdef WORDS_BIGENDIAN
+ in_pixel = ((uint8_t*)row)[3 * x] << 16 |
+ ((uint8_t*)row)[3 * x + 1] << 8 |
+ ((uint8_t*)row)[3 * x + 2];
+#else
+ in_pixel = ((uint8_t*)row)[3 * x] |
+ ((uint8_t*)row)[3 * x + 1] << 8 |
+ ((uint8_t*)row)[3 * x + 2] << 16;
+#endif
+ else
+ in_pixel = row[x];
+
+ /* If the incoming image has no alpha channel, then the input
+ * is opaque and the output should have the maximum alpha value.
+ * For all other channels, their absence implies 0.
+ */
+ if (image_masks.alpha_mask == 0x0)
+ a = 0xff;
+ else
+ a = _field_to_8 (in_pixel & image_masks.alpha_mask, i_a_width, i_a_shift);
+ r = _field_to_8 (in_pixel & image_masks.red_mask , i_r_width, i_r_shift);
+ g = _field_to_8 (in_pixel & image_masks.green_mask, i_g_width, i_g_shift);
+ b = _field_to_8 (in_pixel & image_masks.blue_mask , i_b_width, i_b_shift);
+
+ if (true_color) {
+ out_pixel = _field_from_8 (a, o_a_width, o_a_shift) |
+ _field_from_8_dither (r, o_r_width, o_r_shift, dither_adjustment) |
+ _field_from_8_dither (g, o_g_width, o_g_shift, dither_adjustment) |
+ _field_from_8_dither (b, o_b_width, o_b_shift, dither_adjustment);
+ } else {
+ out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment);
+ }
+
+ XPutPixel (&ximage, x, y, out_pixel);
+ }
+
+ row += rowstride;
+ }
+ }
+
+ status = _cairo_xlib_surface_get_gc (display, surface, &gc);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (ximage.obdata)
+ XShmPutImage (display->display, surface->drawable, gc, &ximage,
+ src_x, src_y, dst_x, dst_y, width, height, True);
+ else
+ XPutImage (display->display, surface->drawable, gc, &ximage,
+ src_x, src_y, dst_x, dst_y, width, height);
+
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+
+ BAIL:
+ cairo_device_release (&display->base);
+
+ if (own_data)
+ free (ximage.data);
+ if (shm_image)
+ cairo_surface_destroy (shm_image);
+ if (pixman_image)
+ pixman_image_unref (pixman_image);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_source(void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+
+ if (extents) {
+ extents->x = extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ }
+
+ return &surface->base;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_rectangle_int_t extents;
+
+ *image_extra = NULL;
+ *image_out = (cairo_image_surface_t *)
+ _cairo_xlib_surface_get_shm (abstract_surface, FALSE);
+ if (*image_out)
+ return (*image_out)->base.status;
+
+ extents.x = extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+
+ *image_out = (cairo_image_surface_t*)
+ _get_image_surface (surface, &extents, TRUE);
+ return (*image_out)->base.status;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_snapshot (void *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_rectangle_int_t extents;
+
+ extents.x = extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+
+ return _get_image_surface (surface, &extents, FALSE);
+}
+
+static void
+_cairo_xlib_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+
+ if (&image->base == surface->shm)
+ return;
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_image_surface_t *
+_cairo_xlib_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_surface_t *image;
+
+ image = _cairo_xlib_surface_get_shm (abstract_surface, FALSE);
+ if (image) {
+ assert (surface->base.damage);
+ surface->fallback++;
+ return _cairo_image_surface_map_to_image (image, extents);
+ }
+
+ image = _get_image_surface (abstract_surface, extents, TRUE);
+ cairo_surface_set_device_offset (image, -extents->x, -extents->y);
+
+ return (cairo_image_surface_t *) image;
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ if (surface->shm) {
+ cairo_rectangle_int_t r;
+
+ assert (surface->fallback);
+ assert (surface->base.damage);
+
+ r.x = image->base.device_transform_inverse.x0;
+ r.y = image->base.device_transform_inverse.y0;
+ r.width = image->width;
+ r.height = image->height;
+
+ TRACE ((stderr, "%s: adding damage (%d,%d)x(%d,%d)\n",
+ __FUNCTION__, r.x, r.y, r.width, r.height));
+ surface->shm->damage =
+ _cairo_damage_add_rectangle (surface->shm->damage, &r);
+
+ return _cairo_image_surface_unmap_image (surface->shm, image);
+ }
+
+ status = _cairo_xlib_surface_draw_image (abstract_surface, image,
+ 0, 0,
+ image->width, image->height,
+ image->base.device_transform_inverse.x0,
+ image->base.device_transform_inverse.y0);
+
+ cairo_surface_finish (&image->base);
+ cairo_surface_destroy (&image->base);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_flush (void *abstract_surface,
+ unsigned flags)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_xlib_surface_put_shm (surface);
+ if (unlikely (status))
+ return status;
+
+ surface->fallback >>= 1;
+ if (surface->shm && _cairo_xlib_shm_surface_is_idle (surface->shm))
+ _cairo_xlib_surface_discard_shm (surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_xlib_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static void
+_cairo_xlib_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+
+ *options = *_cairo_xlib_screen_get_font_options (surface->screen);
+}
+
+static inline cairo_int_status_t
+get_compositor (cairo_xlib_surface_t **surface,
+ const cairo_compositor_t **compositor)
+{
+ cairo_xlib_surface_t *s = *surface;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;;
+
+ if (s->fallback) {
+ assert (s->base.damage != NULL);
+ assert (s->shm != NULL);
+ assert (s->shm->damage != NULL);
+ if (! _cairo_xlib_shm_surface_is_active (s->shm)) {
+ *surface = (cairo_xlib_surface_t *) s->shm;
+ *compositor = ((cairo_image_surface_t *) s->shm)->compositor;
+ s->fallback++;
+ } else {
+ status = _cairo_xlib_surface_put_shm (s);
+ s->fallback = 0;
+ *compositor = s->compositor;
+ }
+ } else
+ *compositor = s->compositor;
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_paint (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_paint (compositor, &surface->base,
+ op, source,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_mask (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_mask (compositor, &surface->base,
+ op, source, mask,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_stroke (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_stroke (compositor, &surface->base,
+ op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_fill (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_fill (compositor, &surface->base,
+ op, source,
+ path, fill_rule, tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_glyphs (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_glyphs (compositor, &surface->base,
+ op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip);
+}
+
+static const cairo_surface_backend_t cairo_xlib_surface_backend = {
+ CAIRO_SURFACE_TYPE_XLIB,
+ _cairo_xlib_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_xlib_surface_create_similar,
+ _cairo_xlib_surface_create_similar_shm,
+ _cairo_xlib_surface_map_to_image,
+ _cairo_xlib_surface_unmap_image,
+
+ _cairo_xlib_surface_source,
+ _cairo_xlib_surface_acquire_source_image,
+ _cairo_xlib_surface_release_source_image,
+ _cairo_xlib_surface_snapshot,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_xlib_surface_get_extents,
+ _cairo_xlib_surface_get_font_options,
+
+ _cairo_xlib_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_xlib_surface_paint,
+ _cairo_xlib_surface_mask,
+ _cairo_xlib_surface_stroke,
+ _cairo_xlib_surface_fill,
+ NULL, /* fill-stroke */
+ _cairo_xlib_surface_glyphs,
+};
+
+/**
+ * _cairo_surface_is_xlib:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is a #cairo_xlib_surface_t
+ *
+ * Return value: True if the surface is an xlib surface
+ **/
+static cairo_bool_t
+_cairo_surface_is_xlib (cairo_surface_t *surface)
+{
+ return surface->backend == &cairo_xlib_surface_backend;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen,
+ Drawable drawable,
+ Visual *visual,
+ XRenderPictFormat *xrender_format,
+ int width,
+ int height,
+ int depth)
+{
+ cairo_xlib_surface_t *surface;
+ cairo_xlib_display_t *display;
+ cairo_status_t status;
+
+ if (depth == 0) {
+ if (xrender_format) {
+ depth = xrender_format->depth;
+
+ /* XXX find matching visual for core/dithering fallbacks? */
+ } else if (visual) {
+ Screen *scr = screen->screen;
+
+ if (visual == DefaultVisualOfScreen (scr)) {
+ depth = DefaultDepthOfScreen (scr);
+ } else {
+ int j, k;
+
+ /* This is ugly, but we have to walk over all visuals
+ * for the display to find the correct depth.
+ */
+ depth = 0;
+ for (j = 0; j < scr->ndepths; j++) {
+ Depth *d = &scr->depths[j];
+ for (k = 0; k < d->nvisuals; k++) {
+ if (&d->visuals[k] == visual) {
+ depth = d->depth;
+ goto found;
+ }
+ }
+ }
+ }
+ }
+
+ if (depth == 0)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+found:
+ ;
+ }
+
+ surface = malloc (sizeof (cairo_xlib_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ status = _cairo_xlib_display_acquire (screen->device, &display);
+ if (unlikely (status)) {
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (status));
+ }
+
+ surface->display = display;
+ if (CAIRO_RENDER_HAS_CREATE_PICTURE (display)) {
+ if (!xrender_format) {
+ if (visual) {
+ xrender_format = XRenderFindVisualFormat (display->display, visual);
+ } else if (depth == 1) {
+ xrender_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ CAIRO_FORMAT_A1);
+ }
+ }
+ }
+
+ cairo_device_release (&display->base);
+
+ _cairo_surface_init (&surface->base,
+ &cairo_xlib_surface_backend,
+ screen->device,
+ _xrender_format_to_content (xrender_format));
+
+ surface->screen = screen;
+ surface->compositor = display->compositor;
+ surface->shm = NULL;
+ surface->fallback = 0;
+
+ surface->drawable = drawable;
+ surface->owns_pixmap = FALSE;
+ surface->use_pixmap = 0;
+ surface->width = width;
+ surface->height = height;
+
+ surface->picture = None;
+ surface->precision = PolyModePrecise;
+
+ surface->embedded_source.picture = None;
+
+ surface->visual = visual;
+ surface->xrender_format = xrender_format;
+ surface->depth = depth;
+
+ /*
+ * Compute the pixel format masks from either a XrenderFormat or
+ * else from a visual; failing that we assume the drawable is an
+ * alpha-only pixmap as it could only have been created that way
+ * through the cairo_xlib_surface_create_for_bitmap function.
+ */
+ if (xrender_format) {
+ surface->a_mask = (unsigned long)
+ surface->xrender_format->direct.alphaMask
+ << surface->xrender_format->direct.alpha;
+ surface->r_mask = (unsigned long)
+ surface->xrender_format->direct.redMask
+ << surface->xrender_format->direct.red;
+ surface->g_mask = (unsigned long)
+ surface->xrender_format->direct.greenMask
+ << surface->xrender_format->direct.green;
+ surface->b_mask = (unsigned long)
+ surface->xrender_format->direct.blueMask
+ << surface->xrender_format->direct.blue;
+ } else if (visual) {
+ surface->a_mask = 0;
+ surface->r_mask = visual->red_mask;
+ surface->g_mask = visual->green_mask;
+ surface->b_mask = visual->blue_mask;
+ } else {
+ if (depth < 32)
+ surface->a_mask = (1 << depth) - 1;
+ else
+ surface->a_mask = 0xffffffff;
+ surface->r_mask = 0;
+ surface->g_mask = 0;
+ surface->b_mask = 0;
+ }
+
+ cairo_list_add (&surface->link, &screen->surfaces);
+
+ return &surface->base;
+}
+
+static Screen *
+_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual)
+{
+ int s, d, v;
+
+ for (s = 0; s < ScreenCount (dpy); s++) {
+ Screen *screen;
+
+ screen = ScreenOfDisplay (dpy, s);
+ if (visual == DefaultVisualOfScreen (screen))
+ return screen;
+
+ for (d = 0; d < screen->ndepths; d++) {
+ Depth *depth;
+
+ depth = &screen->depths[d];
+ for (v = 0; v < depth->nvisuals; v++)
+ if (visual == &depth->visuals[v])
+ return screen;
+ }
+ }
+
+ return NULL;
+}
+
+static cairo_bool_t valid_size (int width, int height)
+{
+ /* Note: the minimum surface size allowed in the X protocol is 1x1.
+ * However, as we historically did not check the minimum size we
+ * allowed applications to lie and set the correct size later (one hopes).
+ * To preserve compatability we must allow applications to use
+ * 0x0 surfaces.
+ */
+ return (width >= 0 && width <= XLIB_COORD_MAX &&
+ height >= 0 && height <= XLIB_COORD_MAX);
+}
+
+/**
+ * cairo_xlib_surface_create:
+ * @dpy: an X Display
+ * @drawable: an X Drawable, (a Pixmap or a Window)
+ * @visual: the visual to use for drawing to @drawable. The depth
+ * of the visual must match the depth of the drawable.
+ * Currently, only TrueColor visuals are fully supported.
+ * @width: the current width of @drawable.
+ * @height: the current height of @drawable.
+ *
+ * Creates an Xlib surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided visual.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xlib_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * When @drawable is a Window containing child windows then drawing to
+ * the created surface will be clipped by those child windows. When
+ * the created surface is used as a source, the contents of the
+ * children will be included.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_xlib_surface_create (Display *dpy,
+ Drawable drawable,
+ Visual *visual,
+ int width,
+ int height)
+{
+ Screen *scr;
+ cairo_xlib_screen_t *screen;
+ cairo_status_t status;
+
+ if (! valid_size (width, height)) {
+ /* you're lying, and you know it! */
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ }
+
+ scr = _cairo_xlib_screen_from_visual (dpy, visual);
+ if (scr == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+ status = _cairo_xlib_screen_get (dpy, scr, &screen);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable));
+
+ return _cairo_xlib_surface_create_internal (screen, drawable,
+ visual, NULL,
+ width, height, 0);
+}
+
+/**
+ * cairo_xlib_surface_create_for_bitmap:
+ * @dpy: an X Display
+ * @bitmap: an X Drawable, (a depth-1 Pixmap)
+ * @screen: the X Screen associated with @bitmap
+ * @width: the current width of @bitmap.
+ * @height: the current height of @bitmap.
+ *
+ * Creates an Xlib surface that draws to the given bitmap.
+ * This will be drawn to as a %CAIRO_FORMAT_A1 object.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_xlib_surface_create_for_bitmap (Display *dpy,
+ Pixmap bitmap,
+ Screen *scr,
+ int width,
+ int height)
+{
+ cairo_xlib_screen_t *screen;
+ cairo_status_t status;
+
+ if (! valid_size (width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ status = _cairo_xlib_screen_get (dpy, scr, &screen);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap));
+
+ return _cairo_xlib_surface_create_internal (screen, bitmap,
+ NULL, NULL,
+ width, height, 1);
+}
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+/**
+ * cairo_xlib_surface_create_with_xrender_format:
+ * @dpy: an X Display
+ * @drawable: an X Drawable, (a Pixmap or a Window)
+ * @screen: the X Screen associated with @drawable
+ * @format: the picture format to use for drawing to @drawable. The depth
+ * of @format must match the depth of the drawable.
+ * @width: the current width of @drawable.
+ * @height: the current height of @drawable.
+ *
+ * Creates an Xlib surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided picture format.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xlib_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_xlib_surface_create_with_xrender_format (Display *dpy,
+ Drawable drawable,
+ Screen *scr,
+ XRenderPictFormat *format,
+ int width,
+ int height)
+{
+ cairo_xlib_screen_t *screen;
+ cairo_status_t status;
+
+ if (! valid_size (width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ status = _cairo_xlib_screen_get (dpy, scr, &screen);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable));
+
+ return _cairo_xlib_surface_create_internal (screen, drawable,
+ _visual_for_xrender_format (scr, format),
+ format, width, height, 0);
+}
+
+/**
+ * cairo_xlib_surface_get_xrender_format:
+ * @surface: an xlib surface
+ *
+ * Gets the X Render picture format that @surface uses for rendering with the
+ * X Render extension. If the surface was created by
+ * cairo_xlib_surface_create_with_xrender_format() originally, the return
+ * value is the format passed to that constructor.
+ *
+ * Return value: the XRenderPictFormat* associated with @surface,
+ * or %NULL if the surface is not an xlib surface
+ * or if the X Render extension is not available.
+ *
+ * Since: 1.6
+ **/
+XRenderPictFormat *
+cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface)
+{
+ cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface;
+
+ /* Throw an error for a non-xlib surface */
+ if (! _cairo_surface_is_xlib (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return xlib_surface->xrender_format;
+}
+#endif
+
+/**
+ * cairo_xlib_surface_set_size:
+ * @surface: a #cairo_surface_t for the XLib backend
+ * @width: the new width of the surface
+ * @height: the new height of the surface
+ *
+ * Informs cairo of the new size of the X Drawable underlying the
+ * surface. For a surface created for a Window (rather than a Pixmap),
+ * this function must be called each time the size of the window
+ * changes. (For a subwindow, you are normally resizing the window
+ * yourself, but for a toplevel window, it is necessary to listen for
+ * ConfigureNotify events.)
+ *
+ * A Pixmap can never change size, so it is never necessary to call
+ * this function on a surface created for a Pixmap.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface,
+ int width,
+ int height)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ if (surface->width == width && surface->height == height)
+ return;
+
+ if (! valid_size (width, height)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ return;
+ }
+
+ status = _cairo_surface_flush (abstract_surface, 0);
+ if (unlikely (status)) {
+ _cairo_surface_set_error (abstract_surface, status);
+ return;
+ }
+
+ _cairo_xlib_surface_discard_shm (surface);
+
+ surface->width = width;
+ surface->height = height;
+}
+
+/**
+ * cairo_xlib_surface_set_drawable:
+ * @surface: a #cairo_surface_t for the XLib backend
+ * @drawable: the new drawable for the surface
+ * @width: the width of the new drawable
+ * @height: the height of the new drawable
+ *
+ * Informs cairo of a new X Drawable underlying the
+ * surface. The drawable must match the display, screen
+ * and format of the existing drawable or the application
+ * will get X protocol errors and will probably terminate.
+ * No checks are done by this function to ensure this
+ * compatibility.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface,
+ Drawable drawable,
+ int width,
+ int height)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ if (! valid_size (width, height)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ return;
+ }
+
+ /* XXX: and what about this case? */
+ if (surface->owns_pixmap)
+ return;
+
+ status = _cairo_surface_flush (abstract_surface, 0);
+ if (unlikely (status)) {
+ _cairo_surface_set_error (abstract_surface, status);
+ return;
+ }
+
+ if (surface->drawable != drawable) {
+ cairo_xlib_display_t *display;
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return;
+
+ X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable));
+
+ if (surface->picture != None) {
+ XRenderFreePicture (display->display, surface->picture);
+ if (unlikely (status)) {
+ status = _cairo_surface_set_error (&surface->base, status);
+ return;
+ }
+
+ surface->picture = None;
+ }
+
+ cairo_device_release (&display->base);
+
+ surface->drawable = drawable;
+ }
+
+ if (surface->width != width || surface->height != height) {
+ _cairo_xlib_surface_discard_shm (surface);
+
+ surface->width = width;
+ surface->height = height;
+ }
+}
+
+/**
+ * cairo_xlib_surface_get_display:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the X Display for the underlying X Drawable.
+ *
+ * Return value: the display.
+ *
+ * Since: 1.2
+ **/
+Display *
+cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface)
+{
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return ((cairo_xlib_display_t *) abstract_surface->device)->display;
+}
+
+/**
+ * cairo_xlib_surface_get_drawable:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the underlying X Drawable used for the surface.
+ *
+ * Return value: the drawable.
+ *
+ * Since: 1.2
+ **/
+Drawable
+cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->drawable;
+}
+
+/**
+ * cairo_xlib_surface_get_screen:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the X Screen for the underlying X Drawable.
+ *
+ * Return value: the screen.
+ *
+ * Since: 1.2
+ **/
+Screen *
+cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return surface->screen->screen;
+}
+
+/**
+ * cairo_xlib_surface_get_visual:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Gets the X Visual associated with @surface, suitable for use with the
+ * underlying X Drawable. If @surface was created by
+ * cairo_xlib_surface_create(), the return value is the Visual passed to that
+ * constructor.
+ *
+ * Return value: the Visual or %NULL if there is no appropriate Visual for
+ * @surface.
+ *
+ * Since: 1.2
+ **/
+Visual *
+cairo_xlib_surface_get_visual (cairo_surface_t *surface)
+{
+ cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface;
+
+ if (! _cairo_surface_is_xlib (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return xlib_surface->visual;
+}
+
+/**
+ * cairo_xlib_surface_get_depth:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the number of bits used to represent each pixel value.
+ *
+ * Return value: the depth of the surface in bits.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->depth;
+}
+
+/**
+ * cairo_xlib_surface_get_width:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the width of the X Drawable underlying the surface in pixels.
+ *
+ * Return value: the width of the surface in pixels.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->width;
+}
+
+/**
+ * cairo_xlib_surface_get_height:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the height of the X Drawable underlying the surface in pixels.
+ *
+ * Return value: the height of the surface in pixels.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->height;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */