1 /* Cairo - a vector graphics library with display and print output
2 *
3 * Copyright © 2007 Chris Wilson
4 *
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.
12 *
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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
18 *
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/
23 *
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.
27 *
28 * The Original Code is the cairo graphics library.
29 *
30 * The Initial Developer of the Original Code is Chris Wilson.
31 *
32 * Contributor(s):
33 * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
34 */
35
36 #include "cairoint.h"
37
38 #include "cairo-xlib-private.h"
39 #include "cairo-xlib-xrender-private.h"
40 #include "cairo-freelist-private.h"
41 #include "cairo-error-private.h"
42
43 #include <X11/Xlibint.h> /* For XESetCloseDisplay */
44
45 typedef int (*cairo_xlib_error_func_t) (Display *display,
46 XErrorEvent *event);
47
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;
65 };
66
67 static cairo_xlib_display_t *_cairo_xlib_display_list;
68
69 static void
70 _cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
71 cairo_xlib_hook_t *hook);
72
73 static void
_cairo_xlib_call_close_display_hooks(cairo_xlib_display_t * display)74 _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
75 {
76 cairo_xlib_screen_t *screen;
77 cairo_xlib_hook_t *hook;
78
79 cairo_list_foreach_entry (screen, cairo_xlib_screen_t, &display->screens, link)
80 _cairo_xlib_screen_close_display (display, screen);
81
82 while (TRUE) {
83 hook = display->close_display_hooks;
84 if (hook == NULL)
85 break;
86
87 _cairo_xlib_remove_close_display_hook_internal (display, hook);
88
89 hook->func (display, hook);
90 }
91 display->closed = TRUE;
92 }
93
94 static void
_cairo_xlib_display_finish(void * abstract_display)95 _cairo_xlib_display_finish (void *abstract_display)
96 {
97 cairo_xlib_display_t *display = abstract_display;
98
99 display->display = NULL;
100 }
101
102 static void
_cairo_xlib_display_destroy(void * abstract_display)103 _cairo_xlib_display_destroy (void *abstract_display)
104 {
105 cairo_xlib_display_t *display = abstract_display;
106
107 /* destroy all outstanding notifies */
108 while (display->workqueue != NULL) {
109 cairo_xlib_job_t *job = display->workqueue;
110 display->workqueue = job->next;
111
112 if (job->type == WORK && job->func.work.destroy != NULL)
113 job->func.work.destroy (job->func.work.data);
114
115 _cairo_freelist_free (&display->wq_freelist, job);
116 }
117 _cairo_freelist_fini (&display->wq_freelist);
118
119 while (! cairo_list_is_empty (&display->screens)) {
120 _cairo_xlib_screen_destroy (cairo_list_first_entry (&display->screens,
121 cairo_xlib_screen_t,
122 link));
123 }
124
125 free (display);
126 }
127
128 static int
_noop_error_handler(Display * display,XErrorEvent * event)129 _noop_error_handler (Display *display,
130 XErrorEvent *event)
131 {
132 return False; /* return value is ignored */
133 }
134
135 static void
_cairo_xlib_display_notify(cairo_xlib_display_t * display)136 _cairo_xlib_display_notify (cairo_xlib_display_t *display)
137 {
138 cairo_xlib_job_t *jobs, *job, *freelist;
139 Display *dpy = display->display;
140
141 /* Optimistic atomic pointer read -- don't care if it is wrong due to
142 * contention as we will check again very shortly.
143 */
144 if (display->workqueue == NULL)
145 return;
146
147 jobs = display->workqueue;
148 while (jobs != NULL) {
149 display->workqueue = NULL;
150
151 /* reverse the list to obtain FIFO order */
152 job = NULL;
153 do {
154 cairo_xlib_job_t *next = jobs->next;
155 jobs->next = job;
156 job = jobs;
157 jobs = next;
158 } while (jobs != NULL);
159 freelist = jobs = job;
160
161 do {
162 job = jobs;
163 jobs = job->next;
164
165 switch (job->type){
166 case WORK:
167 job->func.work.notify (dpy, job->func.work.data);
168 if (job->func.work.destroy != NULL)
169 job->func.work.destroy (job->func.work.data);
170 break;
171
172 case RESOURCE:
173 job->func.resource.notify (dpy, job->func.resource.xid);
174 break;
175 }
176 } while (jobs != NULL);
177
178 do {
179 job = freelist;
180 freelist = job->next;
181 _cairo_freelist_free (&display->wq_freelist, job);
182 } while (freelist != NULL);
183
184 jobs = display->workqueue;
185 }
186 }
187
188 static int
_cairo_xlib_close_display(Display * dpy,XExtCodes * codes)189 _cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
190 {
191 cairo_xlib_display_t *display, **prev, *next;
192 cairo_xlib_error_func_t old_handler;
193
194 CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
195 for (display = _cairo_xlib_display_list; display; display = display->next)
196 if (display->display == dpy)
197 break;
198 CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
199 if (display == NULL)
200 return 0;
201
202 if (! cairo_device_acquire (&display->base)) {
203 /* protect the notifies from triggering XErrors */
204 XSync (dpy, False);
205 old_handler = XSetErrorHandler (_noop_error_handler);
206
207 _cairo_xlib_display_notify (display);
208 _cairo_xlib_call_close_display_hooks (display);
209
210 /* catch any that arrived before marking the display as closed */
211 _cairo_xlib_display_notify (display);
212
213 XSync (dpy, False);
214 XSetErrorHandler (old_handler);
215
216 cairo_device_release (&display->base);
217 }
218
219 /*
220 * Unhook from the global list
221 */
222 CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
223 prev = &_cairo_xlib_display_list;
224 for (display = _cairo_xlib_display_list; display; display = next) {
225 next = display->next;
226 if (display->display == dpy) {
227 *prev = next;
228 break;
229 } else
230 prev = &display->next;
231 }
232 CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
233
234 assert (display != NULL);
235
236 cairo_device_finish (&display->base);
237 cairo_device_destroy (&display->base);
238
239 /* Return value in accordance with requirements of
240 * XESetCloseDisplay */
241 return 0;
242 }
243
244 static const cairo_device_backend_t _cairo_xlib_device_backend = {
245 CAIRO_DEVICE_TYPE_XLIB,
246
247 NULL,
248 NULL,
249
250 NULL, /* flush */
251 _cairo_xlib_display_finish,
252 _cairo_xlib_display_destroy,
253 };
254
255 /**
256 * cairo_xlib_device_create:
257 * @dpy: the display to create the device for
258 *
259 * Gets the device belonging to @dpy, or creates it if it doesn't exist yet.
260 *
261 * Returns: the device belonging to @dpy
262 **/
263 cairo_device_t *
_cairo_xlib_device_create(Display * dpy)264 _cairo_xlib_device_create (Display *dpy)
265 {
266 cairo_xlib_display_t *display;
267 cairo_xlib_display_t **prev;
268 cairo_device_t *device;
269 XExtCodes *codes;
270 const char *env;
271
272 static int buggy_repeat_force = -1;
273
274 CAIRO_MUTEX_INITIALIZE ();
275
276 /* There is an apparent deadlock between this mutex and the
277 * mutex for the display, but it's actually safe. For the
278 * app to call XCloseDisplay() while any other thread is
279 * inside this function would be an error in the logic
280 * app, and the CloseDisplay hook is the only other place we
281 * acquire this mutex.
282 */
283 CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
284
285 for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next)
286 {
287 if (display->display == dpy) {
288 /*
289 * MRU the list
290 */
291 if (prev != &_cairo_xlib_display_list) {
292 *prev = display->next;
293 display->next = _cairo_xlib_display_list;
294 _cairo_xlib_display_list = display;
295 }
296 device = cairo_device_reference (&display->base);
297 goto UNLOCK;
298 }
299 }
300
301 display = malloc (sizeof (cairo_xlib_display_t));
302 if (unlikely (display == NULL)) {
303 device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
304 goto UNLOCK;
305 }
306
307 /* Xlib calls out to the extension close_display hooks in LIFO
308 * order. So we have to ensure that all extensions that we depend
309 * on in our close_display hook are properly initialized before we
310 * add our hook. For now, that means Render, so we call into its
311 * QueryVersion function to ensure it gets initialized.
312 */
313 display->render_major = display->render_minor = -1;
314 XRenderQueryVersion (dpy, &display->render_major, &display->render_minor);
315 env = getenv ("CAIRO_DEBUG");
316 if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) {
317 int max_render_major, max_render_minor;
318
319 env += sizeof ("xrender-version=") - 1;
320 if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2)
321 max_render_major = max_render_minor = -1;
322
323 if (max_render_major < display->render_major ||
324 (max_render_major == display->render_major &&
325 max_render_minor < display->render_minor))
326 {
327 display->render_major = max_render_major;
328 display->render_minor = max_render_minor;
329 }
330 }
331
332 codes = XAddExtension (dpy);
333 if (unlikely (codes == NULL)) {
334 device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
335 free (display);
336 goto UNLOCK;
337 }
338
339 _cairo_device_init (&display->base, &_cairo_xlib_device_backend);
340
341 XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
342
343 _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t));
344
345 cairo_device_reference (&display->base); /* add one for the CloseDisplay */
346 display->display = dpy;
347 cairo_list_init (&display->screens);
348 display->workqueue = NULL;
349 display->close_display_hooks = NULL;
350 display->closed = FALSE;
351
352 memset (display->cached_xrender_formats, 0,
353 sizeof (display->cached_xrender_formats));
354
355 /* Prior to Render 0.10, there is no protocol support for gradients and
356 * we call function stubs instead, which would silently consume the drawing.
357 */
358 #if RENDER_MAJOR == 0 && RENDER_MINOR < 10
359 display->buggy_gradients = TRUE;
360 #else
361 display->buggy_gradients = FALSE;
362 #endif
363 display->buggy_pad_reflect = FALSE;
364 display->buggy_repeat = FALSE;
365
366 /* This buggy_repeat condition is very complicated because there
367 * are multiple X server code bases (with multiple versioning
368 * schemes within a code base), and multiple bugs.
369 *
370 * The X servers:
371 *
372 * 1. The Vendor=="XFree86" code base with release numbers such
373 * as 4.7.0 (VendorRelease==40700000).
374 *
375 * 2. The Vendor=="X.Org" code base (a descendant of the
376 * XFree86 code base). It originally had things like
377 * VendorRelease==60700000 for release 6.7.0 but then changed
378 * its versioning scheme so that, for example,
379 * VendorRelease==10400000 for the 1.4.0 X server within the
380 * X.Org 7.3 release.
381 *
382 * The bugs:
383 *
384 * 1. The original bug that led to the buggy_repeat
385 * workaround. This was a bug that Owen Taylor investigated,
386 * understood well, and characterized against carious X
387 * servers. Confirmed X servers with this bug include:
388 *
389 * "XFree86" <= 40500000
390 * "X.Org" <= 60802000 (only with old numbering >= 60700000)
391 *
392 * 2. A separate bug resulting in a crash of the X server when
393 * using cairo's extend-reflect test case, (which, surprisingly
394 * enough was not passing RepeatReflect to the X server, but
395 * instead using RepeatNormal in a workaround). Nobody to date
396 * has understood the bug well, but it appears to be gone as of
397 * the X.Org 1.4.0 server. This bug is coincidentally avoided
398 * by using the same buggy_repeat workaround. Confirmed X
399 * servers with this bug include:
400 *
401 * "X.org" == 60900000 (old versioning scheme)
402 * "X.org" < 10400000 (new numbering scheme)
403 *
404 * For the old-versioning-scheme X servers we don't know
405 * exactly when second the bug started, but since bug 1 is
406 * present through 6.8.2 and bug 2 is present in 6.9.0 it seems
407 * safest to just blacklist all old-versioning-scheme X servers,
408 * (just using VendorRelease < 70000000), as buggy_repeat=TRUE.
409 */
410 if (strstr (ServerVendor (dpy), "X.Org") != NULL) {
411 if (VendorRelease (dpy) >= 60700000) {
412 if (VendorRelease (dpy) < 70000000)
413 display->buggy_repeat = TRUE;
414
415 /* We know that gradients simply do not work in early Xorg servers */
416 if (VendorRelease (dpy) < 70200000)
417 display->buggy_gradients = TRUE;
418
419 /* And the extended repeat modes were not fixed until much later */
420 display->buggy_pad_reflect = TRUE;
421 } else {
422 if (VendorRelease (dpy) < 10400000)
423 display->buggy_repeat = TRUE;
424
425 /* Too many bugs in the early drivers */
426 if (VendorRelease (dpy) < 10699000)
427 display->buggy_pad_reflect = TRUE;
428 }
429 } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) {
430 if (VendorRelease (dpy) <= 40500000)
431 display->buggy_repeat = TRUE;
432
433 display->buggy_gradients = TRUE;
434 display->buggy_pad_reflect = TRUE;
435 }
436
437 /* gradients don't seem to work */
438 display->buggy_gradients = TRUE;
439
440
441 /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */
442 /* If buggy_repeat_force == -1, then initialize.
443 * - set to -2, meaning "nothing was specified", and we trust the above detection.
444 * - if MOZ_CAIRO_BUGGY_REPEAT is '0' (exactly), then force buggy repeat off
445 * - if MOZ_CAIRO_BUGGY_REPEAT is '1' (exactly), then force buggy repeat on
446 */
447 if (buggy_repeat_force == -1) {
448 const char *flag = getenv("MOZ_CAIRO_FORCE_BUGGY_REPEAT");
449
450 buggy_repeat_force = -2;
451
452 if (flag && flag[0] == '0')
453 buggy_repeat_force = 0;
454 else if (flag && flag[0] == '1')
455 buggy_repeat_force = 1;
456 }
457
458 if (buggy_repeat_force != -2)
459 display->buggy_repeat = (buggy_repeat_force == 1);
460
461 display->next = _cairo_xlib_display_list;
462 _cairo_xlib_display_list = display;
463
464 device = &display->base;
465
466 UNLOCK:
467 CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
468 return device;
469 }
470
471 void
_cairo_xlib_add_close_display_hook(cairo_xlib_display_t * display,cairo_xlib_hook_t * hook)472 _cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display,
473 cairo_xlib_hook_t *hook)
474 {
475 hook->prev = NULL;
476 hook->next = display->close_display_hooks;
477 if (hook->next != NULL)
478 hook->next->prev = hook;
479 display->close_display_hooks = hook;
480 }
481
482 static void
_cairo_xlib_remove_close_display_hook_internal(cairo_xlib_display_t * display,cairo_xlib_hook_t * hook)483 _cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
484 cairo_xlib_hook_t *hook)
485 {
486 if (display->close_display_hooks == hook)
487 display->close_display_hooks = hook->next;
488 else if (hook->prev != NULL)
489 hook->prev->next = hook->next;
490
491 if (hook->next != NULL)
492 hook->next->prev = hook->prev;
493
494 hook->prev = NULL;
495 hook->next = NULL;
496 }
497
498 void
_cairo_xlib_remove_close_display_hook(cairo_xlib_display_t * display,cairo_xlib_hook_t * hook)499 _cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display,
500 cairo_xlib_hook_t *hook)
501 {
502 _cairo_xlib_remove_close_display_hook_internal (display, hook);
503 }
504
505 cairo_status_t
_cairo_xlib_display_queue_resource(cairo_xlib_display_t * display,cairo_xlib_notify_resource_func notify,XID xid)506 _cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
507 cairo_xlib_notify_resource_func notify,
508 XID xid)
509 {
510 cairo_xlib_job_t *job;
511 cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
512
513 if (display->closed == FALSE) {
514 job = _cairo_freelist_alloc (&display->wq_freelist);
515 if (job != NULL) {
516 job->type = RESOURCE;
517 job->func.resource.xid = xid;
518 job->func.resource.notify = notify;
519
520 job->next = display->workqueue;
521 display->workqueue = job;
522
523 status = CAIRO_STATUS_SUCCESS;
524 }
525 }
526
527 return status;
528 }
529
530 cairo_status_t
_cairo_xlib_display_queue_work(cairo_xlib_display_t * display,cairo_xlib_notify_func notify,void * data,void (* destroy)(void *))531 _cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
532 cairo_xlib_notify_func notify,
533 void *data,
534 void (*destroy) (void *))
535 {
536 cairo_xlib_job_t *job;
537 cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
538
539 if (display->closed == FALSE) {
540 job = _cairo_freelist_alloc (&display->wq_freelist);
541 if (job != NULL) {
542 job->type = WORK;
543 job->func.work.data = data;
544 job->func.work.notify = notify;
545 job->func.work.destroy = destroy;
546
547 job->next = display->workqueue;
548 display->workqueue = job;
549
550 status = CAIRO_STATUS_SUCCESS;
551 }
552 }
553
554 return status;
555 }
556
557 cairo_status_t
_cairo_xlib_display_acquire(cairo_device_t * device,cairo_xlib_display_t ** display)558 _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display)
559 {
560 cairo_status_t status;
561
562 status = cairo_device_acquire (device);
563 if (status)
564 return status;
565
566 *display = (cairo_xlib_display_t *) device;
567 _cairo_xlib_display_notify (*display);
568 return status;
569 }
570
571 XRenderPictFormat *
_cairo_xlib_display_get_xrender_format(cairo_xlib_display_t * display,cairo_format_t format)572 _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display,
573 cairo_format_t format)
574 {
575 XRenderPictFormat *xrender_format;
576
577 #if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER
578 xrender_format = display->cached_xrender_formats[format];
579 if (likely (xrender_format != NULL))
580 return xrender_format;
581 #endif
582
583 xrender_format = display->cached_xrender_formats[format];
584 if (xrender_format == NULL) {
585 int pict_format;
586
587 switch (format) {
588 case CAIRO_FORMAT_A1:
589 pict_format = PictStandardA1; break;
590 case CAIRO_FORMAT_A8:
591 pict_format = PictStandardA8; break;
592 case CAIRO_FORMAT_RGB24:
593 pict_format = PictStandardRGB24; break;
594 case CAIRO_FORMAT_RGB16_565: {
595 Visual *visual = NULL;
596 Screen *screen = DefaultScreenOfDisplay(display->display);
597 int j;
598 for (j = 0; j < screen->ndepths; j++) {
599 Depth *d = &screen->depths[j];
600 if (d->depth == 16 && d->nvisuals && &d->visuals[0]) {
601 if (d->visuals[0].red_mask == 0xf800 &&
602 d->visuals[0].green_mask == 0x7e0 &&
603 d->visuals[0].blue_mask == 0x1f)
604 visual = &d->visuals[0];
605 break;
606 }
607 }
608 if (!visual)
609 return NULL;
610 xrender_format = XRenderFindVisualFormat(display->display, visual);
611 break;
612 }
613 case CAIRO_FORMAT_INVALID:
614 default:
615 ASSERT_NOT_REACHED;
616 case CAIRO_FORMAT_ARGB32:
617 pict_format = PictStandardARGB32; break;
618 }
619 if (!xrender_format)
620 xrender_format = XRenderFindStandardFormat (display->display,
621 pict_format);
622 display->cached_xrender_formats[format] = xrender_format;
623 }
624
625 return xrender_format;
626 }
627
628 cairo_xlib_screen_t *
_cairo_xlib_display_get_screen(cairo_xlib_display_t * display,Screen * screen)629 _cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
630 Screen *screen)
631 {
632 cairo_xlib_screen_t *info;
633
634 cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) {
635 if (info->screen == screen) {
636 if (display->screens.next != &info->link)
637 cairo_list_move (&info->link, &display->screens);
638 return info;
639 }
640 }
641
642 return NULL;
643 }
644
645 void
_cairo_xlib_display_get_xrender_version(cairo_xlib_display_t * display,int * major,int * minor)646 _cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display,
647 int *major, int *minor)
648 {
649 *major = display->render_major;
650 *minor = display->render_minor;
651 }
652
653 cairo_bool_t
_cairo_xlib_display_has_repeat(cairo_device_t * device)654 _cairo_xlib_display_has_repeat (cairo_device_t *device)
655 {
656 return ! ((cairo_xlib_display_t *) device)->buggy_repeat;
657 }
658
659 cairo_bool_t
_cairo_xlib_display_has_reflect(cairo_device_t * device)660 _cairo_xlib_display_has_reflect (cairo_device_t *device)
661 {
662 return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect;
663 }
664
665 cairo_bool_t
_cairo_xlib_display_has_gradients(cairo_device_t * device)666 _cairo_xlib_display_has_gradients (cairo_device_t *device)
667 {
668 return ! ((cairo_xlib_display_t *) device)->buggy_gradients;
669 }
670