diff options
author | Uli Schlachter <psychon@znc.in> | 2011-07-03 17:45:58 +0200 |
---|---|---|
committer | Uli Schlachter <psychon@znc.in> | 2011-07-09 09:42:22 +0200 |
commit | 5b9205cc52f50f997c9cd6c5a64faf783d83310f (patch) | |
tree | 6d2305649de8dcd3e3788713ec0da15eee807e4e | |
parent | 05a0b24ecbafccf63e0114889301fc23268a9efc (diff) |
xlib-xcb: Register a XCloseDisplay hook
This commits makes the xlib-xcb backend produce its own cairo_device_t. This
per-Display* device is used to manage the registration of a XCloseDisplay hook
via XAddExtension/XESetCloseDisplay in the same way that the xlib backend does
this. The device is necessary to see if we already registered an extension.
This fixes weird errors when running cairo-test-suite with -a -s. They were
caused because the backend didn't see the XCloseDisplay and the next
XOpenDisplay happened to create a xcb_connection_t with the same address as the
last display. This caused the xcb backend to assume lots of wrongness.
This commit makes use of _cairo_xlib_display_mutex which is otherwise compiled
in but not used anywhere when xlib-xcb is enabled.
Patch v2: Fixed the xcb_device == NULL case and made sure the xcb_device is only
finished on XCloseDisplay, not when all xlib-xcb surfaces are destroyed.
Signed-off-by: Uli Schlachter <psychon@znc.in>
-rw-r--r-- | src/cairo-xlib-xcb-surface.c | 145 |
1 files changed, 142 insertions, 3 deletions
diff --git a/src/cairo-xlib-xcb-surface.c b/src/cairo-xlib-xcb-surface.c index 8fe0e50f..0ffbc4c5 100644 --- a/src/cairo-xlib-xcb-surface.c +++ b/src/cairo-xlib-xcb-surface.c @@ -47,6 +47,22 @@ #include "cairo-xlib-xrender-private.h" #include <X11/Xlib-xcb.h> +#include <X11/Xlibint.h> /* For XESetCloseDisplay */ + +struct cairo_xlib_xcb_display_t { + cairo_device_t base; + + Display *dpy; + cairo_device_t *xcb_device; + XExtCodes *codes; + + cairo_list_t link; +}; +typedef struct cairo_xlib_xcb_display_t cairo_xlib_xcb_display_t; + +/* List of all cairo_xlib_xcb_display_t alive, + * protected by _cairo_xlib_display_mutex */ +static cairo_list_t displays; static cairo_surface_t * _cairo_xlib_xcb_surface_create (void *dpy, @@ -245,6 +261,124 @@ static const cairo_surface_backend_t _cairo_xlib_xcb_surface_backend = { _cairo_xlib_xcb_surface_glyphs, }; +static void +_cairo_xlib_xcb_display_finish (void *abstract_display) +{ + cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) abstract_display; + + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + cairo_list_del (&display->link); + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + cairo_device_destroy (display->xcb_device); + display->xcb_device = NULL; + + XESetCloseDisplay (display->dpy, display->codes->extension, NULL); +} + +static int +_cairo_xlib_xcb_close_display(Display *dpy, XExtCodes *codes) +{ + cairo_xlib_xcb_display_t *display; + + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + cairo_list_foreach_entry (display, + cairo_xlib_xcb_display_t, + &displays, + link) + { + if (display->dpy == dpy) + { + /* _cairo_xlib_xcb_display_finish will lock the mutex again + * -> deadlock (This mutex isn't recursive) */ + cairo_device_reference (&display->base); + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + /* Make sure the xcb and xlib-xcb devices are finished */ + cairo_device_finish (display->xcb_device); + cairo_device_finish (&display->base); + cairo_device_destroy (&display->base); + return 0; + } + } + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + return 0; +} + +static const cairo_device_backend_t _cairo_xlib_xcb_device_backend = { + CAIRO_DEVICE_TYPE_XLIB, + + NULL, + NULL, + + NULL, /* flush */ + _cairo_xlib_xcb_display_finish, + free, /* destroy */ +}; + +static cairo_device_t * +_cairo_xlib_xcb_device_create (Display *dpy, cairo_device_t *xcb_device) +{ + cairo_xlib_xcb_display_t *display = NULL; + cairo_device_t *device; + + if (xcb_device == NULL) + return NULL; + + CAIRO_MUTEX_INITIALIZE (); + + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + if (displays.next == NULL) { + cairo_list_init (&displays); + } + + cairo_list_foreach_entry (display, + cairo_xlib_xcb_display_t, + &displays, + link) + { + if (display->dpy == dpy) { + /* Maintain MRU order. */ + if (displays.next != &display->link) + cairo_list_move (&display->link, &displays); + + /* Grab a reference for our caller */ + device = cairo_device_reference (&display->base); + assert (display->xcb_device == xcb_device); + goto unlock; + } + } + + display = malloc (sizeof (cairo_xlib_xcb_display_t)); + if (unlikely (display == NULL)) { + device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + goto unlock; + } + + display->codes = XAddExtension (dpy); + if (unlikely (display->codes == NULL)) { + device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + free (display); + goto unlock; + } + + _cairo_device_init (&display->base, &_cairo_xlib_xcb_device_backend); + + XESetCloseDisplay (dpy, display->codes->extension, _cairo_xlib_xcb_close_display); + + display->dpy = dpy; + display->xcb_device = cairo_device_reference(xcb_device); + + cairo_list_add (&display->link, &displays); + device = &display->base; + +unlock: + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + return device; +} + static cairo_surface_t * _cairo_xlib_xcb_surface_create (void *dpy, void *scr, @@ -265,9 +399,12 @@ _cairo_xlib_xcb_surface_create (void *dpy, _cairo_surface_init (&surface->base, &_cairo_xlib_xcb_surface_backend, - xcb->device, + _cairo_xlib_xcb_device_create (dpy, xcb->device), xcb->content); + /* _cairo_surface_init() got another reference to the device, drop ours */ + cairo_device_destroy (surface->base.device); + surface->display = dpy; surface->screen = scr; surface->visual = visual; @@ -604,13 +741,15 @@ void cairo_xlib_device_debug_set_precision (cairo_device_t *device, int precision) { - cairo_xcb_device_debug_set_precision (device, precision); + cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device; + cairo_xcb_device_debug_set_precision (display->xcb_device, precision); } int cairo_xlib_device_debug_get_precision (cairo_device_t *device) { - return cairo_xcb_device_debug_get_precision (device); + cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device; + return cairo_xcb_device_debug_get_precision (display->xcb_device); } #endif /* CAIRO_HAS_XLIB_XCB_FUNCTIONS */ |