Line data Source code
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
74 0 : _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 0 : cairo_list_foreach_entry (screen, cairo_xlib_screen_t, &display->screens, link)
80 0 : _cairo_xlib_screen_close_display (display, screen);
81 :
82 : while (TRUE) {
83 0 : hook = display->close_display_hooks;
84 0 : if (hook == NULL)
85 0 : break;
86 :
87 0 : _cairo_xlib_remove_close_display_hook_internal (display, hook);
88 :
89 0 : hook->func (display, hook);
90 : }
91 0 : display->closed = TRUE;
92 0 : }
93 :
94 : static void
95 0 : _cairo_xlib_display_finish (void *abstract_display)
96 : {
97 0 : cairo_xlib_display_t *display = abstract_display;
98 :
99 0 : display->display = NULL;
100 0 : }
101 :
102 : static void
103 0 : _cairo_xlib_display_destroy (void *abstract_display)
104 : {
105 0 : cairo_xlib_display_t *display = abstract_display;
106 :
107 : /* destroy all outstanding notifies */
108 0 : while (display->workqueue != NULL) {
109 0 : cairo_xlib_job_t *job = display->workqueue;
110 0 : display->workqueue = job->next;
111 :
112 0 : if (job->type == WORK && job->func.work.destroy != NULL)
113 0 : job->func.work.destroy (job->func.work.data);
114 :
115 0 : _cairo_freelist_free (&display->wq_freelist, job);
116 : }
117 0 : _cairo_freelist_fini (&display->wq_freelist);
118 :
119 0 : while (! cairo_list_is_empty (&display->screens)) {
120 0 : _cairo_xlib_screen_destroy (cairo_list_first_entry (&display->screens,
121 : cairo_xlib_screen_t,
122 : link));
123 : }
124 :
125 0 : free (display);
126 0 : }
127 :
128 : static int
129 0 : _noop_error_handler (Display *display,
130 : XErrorEvent *event)
131 : {
132 0 : return False; /* return value is ignored */
133 : }
134 :
135 : static void
136 0 : _cairo_xlib_display_notify (cairo_xlib_display_t *display)
137 : {
138 : cairo_xlib_job_t *jobs, *job, *freelist;
139 0 : 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 0 : if (display->workqueue == NULL)
145 0 : return;
146 :
147 0 : jobs = display->workqueue;
148 0 : while (jobs != NULL) {
149 0 : display->workqueue = NULL;
150 :
151 : /* reverse the list to obtain FIFO order */
152 0 : job = NULL;
153 : do {
154 0 : cairo_xlib_job_t *next = jobs->next;
155 0 : jobs->next = job;
156 0 : job = jobs;
157 0 : jobs = next;
158 0 : } while (jobs != NULL);
159 0 : freelist = jobs = job;
160 :
161 : do {
162 0 : job = jobs;
163 0 : jobs = job->next;
164 :
165 0 : switch (job->type){
166 : case WORK:
167 0 : job->func.work.notify (dpy, job->func.work.data);
168 0 : if (job->func.work.destroy != NULL)
169 0 : job->func.work.destroy (job->func.work.data);
170 0 : break;
171 :
172 : case RESOURCE:
173 0 : job->func.resource.notify (dpy, job->func.resource.xid);
174 0 : break;
175 : }
176 0 : } while (jobs != NULL);
177 :
178 : do {
179 0 : job = freelist;
180 0 : freelist = job->next;
181 0 : _cairo_freelist_free (&display->wq_freelist, job);
182 0 : } while (freelist != NULL);
183 :
184 0 : jobs = display->workqueue;
185 : }
186 : }
187 :
188 : static int
189 0 : _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 0 : CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
195 0 : for (display = _cairo_xlib_display_list; display; display = display->next)
196 0 : if (display->display == dpy)
197 0 : break;
198 0 : CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
199 0 : if (display == NULL)
200 0 : return 0;
201 :
202 0 : if (! cairo_device_acquire (&display->base)) {
203 : /* protect the notifies from triggering XErrors */
204 0 : XSync (dpy, False);
205 0 : old_handler = XSetErrorHandler (_noop_error_handler);
206 :
207 0 : _cairo_xlib_display_notify (display);
208 0 : _cairo_xlib_call_close_display_hooks (display);
209 :
210 : /* catch any that arrived before marking the display as closed */
211 0 : _cairo_xlib_display_notify (display);
212 :
213 0 : XSync (dpy, False);
214 0 : XSetErrorHandler (old_handler);
215 :
216 0 : cairo_device_release (&display->base);
217 : }
218 :
219 : /*
220 : * Unhook from the global list
221 : */
222 0 : CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
223 0 : prev = &_cairo_xlib_display_list;
224 0 : for (display = _cairo_xlib_display_list; display; display = next) {
225 0 : next = display->next;
226 0 : if (display->display == dpy) {
227 0 : *prev = next;
228 0 : break;
229 : } else
230 0 : prev = &display->next;
231 : }
232 0 : CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
233 :
234 0 : assert (display != NULL);
235 :
236 0 : cairo_device_finish (&display->base);
237 0 : cairo_device_destroy (&display->base);
238 :
239 : /* Return value in accordance with requirements of
240 : * XESetCloseDisplay */
241 0 : 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 *
264 0 : _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 0 : CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
284 :
285 0 : for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next)
286 : {
287 0 : if (display->display == dpy) {
288 : /*
289 : * MRU the list
290 : */
291 0 : if (prev != &_cairo_xlib_display_list) {
292 0 : *prev = display->next;
293 0 : display->next = _cairo_xlib_display_list;
294 0 : _cairo_xlib_display_list = display;
295 : }
296 0 : device = cairo_device_reference (&display->base);
297 0 : goto UNLOCK;
298 : }
299 : }
300 :
301 0 : display = malloc (sizeof (cairo_xlib_display_t));
302 0 : if (unlikely (display == NULL)) {
303 0 : device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
304 0 : 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 0 : display->render_major = display->render_minor = -1;
314 0 : XRenderQueryVersion (dpy, &display->render_major, &display->render_minor);
315 0 : env = getenv ("CAIRO_DEBUG");
316 0 : if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) {
317 : int max_render_major, max_render_minor;
318 :
319 0 : env += sizeof ("xrender-version=") - 1;
320 0 : if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2)
321 0 : max_render_major = max_render_minor = -1;
322 :
323 0 : if (max_render_major < display->render_major ||
324 0 : (max_render_major == display->render_major &&
325 0 : max_render_minor < display->render_minor))
326 : {
327 0 : display->render_major = max_render_major;
328 0 : display->render_minor = max_render_minor;
329 : }
330 : }
331 :
332 0 : codes = XAddExtension (dpy);
333 0 : if (unlikely (codes == NULL)) {
334 0 : device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
335 0 : free (display);
336 0 : goto UNLOCK;
337 : }
338 :
339 0 : _cairo_device_init (&display->base, &_cairo_xlib_device_backend);
340 :
341 0 : XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
342 :
343 0 : _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t));
344 :
345 0 : cairo_device_reference (&display->base); /* add one for the CloseDisplay */
346 0 : display->display = dpy;
347 0 : cairo_list_init (&display->screens);
348 0 : display->workqueue = NULL;
349 0 : display->close_display_hooks = NULL;
350 0 : display->closed = FALSE;
351 :
352 0 : 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 0 : display->buggy_gradients = FALSE;
362 : #endif
363 0 : display->buggy_pad_reflect = FALSE;
364 0 : 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 0 : if (strstr (ServerVendor (dpy), "X.Org") != NULL) {
411 0 : if (VendorRelease (dpy) >= 60700000) {
412 0 : if (VendorRelease (dpy) < 70000000)
413 0 : display->buggy_repeat = TRUE;
414 :
415 : /* We know that gradients simply do not work in early Xorg servers */
416 0 : if (VendorRelease (dpy) < 70200000)
417 0 : display->buggy_gradients = TRUE;
418 :
419 : /* And the extended repeat modes were not fixed until much later */
420 0 : display->buggy_pad_reflect = TRUE;
421 : } else {
422 0 : if (VendorRelease (dpy) < 10400000)
423 0 : display->buggy_repeat = TRUE;
424 :
425 : /* Too many bugs in the early drivers */
426 0 : if (VendorRelease (dpy) < 10699000)
427 0 : display->buggy_pad_reflect = TRUE;
428 : }
429 0 : } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) {
430 0 : if (VendorRelease (dpy) <= 40500000)
431 0 : display->buggy_repeat = TRUE;
432 :
433 0 : display->buggy_gradients = TRUE;
434 0 : display->buggy_pad_reflect = TRUE;
435 : }
436 :
437 : /* gradients don't seem to work */
438 0 : 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 0 : if (buggy_repeat_force == -1) {
448 0 : const char *flag = getenv("MOZ_CAIRO_FORCE_BUGGY_REPEAT");
449 :
450 0 : buggy_repeat_force = -2;
451 :
452 0 : if (flag && flag[0] == '0')
453 0 : buggy_repeat_force = 0;
454 0 : else if (flag && flag[0] == '1')
455 0 : buggy_repeat_force = 1;
456 : }
457 :
458 0 : if (buggy_repeat_force != -2)
459 0 : display->buggy_repeat = (buggy_repeat_force == 1);
460 :
461 0 : display->next = _cairo_xlib_display_list;
462 0 : _cairo_xlib_display_list = display;
463 :
464 0 : device = &display->base;
465 :
466 : UNLOCK:
467 0 : CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
468 0 : return device;
469 : }
470 :
471 : void
472 0 : _cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display,
473 : cairo_xlib_hook_t *hook)
474 : {
475 0 : hook->prev = NULL;
476 0 : hook->next = display->close_display_hooks;
477 0 : if (hook->next != NULL)
478 0 : hook->next->prev = hook;
479 0 : display->close_display_hooks = hook;
480 0 : }
481 :
482 : static void
483 0 : _cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
484 : cairo_xlib_hook_t *hook)
485 : {
486 0 : if (display->close_display_hooks == hook)
487 0 : display->close_display_hooks = hook->next;
488 0 : else if (hook->prev != NULL)
489 0 : hook->prev->next = hook->next;
490 :
491 0 : if (hook->next != NULL)
492 0 : hook->next->prev = hook->prev;
493 :
494 0 : hook->prev = NULL;
495 0 : hook->next = NULL;
496 0 : }
497 :
498 : void
499 0 : _cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display,
500 : cairo_xlib_hook_t *hook)
501 : {
502 0 : _cairo_xlib_remove_close_display_hook_internal (display, hook);
503 0 : }
504 :
505 : cairo_status_t
506 0 : _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 0 : cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
512 :
513 0 : if (display->closed == FALSE) {
514 0 : job = _cairo_freelist_alloc (&display->wq_freelist);
515 0 : if (job != NULL) {
516 0 : job->type = RESOURCE;
517 0 : job->func.resource.xid = xid;
518 0 : job->func.resource.notify = notify;
519 :
520 0 : job->next = display->workqueue;
521 0 : display->workqueue = job;
522 :
523 0 : status = CAIRO_STATUS_SUCCESS;
524 : }
525 : }
526 :
527 0 : return status;
528 : }
529 :
530 : cairo_status_t
531 0 : _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 0 : cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
538 :
539 0 : if (display->closed == FALSE) {
540 0 : job = _cairo_freelist_alloc (&display->wq_freelist);
541 0 : if (job != NULL) {
542 0 : job->type = WORK;
543 0 : job->func.work.data = data;
544 0 : job->func.work.notify = notify;
545 0 : job->func.work.destroy = destroy;
546 :
547 0 : job->next = display->workqueue;
548 0 : display->workqueue = job;
549 :
550 0 : status = CAIRO_STATUS_SUCCESS;
551 : }
552 : }
553 :
554 0 : return status;
555 : }
556 :
557 : cairo_status_t
558 0 : _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display)
559 : {
560 : cairo_status_t status;
561 :
562 0 : status = cairo_device_acquire (device);
563 0 : if (status)
564 0 : return status;
565 :
566 0 : *display = (cairo_xlib_display_t *) device;
567 0 : _cairo_xlib_display_notify (*display);
568 0 : return status;
569 : }
570 :
571 : XRenderPictFormat *
572 0 : _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 0 : xrender_format = display->cached_xrender_formats[format];
579 0 : if (likely (xrender_format != NULL))
580 0 : return xrender_format;
581 : #endif
582 :
583 0 : xrender_format = display->cached_xrender_formats[format];
584 0 : if (xrender_format == NULL) {
585 : int pict_format;
586 :
587 0 : switch (format) {
588 : case CAIRO_FORMAT_A1:
589 0 : pict_format = PictStandardA1; break;
590 : case CAIRO_FORMAT_A8:
591 0 : pict_format = PictStandardA8; break;
592 : case CAIRO_FORMAT_RGB24:
593 0 : pict_format = PictStandardRGB24; break;
594 : case CAIRO_FORMAT_RGB16_565: {
595 0 : Visual *visual = NULL;
596 0 : Screen *screen = DefaultScreenOfDisplay(display->display);
597 : int j;
598 0 : for (j = 0; j < screen->ndepths; j++) {
599 0 : Depth *d = &screen->depths[j];
600 0 : if (d->depth == 16 && d->nvisuals && &d->visuals[0]) {
601 0 : if (d->visuals[0].red_mask == 0xf800 &&
602 0 : d->visuals[0].green_mask == 0x7e0 &&
603 0 : d->visuals[0].blue_mask == 0x1f)
604 0 : visual = &d->visuals[0];
605 0 : break;
606 : }
607 : }
608 0 : if (!visual)
609 0 : return NULL;
610 0 : xrender_format = XRenderFindVisualFormat(display->display, visual);
611 0 : break;
612 : }
613 : case CAIRO_FORMAT_INVALID:
614 : default:
615 0 : ASSERT_NOT_REACHED;
616 : case CAIRO_FORMAT_ARGB32:
617 0 : pict_format = PictStandardARGB32; break;
618 : }
619 0 : if (!xrender_format)
620 0 : xrender_format = XRenderFindStandardFormat (display->display,
621 : pict_format);
622 0 : display->cached_xrender_formats[format] = xrender_format;
623 : }
624 :
625 0 : return xrender_format;
626 : }
627 :
628 : cairo_xlib_screen_t *
629 0 : _cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
630 : Screen *screen)
631 : {
632 : cairo_xlib_screen_t *info;
633 :
634 0 : cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) {
635 0 : if (info->screen == screen) {
636 0 : if (display->screens.next != &info->link)
637 0 : cairo_list_move (&info->link, &display->screens);
638 0 : return info;
639 : }
640 : }
641 :
642 0 : return NULL;
643 : }
644 :
645 : void
646 0 : _cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display,
647 : int *major, int *minor)
648 : {
649 0 : *major = display->render_major;
650 0 : *minor = display->render_minor;
651 0 : }
652 :
653 : cairo_bool_t
654 0 : _cairo_xlib_display_has_repeat (cairo_device_t *device)
655 : {
656 0 : return ! ((cairo_xlib_display_t *) device)->buggy_repeat;
657 : }
658 :
659 : cairo_bool_t
660 0 : _cairo_xlib_display_has_reflect (cairo_device_t *device)
661 : {
662 0 : return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect;
663 : }
664 :
665 : cairo_bool_t
666 0 : _cairo_xlib_display_has_gradients (cairo_device_t *device)
667 : {
668 0 : return ! ((cairo_xlib_display_t *) device)->buggy_gradients;
669 : }
|