Updated: Sudoku no longer crashes
[moon.git] / cairo / src / cairo-xlib-display.c
blobb2e2755d3b482528926ec8c2be054b225287387e
1 /* Cairo - a vector graphics library with display and print output
3 * Copyright © 2007 Chris Wilson
5 * This library is free software; you can redistribute it and/or
6 * modify it either under the terms of the GNU Lesser General Public
7 * License version 2.1 as published by the Free Software Foundation
8 * (the "LGPL") or, at your option, under the terms of the Mozilla
9 * Public License Version 1.1 (the "MPL"). If you do not alter this
10 * notice, a recipient may use your version of this file under either
11 * the MPL or the LGPL.
13 * You should have received a copy of the LGPL along with this library
14 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
19 * The contents of this file are subject to the Mozilla Public License
20 * Version 1.1 (the "License"); you may not use this file except in
21 * compliance with the License. You may obtain a copy of the License at
22 * http://www.mozilla.org/MPL/
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26 * the specific language governing rights and limitations.
28 * The Original Code is the cairo graphics library.
30 * The Initial Developer of the Original Code is Chris Wilson.
32 * Contributor(s):
33 * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
36 #include "cairoint.h"
38 #include "cairo-xlib-private.h"
39 #include "cairo-xlib-xrender-private.h"
41 #include <fontconfig/fontconfig.h>
43 #include <X11/Xlibint.h> /* For XESetCloseDisplay */
45 typedef int (*cairo_xlib_error_func_t) (Display *display,
46 XErrorEvent *event);
48 struct _cairo_xlib_job {
49 cairo_xlib_job_t *next;
50 enum {
51 RESOURCE,
52 WORK
53 } type;
54 union {
55 struct {
56 cairo_xlib_notify_resource_func notify;
57 XID xid;
58 } resource;
59 struct {
60 cairo_xlib_notify_func notify;
61 void *data;
62 void (*destroy) (void *);
63 } work;
64 } func;
67 static cairo_xlib_display_t *_cairo_xlib_display_list;
69 static void
70 _cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
71 cairo_xlib_hook_t *hook);
73 static void
74 _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
76 cairo_xlib_screen_info_t *screen;
77 cairo_xlib_hook_t *hook;
79 /* call all registered shutdown routines */
80 CAIRO_MUTEX_LOCK (display->mutex);
82 for (screen = display->screens; screen != NULL; screen = screen->next)
83 _cairo_xlib_screen_info_close_display (screen);
85 while (TRUE) {
86 hook = display->close_display_hooks;
87 if (hook == NULL)
88 break;
90 _cairo_xlib_remove_close_display_hook_internal (display, hook);
92 CAIRO_MUTEX_UNLOCK (display->mutex);
93 hook->func (display, hook);
94 CAIRO_MUTEX_LOCK (display->mutex);
96 display->closed = TRUE;
98 CAIRO_MUTEX_UNLOCK (display->mutex);
101 static void
102 _cairo_xlib_display_discard_screens (cairo_xlib_display_t *display)
104 cairo_xlib_screen_info_t *screens;
106 CAIRO_MUTEX_LOCK (display->mutex);
107 screens = display->screens;
108 display->screens = NULL;
109 CAIRO_MUTEX_UNLOCK (display->mutex);
111 while (screens != NULL) {
112 cairo_xlib_screen_info_t *screen = screens;
113 screens = screen->next;
115 _cairo_xlib_screen_info_destroy (screen);
119 cairo_xlib_display_t *
120 _cairo_xlib_display_reference (cairo_xlib_display_t *display)
122 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&display->ref_count));
124 _cairo_reference_count_inc (&display->ref_count);
126 return display;
129 void
130 _cairo_xlib_display_destroy (cairo_xlib_display_t *display)
132 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&display->ref_count));
134 if (! _cairo_reference_count_dec_and_test (&display->ref_count))
135 return;
137 /* destroy all outstanding notifies */
138 while (display->workqueue != NULL) {
139 cairo_xlib_job_t *job = display->workqueue;
140 display->workqueue = job->next;
142 if (job->type == WORK && job->func.work.destroy != NULL)
143 job->func.work.destroy (job->func.work.data);
145 _cairo_freelist_free (&display->wq_freelist, job);
147 _cairo_freelist_fini (&display->wq_freelist);
149 CAIRO_MUTEX_FINI (display->mutex);
151 free (display);
154 static int
155 _noop_error_handler (Display *display,
156 XErrorEvent *event)
158 return False; /* return value is ignored */
160 static int
161 _cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
163 cairo_xlib_display_t *display, **prev, *next;
164 cairo_xlib_error_func_t old_handler;
166 CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
167 for (display = _cairo_xlib_display_list; display; display = display->next)
168 if (display->display == dpy)
169 break;
170 CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
171 if (display == NULL)
172 return 0;
174 /* protect the notifies from triggering XErrors */
175 XSync (dpy, False);
176 old_handler = XSetErrorHandler (_noop_error_handler);
178 _cairo_xlib_display_notify (display);
179 _cairo_xlib_call_close_display_hooks (display);
180 _cairo_xlib_display_discard_screens (display);
182 /* catch any that arrived before marking the display as closed */
183 _cairo_xlib_display_notify (display);
185 XSync (dpy, False);
186 XSetErrorHandler (old_handler);
189 * Unhook from the global list
191 CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
192 prev = &_cairo_xlib_display_list;
193 for (display = _cairo_xlib_display_list; display; display = next) {
194 next = display->next;
195 if (display->display == dpy) {
196 *prev = next;
197 break;
198 } else
199 prev = &display->next;
201 CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
203 assert (display != NULL);
204 _cairo_xlib_display_destroy (display);
206 /* Return value in accordance with requirements of
207 * XESetCloseDisplay */
208 return 0;
211 cairo_xlib_display_t *
212 _cairo_xlib_display_get (Display *dpy)
214 cairo_xlib_display_t *display;
215 cairo_xlib_display_t **prev;
216 XExtCodes *codes;
217 int major_unused, minor_unused;
219 /* There is an apparent deadlock between this mutex and the
220 * mutex for the display, but it's actually safe. For the
221 * app to call XCloseDisplay() while any other thread is
222 * inside this function would be an error in the logic
223 * app, and the CloseDisplay hook is the only other place we
224 * acquire this mutex.
226 CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
228 for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next)
230 if (display->display == dpy) {
232 * MRU the list
234 if (prev != &_cairo_xlib_display_list) {
235 *prev = display->next;
236 display->next = _cairo_xlib_display_list;
237 _cairo_xlib_display_list = display;
239 break;
243 if (display != NULL) {
244 display = _cairo_xlib_display_reference (display);
245 goto UNLOCK;
248 display = malloc (sizeof (cairo_xlib_display_t));
249 if (display == NULL) {
250 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
251 goto UNLOCK;
254 /* Xlib calls out to the extension close_display hooks in LIFO
255 * order. So we have to ensure that all extensions that we depend
256 * on in our close_display hook are properly initialized before we
257 * add our hook. For now, that means Render, so we call into its
258 * QueryVersion function to ensure it gets initialized.
260 XRenderQueryVersion (dpy, &major_unused, &minor_unused);
262 codes = XAddExtension (dpy);
263 if (codes == NULL) {
264 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
265 free (display);
266 display = NULL;
267 goto UNLOCK;
270 XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
272 _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t));
274 CAIRO_REFERENCE_COUNT_INIT (&display->ref_count, 2); /* add one for the CloseDisplay */
275 CAIRO_MUTEX_INIT (display->mutex);
276 display->display = dpy;
277 display->screens = NULL;
278 display->workqueue = NULL;
279 display->close_display_hooks = NULL;
280 display->closed = FALSE;
282 memset (display->cached_xrender_formats, 0,
283 sizeof (display->cached_xrender_formats));
285 display->buggy_repeat = FALSE;
287 /* This buggy_repeat condition is very complicated because there
288 * are multiple X server code bases (with multiple versioning
289 * schemes within a code base), and multiple bugs.
291 * The X servers:
293 * 1. The Vendor=="XFree86" code base with release numbers such
294 * as 4.7.0 (VendorRelease==40700000).
296 * 2. The Vendor=="X.Org" code base (a descendant of the
297 * XFree86 code base). It originally had things like
298 * VendorRelease==60700000 for release 6.7.0 but then changed
299 * its versioning scheme so that, for example,
300 * VendorRelease==10400000 for the 1.4.0 X server within the
301 * X.Org 7.3 release.
303 * The bugs:
305 * 1. The original bug that led to the buggy_repeat
306 * workaround. This was a bug that Owen Taylor investigated,
307 * understood well, and characterized against carious X
308 * servers. Confirmed X servers with this bug include:
310 * "XFree86" <= 40500000
311 * "X.Org" <= 60802000 (only with old numbering >= 60700000)
313 * 2. A separate bug resulting in a crash of the X server when
314 * using cairo's extend-reflect test case, (which, surprisingly
315 * enough was not passing RepeatReflect to the X server, but
316 * instead using RepeatNormal in a workaround). Nobody to date
317 * has understood the bug well, but it appears to be gone as of
318 * the X.Org 1.4.0 server. This bug is coincidentally avoided
319 * by using the same buggy_repeat workaround. Confirmed X
320 * servers with this bug include:
322 * "X.org" == 60900000 (old versioning scheme)
323 * "X.org" < 10400000 (new numbering scheme)
325 * For the old-versioning-scheme X servers we don't know
326 * exactly when second the bug started, but since bug 1 is
327 * present through 6.8.2 and bug 2 is present in 6.9.0 it seems
328 * safest to just blacklist all old-versioning-scheme X servers,
329 * (just using VendorRelase < 70000000), as buggy_repeat=TRUE.
331 if (strstr (ServerVendor (dpy), "X.Org") != NULL) {
332 if (VendorRelease (dpy) >= 60700000 && VendorRelease (dpy) < 70000000)
333 display->buggy_repeat = TRUE;
334 if (VendorRelease (dpy) < 10400000)
335 display->buggy_repeat = TRUE;
336 } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) {
337 if (VendorRelease (dpy) <= 40500000)
338 display->buggy_repeat = TRUE;
341 display->next = _cairo_xlib_display_list;
342 _cairo_xlib_display_list = display;
344 UNLOCK:
345 CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
346 return display;
349 void
350 _cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display,
351 cairo_xlib_hook_t *hook)
353 CAIRO_MUTEX_LOCK (display->mutex);
354 hook->prev = NULL;
355 hook->next = display->close_display_hooks;
356 if (hook->next != NULL)
357 hook->next->prev = hook;
358 display->close_display_hooks = hook;
359 CAIRO_MUTEX_UNLOCK (display->mutex);
362 /* display->mutex must be held */
363 static void
364 _cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
365 cairo_xlib_hook_t *hook)
367 if (display->close_display_hooks == hook)
368 display->close_display_hooks = hook->next;
369 else if (hook->prev != NULL)
370 hook->prev->next = hook->next;
372 if (hook->next != NULL)
373 hook->next->prev = hook->prev;
375 hook->prev = NULL;
376 hook->next = NULL;
379 void
380 _cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display,
381 cairo_xlib_hook_t *hook)
383 CAIRO_MUTEX_LOCK (display->mutex);
384 _cairo_xlib_remove_close_display_hook_internal (display, hook);
385 CAIRO_MUTEX_UNLOCK (display->mutex);
388 cairo_status_t
389 _cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
390 cairo_xlib_notify_resource_func notify,
391 XID xid)
393 cairo_xlib_job_t *job;
394 cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
396 CAIRO_MUTEX_LOCK (display->mutex);
397 if (display->closed == FALSE) {
398 job = _cairo_freelist_alloc (&display->wq_freelist);
399 if (job != NULL) {
400 job->type = RESOURCE;
401 job->func.resource.xid = xid;
402 job->func.resource.notify = notify;
404 job->next = display->workqueue;
405 display->workqueue = job;
407 status = CAIRO_STATUS_SUCCESS;
410 CAIRO_MUTEX_UNLOCK (display->mutex);
412 return status;
415 cairo_status_t
416 _cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
417 cairo_xlib_notify_func notify,
418 void *data,
419 void (*destroy) (void *))
421 cairo_xlib_job_t *job;
422 cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
424 CAIRO_MUTEX_LOCK (display->mutex);
425 if (display->closed == FALSE) {
426 job = _cairo_freelist_alloc (&display->wq_freelist);
427 if (job != NULL) {
428 job->type = WORK;
429 job->func.work.data = data;
430 job->func.work.notify = notify;
431 job->func.work.destroy = destroy;
433 job->next = display->workqueue;
434 display->workqueue = job;
436 status = CAIRO_STATUS_SUCCESS;
439 CAIRO_MUTEX_UNLOCK (display->mutex);
441 return status;
444 void
445 _cairo_xlib_display_notify (cairo_xlib_display_t *display)
447 cairo_xlib_job_t *jobs, *job, *freelist;
448 Display *dpy = display->display;
450 CAIRO_MUTEX_LOCK (display->mutex);
451 jobs = display->workqueue;
452 while (jobs != NULL) {
453 display->workqueue = NULL;
454 CAIRO_MUTEX_UNLOCK (display->mutex);
456 /* reverse the list to obtain FIFO order */
457 job = NULL;
458 do {
459 cairo_xlib_job_t *next = jobs->next;
460 jobs->next = job;
461 job = jobs;
462 jobs = next;
463 } while (jobs != NULL);
464 freelist = jobs = job;
466 do {
467 job = jobs;
468 jobs = job->next;
470 switch (job->type){
471 case WORK:
472 job->func.work.notify (dpy, job->func.work.data);
473 if (job->func.work.destroy != NULL)
474 job->func.work.destroy (job->func.work.data);
475 break;
477 case RESOURCE:
478 job->func.resource.notify (dpy, job->func.resource.xid);
479 break;
481 } while (jobs != NULL);
483 CAIRO_MUTEX_LOCK (display->mutex);
484 do {
485 job = freelist;
486 freelist = job->next;
487 _cairo_freelist_free (&display->wq_freelist, job);
488 } while (freelist != NULL);
490 jobs = display->workqueue;
492 CAIRO_MUTEX_UNLOCK (display->mutex);
495 XRenderPictFormat *
496 _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display,
497 cairo_format_t format)
499 XRenderPictFormat *xrender_format;
501 CAIRO_MUTEX_LOCK (display->mutex);
502 xrender_format = display->cached_xrender_formats[format];
503 if (xrender_format == NULL) {
504 int pict_format;
506 switch (format) {
507 case CAIRO_FORMAT_A1:
508 pict_format = PictStandardA1; break;
509 case CAIRO_FORMAT_A8:
510 pict_format = PictStandardA8; break;
511 case CAIRO_FORMAT_RGB24:
512 pict_format = PictStandardRGB24; break;
513 default:
514 ASSERT_NOT_REACHED;
515 case CAIRO_FORMAT_ARGB32:
516 pict_format = PictStandardARGB32; break;
518 xrender_format = XRenderFindStandardFormat (display->display,
519 pict_format);
520 display->cached_xrender_formats[format] = xrender_format;
522 CAIRO_MUTEX_UNLOCK (display->mutex);
524 return xrender_format;