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