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.
33 * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
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
,
48 struct _cairo_xlib_job
{
49 cairo_xlib_job_t
*next
;
56 cairo_xlib_notify_resource_func notify
;
60 cairo_xlib_notify_func notify
;
62 void (*destroy
) (void *);
67 static cairo_xlib_display_t
*_cairo_xlib_display_list
;
70 _cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t
*display
,
71 cairo_xlib_hook_t
*hook
);
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
);
86 hook
= display
->close_display_hooks
;
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
);
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
);
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
))
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
);
155 _noop_error_handler (Display
*display
,
158 return False
; /* return value is ignored */
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
)
170 CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex
);
174 /* protect the notifies from triggering XErrors */
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
);
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
) {
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 */
211 cairo_xlib_display_t
*
212 _cairo_xlib_display_get (Display
*dpy
)
214 cairo_xlib_display_t
*display
;
215 cairo_xlib_display_t
**prev
;
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
) {
234 if (prev
!= &_cairo_xlib_display_list
) {
235 *prev
= display
->next
;
236 display
->next
= _cairo_xlib_display_list
;
237 _cairo_xlib_display_list
= display
;
243 if (display
!= NULL
) {
244 display
= _cairo_xlib_display_reference (display
);
248 display
= malloc (sizeof (cairo_xlib_display_t
));
249 if (display
== NULL
) {
250 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY
);
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
);
264 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY
);
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.
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
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
;
345 CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex
);
350 _cairo_xlib_add_close_display_hook (cairo_xlib_display_t
*display
,
351 cairo_xlib_hook_t
*hook
)
353 CAIRO_MUTEX_LOCK (display
->mutex
);
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 */
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
;
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
);
389 _cairo_xlib_display_queue_resource (cairo_xlib_display_t
*display
,
390 cairo_xlib_notify_resource_func notify
,
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
);
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
);
416 _cairo_xlib_display_queue_work (cairo_xlib_display_t
*display
,
417 cairo_xlib_notify_func notify
,
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
);
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
);
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 */
459 cairo_xlib_job_t
*next
= jobs
->next
;
463 } while (jobs
!= NULL
);
464 freelist
= jobs
= job
;
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
);
478 job
->func
.resource
.notify (dpy
, job
->func
.resource
.xid
);
481 } while (jobs
!= NULL
);
483 CAIRO_MUTEX_LOCK (display
->mutex
);
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
);
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
) {
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;
515 case CAIRO_FORMAT_ARGB32
:
516 pict_format
= PictStandardARGB32
; break;
518 xrender_format
= XRenderFindStandardFormat (display
->display
,
520 display
->cached_xrender_formats
[format
] = xrender_format
;
522 CAIRO_MUTEX_UNLOCK (display
->mutex
);
524 return xrender_format
;