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 : break;
86 :
87 0 : _cairo_xlib_remove_close_display_hook_internal (display, hook);
88 :
89 0 : hook->func (display, hook);
90 0 : }
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 : 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 : 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 : 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 : 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 : 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 : #ifndef MOZ_EGL_XRENDER_COMPOSITE
426 : /* Too many bugs in the early drivers */
427 0 : if (VendorRelease (dpy) < 10699000)
428 0 : display->buggy_pad_reflect = TRUE;
429 : #endif
430 : }
431 0 : } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) {
432 0 : if (VendorRelease (dpy) <= 40500000)
433 0 : display->buggy_repeat = TRUE;
434 :
435 0 : display->buggy_gradients = TRUE;
436 0 : display->buggy_pad_reflect = TRUE;
437 : }
438 :
439 : /* gradients don't seem to work */
440 0 : display->buggy_gradients = TRUE;
441 :
442 :
443 : /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */
444 : /* If buggy_repeat_force == -1, then initialize.
445 : * - set to -2, meaning "nothing was specified", and we trust the above detection.
446 : * - if MOZ_CAIRO_BUGGY_REPEAT is '0' (exactly), then force buggy repeat off
447 : * - if MOZ_CAIRO_BUGGY_REPEAT is '1' (exactly), then force buggy repeat on
448 : */
449 0 : if (buggy_repeat_force == -1) {
450 0 : const char *flag = getenv("MOZ_CAIRO_FORCE_BUGGY_REPEAT");
451 :
452 0 : buggy_repeat_force = -2;
453 :
454 0 : if (flag && flag[0] == '0')
455 0 : buggy_repeat_force = 0;
456 0 : else if (flag && flag[0] == '1')
457 0 : buggy_repeat_force = 1;
458 : }
459 :
460 0 : if (buggy_repeat_force != -2)
461 0 : display->buggy_repeat = (buggy_repeat_force == 1);
462 :
463 0 : display->next = _cairo_xlib_display_list;
464 0 : _cairo_xlib_display_list = display;
465 :
466 0 : device = &display->base;
467 :
468 : UNLOCK:
469 : CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
470 0 : return device;
471 : }
472 :
473 : void
474 0 : _cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display,
475 : cairo_xlib_hook_t *hook)
476 : {
477 0 : hook->prev = NULL;
478 0 : hook->next = display->close_display_hooks;
479 0 : if (hook->next != NULL)
480 0 : hook->next->prev = hook;
481 0 : display->close_display_hooks = hook;
482 0 : }
483 :
484 : static void
485 0 : _cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
486 : cairo_xlib_hook_t *hook)
487 : {
488 0 : if (display->close_display_hooks == hook)
489 0 : display->close_display_hooks = hook->next;
490 0 : else if (hook->prev != NULL)
491 0 : hook->prev->next = hook->next;
492 :
493 0 : if (hook->next != NULL)
494 0 : hook->next->prev = hook->prev;
495 :
496 0 : hook->prev = NULL;
497 0 : hook->next = NULL;
498 0 : }
499 :
500 : void
501 0 : _cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display,
502 : cairo_xlib_hook_t *hook)
503 : {
504 0 : _cairo_xlib_remove_close_display_hook_internal (display, hook);
505 0 : }
506 :
507 : cairo_status_t
508 0 : _cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
509 : cairo_xlib_notify_resource_func notify,
510 : XID xid)
511 : {
512 : cairo_xlib_job_t *job;
513 0 : cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
514 :
515 0 : if (display->closed == FALSE) {
516 0 : job = _cairo_freelist_alloc (&display->wq_freelist);
517 0 : if (job != NULL) {
518 0 : job->type = RESOURCE;
519 0 : job->func.resource.xid = xid;
520 0 : job->func.resource.notify = notify;
521 :
522 0 : job->next = display->workqueue;
523 0 : display->workqueue = job;
524 :
525 0 : status = CAIRO_STATUS_SUCCESS;
526 : }
527 : }
528 :
529 0 : return status;
530 : }
531 :
532 : cairo_status_t
533 0 : _cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
534 : cairo_xlib_notify_func notify,
535 : void *data,
536 : void (*destroy) (void *))
537 : {
538 : cairo_xlib_job_t *job;
539 0 : cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
540 :
541 0 : if (display->closed == FALSE) {
542 0 : job = _cairo_freelist_alloc (&display->wq_freelist);
543 0 : if (job != NULL) {
544 0 : job->type = WORK;
545 0 : job->func.work.data = data;
546 0 : job->func.work.notify = notify;
547 0 : job->func.work.destroy = destroy;
548 :
549 0 : job->next = display->workqueue;
550 0 : display->workqueue = job;
551 :
552 0 : status = CAIRO_STATUS_SUCCESS;
553 : }
554 : }
555 :
556 0 : return status;
557 : }
558 :
559 : cairo_status_t
560 0 : _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display)
561 : {
562 : cairo_status_t status;
563 :
564 0 : status = cairo_device_acquire (device);
565 0 : if (status)
566 0 : return status;
567 :
568 0 : *display = (cairo_xlib_display_t *) device;
569 0 : _cairo_xlib_display_notify (*display);
570 0 : return status;
571 : }
572 :
573 : XRenderPictFormat *
574 0 : _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display,
575 : cairo_format_t format)
576 : {
577 : XRenderPictFormat *xrender_format;
578 :
579 : #if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER
580 0 : xrender_format = display->cached_xrender_formats[format];
581 0 : if (likely (xrender_format != NULL))
582 0 : return xrender_format;
583 : #endif
584 :
585 0 : xrender_format = display->cached_xrender_formats[format];
586 0 : if (xrender_format == NULL) {
587 : int pict_format;
588 :
589 0 : switch (format) {
590 : case CAIRO_FORMAT_A1:
591 0 : pict_format = PictStandardA1; break;
592 : case CAIRO_FORMAT_A8:
593 0 : pict_format = PictStandardA8; break;
594 : case CAIRO_FORMAT_RGB24:
595 0 : pict_format = PictStandardRGB24; break;
596 : case CAIRO_FORMAT_RGB16_565: {
597 0 : Visual *visual = NULL;
598 0 : Screen *screen = DefaultScreenOfDisplay(display->display);
599 : int j;
600 0 : for (j = 0; j < screen->ndepths; j++) {
601 0 : Depth *d = &screen->depths[j];
602 0 : if (d->depth == 16 && d->nvisuals && &d->visuals[0]) {
603 0 : if (d->visuals[0].red_mask == 0xf800 &&
604 0 : d->visuals[0].green_mask == 0x7e0 &&
605 0 : d->visuals[0].blue_mask == 0x1f)
606 0 : visual = &d->visuals[0];
607 0 : break;
608 : }
609 : }
610 0 : if (!visual)
611 0 : return NULL;
612 0 : xrender_format = XRenderFindVisualFormat(display->display, visual);
613 0 : break;
614 : }
615 : case CAIRO_FORMAT_INVALID:
616 : default:
617 0 : ASSERT_NOT_REACHED;
618 : case CAIRO_FORMAT_ARGB32:
619 0 : pict_format = PictStandardARGB32; break;
620 : }
621 0 : if (!xrender_format)
622 0 : xrender_format = XRenderFindStandardFormat (display->display,
623 : pict_format);
624 0 : display->cached_xrender_formats[format] = xrender_format;
625 : }
626 :
627 0 : return xrender_format;
628 : }
629 :
630 : cairo_xlib_screen_t *
631 0 : _cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
632 : Screen *screen)
633 : {
634 : cairo_xlib_screen_t *info;
635 :
636 0 : cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) {
637 0 : if (info->screen == screen) {
638 0 : if (display->screens.next != &info->link)
639 0 : cairo_list_move (&info->link, &display->screens);
640 0 : return info;
641 : }
642 : }
643 :
644 0 : return NULL;
645 : }
646 :
647 : void
648 0 : _cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display,
649 : int *major, int *minor)
650 : {
651 0 : *major = display->render_major;
652 0 : *minor = display->render_minor;
653 0 : }
654 :
655 : cairo_bool_t
656 0 : _cairo_xlib_display_has_repeat (cairo_device_t *device)
657 : {
658 0 : return ! ((cairo_xlib_display_t *) device)->buggy_repeat;
659 : }
660 :
661 : cairo_bool_t
662 0 : _cairo_xlib_display_has_reflect (cairo_device_t *device)
663 : {
664 0 : return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect;
665 : }
666 :
667 : cairo_bool_t
668 0 : _cairo_xlib_display_has_gradients (cairo_device_t *device)
669 : {
670 0 : return ! ((cairo_xlib_display_t *) device)->buggy_gradients;
671 : }
|