[PATCH] [xshm] Basic infrastructure.

Chris Wilson chris at chris-wilson.co.uk
Thu Aug 14 11:26:49 PDT 2008


Introduce detection of the X Shared Memory extension for a display and a
buddy allocator for shared memory pools.

Having acquired some shared memory, we put it to use in
_get_image_surface() and _draw_image_surface() when possible.

The missing features are how to enable efficient uploading of unknown image
surfaces (i.e. clone_similar() support) which require image->data to be
created within a shared memory segment, and the ability to directly
modify the pixels representing the surface.
---
 configure.in                     |   25 ++
 src/cairo-xlib-display.c         |  734 ++++++++++++++++++++++++++++++++++++++
 src/cairo-xlib-private.h         |   38 ++
 src/cairo-xlib-surface-private.h |    3 +
 src/cairo-xlib-surface.c         |  332 ++++++++++++++++-
 5 files changed, 1115 insertions(+), 17 deletions(-)

diff --git a/configure.in b/configure.in
index d0e8bb6..a74c938 100644
--- a/configure.in
+++ b/configure.in
@@ -342,6 +342,30 @@ CAIRO_BACKEND_ENABLE(xlib, Xlib, xlib, XLIB_SURFACE, auto, [
 		     fi])
 ])
 
+#FIXME: Not strictly a backend, but a handy feature test macro
+CAIRO_BACKEND_ENABLE(xlib_xshm, X Shared Memory, xlib-xshm, XLIB_XSHM, auto, [
+  if test "x$use_xlib" != "xyes"; then
+    use_xlib_xshm="no (requires --enable-xlib)"
+  else
+    dnl Check for XShm header files if the XShm package is not installed:
+    xlib_xshm_BASE=cairo-xlib
+    xlib_xshm_REQUIRES="xext"
+    PKG_CHECK_MODULES(xlib_xshm, $xlib_xshm_REQUIRES, ,
+		      [AC_MSG_RESULT(no)
+		       xlib_xshm_REQUIRES=""
+		       old_CPPFLAGS=$CPPFLAGS
+		       CPPFLAGS="$CPPFLAGS $xlib_CFLAGS $xlib_NONPKGCONFIG_CFLAGS"
+		       AC_CHECK_HEADER(X11/extensions/XShm.h,
+				       [xlib_xshm_NONPKGCONFIG_LIBS="-lXext"],
+				       [use_xlib_xshm="no (requires Xext http://freedesktop.org/Software/xlibs)"], [
+#include <X11/Xlib.h>
+#include <X11/Xdefs.h>
+])
+		       CPPFLAGS=$old_CPPFLAGS
+		      ])
+  fi
+])
+
 CAIRO_BACKEND_ENABLE(xlib_xrender, Xlib Xrender, xlib-xrender, XLIB_XRENDER_SURFACE, auto, [
   if test "x$use_xlib" != "xyes"; then
     use_xlib_xrender="no (requires --enable-xlib)"
@@ -1119,6 +1143,7 @@ echo "  Quartz:        $use_quartz_font"
 echo ""
 echo "the following features:"
 echo "  PNG functions: $use_png"
+echo "  X Shared Memory: $use_xlib_xshm"
 echo ""
 echo "and the following debug options:"
 echo "  gcov support:  $use_gcov"
diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index 895258d..be04a59 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -39,6 +39,12 @@
 #include <fontconfig/fontconfig.h>
 
 #include <X11/Xlibint.h>	/* For XESetCloseDisplay */
+#ifdef CAIRO_HAS_XLIB_XSHM
+#include <X11/extensions/XShm.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <errno.h>
+#endif
 
 typedef int (*cairo_xlib_error_func_t) (Display     *display,
 					XErrorEvent *event);
@@ -65,6 +71,10 @@ struct _cairo_xlib_job {
 static cairo_xlib_display_t *_cairo_xlib_display_list;
 
 static void
+_cairo_xlib_display_shm_mem_pool_destroy (cairo_xlib_display_t *display,
+	                                  cairo_xlib_shm_mem_pool_t *pool);
+
+static void
 _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
 {
     cairo_xlib_screen_info_t	    *screen;
@@ -72,6 +82,7 @@ _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
 
     /* call all registered shutdown routines */
     CAIRO_MUTEX_LOCK (display->mutex);
+    display->closing = TRUE;
 
     for (screen = display->screens; screen != NULL; screen = screen->next)
 	_cairo_xlib_screen_info_close_display (screen);
@@ -99,6 +110,10 @@ _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
 
 	hooks = display->close_display_hooks;
     }
+
+    while (display->shm_pools != NULL)
+	_cairo_xlib_display_shm_mem_pool_destroy (display, display->shm_pools);
+
     display->closed = TRUE;
 
     CAIRO_MUTEX_UNLOCK (display->mutex);
@@ -150,6 +165,7 @@ _cairo_xlib_display_destroy (cairo_xlib_display_t *display)
 
 	_cairo_freelist_free (&display->wq_freelist, job);
     }
+    _cairo_freelist_fini (&display->shm_info_freelist);
     _cairo_freelist_fini (&display->wq_freelist);
     _cairo_freelist_fini (&display->hook_freelist);
 
@@ -215,6 +231,51 @@ _cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
     return 0;
 }
 
+#ifdef CAIRO_HAS_XLIB_XSHM
+static cairo_bool_t _x_error_occurred;
+static int
+_check_error_handler (Display     *display,
+		     XErrorEvent *event)
+{
+    _x_error_occurred = TRUE;
+    return False;		/* return value is ignored */
+}
+static cairo_bool_t
+can_use_shm (Display *dpy)
+{
+    XShmSegmentInfo shm;
+    cairo_xlib_error_func_t old_handler;
+    Status success;
+
+    shm.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
+    if (shm.shmid == -1)
+	return FALSE;
+
+    shm.readOnly = FALSE;
+    shm.shmaddr = shmat (shm.shmid, NULL, 0);
+    if (shm.shmaddr == (char *) -1) {
+	shmctl (shm.shmid, IPC_RMID, NULL);
+	return FALSE;
+    }
+
+    /* still holding _cairo_xlib_display_mutex */
+    _x_error_occurred = FALSE;
+    old_handler = XSetErrorHandler (_check_error_handler);
+
+    success = XShmAttach (dpy, &shm);
+    if (success)
+	XShmDetach (dpy, &shm);
+
+    XSync (dpy, False);
+    XSetErrorHandler (old_handler);
+
+    shmctl (shm.shmid, IPC_RMID, NULL);
+    shmdt (shm.shmaddr);
+
+    return success && ! _x_error_occurred;
+}
+#endif
+
 cairo_xlib_display_t *
 _cairo_xlib_display_get (Display *dpy)
 {
@@ -285,11 +346,30 @@ _cairo_xlib_display_get (Display *dpy)
     display->screens = NULL;
     display->workqueue = NULL;
     display->close_display_hooks = NULL;
+    display->closing = FALSE;
     display->closed = FALSE;
 
     memset (display->cached_xrender_formats, 0,
 	    sizeof (display->cached_xrender_formats));
 
+    display->shm_pools = NULL;
+    display->use_shm = FALSE;
+    display->have_shm_pixmaps = FALSE;
+#ifdef CAIRO_HAS_XLIB_XSHM
+    if (XShmQueryExtension (dpy)) {
+	Bool pixmaps;
+	if (XShmQueryVersion (dpy, &major_unused, &minor_unused, &pixmaps)) {
+	    display->use_shm = TRUE;
+	    if (pixmaps)
+		display->have_shm_pixmaps = XShmPixmapFormat (dpy) == ZPixmap;
+	}
+    }
+    if (display->use_shm)
+	display->use_shm = can_use_shm (dpy);
+#endif
+    _cairo_freelist_init (&display->shm_info_freelist,
+	                  sizeof (cairo_xlib_shm_info_t));
+
     display->buggy_repeat = FALSE;
     if (strstr (ServerVendor (dpy), "X.Org") != NULL) {
 	/* When modularized, the X.Org server VendorRelease was
@@ -519,3 +599,657 @@ _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t	*display,
 
     return xrender_format;
 }
+
+#ifdef CAIRO_HAS_XLIB_XSHM
+/* a simple buddy allocator for memory pools */
+/* XXX a slab allocator will have behave better, or adapt Doug Lea's malloc? */
+typedef struct _cairo_xlib_shm_mem_block cairo_xlib_shm_mem_block_t;
+struct _cairo_xlib_shm_mem_block {
+    unsigned long sync_request;
+    unsigned int bits;
+    cairo_xlib_shm_mem_block_t *prev, *next;
+};
+struct _cairo_xlib_shm_mem_pool {
+    XShmSegmentInfo shm;
+    unsigned long attached;
+
+    char *base;
+    unsigned int nBlocks;
+    cairo_xlib_shm_mem_block_t *blocks;
+    cairo_xlib_shm_mem_block_t *free[32], *free_tail[32];
+    unsigned char *map;
+
+    unsigned int min_bits;     /* Minimum block size is 1 << min_bits */
+    unsigned int num_sizes;
+
+    size_t free_bytes;
+    size_t max_bytes;
+    unsigned int max_free_bits;
+
+    cairo_xlib_shm_mem_pool_t *next;
+};
+
+#define BITTEST(p, n)  ((p)->map[(n) >> 3] &   (128 >> ((n) & 7)))
+#define BITSET(p, n)   ((p)->map[(n) >> 3] |=  (128 >> ((n) & 7)))
+#define BITCLEAR(p, n) ((p)->map[(n) >> 3] &= ~(128 >> ((n) & 7)))
+
+static void
+clear_bits (cairo_xlib_shm_mem_pool_t *pi, size_t first, size_t last)
+{
+    size_t i, n = last;
+    size_t first_full = (first + 7) & ~7;
+    size_t past_full = last & ~7;
+    size_t bytes;
+
+    if (n > first_full)
+	n = first_full;
+    for (i = first; i < n; i++)
+	  BITCLEAR (pi, i);
+
+    if (past_full > first_full) {
+	bytes = past_full - first_full;
+	bytes = bytes >> 3;
+	memset (pi->map + (first_full >> 3), 0, bytes);
+    }
+
+    if (past_full < n)
+	past_full = n;
+    for (i = past_full; i < last; i++)
+	BITCLEAR (pi, i);
+}
+
+static void
+free_bits (cairo_xlib_shm_mem_pool_t *pi,
+	   size_t start,
+	   unsigned int bits,
+	   cairo_bool_t clear)
+{
+    cairo_xlib_shm_mem_block_t *block;
+
+    if (clear)
+	clear_bits (pi, start, start + (1 << bits));
+
+    block = pi->blocks + start;
+    block->sync_request = 0;
+    block->bits = bits;
+    block->prev = pi->free_tail[bits];
+    block->next = NULL;
+    if (pi->free_tail[bits])
+	pi->free_tail[bits]->next = block;
+    else
+	pi->free[bits] = block;
+    pi->free_tail[bits] = block;
+
+    pi->free_bytes += 1 << (bits + pi->min_bits);
+    if (bits > pi->max_free_bits)
+	pi->max_free_bits = bits;
+}
+
+/* Add a chunk to the free list */
+static void
+free_blocks (cairo_xlib_shm_mem_pool_t *pi,
+	     size_t first,
+	     size_t last,
+	     cairo_bool_t clear)
+{
+    size_t i;
+    size_t bits = 0;
+    size_t len = 1;
+
+    i = first;
+    while (i < last) {
+        /* To avoid cost quadratic in the number of different
+	 * blocks produced from this chunk of store, we have to
+	 * use the size of the previous block produced from this
+	 * chunk as the starting point to work out the size of the
+	 * next block we can produce. If you look at the binary
+	 * representation of the starting points of the blocks
+	 * produced, you can see that you first of all increase the
+	 * size of the blocks produced up to some maximum as the
+	 * address dealt with gets offsets added on which zap out
+	 * low order bits, then decrease as the low order bits of the
+	 * final block produced get added in. E.g. as you go from
+	 * 001 to 0111 you generate blocks
+	 * of size 001 at 001 taking you to 010
+	 * of size 010 at 010 taking you to 100
+	 * of size 010 at 100 taking you to 110
+	 * of size 001 at 110 taking you to 111
+	 * So the maximum total cost of the loops below this comment
+	 * is one trip from the lowest blocksize to the highest and
+	 * back again.
+	 */
+	while (bits < pi->num_sizes - 1) {
+	    size_t next_bits = bits + 1;
+	    size_t next_len = len << 1;
+
+	    if (i + next_bits > last) {
+		/* off end of chunk to be freed */
+	        break;
+	    }
+
+	    if (i & (next_len - 1)) /* block would not be on boundary */
+	        break;
+
+	    bits = next_bits;
+	    len = next_len;
+	}
+
+	do{
+	    if (i + len > last) /* off end of chunk to be freed */
+	        continue;
+
+	    if (i & (len - 1)) /* block would not be on boundary */
+	        continue;
+
+	    /* OK */
+	    break;
+
+	    bits--;
+	    len >>=1;
+	} while (len > 0);
+
+	if (len == 0)
+	    break;
+
+	free_bits (pi, i, bits, clear);
+	i += len;
+    }
+}
+
+static cairo_status_t
+_cairo_xlib_shm_mem_pool_init (cairo_xlib_shm_mem_pool_t *pi,
+	                       void *base, size_t bytes,
+			       unsigned int min_bits, unsigned int num_sizes)
+{
+    size_t setBits;
+
+    assert ((((unsigned long) base) & ((1 << min_bits) - 1)) == 0);
+    assert (num_sizes < ARRAY_LENGTH (pi->free));
+
+    pi->base = base;
+    pi->free_bytes = 0;
+    pi->max_bytes = bytes;
+    pi->max_free_bits = 0;
+
+    setBits = bytes >> min_bits;
+    pi->blocks = calloc (setBits, sizeof (cairo_xlib_shm_mem_block_t));
+    if (pi->blocks == NULL)
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    pi->nBlocks = setBits;
+    pi->min_bits = min_bits;
+    pi->num_sizes = num_sizes;
+    memset (pi->free, 0, sizeof (pi->free));
+    memset (pi->free_tail, 0, sizeof (pi->free_tail));
+
+    pi->map = malloc ((setBits + 7) >> 3);
+    if (pi->map == NULL) {
+	free (pi->blocks);
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    memset (pi->map, -1, (setBits + 7) >> 3);
+    clear_bits (pi, 0, setBits);
+
+    /* Now add all blocks to the free list */
+    free_blocks (pi, 0, setBits, 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xlib_display_shm_mem_pool_fini (cairo_xlib_shm_mem_pool_t *pool)
+{
+    free (pool->map);
+    free (pool->blocks);
+}
+
+static cairo_xlib_shm_mem_block_t *
+get_buddy (cairo_xlib_shm_mem_pool_t *pi,
+	   size_t offset,
+	   unsigned int bits,
+	   unsigned long processed)
+{
+    cairo_xlib_shm_mem_block_t *block;
+
+    assert (offset + (1 << bits) <= pi->nBlocks);
+
+    if (BITTEST (pi, offset + (1 << bits) - 1))
+	return NULL; /* buddy is allocated */
+
+    block = pi->blocks + offset;
+    if (block->bits != bits)
+	return NULL; /* buddy is partially allocated */
+    if (block->sync_request > processed)
+	return NULL; /* buddy is still in use */
+
+    return block;
+}
+
+static void
+merge_buddies (cairo_xlib_shm_mem_pool_t *pi,
+	       cairo_xlib_shm_mem_block_t *block,
+	       unsigned int max_bits,
+	       unsigned long sync_request,
+	       unsigned long processed)
+{
+    size_t block_offset = block_offset = block - pi->blocks;
+    unsigned int bits = block->bits;
+    cairo_xlib_shm_mem_block_t *other;
+
+    if (sync_request < processed) {
+	while (bits < max_bits - 1) {
+	    /* while you can, merge two blocks and get a legal block size */
+	    size_t buddy_offset = block_offset ^ (1 << bits);
+
+	    block = get_buddy (pi, buddy_offset, bits, processed);
+	    if (block == NULL)
+		break;
+
+	    /* remove buddy from free lists */
+	    if (block->prev != NULL) {
+		block->prev->next = block->next;
+	    } else {
+		assert (pi->free[bits] == block);
+		pi->free[bits] = block->next;
+	    }
+	    if (block->next != NULL) {
+		block->next->prev = block->prev;
+	    } else {
+		assert (pi->free_tail[bits] == block);
+		pi->free_tail[bits] = block->prev;
+	    }
+
+	    if (buddy_offset < block_offset) {
+		/* Merged block starts at buddy */
+		if (block->sync_request > sync_request)
+		    sync_request = block->sync_request;
+		block_offset = buddy_offset;
+	    }
+
+	    bits++;
+	}
+    }
+
+    block = pi->blocks + block_offset;
+    block->bits = bits;
+    block->sync_request = sync_request;
+
+    /* add to free list (sorted by sync_request) */
+    for (other = pi->free_tail[bits]; other != NULL; other = other->prev) {
+	if (sync_request > other->sync_request)
+	    break;
+    }
+    block->next = NULL;
+    block->prev = NULL;
+    if (other != NULL) {
+	block->next = other;
+	block->prev = other->prev;
+	if (other->prev != NULL)
+	    other->prev->next = block;
+	other->prev = block;
+    }
+    if (block->prev == NULL) {
+	block->next = pi->free[bits];
+	if (pi->free[bits] != NULL)
+	    pi->free[bits]->prev = block;
+	pi->free[bits] = block;
+    }
+    if (block->next == NULL) {
+	block->prev = pi->free_tail[bits];
+	if (pi->free_tail[bits] != NULL)
+	    pi->free_tail[bits]->next = block;
+	pi->free_tail[bits] = block;
+    }
+
+    if (bits > pi->max_free_bits)
+	pi->max_free_bits = bits;
+}
+
+/* attempt to merge all available buddies up to a particular size */
+static unsigned int
+merge_bits (cairo_xlib_shm_mem_pool_t *pi,
+	    unsigned int max_bits,
+	    unsigned long processed)
+{
+    unsigned int bits;
+    cairo_xlib_shm_mem_block_t *block, *buddy, *prev;
+
+    for (bits = 0; bits < max_bits - 1; bits++) {
+	for (block = pi->free_tail[bits]; block != NULL; block = prev) {
+	    size_t buddy_offset = (block - pi->blocks) ^ (1 << bits);
+
+	    if (block->sync_request > processed)
+		break;
+
+	    prev = block->prev;
+	    buddy = get_buddy (pi, buddy_offset, bits, processed);
+	    if (buddy == NULL)
+		continue;
+
+	    if (buddy == prev)
+		prev = buddy->prev;
+
+	    /* remove block from free lists */
+	    if (block->prev != NULL) {
+		block->prev->next = block->next;
+	    } else {
+		assert (pi->free[bits] == block);
+		pi->free[bits] = block->next;
+	    }
+	    if (block->next != NULL) {
+		block->next->prev = block->prev;
+	    } else {
+		assert (pi->free_tail[bits] == block);
+		pi->free_tail[bits] = block->prev;
+	    }
+
+	    merge_buddies (pi, block, max_bits, block->sync_request, -1U);
+	}
+    }
+
+    return pi->max_free_bits;
+}
+
+
+/* find store for 1 << bits blocks */
+static void *
+buddy_malloc (cairo_xlib_shm_mem_pool_t *pi,
+	      unsigned int bits,
+	      unsigned long *sync_request)
+{
+    unsigned int b;
+    size_t offset;
+    size_t past;
+    cairo_xlib_shm_mem_block_t *block;
+    unsigned long processed = *sync_request;
+    unsigned long min_sync_request = -1UL;
+
+    if (bits > pi->max_free_bits &&
+	bits > merge_bits (pi, bits, processed))
+	return NULL;
+
+    /* Find a list with blocks big enough on it */
+    block = NULL;
+    for (b = bits; b <= pi->max_free_bits; b++) {
+	cairo_xlib_shm_mem_block_t *bb = pi->free[b];
+	if (bb != NULL) {
+	    if (bb->sync_request < min_sync_request) {
+		block = bb;
+		min_sync_request = bb->sync_request;
+		if (min_sync_request <= processed)
+		    break;
+	    }
+	}
+    }
+    assert (block != NULL);
+
+    if (block->sync_request > processed)
+	return NULL;
+
+    b = block->bits;
+    pi->free[b] = block->next;
+    if (pi->free[b] == NULL)
+	pi->free_tail[b] = NULL;
+    else
+	pi->free[b]->prev = NULL;
+
+    while (pi->free[pi->max_free_bits] == NULL) {
+	if (--pi->max_free_bits == 0)
+	    break;
+    }
+
+    /* Mark end of allocated area */
+    offset = block - pi->blocks;
+    past = offset + (1 << bits);
+    BITSET (pi, past - 1);
+    block->bits = bits;
+
+    /* If we used a larger free block than we needed, free the rest */
+    pi->free_bytes -= 1 << (b + pi->min_bits);
+    free_blocks (pi, past, offset + (1 << b), 0);
+
+    *sync_request = min_sync_request;
+    return pi->base + ((block - pi->blocks) << pi->min_bits);
+}
+
+static void *
+_cairo_xlib_shm_mem_pool_malloc (cairo_xlib_shm_mem_pool_t *pi,
+	                         size_t bytes,
+				 unsigned long *sync_request)
+{
+    unsigned int bits;
+    size_t size;
+
+    size = 1 << pi->min_bits;
+    for (bits = 0; size < bytes; bits++)
+	size <<= 1;
+    if (bits >= pi->num_sizes)
+	return NULL;
+
+    return buddy_malloc (pi, bits, sync_request);
+}
+
+static void
+_cairo_xlib_shm_mem_pool_free (cairo_xlib_shm_mem_pool_t *pi,
+	                       char *storage,
+			       unsigned long sync_request,
+			       unsigned long processed)
+{
+    size_t block_offset;
+    cairo_xlib_shm_mem_block_t *block;
+
+    block_offset = (storage - pi->base) >> pi->min_bits;
+    block = pi->blocks + block_offset;
+
+    BITCLEAR (pi, block_offset + ((1 << block->bits) - 1));
+    pi->free_bytes += 1 << (block->bits + pi->min_bits);
+
+    merge_buddies (pi, block, pi->num_sizes, sync_request, processed);
+}
+
+cairo_int_status_t
+_cairo_xlib_display_allocate_shm_info (cairo_xlib_display_t *display,
+                                       size_t size,
+				       cairo_bool_t needs_sync,
+	                               cairo_xlib_shm_info_t **shm_info_out)
+{
+    cairo_xlib_shm_info_t *shm_info;
+    Display *dpy = display->display;
+    Status success;
+    cairo_xlib_shm_mem_pool_t *pool, *next;
+    size_t bytes, maxbits = 16, minbits = 8;
+    void *mem = NULL;
+    unsigned long processed = LastKnownRequestProcessed (dpy);
+    unsigned long sync_request = needs_sync ? processed : -1UL;
+    cairo_status_t status;
+
+    assert (display->use_shm);
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    for (pool = display->shm_pools; pool != NULL; pool = next) {
+	next = pool->next;
+	if (pool->free_bytes > size) {
+	    mem = _cairo_xlib_shm_mem_pool_malloc (pool, size, &sync_request);
+	    if (mem != NULL)
+		break;
+	}
+	/* scan for old, unused pools */
+	if (pool->free_bytes == pool->max_bytes &&
+	    pool->attached < processed)
+	{
+	    _cairo_xlib_display_shm_mem_pool_destroy (display, pool);
+	}
+    }
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    if (pool == NULL) {
+	pool = malloc (sizeof (cairo_xlib_shm_mem_pool_t));
+	if (pool == NULL)
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	bytes = 1 << maxbits;
+	while (bytes <= size)
+	    bytes <<= 1, maxbits++;
+	bytes <<= 3;
+
+	pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
+	if (pool->shm.shmid == -1) {
+	    int err = errno;
+	    if (! (err == EINVAL || err == ENOMEM))
+		display->use_shm = FALSE;
+	    free (pool);
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+
+	pool->shm.readOnly = FALSE;
+	pool->shm.shmaddr = shmat (pool->shm.shmid, NULL, 0);
+	if (pool->shm.shmaddr == (char *) -1) {
+	    shmctl (pool->shm.shmid, IPC_RMID, NULL);
+	    free (pool);
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+
+	pool->attached = NextRequest (dpy);
+	success = XShmAttach (dpy, &pool->shm);
+	shmctl (pool->shm.shmid, IPC_RMID, NULL);
+
+	if (! success) {
+	    shmdt (pool->shm.shmaddr);
+	    free (pool);
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+
+	status = _cairo_xlib_shm_mem_pool_init (pool,
+		                                pool->shm.shmaddr, bytes,
+						minbits,
+						maxbits - minbits + 1);
+	if (status) {
+	    XShmDetach (dpy, &pool->shm);
+	    shmdt (pool->shm.shmaddr);
+	    free (pool);
+	    return status;
+	}
+
+	CAIRO_MUTEX_LOCK (display->mutex);
+	pool->next = display->shm_pools;
+	display->shm_pools = pool;
+
+	mem =  _cairo_xlib_shm_mem_pool_malloc (pool, size, &sync_request);
+	CAIRO_MUTEX_UNLOCK (display->mutex);
+
+	assert (mem != NULL);
+    }
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    shm_info = _cairo_freelist_alloc (&display->shm_info_freelist);
+    if (shm_info == NULL) {
+	_cairo_xlib_shm_mem_pool_free (pool, mem, sync_request, processed);
+	CAIRO_MUTEX_UNLOCK (display->mutex);
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    shm_info->display = _cairo_xlib_display_reference (display);
+    shm_info->pool = pool;
+    shm_info->shm = pool->shm;
+    shm_info->mem = mem;
+    shm_info->last_read = 0;
+    shm_info->last_write = 0;
+
+    if (processed < sync_request) {
+	for (pool = display->shm_pools; pool != NULL; pool = pool->next)
+	    merge_bits (pool, pool->num_sizes, -1U);
+
+	/* XXX force the completion of any outstanding requests */
+	XSync (dpy, False);
+	processed = LastKnownRequestProcessed (dpy);
+    }
+
+    /* scan for old, unused pools */
+    for (pool = display->shm_pools; pool != NULL; pool = next) {
+	next = pool->next;
+	if (pool->free_bytes == pool->max_bytes &&
+	    pool->attached < processed)
+	{
+	    _cairo_xlib_display_shm_mem_pool_destroy (display, pool);
+	}
+    }
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    /* XXX cairo_image_surface_create() implies that the data should be zeroed,
+     * but does anyone actually rely on that? Cairo itself doesn't. */
+    memset (mem, 0, size);
+
+    *shm_info_out = shm_info;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_xlib_shm_info_destroy (cairo_xlib_shm_info_t *shm_info)
+{
+    cairo_xlib_display_t *display = shm_info->display;
+    cairo_xlib_shm_mem_pool_t *pool, *next;
+    unsigned long sync_request, processed;
+
+    sync_request = shm_info->last_read;
+    if (shm_info->last_write > sync_request)
+	sync_request = shm_info->last_write;
+    processed = LastKnownRequestProcessed (display->display);
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    _cairo_xlib_shm_mem_pool_free (shm_info->pool,
+	                           shm_info->mem,
+				   sync_request,
+				   processed);
+    _cairo_freelist_free (&display->shm_info_freelist, shm_info);
+
+    /* scan for old, unused pools - hold at least one in reserve */
+    for (pool = display->shm_pools; pool->next != NULL; pool = next) {
+	next = pool->next;
+	if (pool->free_bytes == pool->max_bytes &&
+	    pool->attached < processed)
+	{
+	    _cairo_xlib_display_shm_mem_pool_destroy (display, pool);
+	}
+    }
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    _cairo_xlib_display_destroy (display);
+}
+
+static void
+_cairo_xlib_display_shm_mem_pool_destroy (cairo_xlib_display_t *display,
+	                                  cairo_xlib_shm_mem_pool_t *pool)
+{
+    cairo_xlib_shm_mem_pool_t **prev, *p;
+
+    if (! display->closing) {
+	Display *dpy = display->display;
+
+	/* can't call shmdt until XShmAttach has been processed */
+	if (LastKnownRequestProcessed (dpy) < pool->attached)
+	    return;
+
+	XShmDetach (dpy, &pool->shm);
+    }
+
+    for (prev = &display->shm_pools; (p = *prev); prev = &p->next) {
+	if (p == pool) {
+	    *prev = p->next;
+	    break;
+	}
+    }
+    assert (p != NULL);
+
+    shmdt (pool->shm.shmaddr);
+
+    _cairo_xlib_display_shm_mem_pool_fini (pool);
+    free (pool);
+}
+#else
+static void
+_cairo_xlib_display_shm_mem_pool_destroy (cairo_xlib_display_t *display,
+	                                  cairo_xlib_shm_mem_pool_t *pool)
+{
+}
+#endif
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 61ce889..b78bc5d 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -41,6 +41,10 @@
 #include "cairo-mutex-private.h"
 #include "cairo-reference-count-private.h"
 
+#ifdef CAIRO_HAS_XLIB_XSHM
+#include <X11/extensions/XShm.h>
+#endif
+
 typedef struct _cairo_xlib_display cairo_xlib_display_t;
 typedef struct _cairo_xlib_hook cairo_xlib_hook_t;
 typedef struct _cairo_xlib_job cairo_xlib_job_t;
@@ -53,6 +57,24 @@ struct _cairo_xlib_hook {
     void *data;
 };
 
+typedef struct _cairo_xlib_shm_mem_pool cairo_xlib_shm_mem_pool_t;
+typedef struct _cairo_xlib_shm_info	cairo_xlib_shm_info_t;
+
+struct _cairo_xlib_shm_info {
+    cairo_xlib_display_t *display;
+#ifdef CAIRO_HAS_XLIB_XSHM
+    XShmSegmentInfo shm;
+#endif
+    void *mem;
+    unsigned long last_read;
+    unsigned long last_write;
+    cairo_xlib_shm_mem_pool_t *pool;
+};
+
+cairo_private void
+_cairo_xlib_shm_destroy (cairo_xlib_display_t *display,
+			 cairo_xlib_shm_info_t *shm_info);
+
 struct _cairo_xlib_display {
     cairo_xlib_display_t *next;
     cairo_reference_count_t ref_count;
@@ -66,9 +88,15 @@ struct _cairo_xlib_display {
     cairo_xlib_job_t *workqueue;
     cairo_freelist_t wq_freelist;
 
+    cairo_xlib_shm_mem_pool_t *shm_pools;
+    cairo_freelist_t shm_info_freelist;
+
     cairo_freelist_t hook_freelist;
     cairo_xlib_hook_t *close_display_hooks;
     unsigned int buggy_repeat :1;
+    unsigned int use_shm :1;
+    unsigned int have_shm_pixmaps :1;
+    unsigned int closing :1;
     unsigned int closed :1;
 };
 
@@ -108,6 +136,7 @@ _cairo_xlib_display_get (Display *display);
 
 cairo_private cairo_xlib_display_t *
 _cairo_xlib_display_reference (cairo_xlib_display_t *info);
+
 cairo_private void
 _cairo_xlib_display_destroy (cairo_xlib_display_t *info);
 
@@ -132,6 +161,15 @@ cairo_private XRenderPictFormat *
 _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t	*display,
 	                                cairo_format_t		 format);
 
+cairo_private cairo_int_status_t
+_cairo_xlib_display_allocate_shm_info (cairo_xlib_display_t *display,
+                                       size_t size,
+				       cairo_bool_t needs_sync,
+	                               cairo_xlib_shm_info_t **shm_info_out);
+
+cairo_private void
+_cairo_xlib_shm_info_destroy (cairo_xlib_shm_info_t *shm_info);
+
 cairo_private cairo_xlib_screen_info_t *
 _cairo_xlib_screen_info_get (Display *display, Screen *screen);
 
diff --git a/src/cairo-xlib-surface-private.h b/src/cairo-xlib-surface-private.h
index d5df19c..0a4b351 100644
--- a/src/cairo-xlib-surface-private.h
+++ b/src/cairo-xlib-surface-private.h
@@ -55,6 +55,9 @@ struct _cairo_xlib_surface {
 
     int use_pixmap;
 
+    cairo_bool_t		 use_shm;
+    cairo_xlib_shm_info_t	*shm_info;
+
     int render_major;
     int render_minor;
 
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 4877e7e..e6a08e0 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -34,6 +34,7 @@
  * Contributor(s):
  *	Carl D. Worth <cworth at cworth.org>
  *	Behdad Esfahbod <behdad at behdad.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
  */
 
 #include "cairoint.h"
@@ -45,6 +46,31 @@
 
 #include <X11/Xutil.h> /* for XDestroyImage */
 
+#ifdef CAIRO_HAS_XLIB_XSHM
+#include <X11/extensions/XShm.h>
+
+/* if an image has too few pixels, skip the XShm paths */
+#define SMALL_IMAGE 1500 /* bytes */
+
+static cairo_int_status_t
+_cairo_xlib_surface_create_shm_image (cairo_xlib_surface_t *target,
+				      int width,
+				      int height,
+				      cairo_image_surface_t **image_out,
+				      cairo_xlib_shm_info_t **shm_info_out);
+
+static const cairo_user_data_key_t _cairo_xlib_shm_info_key;
+
+static void
+_cairo_xlib_shm_detach (void *data);
+#endif
+
+static void
+_cairo_xlib_surface_shm_mark_read (cairo_xlib_surface_t *surface);
+
+static void
+_cairo_xlib_surface_shm_mark_write (cairo_xlib_surface_t *surface);
+
 /* Xlib doesn't define a typedef, so define one ourselves */
 typedef int (*cairo_xlib_error_func_t) (Display     *display,
 					XErrorEvent *event);
@@ -545,6 +571,78 @@ static const int8_t dither_pattern[4][4] = {
 };
 #undef X
 
+static void
+_ximage_init_for_image_surface (XImage *ximage,
+				cairo_image_surface_t *image,
+				void *obdata)
+{
+    int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
+    cairo_format_masks_t image_masks;
+    int ret;
+
+    _pixman_format_to_masks (image->pixman_format, &image_masks);
+
+    ximage->width = image->width;
+    ximage->height = image->height;
+    ximage->format = ZPixmap;
+    ximage->data = (char *) image->data;
+    ximage->obdata = obdata;
+    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 = image->depth;
+    ximage->bytes_per_line = image->stride;
+    ximage->bits_per_pixel = image_masks.bpp;
+    ximage->red_mask = image_masks.red_mask;
+    ximage->green_mask = image_masks.green_mask;
+    ximage->blue_mask = image_masks.blue_mask;
+    ximage->xoffset = 0;
+
+    ret = XInitImage (ximage);
+    assert (ret != 0);
+}
+
+#ifdef CAIRO_HAS_XLIB_XSHM
+static cairo_int_status_t
+get_shm_image_surface (cairo_xlib_surface_t *target,
+	               int x1, int y1, int x2, int y2,
+		       cairo_image_surface_t **image_out)
+{
+    XImage ximage;
+    cairo_int_status_t status;
+    cairo_image_surface_t *image;
+    cairo_xlib_shm_info_t *shm_info;
+    cairo_xlib_error_func_t old_handler;
+    Status success;
+
+    status = _cairo_xlib_surface_create_shm_image (target,
+			                           x2 - x1, y2 - y1,
+						   &image,
+						   &shm_info);
+    if (status)
+	return status;
+
+    _ximage_init_for_image_surface (&ximage, image, &shm_info->shm);
+
+    old_handler = XSetErrorHandler (_noop_error_handler);
+
+    success = XShmGetImage (target->dpy, target->drawable,
+			    &ximage, x1, y1, AllPlanes);
+
+    XSetErrorHandler (old_handler);
+
+    if (! success) {
+	/* most probably a BadMatch from an offscreen window... */
+	cairo_surface_destroy (&image->base);
+	target->use_pixmap = CAIRO_ASSUME_PIXMAP;
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    *image_out = image;
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif
 
 static cairo_status_t
 _get_image_surface (cairo_xlib_surface_t    *surface,
@@ -594,10 +692,17 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 	image_rect->height = y2 - y1;
     }
 
-    /* XXX: This should try to use the XShm extension if available */
+#ifdef CAIRO_HAS_XLIB_XSHM
+    if (surface->use_pixmap == 0) {
+	cairo_status_t status;
 
-    if (surface->use_pixmap == 0)
-    {
+	status = get_shm_image_surface (surface, x1, y1, x2, y2, image_out);
+	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	    return status;
+    }
+#endif
+
+    if (surface->use_pixmap == 0) {
 	cairo_xlib_error_func_t old_handler;
 
 	old_handler = XSetErrorHandler (_noop_error_handler);
@@ -613,18 +718,14 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 	/* If we get an error, the surface must have been a window,
 	 * so retry with the safe code path.
 	 */
-	if (!ximage)
+	if (ximage == NULL)
 	    surface->use_pixmap = CAIRO_ASSUME_PIXMAP;
-    }
-    else
-    {
+    } else {
 	surface->use_pixmap--;
 	ximage = NULL;
     }
 
-    if (!ximage)
-    {
-
+    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
@@ -665,7 +766,7 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
     xlib_masks.blue_mask = surface->b_mask;
 
     status = _pixman_format_from_masks (&xlib_masks, &pixman_format);
-    if (xlib_masks.bpp >= 24 &&status == CAIRO_STATUS_SUCCESS) {
+    if (xlib_masks.bpp >= 24 && status == CAIRO_STATUS_SUCCESS) {
 	image = (cairo_image_surface_t*)
 	    _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data,
 							    pixman_format,
@@ -889,6 +990,53 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
     int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
     cairo_status_t status;
     cairo_bool_t own_data;
+#ifdef CAIRO_HAS_XLIB_XSHM
+    cairo_xlib_shm_info_t *shm_info;
+#endif
+
+    /* fix up image size to fit within bounds (required for XShm) */
+    if (dst_x < 0) {
+	src_x += dst_x;
+	width += dst_x;
+	dst_x = 0;
+    }
+    if (dst_x > surface->width)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (src_x < 0) {
+	width += src_x;
+	src_x = 0;
+    }
+    if (src_x > image->width)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (src_x + width > image->width)
+	width = image->width - src_x;
+    if (dst_x + width > surface->width)
+	width = surface->width - dst_x;
+
+    if (dst_y < 0) {
+	src_y += dst_y;
+	height += dst_y;
+	dst_y = 0;
+    }
+    if (dst_y > surface->height)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (src_y < 0) {
+	height += src_y;
+	src_y = 0;
+    }
+    if (src_y > image->height)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (src_y + height > image->height)
+	height = image->height - src_y;
+    if (dst_y + height > surface->height)
+	height = surface->height - dst_y;
+
+    if (width == 0 || height == 0)
+	return CAIRO_STATUS_SUCCESS;
 
     _pixman_format_to_masks (image->pixman_format, &image_masks);
 
@@ -913,7 +1061,7 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
 
 	ximage.bits_per_pixel = image_masks.bpp;
 	ximage.bytes_per_line = image->stride;
-	ximage.data = (char *)image->data;
+	ximage.data = (char *) image->data;
 	own_data = FALSE;
 
 	ret = XInitImage (&ximage);
@@ -1020,9 +1168,24 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
     if (status)
 	goto BAIL;
 
-    XPutImage(surface->dpy, surface->drawable, surface->gc,
-	      &ximage, src_x, src_y, dst_x, dst_y,
-	      width, height);
+#ifdef CAIRO_HAS_XLIB_XSHM
+    shm_info = _cairo_user_data_array_get_data (&image->base.user_data,
+						&_cairo_xlib_shm_info_key);
+    if (shm_info != NULL && ximage.data == (char *) image->data) {
+	ximage.obdata = (char *) &shm_info->shm;
+	shm_info->last_read = NextRequest (surface->dpy);
+	XShmPutImage (surface->dpy, surface->drawable, surface->gc,
+		      &ximage, src_x, src_y, dst_x, dst_y,
+		      width, height,
+		      False);
+    } else
+	/* XXX upload via XShm? */
+#endif
+    {
+	XPutImage (surface->dpy, surface->drawable, surface->gc,
+		   &ximage, src_x, src_y, dst_x, dst_y,
+		   width, height);
+    }
 
   BAIL:
     if (own_data)
@@ -1159,7 +1322,6 @@ _cairo_xlib_surface_clone_similar (void			*abstract_surface,
 	}
 
 	*clone_out = &clone->base;
-
 	return CAIRO_STATUS_SUCCESS;
     }
 
@@ -1614,6 +1776,24 @@ _render_operator (cairo_operator_t op)
     }
 }
 
+static void
+_cairo_xlib_surface_shm_mark_write (cairo_xlib_surface_t *surface)
+{
+    if (surface->shm_info == NULL)
+	return;
+
+    surface->shm_info->last_write = NextRequest (surface->dpy) - 1;
+}
+
+static void
+_cairo_xlib_surface_shm_mark_read (cairo_xlib_surface_t *surface)
+{
+    if (surface->shm_info == NULL)
+	return;
+
+    surface->shm_info->last_read = NextRequest (surface->dpy) - 1;
+}
+
 static cairo_int_status_t
 _cairo_xlib_surface_composite (cairo_operator_t		op,
 			       cairo_pattern_t		*src_pattern,
@@ -1755,6 +1935,10 @@ _cairo_xlib_surface_composite (cairo_operator_t		op,
     default:
 	ASSERT_NOT_REACHED;
     }
+    _cairo_xlib_surface_shm_mark_write (dst);
+    _cairo_xlib_surface_shm_mark_read (src);
+    if (mask)
+	_cairo_xlib_surface_shm_mark_read (mask);
 
     if (!_cairo_operator_bounded_by_source (op))
       status = _cairo_surface_composite_fixup_unbounded (&dst->base,
@@ -1875,6 +2059,7 @@ _cairo_xlib_surface_fill_rectangles (void		     *abstract_surface,
 			   _render_operator (op),
 			   surface->dst_picture,
 			   &render_color, xrects, num_rects);
+    _cairo_xlib_surface_shm_mark_write (surface);
 
     if (xrects != static_xrects)
         free (xrects);
@@ -2127,6 +2312,8 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t	op,
         if (xtraps != xtraps_stack)
             free(xtraps);
     }
+    _cairo_xlib_surface_shm_mark_write (dst);
+    _cairo_xlib_surface_shm_mark_read (src);
 
  BAIL:
     _cairo_pattern_release_surface (pattern, &src->base, &attributes);
@@ -2256,6 +2443,18 @@ _cairo_xlib_surface_get_font_options (void                  *abstract_surface,
     *options = surface->screen_info->font_options;
 }
 
+static cairo_status_t
+_cairo_xlib_surface_mark_dirty_rectangle (void	    *abstract_surface,
+				          int	     x,
+					  int	     y,
+					  int	     width,
+					  int	     height)
+{
+    _cairo_xlib_surface_shm_mark_write (abstract_surface);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static void
 _cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
 
@@ -2304,6 +2503,99 @@ _cairo_xlib_surface_reset (void *abstract_surface)
     return CAIRO_STATUS_SUCCESS;
 }
 
+#ifdef CAIRO_HAS_XLIB_XSHM
+static void
+_cairo_xlib_shm_detach (void *data)
+{
+    cairo_xlib_shm_info_t *shm_info = data;
+    cairo_xlib_display_t *display = shm_info->display;
+
+    _cairo_xlib_remove_close_display_hooks (display->display, shm_info);
+    _cairo_xlib_shm_info_destroy (shm_info);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_create_shm_image (cairo_xlib_surface_t *target,
+				      int width,
+				      int height,
+				      cairo_image_surface_t **image_out,
+				      cairo_xlib_shm_info_t **shm_info_out)
+{
+    cairo_xlib_display_t *display = target->screen_info->display;
+    cairo_format_masks_t xlib_masks;
+    pixman_format_code_t pixman_format;
+    cairo_surface_t *image;
+    cairo_xlib_shm_info_t *shm_info;
+    cairo_status_t status;
+    size_t size, stride;
+
+    if (! target->use_shm)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (target->dpy == NULL || ! display->use_shm) {
+	target->use_shm = FALSE;
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (target->depth > 16) {
+	xlib_masks.bpp = 32;
+    } else if (target->depth > 8) {
+	xlib_masks.bpp = 16;
+    } else if (target->depth > 1) {
+	xlib_masks.bpp = 8;
+    } else {
+	xlib_masks.bpp = 1;
+    }
+    xlib_masks.alpha_mask = target->a_mask;
+    xlib_masks.red_mask   = target->r_mask;
+    xlib_masks.green_mask = target->g_mask;
+    xlib_masks.blue_mask  = target->b_mask;
+
+    status = _pixman_format_from_masks (&xlib_masks, &pixman_format);
+    if (status) {
+	/* Cannot be represented by a pixman_format, ignore in future */
+	target->use_shm = FALSE;
+	return status;
+    }
+
+    stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, xlib_masks.bpp);
+    size = stride * height;
+    if (size < SMALL_IMAGE)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_xlib_display_allocate_shm_info (display,
+	                                            size, FALSE,
+						    &shm_info);
+    if (status)
+	return status;
+
+    image = _cairo_image_surface_create_with_pixman_format (shm_info->mem,
+	                                                    pixman_format,
+							    width,
+							    height,
+							    stride);
+    status = image->status;
+    if (status) {
+	_cairo_xlib_shm_detach (shm_info);
+	return status;
+    }
+
+    status = _cairo_user_data_array_set_data (&image->user_data,
+	                                      &_cairo_xlib_shm_info_key,
+					      shm_info,
+					      _cairo_xlib_shm_detach);
+    if (status) {
+	cairo_surface_destroy (image);
+	_cairo_xlib_shm_detach (shm_info);
+	return status;
+    }
+
+    *image_out = (cairo_image_surface_t *) image;
+    *shm_info_out = shm_info;
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
 static const cairo_surface_backend_t cairo_xlib_surface_backend = {
     CAIRO_SURFACE_TYPE_XLIB,
     _cairo_xlib_surface_create_similar,
@@ -2324,7 +2616,7 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = {
     NULL, /* old_show_glyphs */
     _cairo_xlib_surface_get_font_options,
     NULL, /* flush */
-    NULL, /* mark_dirty_rectangle */
+    _cairo_xlib_surface_mark_dirty_rectangle,
     _cairo_xlib_surface_scaled_font_fini,
     _cairo_xlib_surface_scaled_glyph_fini,
 
@@ -2473,6 +2765,9 @@ _cairo_xlib_surface_create_internal (Display		       *dpy,
     surface->width = width;
     surface->height = height;
 
+    surface->use_shm = screen_info->display->use_shm;
+    surface->shm_info = NULL;
+
     surface->buggy_repeat = screen_info->display->buggy_repeat;
     if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) {
 	/* so we can use the XTile fallback */
@@ -2492,6 +2787,7 @@ _cairo_xlib_surface_create_internal (Display		       *dpy,
     surface->have_clip_rects = FALSE;
     surface->clip_rects = surface->embedded_clip_rects;
     surface->num_clip_rects = 0;
+    surface->clip_dirty = 0;
 
     /*
      * Compute the pixel format masks from either a XrenderFormat or
@@ -3886,6 +4182,8 @@ _cairo_xlib_surface_show_glyphs (void                *abstract_dst,
 					      src,
 					      &attributes,
 					      remaining_glyphs);
+    _cairo_xlib_surface_shm_mark_write (dst);
+    _cairo_xlib_surface_shm_mark_read (src);
 
   BAIL1:
     if (src)
-- 
1.5.4.3


--=-vy8YbLXeHNclDGl7h0NH--



More information about the cairo mailing list