[cairo-commit] 10 commits - NEWS src/cairo-debug.c src/cairo-directfb-surface.c src/cairo-glitz-surface.c src/cairo-image-surface.c src/cairoint.h src/cairo-mutex-list-private.h src/cairo-pattern.c src/cairo-pdf-surface.c src/cairo-surface.c src/cairo-win32-surface.c src/cairo-xcb-surface.c src/cairo-xlib-display.c src/cairo-xlib-private.h src/cairo-xlib-screen.c src/cairo-xlib-surface.c src/cairo-xlib-surface-private.h src/Makefile.am

Chris Wilson ickle at kemper.freedesktop.org
Sat May 5 04:26:50 PDT 2007


 NEWS                             |   21 +
 src/Makefile.am                  |    8 
 src/cairo-debug.c                |    4 
 src/cairo-directfb-surface.c     |   15 +
 src/cairo-glitz-surface.c        |   40 +++
 src/cairo-image-surface.c        |   29 ++
 src/cairo-mutex-list-private.h   |    5 
 src/cairo-pattern.c              |  141 ++++++++++--
 src/cairo-pdf-surface.c          |    3 
 src/cairo-surface.c              |   57 ++++
 src/cairo-win32-surface.c        |   29 ++
 src/cairo-xcb-surface.c          |   39 +++
 src/cairo-xlib-display.c         |  453 +++++++++++++++++++++++++++++++++++++++
 src/cairo-xlib-private.h         |   62 ++++-
 src/cairo-xlib-screen.c          |  349 ++++++++++--------------------
 src/cairo-xlib-surface-private.h |    7 
 src/cairo-xlib-surface.c         |  314 +++++++++++++++++++++------
 src/cairoint.h                   |   22 +
 18 files changed, 1265 insertions(+), 333 deletions(-)

New commits:
diff-tree 966a39396eb75d9d4ecc9997bb21d62fba176f78 (from 056d149212ce4999f8984db62b880ff2eb998ab3)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri May 4 15:25:18 2007 +0100

    [NEWS] Surface cache for solid patterns
    
    Add an entry describing the surface cache to NEWS.

diff --git a/NEWS b/NEWS
index 525bc7d..7b6f2f5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,24 @@
+Release 1.4.x
+=========================================================
+«Preamble»
+
+Surface cache for solid patterns
+--------------------------------
+Originally written by Jorn Baayen, the introduction of a small cache
+for surfaces created for solid patterns dramatically reduced the amount
+the number of required surface allocations and backend traffic.  For
+example, this reduces the volume of X requests during text rendering
+to the same level as Xft.  Unfortunately, the cache had to be removed
+when complications arose with threaded applications, like Evince,
+which were designed to only make lockless X requests from a single
+thread, but Cairo violated this design when it ejected cached surfaces,
+with corresponding X traffic, from auxiliary threads.  A solution was
+proposed by Behdad Esfahbod, and implemented by Chris Wilson, to
+introduce work queues for the xlib backend that deferred the
+destruction of the X resources until the next time the application
+directly operated on a xlib surface.
+
+
 Release 1.4.6 (2007-05-01 Carl Worth <cworth at cworth.org>)
 =========================================================
 This is the third update in cairo's stable 1.4 series. It comes a
diff-tree 056d149212ce4999f8984db62b880ff2eb998ab3 (from a8c8e17d845c7060286dac58b553fb717b270788)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 2 12:54:49 2007 +0100

    [cairo-pattern] Don't cache fallback surfaces
    
    _cairo_surface_create_similar_solid() may return an image surface,
    should the backend not support the required content or should it
    encounter an error whilst creating the surface. In those circumstances
    we choose not to cache the fallback surface.

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index ef7fc05..2f65261 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1318,7 +1318,12 @@ _cairo_pattern_acquire_surface_for_solid
 	goto UNLOCK;
     }
 
-    assert (_cairo_surface_is_similar (surface, dst, pattern->content));
+    if (! _cairo_surface_is_similar (surface, dst, pattern->content)) {
+	/* in the rare event of a substitute surface being returned (e.g.
+	 * malloc failure) don't cache the fallback surface */
+	*out = surface;
+	goto NOCACHE;
+    }
 
     /* Cache new */
     if (solid_surface_cache.size < MAX_SURFACE_CACHE_SIZE) {
@@ -1336,6 +1341,7 @@ _cairo_pattern_acquire_surface_for_solid
 DONE:
     *out = cairo_surface_reference (solid_surface_cache.cache[i].surface);
 
+NOCACHE:
     attribs->x_offset = attribs->y_offset = 0;
     cairo_matrix_init_identity (&attribs->matrix);
     attribs->extend = CAIRO_EXTEND_REPEAT;
diff-tree a8c8e17d845c7060286dac58b553fb717b270788 (from 52472b740e821dee444ede1fff041a99d344daab)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 2 10:00:22 2007 +0100

    [cairo-pattern] Cache surface for solid patterns
    
    Original work by Jorn Baayen <jorn at openedhand.com>,
    2715f2098127d04d2f9e304580a26cd0842c0e64
    
    We use a small cache of size 16 for surfaces created for solid patterns.
    This mainly helps with the X backends where we don't have to create a
    pattern for every operation, so we save a lot on X traffic.  Xft uses a
    similar cache, so cairo's text rendering traffic with the xlib backend
    now completely matches that of Xft.
    
    The cache uses an static index variable, which itself acts like a cache of
    size 1, remembering the most recently used solid pattern.  So repeated
    lookups for the same pattern hit immediately.  If that fails, the cache is
    searched linearly, and if that fails too, a new surface is created and a
    random member of the cache is evicted.
    
    A cached surface can only be reused if it is similar to the destination.
    In order to check for similar surfaces a new test is introduced for the
    backends to determine that the cached surface is as would be returned by
    a _create_similar() call for the destination and content.
    
    As surfaces are in general complex encapsulation of graphics state we
    only return unshared cached surfaces and reset them (to clear any error
    conditions and graphics state). In practice this makes little difference
    to the efficacy of the cache during various benchmarks. However, in order
    to transparently share solid surfaces it would be possible to implement a
    COW scheme.
    
    Cache hit rates:    (hit same index + hit in cache) / lookups
    cairo-perf:         (42346 + 28480) / 159600 = 44.38%
    gtk-theme-torturer: (3023  + 3502)  / 6528   = 99.95%
    gtk-perf:           (8270  + 3190)  / 21504  = 53.29%
    
    This translates into a reduction of about 25% of the XRENDER traffic during
    cairo-perf.

diff --git a/src/cairo-directfb-surface.c b/src/cairo-directfb-surface.c
index 4a62846..a7c9926 100644
--- a/src/cairo-directfb-surface.c
+++ b/src/cairo-directfb-surface.c
@@ -1511,6 +1511,17 @@ _cairo_directfb_surface_show_glyphs ( vo
 #endif /* DFB_SHOW_GLYPHS */
 
 
+static cairo_bool_t
+_cairo_directfb_surface_is_simlar (void *surface_a,
+	                           void *surface_b,
+				   cairo_content_t content)
+{
+    cairo_directfb_surface_t *a = (cairo_directfb_surface_t *) surface_a;
+    cairo_directfb_surface_t *b = (cairo_directfb_surface_t *) surface_b;
+
+    return a->dfb == b->dfb;
+}
+
 static cairo_surface_backend_t cairo_directfb_surface_backend = {
          CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/
         _cairo_directfb_surface_create_similar,/*create_similar*/
@@ -1560,7 +1571,9 @@ static cairo_surface_backend_t cairo_dir
 #else
         NULL, /* show_glyphs */
 #endif
-        NULL /* snapshot */
+        NULL, /* snapshot */
+	_cairo_directfb_is_similar,
+	NULL /* reset */
 };
 
 
diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c
index 09e6755..5c8cc6b 100644
--- a/src/cairo-glitz-surface.c
+++ b/src/cairo-glitz-surface.c
@@ -2175,6 +2175,33 @@ _cairo_glitz_surface_flush (void *abstra
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_bool_t
+_cairo_glitz_surface_is_similar (void *surface_a,
+	                         void *surface_b,
+				 cairo_content_t content)
+{
+    cairo_glitz_surface_t *a = (cairo_glitz_surface_t *) surface_a;
+    cairo_glitz_surface_t *b = (cairo_glitz_surface_t *) surface_b;
+
+    glitz_drawable_t *drawable_a = glitz_surface_get_drawable (a->surface);
+    glitz_drawable_t *drawable_b = glitz_surface_get_drawable (b->surface);
+
+    return drawable_a == drawable_b;
+}
+
+static cairo_status_t
+_cairo_glitz_surface_reset (void *abstract_surface)
+{
+    cairo_glitz_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = _cairo_glitz_surface_set_clip_region (surface, NULL);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static const cairo_surface_backend_t cairo_glitz_surface_backend = {
     CAIRO_SURFACE_TYPE_GLITZ,
     _cairo_glitz_surface_create_similar,
@@ -2197,7 +2224,18 @@ static const cairo_surface_backend_t cai
     _cairo_glitz_surface_flush,
     NULL, /* mark_dirty_rectangle */
     _cairo_glitz_surface_scaled_font_fini,
-    _cairo_glitz_surface_scaled_glyph_fini
+    _cairo_glitz_surface_scaled_glyph_fini,
+
+    NULL, /* paint */
+    NULL, /* mask */
+    NULL, /* stroke */
+    NULL, /* fill */
+    NULL, /* show_glyphs */
+
+    NULL, /* snapshot */
+    _cairo_glitz_surface_is_compatible,
+
+    _cairo_glitz_surface_reset
 };
 
 static const cairo_surface_backend_t *
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 3aa4cb4..441947f 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -1071,6 +1071,19 @@ _cairo_image_surface_get_font_options (v
     cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
 }
 
+static cairo_status_t
+_cairo_image_surface_reset (void *abstract_surface)
+{
+    cairo_image_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = _cairo_image_surface_set_clip_region (surface, NULL);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 /**
  * _cairo_surface_is_image:
  * @surface: a #cairo_surface_t
@@ -1103,7 +1116,21 @@ const cairo_surface_backend_t cairo_imag
     NULL, /* intersect_clip_path */
     _cairo_image_surface_get_extents,
     NULL, /* old_show_glyphs */
-    _cairo_image_surface_get_font_options
+    _cairo_image_surface_get_font_options,
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, //* font_fini */
+    NULL, //* glyph_fini */
+
+    NULL, /* paint */
+    NULL, /* mask */
+    NULL, /* stroke */
+    NULL, /* fill */
+    NULL, /* show_glyphs */
+    NULL,  /* snapshot */
+    NULL, /* is_similar */
+
+    _cairo_image_surface_reset
 };
 
 /* A convenience function for when one needs to coerce an image
diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h
index 3737831..abb4a9a 100644
--- a/src/cairo-mutex-list-private.h
+++ b/src/cairo-mutex-list-private.h
@@ -32,7 +32,8 @@
  */
 
 
-CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_cache_lock);
+CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_pattern_cache_lock);
+CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock);
 
 CAIRO_MUTEX_DECLARE (_cairo_font_face_mutex);
 CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex);
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index b6c8df7..ef7fc05 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -285,7 +285,7 @@ _cairo_pattern_create_solid (const cairo
 {
     cairo_solid_pattern_t *pattern = NULL;
 
-    CAIRO_MUTEX_LOCK (_cairo_pattern_solid_cache_lock);
+    CAIRO_MUTEX_LOCK (_cairo_pattern_solid_pattern_cache_lock);
 
     if (solid_pattern_cache.size) {
 	int i = --solid_pattern_cache.size %
@@ -294,7 +294,7 @@ _cairo_pattern_create_solid (const cairo
 	solid_pattern_cache.patterns[i] = NULL;
     }
 
-    CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_cache_lock);
+    CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_pattern_cache_lock);
 
     if (pattern == NULL) {
 	/* None cached, need to create a new pattern. */
@@ -309,12 +309,12 @@ _cairo_pattern_create_solid (const cairo
     return &pattern->base;
 }
 
-void
-_cairo_pattern_reset_static_data (void)
+static void
+_cairo_pattern_reset_solid_pattern_cache (void)
 {
     int i;
 
-    CAIRO_MUTEX_LOCK (_cairo_pattern_solid_cache_lock);
+    CAIRO_MUTEX_LOCK (_cairo_pattern_solid_pattern_cache_lock);
 
     for (i = 0; i < MIN (ARRAY_LENGTH (solid_pattern_cache.patterns), solid_pattern_cache.size); i++) {
 	free (solid_pattern_cache.patterns[i]);
@@ -322,7 +322,7 @@ _cairo_pattern_reset_static_data (void)
     }
     solid_pattern_cache.size = 0;
 
-    CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_cache_lock);
+    CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_pattern_cache_lock);
 }
 
 static const cairo_pattern_t *
@@ -636,7 +636,7 @@ cairo_pattern_destroy (cairo_pattern_t *
     if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
 	int i;
 
-	CAIRO_MUTEX_LOCK (_cairo_pattern_solid_cache_lock);
+	CAIRO_MUTEX_LOCK (_cairo_pattern_solid_pattern_cache_lock);
 
 	i = solid_pattern_cache.size++ %
 	    ARRAY_LENGTH (solid_pattern_cache.patterns);
@@ -646,7 +646,7 @@ cairo_pattern_destroy (cairo_pattern_t *
 
 	solid_pattern_cache.patterns[i] = (cairo_solid_pattern_t *) pattern;
 
-	CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_cache_lock);
+	CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_pattern_cache_lock);
     } else {
 	free (pattern);
     }
@@ -1241,6 +1241,35 @@ _cairo_pattern_acquire_surface_for_gradi
     return status;
 }
 
+/* We maintain a small cache here, because we don't want to constantly
+ * recreate surfaces for simple solid colors. */
+#define MAX_SURFACE_CACHE_SIZE 16
+static struct {
+    struct _cairo_pattern_solid_surface_cache{
+	cairo_color_t    color;
+	cairo_surface_t *surface;
+    } cache[MAX_SURFACE_CACHE_SIZE];
+    int size;
+} solid_surface_cache;
+
+static cairo_bool_t
+_cairo_pattern_solid_surface_matches (
+	const struct _cairo_pattern_solid_surface_cache	    *cache,
+	const cairo_solid_pattern_t			    *pattern,
+	cairo_surface_t					    *dst)
+{
+    if (cache->surface->ref_count != 1)
+	return FALSE;
+
+    if (! _cairo_color_equal (&cache->color, &pattern->color))
+	return FALSE;
+
+    if (! _cairo_surface_is_similar (cache->surface, dst, pattern->content))
+	return FALSE;
+
+    return TRUE;
+}
+
 static cairo_int_status_t
 _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t	     *pattern,
 					  cairo_surface_t	     *dst,
@@ -1251,13 +1280,61 @@ _cairo_pattern_acquire_surface_for_solid
 					  cairo_surface_t	     **out,
 					  cairo_surface_attributes_t *attribs)
 {
-    *out = _cairo_surface_create_similar_solid (dst,
-						pattern->content,
-						1, 1,
-						&pattern->color,
-						&pattern->base);
-    if ((*out)->status)
-	return CAIRO_STATUS_NO_MEMORY;
+    static int i;
+
+    cairo_surface_t *surface;
+    cairo_status_t   status;
+
+    CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
+
+    /* Check cache first */
+    if (i < solid_surface_cache.size &&
+	    _cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i],
+		                                  pattern,
+						  dst))
+    {
+	if (! _cairo_surface_reset (solid_surface_cache.cache[i].surface))
+	    goto DONE;
+    }
+
+    for (i = 0 ; i < solid_surface_cache.size; i++) {
+	if (_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i],
+		                                  pattern,
+						  dst))
+	{
+	    if (! _cairo_surface_reset (solid_surface_cache.cache[i].surface))
+		goto DONE;
+	}
+    }
+
+    /* Not cached, need to create new */
+    surface = _cairo_surface_create_similar_solid (dst,
+	                                           pattern->content,
+						   1, 1,
+						   &pattern->color,
+						   &pattern->base);
+    if (surface->status) {
+	status = surface->status;
+	goto UNLOCK;
+    }
+
+    assert (_cairo_surface_is_similar (surface, dst, pattern->content));
+
+    /* Cache new */
+    if (solid_surface_cache.size < MAX_SURFACE_CACHE_SIZE) {
+	solid_surface_cache.size++;
+    } else {
+	i = rand () % MAX_SURFACE_CACHE_SIZE;
+
+	/* Evict old */
+	cairo_surface_destroy (solid_surface_cache.cache[i].surface);
+    }
+
+    solid_surface_cache.cache[i].color   = pattern->color;
+    solid_surface_cache.cache[i].surface = surface;
+
+DONE:
+    *out = cairo_surface_reference (solid_surface_cache.cache[i].surface);
 
     attribs->x_offset = attribs->y_offset = 0;
     cairo_matrix_init_identity (&attribs->matrix);
@@ -1265,7 +1342,26 @@ _cairo_pattern_acquire_surface_for_solid
     attribs->filter = CAIRO_FILTER_NEAREST;
     attribs->acquired = FALSE;
 
-    return CAIRO_STATUS_SUCCESS;
+    status = CAIRO_STATUS_SUCCESS;
+
+UNLOCK:
+    CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
+
+    return status;
+}
+
+static void
+_cairo_pattern_reset_solid_surface_cache (void)
+{
+    int i;
+
+    CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
+
+    for (i = 0; i < solid_surface_cache.size; i++)
+	cairo_surface_destroy (solid_surface_cache.cache[i].surface);
+    solid_surface_cache.size = 0;
+
+    CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
 }
 
 /**
@@ -2063,3 +2159,10 @@ cairo_pattern_get_radial_circles (cairo_
 
     return CAIRO_STATUS_SUCCESS;
 }
+
+void
+_cairo_pattern_reset_static_data (void)
+{
+    _cairo_pattern_reset_solid_pattern_cache ();
+    _cairo_pattern_reset_solid_surface_cache ();
+}
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 00dcd7c..f488bde 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -3637,6 +3637,9 @@ static const cairo_surface_backend_t cai
     _cairo_pdf_surface_fill,
     _cairo_pdf_surface_show_glyphs,
     NULL, /* snapshot */
+
+    NULL, /* is_compatible */
+    NULL, /* reset */
 };
 
 static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend = {
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index cf8acf0..7c8a1e3 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -407,6 +407,34 @@ cairo_surface_destroy (cairo_surface_t *
 slim_hidden_def(cairo_surface_destroy);
 
 /**
+ * cairo_surface_reset:
+ * @surface: a #cairo_surface_t
+ *
+ * Resets the surface back to defaults such that it may be reused in lieu
+ * of creating a new surface.
+ **/
+cairo_status_t
+_cairo_surface_reset (cairo_surface_t *surface)
+{
+    if (surface == NULL || surface->ref_count == CAIRO_REF_COUNT_INVALID)
+	return CAIRO_STATUS_SUCCESS;
+
+    assert (surface->ref_count == 1);
+
+    _cairo_user_data_array_fini (&surface->user_data);
+
+    if (surface->backend->reset != NULL) {
+	cairo_status_t status = surface->backend->reset (surface);
+	if (status)
+	    return status;
+    }
+
+    _cairo_surface_init (surface, surface->backend, surface->content);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
  * cairo_surface_get_reference_count:
  * @surface: a #cairo_surface_t
  *
@@ -1062,6 +1090,35 @@ _cairo_surface_snapshot (cairo_surface_t
     return _cairo_surface_fallback_snapshot (surface);
 }
 
+/**
+ * _cairo_surface_is_similar
+ * @surface_a: a #cairo_surface_t
+ * @surface_b: a #cairo_surface_t
+ * @content: a #cairo_content_t
+ *
+ * Find out whether the given surfaces share the same backend,
+ * and if so, whether they can be considered similar.
+ *
+ * The definition of "similar" depends on the backend. In
+ * general, it means that the surface is equivalent to one
+ * that would have been generated by a call to cairo_surface_create_similar.
+ *
+ * Return value: TRUE if the surfaces are similar.
+ **/
+cairo_bool_t
+_cairo_surface_is_similar (cairo_surface_t *surface_a,
+	                   cairo_surface_t *surface_b,
+			   cairo_content_t content)
+{
+    if (surface_a->backend != surface_b->backend)
+	return FALSE;
+
+    if (surface_a->backend->is_similar != NULL)
+	return surface_a->backend->is_similar (surface_a, surface_b, content);
+
+    return TRUE;
+}
+
 cairo_status_t
 _cairo_surface_composite (cairo_operator_t	op,
 			  cairo_pattern_t	*src,
diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
index a907fd8..450887a 100644
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -1855,6 +1855,30 @@ cairo_win32_surface_get_image (cairo_sur
     return ((cairo_win32_surface_t*)surface)->image;
 }
 
+static cairo_bool_t
+_cairo_win32_surface_is_similar (void *surface_a,
+	                         void *surface_b,
+				 cairo_content_t content)
+{
+    cairo_win32_surface_t *a = surface_a;
+    cairo_win32_surface_t *b = surface_b;
+
+    return a->dc == b->dc;
+}
+
+static cairo_status_t
+_cairo_win32_surface_reset (void *abstract_surface)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = _cairo_win32_surface_set_clip_region (surface, NULL);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static const cairo_surface_backend_t cairo_win32_surface_backend = {
     CAIRO_SURFACE_TYPE_WIN32,
     _cairo_win32_surface_create_similar,
@@ -1885,7 +1909,10 @@ static const cairo_surface_backend_t cai
     NULL, /* fill */
     _cairo_win32_surface_show_glyphs,
 
-    NULL  /* snapshot */
+    NULL,  /* snapshot */
+    _cairo_win32_surface_is_similar,
+
+    _cairo_win32_surface_reset
 };
 
 /* Notes:
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index e8ab0fb..304300a 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -1577,6 +1577,39 @@ _cairo_xcb_surface_show_glyphs (void    
 				 int		      num_glyphs,
 				 cairo_scaled_font_t *scaled_font);
 
+static cairo_bool_t
+_cairo_xcb_surface_is_similar (void *surface_a,
+	                       void *surface_b,
+			       cairo_content_t content)
+{
+    cairo_xcb_surface_t *a = surface_a;
+    cairo_xcb_surface_t *b = surface_b;
+    xcb_render_pictforminfo_t xrender_format;
+
+    if (! _cairo_xcb_surface_same_screen (dst, src))
+	return FALSE;
+
+    /* now check that the target is a similar format */
+    xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (b->dpy,
+	    _cairo_format_from_content (content));
+
+    return a->xrender_format == xrender_format;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_reset (void *abstract_surface)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = _cairo_xcb_surface_set_clip_region (surface, NULL);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
 /* XXX: move this to the bottom of the file, XCB and Xlib */
 
 static const cairo_surface_backend_t cairo_xcb_surface_backend = {
@@ -1608,7 +1641,11 @@ static const cairo_surface_backend_t cai
     NULL, /* stroke */
     NULL, /* fill */
     _cairo_xcb_surface_show_glyphs,
-    NULL  /* snapshot */
+    NULL,  /* snapshot */
+
+    _cairo_xcb_surface_is_similar,
+
+    _cairo_xcb_surface_reset
 };
 
 /**
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 4f0c208..9dbe8cf 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -1805,6 +1805,46 @@ static void
 _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
 				       cairo_scaled_font_t  *scaled_font);
 
+static cairo_bool_t
+_cairo_xlib_surface_is_similar (void		*surface_a,
+	                        void		*surface_b,
+				cairo_content_t	 content)
+{
+    cairo_xlib_surface_t *a = surface_a;
+    cairo_xlib_surface_t *b = surface_b;
+    XRenderPictFormat *xrender_format = b->xrender_format;
+
+    if (!_cairo_xlib_surface_same_screen (a, b))
+	return FALSE;
+
+    /* now inspect the content to check that a is similar to b */
+    if (xrender_format == NULL && b->visual != NULL)
+        xrender_format = XRenderFindVisualFormat (b->dpy, b->visual);
+
+    if (xrender_format == NULL ||
+	_xrender_format_to_content (xrender_format) != content)
+    {
+	xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (b->dpy,
+		_cairo_format_from_content (content));
+    }
+
+
+    return a->xrender_format == xrender_format;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_reset (void *abstract_surface)
+{
+    cairo_xlib_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = _cairo_xlib_surface_set_clip_region (surface, NULL);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static const cairo_surface_backend_t cairo_xlib_surface_backend = {
     CAIRO_SURFACE_TYPE_XLIB,
     _cairo_xlib_surface_create_similar,
@@ -1834,7 +1874,10 @@ static const cairo_surface_backend_t cai
     NULL, /* stroke */
     NULL, /* fill */
     _cairo_xlib_surface_show_glyphs,
-    NULL  /* snapshot */
+    NULL,  /* snapshot */
+    _cairo_xlib_surface_is_similar,
+
+    _cairo_xlib_surface_reset
 };
 
 /**
diff --git a/src/cairoint.h b/src/cairoint.h
index 3d1b17d..43c9ca5 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -890,6 +890,14 @@ struct _cairo_surface_backend {
 
     cairo_surface_t *
     (*snapshot)			(void			*surface);
+
+    cairo_bool_t
+    (*is_similar)		(void			*surface_a,
+	                         void			*surface_b,
+				 cairo_content_t         content);
+
+    cairo_warn cairo_status_t
+    (*reset)			(void			*surface);
 };
 
 typedef struct _cairo_format_masks {
@@ -1868,6 +1876,14 @@ _cairo_surface_clone_similar (cairo_surf
 cairo_private cairo_surface_t *
 _cairo_surface_snapshot (cairo_surface_t *surface);
 
+cairo_private cairo_bool_t
+_cairo_surface_is_similar (cairo_surface_t *surface_a,
+	                   cairo_surface_t *surface_b,
+			   cairo_content_t  content);
+
+cairo_private cairo_status_t
+_cairo_surface_reset (cairo_surface_t *surface);
+
 cairo_private unsigned int
 _cairo_surface_get_current_clip_serial (cairo_surface_t *surface);
 
diff-tree 52472b740e821dee444ede1fff041a99d344daab (from a81823cdf9a147d02bb2365558ba667ab52fb3c9)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 1 18:34:47 2007 +0100

    [cairo-xlib-surface] Defer application of clip mask until required.
    
    By deferring the issuing of the X requests to set the clip mask we can
    theoretically avoid some redundant requests, but primarily we remove
    another path where X requests are emitted.

diff --git a/src/cairo-xlib-surface-private.h b/src/cairo-xlib-surface-private.h
index 15724d3..3bbf43e 100644
--- a/src/cairo-xlib-surface-private.h
+++ b/src/cairo-xlib-surface-private.h
@@ -79,6 +79,7 @@ struct _cairo_xlib_surface {
 
     Picture dst_picture, src_picture;
 
+    unsigned int clip_dirty;
     cairo_bool_t have_clip_rects;
     XRectangle embedded_clip_rects[4];
     XRectangle *clip_rects;
@@ -90,4 +91,10 @@ struct _cairo_xlib_surface {
     XTransform xtransform;
 };
 
+enum {
+    CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC      = 0x01,
+    CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE = 0x02,
+    CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL     = 0x03
+};
+
 #endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 52af255..4f0c208 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -693,21 +693,33 @@ _cairo_xlib_surface_ensure_src_picture (
 static void
 _cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_surface_t *surface)
 {
-    if (surface->have_clip_rects)
+    if (surface->have_clip_rects) {
 	XRenderSetPictureClipRectangles (surface->dpy, surface->dst_picture,
 					 0, 0,
 					 surface->clip_rects,
 					 surface->num_clip_rects);
+    } else {
+	XRenderPictureAttributes pa;
+	pa.clip_mask = None;
+	XRenderChangePicture (surface->dpy, surface->dst_picture,
+			      CPClipMask, &pa);
+    }
+
+    surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE;
 }
 
 static void
 _cairo_xlib_surface_set_gc_clip_rects (cairo_xlib_surface_t *surface)
 {
-    if (surface->have_clip_rects)
+    if (surface->have_clip_rects) {
 	XSetClipRectangles(surface->dpy, surface->gc,
 			   0, 0,
 			   surface->clip_rects,
 			   surface->num_clip_rects, YXSorted);
+    } else
+	XSetClipMask (surface->dpy, surface->gc, None);
+
+    surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC;
 }
 
 static void
@@ -719,8 +731,8 @@ _cairo_xlib_surface_ensure_dst_picture (
 						     surface->xrender_format,
 						     0, NULL);
 	_cairo_xlib_surface_set_picture_clip_rects (surface);
-    }
-
+    } else if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE)
+	_cairo_xlib_surface_set_picture_clip_rects (surface);
 }
 
 static cairo_status_t
@@ -728,20 +740,20 @@ _cairo_xlib_surface_ensure_gc (cairo_xli
 {
     XGCValues gcv;
 
-    if (surface->gc)
-	return CAIRO_STATUS_SUCCESS;
-
-    surface->gc = _cairo_xlib_screen_get_gc (surface->screen_info,
-	                                     surface->depth);
     if (surface->gc == NULL) {
-	gcv.graphics_exposures = False;
-	surface->gc = XCreateGC (surface->dpy, surface->drawable,
-				 GCGraphicsExposures, &gcv);
-	if (!surface->gc)
-	    return CAIRO_STATUS_NO_MEMORY;
-    }
+	surface->gc = _cairo_xlib_screen_get_gc (surface->screen_info,
+						 surface->depth);
+	if (surface->gc == NULL) {
+	    gcv.graphics_exposures = False;
+	    surface->gc = XCreateGC (surface->dpy, surface->drawable,
+				     GCGraphicsExposures, &gcv);
+	    if (!surface->gc)
+		return CAIRO_STATUS_NO_MEMORY;
+	}
 
-    _cairo_xlib_surface_set_gc_clip_rects (surface);
+	_cairo_xlib_surface_set_gc_clip_rects (surface);
+    } else if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC)
+	_cairo_xlib_surface_set_gc_clip_rects (surface);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1032,8 +1044,8 @@ _cairo_xlib_surface_set_repeat (cairo_xl
 }
 
 static cairo_int_status_t
-_cairo_xlib_surface_set_attributes (cairo_xlib_surface_t	  *surface,
-				       cairo_surface_attributes_t *attributes)
+_cairo_xlib_surface_set_attributes (cairo_xlib_surface_t	    *surface,
+				    cairo_surface_attributes_t	    *attributes)
 {
     cairo_int_status_t status;
 
@@ -1716,13 +1728,11 @@ static cairo_int_status_t
 _cairo_xlib_surface_set_clip_region (void              *abstract_surface,
 				     pixman_region16_t *region)
 {
-    cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+    cairo_xlib_surface_t *surface = abstract_surface;
 
     if (surface->have_clip_rects == FALSE && region == NULL)
 	return CAIRO_STATUS_SUCCESS;
 
-    _cairo_xlib_display_notify (surface->screen_info->display);
-
     if (surface->clip_rects != surface->embedded_clip_rects) {
 	free (surface->clip_rects);
 	surface->clip_rects = surface->embedded_clip_rects;
@@ -1731,17 +1741,7 @@ _cairo_xlib_surface_set_clip_region (voi
     surface->have_clip_rects = FALSE;
     surface->num_clip_rects = 0;
 
-    if (region == NULL) {
-	if (surface->gc)
-	    XSetClipMask (surface->dpy, surface->gc, None);
-
-	if (surface->xrender_format && surface->dst_picture) {
-	    XRenderPictureAttributes pa;
-	    pa.clip_mask = None;
-	    XRenderChangePicture (surface->dpy, surface->dst_picture,
-				  CPClipMask, &pa);
-	}
-    } else {
+    if (region != NULL) {
 	pixman_box16_t *boxes;
 	XRectangle *rects = NULL;
 	int n_boxes, i;
@@ -1767,14 +1767,10 @@ _cairo_xlib_surface_set_clip_region (voi
 	surface->have_clip_rects = TRUE;
 	surface->clip_rects = rects;
 	surface->num_clip_rects = n_boxes;
-
-	if (surface->gc)
-	    _cairo_xlib_surface_set_gc_clip_rects (surface);
-
-	if (surface->dst_picture)
-	    _cairo_xlib_surface_set_picture_clip_rects (surface);
     }
 
+    surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL;
+
     return CAIRO_STATUS_SUCCESS;
 }
 
diff-tree a81823cdf9a147d02bb2365558ba667ab52fb3c9 (from c29ffc64ca341623d21a7861f1b5f131b420d20f)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Apr 20 17:32:25 2007 +0100

    [cairo-xlib-display] Allocate the close_display hooks from a freelist.
    
    Similar to job allocation, but only useful for toolkits like GTK+ which
    recreate a new xlib surface for each expose.

diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index 423d23e..5de9011 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -68,7 +68,7 @@ static void
 _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
 {
     cairo_xlib_screen_info_t	    *screen;
-    cairo_xlib_hook_t		    *hooks;
+    cairo_xlib_hook_t		    *hooks, *list;
 
     /* call all registered shutdown routines */
     CAIRO_MUTEX_LOCK (display->mutex);
@@ -81,16 +81,22 @@ _cairo_xlib_call_close_display_hooks (ca
 	display->close_display_hooks = NULL;
 	CAIRO_MUTEX_UNLOCK (display->mutex);
 
+	list = hooks;
 	do {
-	    cairo_xlib_hook_t *hook = hooks;
-	    hooks = hook->next;
+	    cairo_xlib_hook_t *hook = list;
+	    list = hook->next;
 
 	    hook->func (display->display, hook->data);
+	} while (list != NULL);
+
+	CAIRO_MUTEX_LOCK (display->mutex);
+	do {
+	    cairo_xlib_hook_t *hook = hooks;
+	    hooks = hook->next;
 
-	    free (hook);
+	    _cairo_freelist_free (&display->hook_freelist, hook);
 	} while (hooks != NULL);
 
-	CAIRO_MUTEX_LOCK (display->mutex);
 	hooks = display->close_display_hooks;
     }
     display->closed = TRUE;
@@ -153,6 +159,7 @@ _cairo_xlib_display_destroy (cairo_xlib_
 	    _cairo_freelist_free (&display->wq_freelist, job);
 	}
 	_cairo_freelist_fini (&display->wq_freelist);
+	_cairo_freelist_fini (&display->hook_freelist);
 
 	CAIRO_MUTEX_UNLOCK (display->mutex);
 
@@ -263,6 +270,8 @@ _cairo_xlib_display_get (Display *dpy)
     XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
 
     _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t));
+    _cairo_freelist_init (&display->hook_freelist, sizeof (cairo_xlib_hook_t));
+
     display->ref_count = 2; /* add one for the CloseDisplay */
     CAIRO_MUTEX_INIT (display->mutex);
     display->display = dpy;
@@ -290,20 +299,20 @@ _cairo_xlib_add_close_display_hook (Disp
     if (display == NULL)
 	return FALSE;
 
-    hook = malloc (sizeof (cairo_xlib_hook_t));
-    if (hook != NULL) {
-	hook->func = func;
-	hook->data = data;
-	hook->key = key;
+    CAIRO_MUTEX_LOCK (display->mutex);
+    if (display->closed == FALSE) {
+	hook = _cairo_freelist_alloc (&display->hook_freelist);
+	if (hook != NULL) {
+	    hook->func = func;
+	    hook->data = data;
+	    hook->key = key;
 
-	CAIRO_MUTEX_LOCK (display->mutex);
-	if (display->closed == FALSE) {
 	    hook->next = display->close_display_hooks;
 	    display->close_display_hooks = hook;
 	    ret = TRUE;
 	}
-	CAIRO_MUTEX_UNLOCK (display->mutex);
     }
+    CAIRO_MUTEX_UNLOCK (display->mutex);
 
     _cairo_xlib_display_destroy (display);
 
@@ -326,7 +335,7 @@ _cairo_xlib_remove_close_display_hooks (
 	next = hook->next;
 	if (hook->key == key) {
 	    *prev = hook->next;
-	    free (hook);
+	    _cairo_freelist_free (&display->hook_freelist, hook);
 	} else
 	    prev = &hook->next;
     }
@@ -342,24 +351,21 @@ _cairo_xlib_display_queue_resource (cair
 				    XID xid)
 {
     cairo_xlib_job_t *job;
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
-
-    job = _cairo_freelist_alloc (&display->wq_freelist);
-    if (job == NULL)
-	return CAIRO_STATUS_NO_MEMORY;
-
-    job->type = RESOURCE;
-    job->func.resource.xid = xid;
-    job->func.resource.notify = notify;
+    cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
 
     CAIRO_MUTEX_LOCK (display->mutex);
     if (display->closed == FALSE) {
-	job->next = display->workqueue;
-	display->workqueue = job;
-    } else {
-	_cairo_freelist_free (&display->wq_freelist, job);
-	job = NULL;
-	status = CAIRO_STATUS_NO_MEMORY;
+	job = _cairo_freelist_alloc (&display->wq_freelist);
+	if (job != NULL) {
+	    job->type = RESOURCE;
+	    job->func.resource.xid = xid;
+	    job->func.resource.notify = notify;
+
+	    job->next = display->workqueue;
+	    display->workqueue = job;
+
+	    status = CAIRO_STATUS_SUCCESS;
+	}
     }
     CAIRO_MUTEX_UNLOCK (display->mutex);
 
@@ -373,25 +379,22 @@ _cairo_xlib_display_queue_work (cairo_xl
 				void (*destroy) (void *))
 {
     cairo_xlib_job_t *job;
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
-
-    job = _cairo_freelist_alloc (&display->wq_freelist);
-    if (job == NULL)
-	return CAIRO_STATUS_NO_MEMORY;
-
-    job->type = WORK;
-    job->func.work.data    = data;
-    job->func.work.notify  = notify;
-    job->func.work.destroy = destroy;
+    cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
 
     CAIRO_MUTEX_LOCK (display->mutex);
     if (display->closed == FALSE) {
-	job->next = display->workqueue;
-	display->workqueue = job;
-    } else {
-	_cairo_freelist_free (&display->wq_freelist, job);
-	job = NULL;
-	status = CAIRO_STATUS_NO_MEMORY;
+	job = _cairo_freelist_alloc (&display->wq_freelist);
+	if (job != NULL) {
+	    job->type = WORK;
+	    job->func.work.data    = data;
+	    job->func.work.notify  = notify;
+	    job->func.work.destroy = destroy;
+
+	    job->next = display->workqueue;
+	    display->workqueue = job;
+
+	    status = CAIRO_STATUS_SUCCESS;
+	}
     }
     CAIRO_MUTEX_UNLOCK (display->mutex);
 
@@ -401,7 +404,7 @@ _cairo_xlib_display_queue_work (cairo_xl
 void
 _cairo_xlib_display_notify (cairo_xlib_display_t *display)
 {
-    cairo_xlib_job_t *jobs, *job;
+    cairo_xlib_job_t *jobs, *job, *freelist;
 
     CAIRO_MUTEX_LOCK (display->mutex);
     jobs = display->workqueue;
@@ -417,7 +420,7 @@ _cairo_xlib_display_notify (cairo_xlib_d
 	    job = jobs;
 	    jobs = next;
 	} while (jobs != NULL);
-	jobs = job;
+	freelist = jobs = job;
 
 	do {
 	    job = jobs;
@@ -435,11 +438,15 @@ _cairo_xlib_display_notify (cairo_xlib_d
 			                   job->func.resource.xid);
 		break;
 	    }
-
-	    _cairo_freelist_free (&display->wq_freelist, job);
 	} while (jobs != NULL);
 
 	CAIRO_MUTEX_LOCK (display->mutex);
+	do {
+	    job = freelist;
+	    freelist = job->next;
+	    _cairo_freelist_free (&display->wq_freelist, job);
+	} while (freelist != NULL);
+
 	jobs = display->workqueue;
     }
     CAIRO_MUTEX_UNLOCK (display->mutex);
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index bdbd7b7..4594f13 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -61,6 +61,7 @@ struct _cairo_xlib_display {
     cairo_xlib_job_t *workqueue;
     cairo_freelist_t wq_freelist;
 
+    cairo_freelist_t hook_freelist;
     cairo_xlib_hook_t *close_display_hooks;
     unsigned int closed :1;
 };
diff-tree c29ffc64ca341623d21a7861f1b5f131b420d20f (from 1d6f7b7a0282d1b24bec82d9ad113b0d0a0cde98)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Apr 20 16:33:56 2007 +0100

    [cairo-xlib-display] Cache the screen_info for the lifetime of the display.
    
    Hold an extra reference to the screen_info and explicitly destroy the
    list of screens on CloseDisplay.

diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index b11eb5e..423d23e 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -98,6 +98,24 @@ _cairo_xlib_call_close_display_hooks (ca
     CAIRO_MUTEX_UNLOCK (display->mutex);
 }
 
+static void
+_cairo_xlib_display_discard_screens (cairo_xlib_display_t *display)
+{
+    cairo_xlib_screen_info_t *screens;
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    screens = display->screens;
+    display->screens = NULL;
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    while (screens != NULL) {
+	cairo_xlib_screen_info_t *screen = screens;
+	screens = screen->next;
+
+	_cairo_xlib_screen_info_destroy (screen);
+    }
+}
+
 cairo_xlib_display_t *
 _cairo_xlib_display_reference (cairo_xlib_display_t *display)
 {
@@ -173,6 +191,7 @@ _cairo_xlib_close_display (Display *dpy,
 
 	    _cairo_xlib_display_notify (display);
 	    _cairo_xlib_call_close_display_hooks (display);
+	    _cairo_xlib_display_discard_screens (display);
 
 	    /* catch any that arrived before marking the display as closed */
 	    _cairo_xlib_display_notify (display);
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
index 0245a43..4ff3b8a 100644
--- a/src/cairo-xlib-screen.c
+++ b/src/cairo-xlib-screen.c
@@ -287,7 +287,6 @@ _cairo_xlib_screen_info_destroy (cairo_x
 	    break;
 	}
     }
-    assert (list != NULL);
     CAIRO_MUTEX_UNLOCK (info->display->mutex);
 
     _cairo_xlib_screen_info_close_display (info);
@@ -333,7 +332,7 @@ _cairo_xlib_screen_info_get (Display *dp
     } else {
 	info = malloc (sizeof (cairo_xlib_screen_info_t));
 	if (info != NULL) {
-	    info->ref_count = 1;
+	    info->ref_count = 2; /* Add one for display cache */
 	    info->display = _cairo_xlib_display_reference (display);
 	    info->screen = screen;
 	    info->has_render = FALSE;
diff-tree 1d6f7b7a0282d1b24bec82d9ad113b0d0a0cde98 (from 88c6d25d4e53ddad6f3d465b2f5249c76a421b82)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Apr 19 13:01:52 2007 +0100

    [cairo-xlib] Cache freed GCs
    
    Maintain a cache of freed GCs, one for each used depth.

diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index 64286cd..b11eb5e 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -67,11 +67,15 @@ static cairo_xlib_display_t *_cairo_xlib
 static void
 _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
 {
-    cairo_xlib_hook_t *hooks;
+    cairo_xlib_screen_info_t	    *screen;
+    cairo_xlib_hook_t		    *hooks;
 
     /* call all registered shutdown routines */
     CAIRO_MUTEX_LOCK (display->mutex);
 
+    for (screen = display->screens; screen != NULL; screen = screen->next)
+	_cairo_xlib_screen_info_close_display (screen);
+
     hooks = display->close_display_hooks;
     while (hooks != NULL) {
 	display->close_display_hooks = NULL;
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 436c67f..bdbd7b7 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -74,6 +74,9 @@ struct _cairo_xlib_screen_info {
     cairo_bool_t has_render;
 
     cairo_font_options_t font_options;
+
+    GC gc[6];
+    unsigned int gc_needs_clip_reset;
 };
 
 cairo_private cairo_xlib_display_t *
@@ -109,6 +112,13 @@ _cairo_xlib_screen_info_reference (cairo
 cairo_private void
 _cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info);
 
+cairo_private void
+_cairo_xlib_screen_info_close_display (cairo_xlib_screen_info_t *info);
+
+cairo_private GC
+_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, int depth);
+cairo_private cairo_status_t
+_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, int depth, GC gc, cairo_bool_t reset_clip);
 
 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
 
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
index 4705244..0245a43 100644
--- a/src/cairo-xlib-screen.c
+++ b/src/cairo-xlib-screen.c
@@ -255,6 +255,19 @@ _cairo_xlib_screen_info_reference (cairo
 }
 
 void
+_cairo_xlib_screen_info_close_display (cairo_xlib_screen_info_t *info)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+	if (info->gc[i] != NULL) {
+	    XFreeGC (info->display->display, info->gc[i]);
+	    info->gc[i] = NULL;
+	}
+    }
+}
+
+void
 _cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info)
 {
     cairo_xlib_screen_info_t **prev;
@@ -277,6 +290,8 @@ _cairo_xlib_screen_info_destroy (cairo_x
     assert (list != NULL);
     CAIRO_MUTEX_UNLOCK (info->display->mutex);
 
+    _cairo_xlib_screen_info_close_display (info);
+
     _cairo_xlib_display_destroy (info->display);
 
     free (info);
@@ -323,6 +338,8 @@ _cairo_xlib_screen_info_get (Display *dp
 	    info->screen = screen;
 	    info->has_render = FALSE;
 	    _cairo_font_options_init_default (&info->font_options);
+	    memset (info->gc, 0, sizeof (info->gc));
+	    info->gc_needs_clip_reset = 0;
 
 	    if (screen) {
 		int event_base, error_base;
@@ -343,3 +360,56 @@ DONE:
 
     return info;
 }
+
+static int
+depth_to_index (int depth)
+{
+    switch(depth){
+	case 1:  return 0;
+	case 8:  return 1;
+	case 15: return 2;
+	case 16: return 3;
+	case 24: return 4;
+	case 32: return 5;
+    }
+    return 0;
+}
+
+GC
+_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, int depth)
+{
+    GC gc;
+
+    depth = depth_to_index (depth);
+
+    gc = info->gc[depth];
+    info->gc[depth] = NULL;
+
+    if (info->gc_needs_clip_reset & (1 << depth))
+	XSetClipMask(info->display->display, gc, None);
+
+    return gc;
+}
+
+cairo_status_t
+_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, int depth, GC gc, cairo_bool_t reset_clip)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    depth = depth_to_index (depth);
+
+    if (info->gc[depth] != NULL) {
+	status = _cairo_xlib_display_queue_work (info->display,
+		                               (cairo_xlib_notify_func) XFreeGC,
+					       info->gc[depth],
+					       NULL);
+    }
+
+    info->gc[depth] = gc;
+    if (reset_clip)
+	info->gc_needs_clip_reset |= 1 << depth;
+    else
+	info->gc_needs_clip_reset &= ~(1 << depth);
+
+    return status;
+}
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 851d282..52af255 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -309,10 +309,10 @@ _cairo_xlib_surface_finish (void *abstra
 
     if (surface->gc != NULL) {
 	cairo_status_t status2;
-	status2 = _cairo_xlib_display_queue_work (display,
-		                               (cairo_xlib_notify_func) XFreeGC,
-					       surface->gc,
-					       NULL);
+	status2 = _cairo_xlib_screen_put_gc (surface->screen_info,
+		                   surface->depth,
+				   surface->gc,
+				   surface->have_clip_rects);
 	if (status2 == CAIRO_STATUS_SUCCESS)
 	    surface->gc = NULL;
 	else if (status == CAIRO_STATUS_SUCCESS)
@@ -731,11 +731,15 @@ _cairo_xlib_surface_ensure_gc (cairo_xli
     if (surface->gc)
 	return CAIRO_STATUS_SUCCESS;
 
-    gcv.graphics_exposures = False;
-    surface->gc = XCreateGC (surface->dpy, surface->drawable,
-			     GCGraphicsExposures, &gcv);
-    if (!surface->gc)
-	return CAIRO_STATUS_NO_MEMORY;
+    surface->gc = _cairo_xlib_screen_get_gc (surface->screen_info,
+	                                     surface->depth);
+    if (surface->gc == NULL) {
+	gcv.graphics_exposures = False;
+	surface->gc = XCreateGC (surface->dpy, surface->drawable,
+				 GCGraphicsExposures, &gcv);
+	if (!surface->gc)
+	    return CAIRO_STATUS_NO_MEMORY;
+    }
 
     _cairo_xlib_surface_set_gc_clip_rects (surface);
 
diff-tree 88c6d25d4e53ddad6f3d465b2f5249c76a421b82 (from 8ad30ccdb0a00701b15003edb2fe0cd4a8a9dfb7)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Apr 17 12:03:07 2007 +0100

    [cairo-xlib-surface] Defer xlib resource cleanup.
    
    Due to caching, destruction of X11 resources may occur outside of a
    usable X11 context. To avoid this, we defer the destruction onto a work
    queue which will be run the next time we try to use the X11 connection
    on behalf of the user (at which point we must have a usable X11 context!)
    or we are closing the Display.

diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index fcc9b57..851d282 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -268,17 +268,56 @@ static cairo_status_t
 _cairo_xlib_surface_finish (void *abstract_surface)
 {
     cairo_xlib_surface_t *surface = abstract_surface;
-    if (surface->dst_picture != None)
-	XRenderFreePicture (surface->dpy, surface->dst_picture);
-
-    if (surface->src_picture != None)
-	XRenderFreePicture (surface->dpy, surface->src_picture);
-
-    if (surface->owns_pixmap)
-	XFreePixmap (surface->dpy, surface->drawable);
-
-    if (surface->gc != NULL)
-	XFreeGC (surface->dpy, surface->gc);
+    cairo_xlib_display_t *display = surface->screen_info ?
+	                            surface->screen_info->display :
+				    NULL;
+    cairo_status_t        status  = CAIRO_STATUS_SUCCESS;
+
+    if (surface->dst_picture != None) {
+	cairo_status_t status2;
+	status2 = _cairo_xlib_display_queue_resource (display,
+		                                      XRenderFreePicture,
+						      surface->dst_picture);
+	if (status2 == CAIRO_STATUS_SUCCESS)
+	    surface->dst_picture = None;
+	else if (status == CAIRO_STATUS_SUCCESS)
+	    status = status2;
+    }
+
+    if (surface->src_picture != None) {
+	cairo_status_t status2;
+	status2 = _cairo_xlib_display_queue_resource (display,
+		                                      XRenderFreePicture,
+						      surface->src_picture);
+	if (status2 == CAIRO_STATUS_SUCCESS)
+	    surface->src_picture = None;
+	else if (status == CAIRO_STATUS_SUCCESS)
+	    status = status2;
+    }
+
+    if (surface->owns_pixmap) {
+	cairo_status_t status2;
+	status2 = _cairo_xlib_display_queue_resource (display,
+		                           (cairo_xlib_notify_resource_func) XFreePixmap,
+					   surface->drawable);
+	if (status2 == CAIRO_STATUS_SUCCESS) {
+	    surface->owns_pixmap = FALSE;
+	    surface->drawable = None;
+	} else if (status == CAIRO_STATUS_SUCCESS)
+	    status = status2;
+    }
+
+    if (surface->gc != NULL) {
+	cairo_status_t status2;
+	status2 = _cairo_xlib_display_queue_work (display,
+		                               (cairo_xlib_notify_func) XFreeGC,
+					       surface->gc,
+					       NULL);
+	if (status2 == CAIRO_STATUS_SUCCESS)
+	    surface->gc = NULL;
+	else if (status == CAIRO_STATUS_SUCCESS)
+	    status = status2;
+    }
 
     if (surface->clip_rects != surface->embedded_clip_rects)
 	free (surface->clip_rects);
@@ -286,9 +325,12 @@ _cairo_xlib_surface_finish (void *abstra
     if (surface->screen_info != NULL)
 	_cairo_xlib_screen_info_destroy (surface->screen_info);
 
-    surface->dpy = NULL;
+    if (surface->dpy != NULL) {
+	_cairo_xlib_remove_close_display_hooks (surface->dpy, surface);
+	surface->dpy = NULL;
+    }
 
-    return CAIRO_STATUS_SUCCESS;
+    return status;
 }
 
 static int
@@ -1809,6 +1851,35 @@ _cairo_surface_is_xlib (cairo_surface_t 
     return surface->backend == &cairo_xlib_surface_backend;
 }
 
+static void
+_cairo_xlib_surface_detach_display (Display *dpy, void *data)
+{
+    cairo_xlib_surface_t *surface = data;
+
+    surface->dpy = NULL;
+
+    if (surface->dst_picture != None) {
+	XRenderFreePicture (dpy, surface->dst_picture);
+	surface->dst_picture = None;
+    }
+
+    if (surface->src_picture != None) {
+	XRenderFreePicture (dpy, surface->src_picture);
+	surface->src_picture = None;
+    }
+
+    if (surface->owns_pixmap) {
+	XFreePixmap (dpy, surface->drawable);
+	surface->drawable = None;
+	surface->owns_pixmap = FALSE;
+    }
+
+    if (surface->gc != NULL) {
+	XFreeGC (dpy, surface->gc);
+	surface->gc = NULL;
+    }
+}
+
 static cairo_surface_t *
 _cairo_xlib_surface_create_internal (Display		       *dpy,
 				     Drawable		        drawable,
@@ -1835,6 +1906,14 @@ _cairo_xlib_surface_create_internal (Dis
 	return (cairo_surface_t*) &_cairo_surface_nil;
     }
 
+    if (! _cairo_xlib_add_close_display_hook (dpy,
+	    _cairo_xlib_surface_detach_display, surface, surface)) {
+	free (surface);
+	_cairo_xlib_screen_info_destroy (screen_info);
+	_cairo_error (CAIRO_STATUS_NO_MEMORY);
+	return (cairo_surface_t*) &_cairo_surface_nil;
+    }
+
     if (xrender_format) {
 	depth = xrender_format->depth;
     } else if (visual) {
@@ -2086,6 +2165,7 @@ cairo_xlib_surface_set_drawable (cairo_s
 				 int		    height)
 {
     cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface;
+    cairo_status_t status;
 
     if (! _cairo_surface_is_xlib (abstract_surface)) {
 	_cairo_surface_set_error (abstract_surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
@@ -2097,14 +2177,31 @@ cairo_xlib_surface_set_drawable (cairo_s
 	return;
 
     if (surface->drawable != drawable) {
-	if (surface->dst_picture)
-	    XRenderFreePicture (surface->dpy, surface->dst_picture);
+	if (surface->dst_picture != None) {
+	    status = _cairo_xlib_display_queue_resource (
+		                                  surface->screen_info->display,
+						  XRenderFreePicture,
+						  surface->dst_picture);
+	    if (status) {
+		_cairo_surface_set_error (&surface->base, status);
+		return;
+	    }
 
-	if (surface->src_picture)
-	    XRenderFreePicture (surface->dpy, surface->src_picture);
+	    surface->dst_picture = None;
+	}
 
-	surface->dst_picture = None;
-	surface->src_picture = None;
+	if (surface->src_picture != None) {
+	    status = _cairo_xlib_display_queue_resource (
+		                                  surface->screen_info->display,
+						  XRenderFreePicture,
+						  surface->src_picture);
+	    if (status) {
+		_cairo_surface_set_error (&surface->base, status);
+		return;
+	    }
+
+	    surface->src_picture = None;
+	}
 
 	surface->drawable = drawable;
     }
@@ -2335,12 +2432,34 @@ _cairo_xlib_surface_scaled_font_fini (ca
     cairo_xlib_surface_font_private_t	*font_private = scaled_font->surface_private;
 
     if (font_private) {
+	cairo_xlib_display_t *display;
+
 	_cairo_xlib_remove_close_display_hooks (font_private->dpy, scaled_font);
-	XRenderFreeGlyphSet (font_private->dpy, font_private->glyphset);
+
+	display = _cairo_xlib_display_get (font_private->dpy);
+	if (display != NULL) {
+	    cairo_status_t status = _cairo_xlib_display_queue_resource (display,
+					                XRenderFreeGlyphSet,
+					                font_private->glyphset);
+	    (void) status; /* XXX cannot propagate failure */
+	    _cairo_xlib_display_destroy (display);
+	}
+
 	free (font_private);
     }
 }
 
+struct _cairo_xlib_render_free_glyphs {
+    GlyphSet glyphset;
+    unsigned long glyph_index;
+};
+static void _cairo_xlib_render_free_glyphs (Display *dpy, struct _cairo_xlib_render_free_glyphs *arg)
+{
+    XRenderFreeGlyphs (dpy,
+	               arg->glyphset,
+	               &arg->glyph_index, 1);
+}
+
 static void
 _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
 				       cairo_scaled_font_t  *scaled_font)
@@ -2348,10 +2467,23 @@ _cairo_xlib_surface_scaled_glyph_fini (c
     cairo_xlib_surface_font_private_t	*font_private = scaled_font->surface_private;
 
     if (font_private != NULL && scaled_glyph->surface_private != NULL) {
-	unsigned long	glyph_index = _cairo_scaled_glyph_index(scaled_glyph);
-	XRenderFreeGlyphs (font_private->dpy,
-			   font_private->glyphset,
-			   &glyph_index, 1);
+	cairo_xlib_display_t *display = _cairo_xlib_display_get (font_private->dpy);
+	if (display != NULL) {
+	    struct _cairo_xlib_render_free_glyphs *arg = malloc (sizeof (*arg));
+	    if (arg != NULL) {
+		cairo_status_t status;
+		arg->glyphset = font_private->glyphset;
+		arg->glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
+		status = _cairo_xlib_display_queue_work (display,
+			(cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs,
+			arg,
+			free);
+		(void) status; /* XXX cannot propagate failure */
+	    }
+
+	    _cairo_xlib_display_destroy (display);
+
+	}
     }
 }
 
diff-tree 8ad30ccdb0a00701b15003edb2fe0cd4a8a9dfb7 (from dd8681b76b088bab45c99510258920f027490f70)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Apr 19 12:15:04 2007 +0100

    [cairo-xlib] Introduce a workqueue for deferred destruction of X resources.
    
    Due to the nature of the reference counting, an X resource may be
    destroyed later than anticipated and possibly from a different thread
    than the original context. This becomes an issue for applications that
    carefully manage their single X connection from a single thread and do
    not use locking and are then suprised when cairo triggers X traffic when
    performing work for a different part of the application in another thread.

diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index 2faf4f9..64286cd 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -40,8 +40,29 @@
 #include <X11/Xlibint.h>	/* For XESetCloseDisplay */
 #include <X11/extensions/Xrender.h>
 
+typedef int (*cairo_xlib_error_func_t) (Display     *display,
+					XErrorEvent *event);
 
-static cairo_xlib_display_t *_cairo_xlib_display_list = NULL;
+struct _cairo_xlib_job {
+    cairo_xlib_job_t *next;
+    enum {
+	RESOURCE,
+	WORK
+    } type;
+    union {
+	struct {
+	    cairo_xlib_notify_resource_func notify;
+	    XID xid;
+	} resource;
+	struct {
+	    cairo_xlib_notify_func notify;
+	    void *data;
+	    void (*destroy) (void *);
+	} work;
+    } func;
+};
+
+static cairo_xlib_display_t *_cairo_xlib_display_list;
 
 static void
 _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
@@ -50,6 +71,7 @@ _cairo_xlib_call_close_display_hooks (ca
 
     /* call all registered shutdown routines */
     CAIRO_MUTEX_LOCK (display->mutex);
+
     hooks = display->close_display_hooks;
     while (hooks != NULL) {
 	display->close_display_hooks = NULL;
@@ -68,6 +90,7 @@ _cairo_xlib_call_close_display_hooks (ca
 	hooks = display->close_display_hooks;
     }
     display->closed = TRUE;
+
     CAIRO_MUTEX_UNLOCK (display->mutex);
 }
 
@@ -97,6 +120,18 @@ _cairo_xlib_display_destroy (cairo_xlib_
     CAIRO_MUTEX_LOCK (display->mutex);
     assert (display->ref_count > 0);
     if (--display->ref_count == 0) {
+	/* destroy all outstanding notifies */
+	while (display->workqueue != NULL) {
+	    cairo_xlib_job_t *job = display->workqueue;
+	    display->workqueue = job->next;
+
+	    if (job->type == WORK && job->func.work.destroy != NULL)
+		job->func.work.destroy (job->func.work.data);
+
+	    _cairo_freelist_free (&display->wq_freelist, job);
+	}
+	_cairo_freelist_fini (&display->wq_freelist);
+
 	CAIRO_MUTEX_UNLOCK (display->mutex);
 
 	free (display);
@@ -105,6 +140,12 @@ _cairo_xlib_display_destroy (cairo_xlib_
 }
 
 static int
+_noop_error_handler (Display     *display,
+		     XErrorEvent *event)
+{
+    return False;		/* return value is ignored */
+}
+static int
 _cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
 {
     cairo_xlib_display_t *display, **prev, *next;
@@ -117,13 +158,26 @@ _cairo_xlib_close_display (Display *dpy,
     for (display = _cairo_xlib_display_list; display; display = next) {
 	next = display->next;
 	if (display->display == dpy) {
+	    cairo_xlib_error_func_t old_handler;
+
 	    /* drop the list mutex whilst triggering the hooks */
 	    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
 
+	    /* protect the notifies from triggering XErrors */
+	    XSync (dpy, False);
+	    old_handler = XSetErrorHandler (_noop_error_handler);
+
+	    _cairo_xlib_display_notify (display);
 	    _cairo_xlib_call_close_display_hooks (display);
-	    _cairo_xlib_display_destroy (display);
+
+	    /* catch any that arrived before marking the display as closed */
+	    _cairo_xlib_display_notify (display);
+
+	    XSync (dpy, False);
+	    XSetErrorHandler (old_handler);
 
 	    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+	    _cairo_xlib_display_destroy (display);
 	    *prev = next;
 	    break;
 	} else
@@ -185,10 +239,12 @@ _cairo_xlib_display_get (Display *dpy)
 
     XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
 
+    _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t));
     display->ref_count = 2; /* add one for the CloseDisplay */
     CAIRO_MUTEX_INIT (display->mutex);
     display->display = dpy;
     display->screens = NULL;
+    display->workqueue = NULL;
     display->close_display_hooks = NULL;
     display->closed = FALSE;
 
@@ -256,3 +312,112 @@ _cairo_xlib_remove_close_display_hooks (
 
     _cairo_xlib_display_destroy (display);
 }
+
+cairo_status_t
+_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
+	                            cairo_xlib_notify_resource_func notify,
+				    XID xid)
+{
+    cairo_xlib_job_t *job;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    job = _cairo_freelist_alloc (&display->wq_freelist);
+    if (job == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    job->type = RESOURCE;
+    job->func.resource.xid = xid;
+    job->func.resource.notify = notify;
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    if (display->closed == FALSE) {
+	job->next = display->workqueue;
+	display->workqueue = job;
+    } else {
+	_cairo_freelist_free (&display->wq_freelist, job);
+	job = NULL;
+	status = CAIRO_STATUS_NO_MEMORY;
+    }
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
+	                        cairo_xlib_notify_func notify,
+				void *data,
+				void (*destroy) (void *))
+{
+    cairo_xlib_job_t *job;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    job = _cairo_freelist_alloc (&display->wq_freelist);
+    if (job == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    job->type = WORK;
+    job->func.work.data    = data;
+    job->func.work.notify  = notify;
+    job->func.work.destroy = destroy;
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    if (display->closed == FALSE) {
+	job->next = display->workqueue;
+	display->workqueue = job;
+    } else {
+	_cairo_freelist_free (&display->wq_freelist, job);
+	job = NULL;
+	status = CAIRO_STATUS_NO_MEMORY;
+    }
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    return status;
+}
+
+void
+_cairo_xlib_display_notify (cairo_xlib_display_t *display)
+{
+    cairo_xlib_job_t *jobs, *job;
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    jobs = display->workqueue;
+    while (jobs != NULL) {
+	display->workqueue = NULL;
+	CAIRO_MUTEX_UNLOCK (display->mutex);
+
+	/* reverse the list to obtain FIFO order */
+	job = NULL;
+	do {
+	    cairo_xlib_job_t *next = jobs->next;
+	    jobs->next = job;
+	    job = jobs;
+	    jobs = next;
+	} while (jobs != NULL);
+	jobs = job;
+
+	do {
+	    job = jobs;
+	    jobs = job->next;
+
+	    switch (job->type){
+	    case WORK:
+		job->func.work.notify (display->display, job->func.work.data);
+		if (job->func.work.destroy != NULL)
+		    job->func.work.destroy (job->func.work.data);
+		break;
+
+	    case RESOURCE:
+		job->func.resource.notify (display->display,
+			                   job->func.resource.xid);
+		break;
+	    }
+
+	    _cairo_freelist_free (&display->wq_freelist, job);
+	} while (jobs != NULL);
+
+	CAIRO_MUTEX_LOCK (display->mutex);
+	jobs = display->workqueue;
+    }
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+}
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 63b4a98..436c67f 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -35,9 +35,13 @@
 
 #include "cairoint.h"
 #include "cairo-xlib.h"
+#include "cairo-freelist-private.h"
 
 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;
+typedef void (*cairo_xlib_notify_func) (Display *, void *);
+typedef void (*cairo_xlib_notify_resource_func) (Display *, XID);
 
 struct _cairo_xlib_hook {
     cairo_xlib_hook_t *next;
@@ -54,6 +58,9 @@ struct _cairo_xlib_display {
     Display *display;
     cairo_xlib_screen_info_t *screens;
 
+    cairo_xlib_job_t *workqueue;
+    cairo_freelist_t wq_freelist;
+
     cairo_xlib_hook_t *close_display_hooks;
     unsigned int closed :1;
 };
@@ -82,6 +89,17 @@ _cairo_xlib_add_close_display_hook (Disp
 cairo_private void
 _cairo_xlib_remove_close_display_hooks (Display *display, const void *key);
 
+cairo_private cairo_status_t
+_cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
+	                        cairo_xlib_notify_func notify,
+				void *data,
+				void (*destroy)(void *));
+cairo_private cairo_status_t
+_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
+	                           cairo_xlib_notify_resource_func notify,
+				   XID resource);
+cairo_private void
+_cairo_xlib_display_notify (cairo_xlib_display_t *display);
 
 cairo_private cairo_xlib_screen_info_t *
 _cairo_xlib_screen_info_get (Display *display, Screen *screen);
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
index 4a38b91..4705244 100644
--- a/src/cairo-xlib-screen.c
+++ b/src/cairo-xlib-screen.c
@@ -242,7 +242,6 @@ _cairo_xlib_init_screen_font_options (Di
     cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON);
 }
 
-
 cairo_xlib_screen_info_t *
 _cairo_xlib_screen_info_reference (cairo_xlib_screen_info_t *info)
 {
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index f7b01a4..fcc9b57 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -220,6 +220,8 @@ _cairo_xlib_surface_create_similar (void
     cairo_xlib_surface_t *surface;
     Pixmap pix;
 
+    _cairo_xlib_display_notify (src->screen_info->display);
+
     /* Start by examining the surface's XRenderFormat, or if it
      * doesn't have one, then look one up through its visual (in the
      * case of a bitmap, it won't even have that). */
@@ -754,6 +756,8 @@ _cairo_xlib_surface_acquire_source_image
     cairo_image_surface_t *image;
     cairo_status_t status;
 
+    _cairo_xlib_display_notify (surface->screen_info->display);
+
     status = _get_image_surface (surface, NULL, &image, NULL);
     if (status)
 	return status;
@@ -783,6 +787,8 @@ _cairo_xlib_surface_acquire_dest_image (
     cairo_image_surface_t *image;
     cairo_status_t status;
 
+    _cairo_xlib_display_notify (surface->screen_info->display);
+
     status = _get_image_surface (surface, interest_rect, &image, image_rect_out);
     if (status)
 	return status;
@@ -834,6 +840,8 @@ _cairo_xlib_surface_clone_similar (void	
     cairo_xlib_surface_t *clone;
     cairo_status_t status;
 
+    _cairo_xlib_display_notify (surface->screen_info->display);
+
     if (src->backend == surface->base.backend ) {
 	cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src;
 
@@ -1268,6 +1276,8 @@ _cairo_xlib_surface_composite (cairo_ope
     int				itx, ity;
     cairo_bool_t		is_integer_translation;
 
+    _cairo_xlib_display_notify (dst->screen_info->display);
+
     if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
@@ -1417,6 +1427,8 @@ _cairo_xlib_surface_fill_rectangles (voi
     cairo_xlib_surface_t *surface = abstract_surface;
     XRenderColor render_color;
 
+    _cairo_xlib_display_notify (surface->screen_info->display);
+
     if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
@@ -1549,6 +1561,8 @@ _cairo_xlib_surface_composite_trapezoids
     int				render_src_x, render_src_y;
     XRenderPictFormat		*pict_format;
 
+    _cairo_xlib_display_notify (dst->screen_info->display);
+
     if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
@@ -1661,6 +1675,8 @@ _cairo_xlib_surface_set_clip_region (voi
     if (surface->have_clip_rects == FALSE && region == NULL)
 	return CAIRO_STATUS_SUCCESS;
 
+    _cairo_xlib_display_notify (surface->screen_info->display);
+
     if (surface->clip_rects != surface->embedded_clip_rects) {
 	free (surface->clip_rects);
 	surface->clip_rects = surface->embedded_clip_rects;
@@ -2266,7 +2282,7 @@ typedef struct _cairo_xlib_surface_font_
 
 static void
 _cairo_xlib_surface_remove_scaled_font (Display *dpy,
-	                               void    *data)
+	                                void    *data)
 {
     cairo_scaled_font_t *scaled_font = data;
     cairo_xlib_surface_font_private_t	*font_private;
@@ -2278,7 +2294,7 @@ _cairo_xlib_surface_remove_scaled_font (
     _cairo_scaled_font_reset_cache (scaled_font);
     CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
 
-    if (font_private) {
+    if (font_private != NULL) {
 	XRenderFreeGlyphSet (font_private->dpy, font_private->glyphset);
 	free (font_private);
     }
@@ -2947,6 +2963,7 @@ _cairo_xlib_surface_show_glyphs (void   
 	_cairo_pattern_fini (&solid_pattern.base);
   BAIL0:
     _cairo_scaled_font_thaw_cache (scaled_font);
+    _cairo_xlib_display_notify (dst->screen_info->display);
 
     return status;
 }
diff-tree dd8681b76b088bab45c99510258920f027490f70 (from 9919f1ed9ccbec637252e06dd1de5f4665c873b4)
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Apr 16 15:04:08 2007 +0100

    [cairo-xlib] Split per-display attributes from per-screen.
    
    Previously, we stored the per-display attributes inside a special
    screen=NULL _cairo_xlib_screen_info_t. Now we keep track of known X
    displays and store the screen information beneath the display structure
    alongside the per-display hooks.

diff --git a/src/Makefile.am b/src/Makefile.am
index ae1b6dc..5263867 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,8 +47,12 @@ endif
 
 if CAIRO_HAS_XLIB_SURFACE
 libcairo_xlib_headers = cairo-xlib.h cairo-xlib-xrender.h
-libcairo_xlib_sources = cairo-xlib-surface.c cairo-xlib-surface-private.h \
-			cairo-xlib-screen.c cairo-xlib-private.h
+libcairo_xlib_sources = cairo-xlib-surface.c \
+			cairo-xlib-surface-private.h \
+			cairo-xlib-display.c \
+			cairo-xlib-screen.c \
+			cairo-xlib-private.h \
+			cairo-xlib-test.h
 backend_pkgconfigs += cairo-xlib.pc
 endif
 
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index dd57dd7..6a50353 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -59,10 +59,6 @@
 void
 cairo_debug_reset_static_data (void)
 {
-#if CAIRO_HAS_XLIB_SURFACE
-    _cairo_xlib_screen_reset_static_data ();
-#endif
-
     _cairo_font_reset_static_data ();
 
 #if CAIRO_HAS_FT_FONT
diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h
index 7e65ddc..3737831 100644
--- a/src/cairo-mutex-list-private.h
+++ b/src/cairo-mutex-list-private.h
@@ -42,7 +42,7 @@ CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_
 #endif
 
 #if CAIRO_HAS_XLIB_SURFACE
-CAIRO_MUTEX_DECLARE (_cairo_xlib_screen_mutex);
+CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex);
 #endif
 
 
diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
new file mode 100644
index 0000000..2faf4f9
--- /dev/null
+++ b/src/cairo-xlib-display.c
@@ -0,0 +1,258 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Chris Wilson.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xlib-private.h"
+
+#include <fontconfig/fontconfig.h>
+
+#include <X11/Xlibint.h>	/* For XESetCloseDisplay */
+#include <X11/extensions/Xrender.h>
+
+
+static cairo_xlib_display_t *_cairo_xlib_display_list = NULL;
+
+static void
+_cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
+{
+    cairo_xlib_hook_t *hooks;
+
+    /* call all registered shutdown routines */
+    CAIRO_MUTEX_LOCK (display->mutex);
+    hooks = display->close_display_hooks;
+    while (hooks != NULL) {
+	display->close_display_hooks = NULL;
+	CAIRO_MUTEX_UNLOCK (display->mutex);
+
+	do {
+	    cairo_xlib_hook_t *hook = hooks;
+	    hooks = hook->next;
+
+	    hook->func (display->display, hook->data);
+
+	    free (hook);
+	} while (hooks != NULL);
+
+	CAIRO_MUTEX_LOCK (display->mutex);
+	hooks = display->close_display_hooks;
+    }
+    display->closed = TRUE;
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+}
+
+cairo_xlib_display_t *
+_cairo_xlib_display_reference (cairo_xlib_display_t *display)
+{
+    if (display == NULL)
+	return NULL;
+
+    /* use our mutex until we get a real atomic inc */
+    CAIRO_MUTEX_LOCK (display->mutex);
+
+    assert (display->ref_count > 0);
+    display->ref_count++;
+
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    return display;
+}
+
+void
+_cairo_xlib_display_destroy (cairo_xlib_display_t *display)
+{
+    if (display == NULL)
+	return;
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    assert (display->ref_count > 0);
+    if (--display->ref_count == 0) {
+	CAIRO_MUTEX_UNLOCK (display->mutex);
+
+	free (display);
+    } else
+	CAIRO_MUTEX_UNLOCK (display->mutex);
+}
+
+static int
+_cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
+{
+    cairo_xlib_display_t *display, **prev, *next;
+
+    /*
+     * Unhook from the global list
+     */
+    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+    prev = &_cairo_xlib_display_list;
+    for (display = _cairo_xlib_display_list; display; display = next) {
+	next = display->next;
+	if (display->display == dpy) {
+	    /* drop the list mutex whilst triggering the hooks */
+	    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+	    _cairo_xlib_call_close_display_hooks (display);
+	    _cairo_xlib_display_destroy (display);
+
+	    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+	    *prev = next;
+	    break;
+	} else
+	    prev = &display->next;
+    }
+    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+    /* Return value in accordance with requirements of
+     * XESetCloseDisplay */
+    return 0;
+}
+
+cairo_xlib_display_t *
+_cairo_xlib_display_get (Display *dpy)
+{
+    cairo_xlib_display_t *display;
+    cairo_xlib_display_t **prev;
+    XExtCodes *codes;
+
+    /* There is an apparent deadlock between this mutex and the
+     * mutex for the display, but it's actually safe. For the
+     * app to call XCloseDisplay() while any other thread is
+     * inside this function would be an error in the logic
+     * app, and the CloseDisplay hook is the only other place we
+     * acquire this mutex.
+     */
+    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+
+    for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next)
+    {
+	if (display->display == dpy) {
+	    /*
+	     * MRU the list
+	     */
+	    if (prev != &_cairo_xlib_display_list) {
+		*prev = display->next;
+		display->next = _cairo_xlib_display_list;
+		_cairo_xlib_display_list = display;
+	    }
+	    break;
+	}
+    }
+
+    if (display != NULL) {
+	display = _cairo_xlib_display_reference (display);
+	goto UNLOCK;
+    }
+
+    display = malloc (sizeof (cairo_xlib_display_t));
+    if (display == NULL)
+	goto UNLOCK;
+
+    codes = XAddExtension (dpy);
+    if (codes == NULL) {
+	free (display);
+	display = NULL;
+	goto UNLOCK;
+    }
+
+    XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
+
+    display->ref_count = 2; /* add one for the CloseDisplay */
+    CAIRO_MUTEX_INIT (display->mutex);
+    display->display = dpy;
+    display->screens = NULL;
+    display->close_display_hooks = NULL;
+    display->closed = FALSE;
+
+    display->next = _cairo_xlib_display_list;
+    _cairo_xlib_display_list = display;
+
+UNLOCK:
+    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+    return display;
+}
+
+cairo_bool_t
+_cairo_xlib_add_close_display_hook (Display *dpy, void (*func) (Display *, void *), void *data, const void *key)
+{
+    cairo_xlib_display_t *display;
+    cairo_xlib_hook_t *hook;
+    cairo_bool_t ret = FALSE;
+
+    display = _cairo_xlib_display_get (dpy);
+    if (display == NULL)
+	return FALSE;
+
+    hook = malloc (sizeof (cairo_xlib_hook_t));
+    if (hook != NULL) {
+	hook->func = func;
+	hook->data = data;
+	hook->key = key;
+
+	CAIRO_MUTEX_LOCK (display->mutex);
+	if (display->closed == FALSE) {
+	    hook->next = display->close_display_hooks;
+	    display->close_display_hooks = hook;
+	    ret = TRUE;
+	}
+	CAIRO_MUTEX_UNLOCK (display->mutex);
+    }
+
+    _cairo_xlib_display_destroy (display);
+
+    return ret;
+}
+
+void
+_cairo_xlib_remove_close_display_hooks (Display *dpy, const void *key)
+{
+    cairo_xlib_display_t *display;
+    cairo_xlib_hook_t *hook, *next, **prev;
+
+    display = _cairo_xlib_display_get (dpy);
+    if (display == NULL)
+	return;
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    prev = &display->close_display_hooks;
+    for (hook = display->close_display_hooks; hook != NULL; hook = next) {
+	next = hook->next;
+	if (hook->key == key) {
+	    *prev = hook->next;
+	    free (hook);
+	} else
+	    prev = &hook->next;
+    }
+    *prev = NULL;
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    _cairo_xlib_display_destroy (display);
+}
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 1fd47fa..63b4a98 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -36,28 +36,53 @@
 #include "cairoint.h"
 #include "cairo-xlib.h"
 
+typedef struct _cairo_xlib_display cairo_xlib_display_t;
 typedef struct _cairo_xlib_hook cairo_xlib_hook_t;
 
 struct _cairo_xlib_hook {
     cairo_xlib_hook_t *next;
     void (*func) (Display *display, void *data);
     void *data;
-    void *key;
+    const void *key;
+};
+
+struct _cairo_xlib_display {
+    cairo_xlib_display_t *next;
+    unsigned int ref_count;
+    cairo_mutex_t mutex;
+
+    Display *display;
+    cairo_xlib_screen_info_t *screens;
+
+    cairo_xlib_hook_t *close_display_hooks;
+    unsigned int closed :1;
 };
 
 struct _cairo_xlib_screen_info {
     cairo_xlib_screen_info_t *next;
     unsigned int ref_count;
 
-    Display *display;
+    cairo_xlib_display_t *display;
     Screen *screen;
     cairo_bool_t has_render;
 
     cairo_font_options_t font_options;
-
-    cairo_xlib_hook_t *close_display_hooks;
 };
 
+cairo_private cairo_xlib_display_t *
+_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);
+
+cairo_private cairo_bool_t
+_cairo_xlib_add_close_display_hook (Display *display, void (*func) (Display *, void *), void *data, const void *key);
+cairo_private void
+_cairo_xlib_remove_close_display_hooks (Display *display, const void *key);
+
+
 cairo_private cairo_xlib_screen_info_t *
 _cairo_xlib_screen_info_get (Display *display, Screen *screen);
 
@@ -66,10 +91,6 @@ _cairo_xlib_screen_info_reference (cairo
 cairo_private void
 _cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info);
 
-cairo_private cairo_bool_t
-_cairo_xlib_add_close_display_hook (Display *display, void (*func) (Display *, void *), void *data, void *key);
-cairo_private void
-_cairo_xlib_remove_close_display_hook (Display *display, void *key);
 
 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
 
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
index ef72d53..4a38b91 100644
--- a/src/cairo-xlib-screen.c
+++ b/src/cairo-xlib-screen.c
@@ -58,7 +58,6 @@
 
 #include <fontconfig/fontconfig.h>
 
-#include <X11/Xlibint.h>	/* For XESetCloseDisplay */
 #include <X11/extensions/Xrender.h>
 
 static int
@@ -133,7 +132,7 @@ get_integer_default (Display    *dpy,
 #endif
 
 static void
-_cairo_xlib_init_screen_font_options (cairo_xlib_screen_info_t *info)
+_cairo_xlib_init_screen_font_options (Display *dpy, cairo_xlib_screen_info_t *info)
 {
     cairo_bool_t xft_hinting;
     cairo_bool_t xft_antialias;
@@ -143,23 +142,23 @@ _cairo_xlib_init_screen_font_options (ca
     cairo_subpixel_order_t subpixel_order;
     cairo_hint_style_t hint_style;
 
-    if (!get_boolean_default (info->display, "antialias", &xft_antialias))
+    if (!get_boolean_default (dpy, "antialias", &xft_antialias))
 	xft_antialias = TRUE;
 
-    if (!get_boolean_default (info->display, "hinting", &xft_hinting))
+    if (!get_boolean_default (dpy, "hinting", &xft_hinting))
 	xft_hinting = TRUE;
 
-    if (!get_integer_default (info->display, "hintstyle", &xft_hintstyle))
+    if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle))
 	xft_hintstyle = FC_HINT_FULL;
 
-    if (!get_integer_default (info->display, "rgba", &xft_rgba))
+    if (!get_integer_default (dpy, "rgba", &xft_rgba))
     {
 	xft_rgba = FC_RGBA_UNKNOWN;
 
 #if RENDER_MAJOR > 0 || RENDER_MINOR >= 6
 	if (info->has_render)
 	{
-	    int render_order = XRenderQuerySubpixelOrder (info->display,
+	    int render_order = XRenderQuerySubpixelOrder (dpy,
 							  XScreenNumberOfScreen (info->screen));
 
 	    switch (render_order)
@@ -243,37 +242,6 @@ _cairo_xlib_init_screen_font_options (ca
     cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON);
 }
 
-static cairo_xlib_screen_info_t *_cairo_xlib_screen_list = NULL;
-
-/* NOTE: This function must be called with _cairo_xlib_screen_mutex held. */
-static void
-_cairo_xlib_call_close_display_hooks (cairo_xlib_screen_info_t *info)
-{
-    /* call all registered shutdown routines */
-    while (info->close_display_hooks != NULL) {
-	cairo_xlib_hook_t *hooks = info->close_display_hooks;
-	info->close_display_hooks = NULL;
-
-	/* drop the list mutex whilst calling the hooks */
-	CAIRO_MUTEX_UNLOCK (_cairo_xlib_screen_mutex);
-	do {
-	    cairo_xlib_hook_t *hook = hooks;
-	    hooks = hook->next;
-
-	    hook->func (info->display, hook->data);
-
-	    free (hook);
-	} while (hooks != NULL);
-	CAIRO_MUTEX_LOCK (_cairo_xlib_screen_mutex);
-    }
-}
-
-static void
-_cairo_xlib_screen_info_reference_lock_held (cairo_xlib_screen_info_t *info)
-{
-    assert (info->ref_count > 0);
-    info->ref_count++;
-}
 
 cairo_xlib_screen_info_t *
 _cairo_xlib_screen_info_reference (cairo_xlib_screen_info_t *info)
@@ -281,247 +249,98 @@ _cairo_xlib_screen_info_reference (cairo
     if (info == NULL)
 	return NULL;
 
-    /* use our global mutex until we get a real atomic inc */
-    CAIRO_MUTEX_LOCK (_cairo_xlib_screen_mutex);
-
-    _cairo_xlib_screen_info_reference_lock_held (info);
-
-    CAIRO_MUTEX_UNLOCK (_cairo_xlib_screen_mutex);
-
-    return info;
-}
-
-static void
-_cairo_xlib_screen_info_destroy_lock_held (cairo_xlib_screen_info_t *info)
-{
     assert (info->ref_count > 0);
-    if (--info->ref_count)
-	return;
+    info->ref_count++;
 
-    _cairo_xlib_call_close_display_hooks (info);
-    free (info);
+    return info;
 }
 
 void
 _cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info)
 {
+    cairo_xlib_screen_info_t **prev;
+    cairo_xlib_screen_info_t *list;
+
     if (info == NULL)
 	return;
 
-    CAIRO_MUTEX_LOCK (_cairo_xlib_screen_mutex);
-
-    _cairo_xlib_screen_info_destroy_lock_held (info);
-
-    CAIRO_MUTEX_UNLOCK (_cairo_xlib_screen_mutex);
-}
-
-static int
-_cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
-{
-    cairo_xlib_screen_info_t *info, **prev, *next;
+    assert (info->ref_count > 0);
+    if (--info->ref_count)
+	return;
 
-    /*
-     * Unhook from the global list
-     */
-    CAIRO_MUTEX_LOCK (_cairo_xlib_screen_mutex);
-
-    prev = &_cairo_xlib_screen_list;
-    for (info = _cairo_xlib_screen_list; info; info = next) {
-	next = info->next;
-	if (info->display == dpy) {
-	    /* trigger the hooks explicitly as we know the display is closing */
-	    _cairo_xlib_call_close_display_hooks (info);
-	    _cairo_xlib_screen_info_destroy_lock_held (info);
-	    *prev = next;
-	} else {
-	    prev = &info->next;
+    CAIRO_MUTEX_LOCK (info->display->mutex);
+    for (prev = &info->display->screens; (list = *prev); prev = &list->next) {
+	if (list == info) {
+	    *prev = info->next;
+	    break;
 	}
     }
-    *prev = NULL;
-    CAIRO_MUTEX_UNLOCK (_cairo_xlib_screen_mutex);
+    assert (list != NULL);
+    CAIRO_MUTEX_UNLOCK (info->display->mutex);
 
-    /* Return value in accordance with requirements of
-     * XESetCloseDisplay */
-    return 0;
-}
-
-static void
-_cairo_xlib_screen_info_reset (void)
-{
-    /*
-     * Delete everything in the list.
-     */
-    CAIRO_MUTEX_LOCK (_cairo_xlib_screen_mutex);
-
-    while (_cairo_xlib_screen_list != NULL) {
-	cairo_xlib_screen_info_t *info = _cairo_xlib_screen_list;
-	_cairo_xlib_screen_list = info->next;
-	_cairo_xlib_screen_info_destroy_lock_held (info);
-    }
-
-    CAIRO_MUTEX_UNLOCK (_cairo_xlib_screen_mutex);
+    _cairo_xlib_display_destroy (info->display);
 
+    free (info);
 }
 
-static cairo_xlib_screen_info_t *
-_cairo_xlib_screen_info_get_lock_held (Display *dpy, Screen *screen)
-{
-    cairo_xlib_screen_info_t *info;
-    cairo_xlib_screen_info_t **prev;
-    int event_base, error_base;
-    XExtCodes *codes;
-    cairo_bool_t seen_display = FALSE;
-
-    for (prev = &_cairo_xlib_screen_list; (info = *prev); prev = &(*prev)->next)
-    {
-	if (info->display == dpy) {
-	    seen_display = TRUE;
-	    if (info->screen == screen) {
-		/*
-		 * MRU the list
-		 */
-		if (prev != &_cairo_xlib_screen_list) {
-		    *prev = info->next;
-		    info->next = _cairo_xlib_screen_list;
-		    _cairo_xlib_screen_list = info;
-		}
-		break;
-	    }
-	}
-    }
-
-    if (info)
-	return info;
-
-    info = malloc (sizeof (cairo_xlib_screen_info_t));
-    if (!info)
-	return NULL;
-
-    if (!seen_display) {
-	codes = XAddExtension (dpy);
-	if (!codes) {
-	    free (info);
-	    return NULL;
-	}
-
-	XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
-    }
-
-    info->ref_count = 1;
-    info->display = dpy;
-    info->screen = screen;
-    info->close_display_hooks = NULL;
-    info->has_render = FALSE;
-    _cairo_font_options_init_default (&info->font_options);
-
-    if (screen) {
-	info->has_render = (XRenderQueryExtension (dpy, &event_base, &error_base) &&
-			    (XRenderFindVisualFormat (dpy, DefaultVisual (dpy, DefaultScreen (dpy))) != 0));
-	_cairo_xlib_init_screen_font_options (info);
-    }
-
-    info->next = _cairo_xlib_screen_list;
-    _cairo_xlib_screen_list = info;
-
-    return info;
-}
 cairo_xlib_screen_info_t *
 _cairo_xlib_screen_info_get (Display *dpy, Screen *screen)
 {
-    cairo_xlib_screen_info_t *info;
-
-    /* There is an apparent deadlock between this mutex and the
-     * mutex for the display, but it's actually safe. For the
-     * app to call XCloseDisplay() while any other thread is
-     * inside this function would be an error in the logic
-     * app, and the CloseDisplay hook is the only other place we
-     * acquire this mutex.
-     */
-    CAIRO_MUTEX_LOCK (_cairo_xlib_screen_mutex);
-
-    info = _cairo_xlib_screen_info_get_lock_held (dpy, screen);
-    if (info != NULL)
-	_cairo_xlib_screen_info_reference_lock_held (info);
-
-    CAIRO_MUTEX_UNLOCK (_cairo_xlib_screen_mutex);
+    cairo_xlib_display_t *display;
+    cairo_xlib_screen_info_t *info = NULL, **prev;
 
-    return info;
-}
+    display = _cairo_xlib_display_get (dpy);
+    if (display == NULL)
+	return NULL;
 
-cairo_bool_t
-_cairo_xlib_add_close_display_hook (Display *dpy, void (*func) (Display *, void *), void *data, void *key)
-{
-    cairo_xlib_screen_info_t *info;
-    cairo_xlib_hook_t *hook;
-    cairo_xlib_hook_t **prev;
-    cairo_bool_t success = FALSE;
-
-    CAIRO_MUTEX_LOCK (_cairo_xlib_screen_mutex);
-
-    info = _cairo_xlib_screen_info_get_lock_held (dpy,  NULL);
-    if (!info)
-	goto unlock;
+    CAIRO_MUTEX_LOCK (display->mutex);
+    if (display->closed) {
+	CAIRO_MUTEX_UNLOCK (display->mutex);
+	goto DONE;
+    }
 
-    for (prev = &info->close_display_hooks; (hook = *prev); prev = &hook->next)
-    {
-	if (hook->key == key) {
+    for (prev = &display->screens; (info = *prev); prev = &(*prev)->next) {
+	if (info->screen == screen) {
 	    /*
 	     * MRU the list
 	     */
-	    if (prev != &info->close_display_hooks) {
-		*prev = hook->next;
-		hook->next = info->close_display_hooks;
-		info->close_display_hooks = hook;
+	    if (prev != &display->screens) {
+		*prev = info->next;
+		info->next = display->screens;
+		display->screens = info;
 	    }
 	    break;
 	}
     }
+    CAIRO_MUTEX_UNLOCK (display->mutex);
 
-    if (!hook) {
-	hook = malloc (sizeof (cairo_xlib_hook_t));
-	if (!hook)
-	    goto unlock;
-	hook->func = func;
-	hook->data = data;
-	hook->key = key;
-	hook->next = info->close_display_hooks;
-	info->close_display_hooks = hook;
-    }
-
-    success = TRUE;
- unlock:
-    CAIRO_MUTEX_UNLOCK (_cairo_xlib_screen_mutex);
-    return success;
-}
-
-void
-_cairo_xlib_remove_close_display_hook (Display *dpy, void *key)
-{
-    cairo_xlib_screen_info_t *info;
-    cairo_xlib_hook_t *hook;
-    cairo_xlib_hook_t **prev;
-
-    CAIRO_MUTEX_LOCK (_cairo_xlib_screen_mutex);
-
-    info = _cairo_xlib_screen_info_get_lock_held (dpy, NULL);
-    if (!info)
-	goto unlock;
+    if (info != NULL) {
+	info = _cairo_xlib_screen_info_reference (info);
+    } else {
+	info = malloc (sizeof (cairo_xlib_screen_info_t));
+	if (info != NULL) {
+	    info->ref_count = 1;
+	    info->display = _cairo_xlib_display_reference (display);
+	    info->screen = screen;
+	    info->has_render = FALSE;
+	    _cairo_font_options_init_default (&info->font_options);
+
+	    if (screen) {
+		int event_base, error_base;
+		info->has_render = (XRenderQueryExtension (dpy, &event_base, &error_base) &&
+			(XRenderFindVisualFormat (dpy, DefaultVisual (dpy, DefaultScreen (dpy))) != 0));
+		_cairo_xlib_init_screen_font_options (dpy, info);
+	    }
 
-    for (prev = &info->close_display_hooks; (hook = *prev); prev = &hook->next)
-    {
-	if (hook->key == key) {
-	    *prev = hook->next;
-	    free (hook);
-	    break;
+	    CAIRO_MUTEX_LOCK (display->mutex);
+	    info->next = display->screens;
+	    display->screens = info;
+	    CAIRO_MUTEX_UNLOCK (display->mutex);
 	}
     }
 
-unlock:
-    CAIRO_MUTEX_UNLOCK (_cairo_xlib_screen_mutex);
-}
+DONE:
+    _cairo_xlib_display_destroy (display);
 
-void
-_cairo_xlib_screen_reset_static_data (void)
-{
-    _cairo_xlib_screen_info_reset ();
+    return info;
 }
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 8ad9f73..f7b01a4 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -2319,7 +2319,7 @@ _cairo_xlib_surface_scaled_font_fini (ca
     cairo_xlib_surface_font_private_t	*font_private = scaled_font->surface_private;
 
     if (font_private) {
-	_cairo_xlib_remove_close_display_hook (font_private->dpy, scaled_font);
+	_cairo_xlib_remove_close_display_hooks (font_private->dpy, scaled_font);
 	XRenderFreeGlyphSet (font_private->dpy, font_private->glyphset);
 	free (font_private);
     }
diff --git a/src/cairoint.h b/src/cairoint.h
index 4ecfc0d..3d1b17d 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -512,12 +512,6 @@ _cairo_font_reset_static_data (void);
 cairo_private void
 _cairo_ft_font_reset_static_data (void);
 
-cairo_private void
-_cairo_xlib_surface_reset_static_data (void);
-
-cairo_private void
-_cairo_xlib_screen_reset_static_data (void);
-
 /* the font backend interface */
 
 struct _cairo_unscaled_font_backend {


More information about the cairo-commit mailing list