1 /* Cairo - a vector graphics library with display and print output
2 *
3 * Copyright © 2009 Intel Corporation
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 * Authors:
29 * Chris Wilson <chris@chris-wilson.co.uk>
30 */
31
32
33 #include "cairoint.h"
34
35 #include "cairo-xcb-private.h"
36 #include "cairo-hash-private.h"
37 #include "cairo-freelist-private.h"
38 #include "cairo-list-private.h"
39
40 #include <xcb/xcbext.h>
41 #include <xcb/bigreq.h>
42 #include <errno.h>
43
44 #if CAIRO_HAS_XCB_DRM_FUNCTIONS
45 #include <xcb/dri2.h>
46 #endif
47
48 #if CAIRO_HAS_XCB_SHM_FUNCTIONS
49 #include <sys/ipc.h>
50 #include <sys/shm.h>
51 #include <xcb/shm.h>
52 #endif
53
54 typedef struct _cairo_xcb_xrender_format {
55 cairo_hash_entry_t key;
56 xcb_render_pictformat_t xrender_format;
57 } cairo_xcb_xrender_format_t;
58
59 typedef struct _cairo_xcb_xid {
60 cairo_list_t link;
61 uint32_t xid;
62 } cairo_xcb_xid_t;
63
64 #define XCB_RENDER_AT_LEAST(V, major, minor) \
65 (((V)->major_version > major) || \
66 (((V)->major_version == major) && ((V)->minor_version >= minor)))
67
68 #define XCB_RENDER_HAS_CREATE_PICTURE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
69 #define XCB_RENDER_HAS_COMPOSITE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
70 #define XCB_RENDER_HAS_COMPOSITE_TEXT(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
71
72 #define XCB_RENDER_HAS_FILL_RECTANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 1)
73
74 #define XCB_RENDER_HAS_DISJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2)
75 #define XCB_RENDER_HAS_CONJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2)
76
77 #define XCB_RENDER_HAS_TRAPEZOIDS(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
78 #define XCB_RENDER_HAS_TRIANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
79 #define XCB_RENDER_HAS_TRISTRIP(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
80 #define XCB_RENDER_HAS_TRIFAN(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
81 #define XCB_RENDER_HAS_SPANS(surface) XCB_RENDER_AT_LEAST((surface), 0, 12)
82
83 #define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface) XCB_RENDER_AT_LEAST((surface), 0, 6)
84 #define XCB_RENDER_HAS_FILTERS(surface) XCB_RENDER_AT_LEAST((surface), 0, 6)
85
86 #define XCB_RENDER_HAS_EXTENDED_REPEAT(surface) XCB_RENDER_AT_LEAST((surface), 0, 10)
87 #define XCB_RENDER_HAS_GRADIENTS(surface) XCB_RENDER_AT_LEAST((surface), 0, 10)
88
89 #define XCB_RENDER_HAS_PDF_OPERATORS(surface) XCB_RENDER_AT_LEAST((surface), 0, 11)
90
91 static cairo_list_t connections;
92
93 static cairo_status_t
_cairo_xcb_connection_find_visual_formats(cairo_xcb_connection_t * connection,const xcb_render_query_pict_formats_reply_t * formats)94 _cairo_xcb_connection_find_visual_formats (cairo_xcb_connection_t *connection,
95 const xcb_render_query_pict_formats_reply_t *formats)
96 {
97 xcb_render_pictscreen_iterator_t screens;
98 xcb_render_pictdepth_iterator_t depths;
99 xcb_render_pictvisual_iterator_t visuals;
100
101 for (screens = xcb_render_query_pict_formats_screens_iterator (formats);
102 screens.rem;
103 xcb_render_pictscreen_next (&screens))
104 {
105 for (depths = xcb_render_pictscreen_depths_iterator (screens.data);
106 depths.rem;
107 xcb_render_pictdepth_next (&depths))
108 {
109 for (visuals = xcb_render_pictdepth_visuals_iterator (depths.data);
110 visuals.rem;
111 xcb_render_pictvisual_next (&visuals))
112 {
113 cairo_xcb_xrender_format_t *f;
114 cairo_status_t status;
115
116 f = malloc (sizeof (cairo_xcb_xrender_format_t));
117 if (unlikely (f == NULL))
118 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
119
120 f->key.hash = visuals.data->visual;
121 f->xrender_format = visuals.data->format;
122 status = _cairo_hash_table_insert (connection->visual_to_xrender_format,
123 &f->key);
124 if (unlikely (status))
125 return status;
126 }
127 }
128 }
129
130 return CAIRO_STATUS_SUCCESS;
131 }
132
133 #if 0
134 static xcb_format_t *
135 find_format_for_depth (const xcb_setup_t *setup, uint8_t depth)
136 {
137 xcb_format_t *fmt = xcb_setup_pixmap_formats (setup);
138 xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length (setup);
139
140 for (; fmt != fmtend; ++fmt)
141 if (fmt->depth == depth)
142 return fmt;
143
144 return 0;
145 }
146 #endif
147
148 static cairo_status_t
_cairo_xcb_connection_parse_xrender_formats(cairo_xcb_connection_t * connection,const xcb_render_query_pict_formats_reply_t * formats)149 _cairo_xcb_connection_parse_xrender_formats (cairo_xcb_connection_t *connection,
150 const xcb_render_query_pict_formats_reply_t *formats)
151 {
152 xcb_render_pictforminfo_iterator_t i;
153 cairo_status_t status;
154
155 for (i = xcb_render_query_pict_formats_formats_iterator (formats);
156 i.rem;
157 xcb_render_pictforminfo_next (&i))
158 {
159 cairo_format_masks_t masks;
160 pixman_format_code_t pixman_format;
161
162 if (i.data->type != XCB_RENDER_PICT_TYPE_DIRECT)
163 continue;
164
165 masks.alpha_mask =
166 (unsigned long) i.data->direct.alpha_mask << i.data->direct.alpha_shift;
167 masks.red_mask =
168 (unsigned long) i.data->direct.red_mask << i.data->direct.red_shift;
169 masks.green_mask =
170 (unsigned long) i.data->direct.green_mask << i.data->direct.green_shift;
171 masks.blue_mask =
172 (unsigned long) i.data->direct.blue_mask << i.data->direct.blue_shift;
173 masks.bpp = i.data->depth;
174
175 if (_pixman_format_from_masks (&masks, &pixman_format)) {
176 cairo_hash_entry_t key;
177
178 key.hash = pixman_format;
179 if (! _cairo_hash_table_lookup (connection->xrender_formats, &key)) {
180 cairo_xcb_xrender_format_t *f;
181
182 f = malloc (sizeof (cairo_xcb_xrender_format_t));
183 if (unlikely (f == NULL))
184 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
185
186 f->key.hash = pixman_format;
187 f->xrender_format = i.data->id;
188 status = _cairo_hash_table_insert (connection->xrender_formats,
189 &f->key);
190 if (unlikely (status))
191 return status;
192
193 #if 0
194 printf ("xrender %x -> (%lx, %lx, %lx, %lx, %d) %x [%d, %d]\n",
195 i.data->id,
196 masks.alpha_mask,
197 masks.red_mask,
198 masks.green_mask,
199 masks.blue_mask,
200 masks.bpp,
201 pixman_format,
202 PIXMAN_FORMAT_DEPTH(pixman_format),
203 PIXMAN_FORMAT_BPP(pixman_format));
204 #endif
205 }
206 }
207 }
208
209 status = _cairo_xcb_connection_find_visual_formats (connection, formats);
210 if (unlikely (status))
211 return status;
212
213 connection->standard_formats[CAIRO_FORMAT_A1] =
214 _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a1);
215
216 connection->standard_formats[CAIRO_FORMAT_A8] =
217 _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8);
218
219 connection->standard_formats[CAIRO_FORMAT_RGB24] =
220 _cairo_xcb_connection_get_xrender_format (connection,
221 PIXMAN_FORMAT (24,
222 PIXMAN_TYPE_ARGB,
223 0, 8, 8, 8));
224 if (connection->standard_formats[CAIRO_FORMAT_RGB24] == XCB_NONE) {
225 connection->standard_formats[CAIRO_FORMAT_RGB24] =
226 _cairo_xcb_connection_get_xrender_format (connection,
227 PIXMAN_FORMAT (24, PIXMAN_TYPE_ABGR,
228 0, 8, 8, 8));
229 }
230
231 connection->standard_formats[CAIRO_FORMAT_ARGB32] =
232 _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8r8g8b8);
233 if (connection->standard_formats[CAIRO_FORMAT_ARGB32] == XCB_NONE) {
234 connection->standard_formats[CAIRO_FORMAT_ARGB32] =
235 _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8b8g8r8);
236 }
237
238 return CAIRO_STATUS_SUCCESS;
239 }
240
241 /*
242 * We require support for depth 1, 8, 24 and 32 pixmaps
243 */
244 #define DEPTH_MASK(d) (1 << ((d) - 1))
245 #define REQUIRED_DEPTHS (DEPTH_MASK(1) | \
246 DEPTH_MASK(8) | \
247 DEPTH_MASK(24) | \
248 DEPTH_MASK(32))
249 static cairo_bool_t
pixmap_depths_usable(cairo_xcb_connection_t * connection,uint32_t missing,xcb_drawable_t root)250 pixmap_depths_usable (cairo_xcb_connection_t *connection,
251 uint32_t missing,
252 xcb_drawable_t root)
253 {
254 xcb_connection_t *c = connection->xcb_connection;
255 xcb_void_cookie_t create_cookie[32];
256 xcb_pixmap_t pixmap;
257 cairo_bool_t success = TRUE;
258 int depth, i, j;
259
260 pixmap = _cairo_xcb_connection_get_xid (connection);
261
262 for (depth = 1, i = 0; depth <= 32; depth++) {
263 if (missing & DEPTH_MASK(depth)) {
264 create_cookie[i] = xcb_create_pixmap_checked (c, depth, pixmap, root, 1, 1);
265 xcb_free_pixmap (c, pixmap);
266 if (!create_cookie[i].sequence) {
267 success = FALSE;
268 break;
269 }
270 i++;
271 }
272 }
273
274 for (j = 0; j < i; j++) {
275 xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[j]);
276 success &= create_error == NULL;
277 free (create_error);
278 }
279
280 _cairo_xcb_connection_put_xid (connection, pixmap);
281
282 return success;
283 }
284
285 static cairo_bool_t
has_required_depths(cairo_xcb_connection_t * connection)286 has_required_depths (cairo_xcb_connection_t *connection)
287 {
288 xcb_screen_iterator_t screens;
289
290 for (screens = xcb_setup_roots_iterator (connection->root);
291 screens.rem;
292 xcb_screen_next (&screens))
293 {
294 xcb_depth_iterator_t depths;
295 uint32_t missing = REQUIRED_DEPTHS;
296
297 for (depths = xcb_screen_allowed_depths_iterator (screens.data);
298 depths.rem;
299 xcb_depth_next (&depths))
300 {
301 missing &= ~DEPTH_MASK (depths.data->depth);
302 }
303 if (missing == 0)
304 continue;
305
306 /*
307 * Ok, this is ugly. It should be sufficient at this
308 * point to just return false, but Xinerama is broken at
309 * this point and only advertises depths which have an
310 * associated visual. Of course, the other depths still
311 * work, but the only way to find out is to try them.
312 */
313 if (! pixmap_depths_usable (connection, missing, screens.data->root))
314 return FALSE;
315 }
316
317 return TRUE;
318 }
319
320 static cairo_status_t
_cairo_xcb_connection_query_render(cairo_xcb_connection_t * connection)321 _cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection)
322 {
323 xcb_connection_t *c = connection->xcb_connection;
324 xcb_render_query_version_cookie_t version_cookie;
325 xcb_render_query_pict_formats_cookie_t formats_cookie;
326 xcb_render_query_version_reply_t *version;
327 xcb_render_query_pict_formats_reply_t *formats;
328 cairo_status_t status;
329 cairo_bool_t present;
330
331 version_cookie = xcb_render_query_version (c, 0, 10);
332 formats_cookie = xcb_render_query_pict_formats (c);
333
334 present = has_required_depths (connection);
335 version = xcb_render_query_version_reply (c, version_cookie, 0);
336 formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0);
337 if (! present || version == NULL || formats == NULL) {
338 free (version);
339 free (formats);
340 return CAIRO_STATUS_SUCCESS;
341 }
342
343 /* always true if the extension is present (i.e. >= 0.0) */
344 connection->flags |= CAIRO_XCB_HAS_RENDER;
345 connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE;
346 connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS;
347
348 if (XCB_RENDER_HAS_FILL_RECTANGLES (version))
349 connection->flags |= CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
350
351 if (XCB_RENDER_HAS_TRAPEZOIDS (version))
352 connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
353
354 if (XCB_RENDER_HAS_PICTURE_TRANSFORM (version))
355 connection->flags |= CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
356
357 if (XCB_RENDER_HAS_FILTERS (version))
358 connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS;
359
360 if (XCB_RENDER_HAS_PDF_OPERATORS (version))
361 connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
362
363 if (XCB_RENDER_HAS_EXTENDED_REPEAT (version))
364 connection->flags |= CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
365
366 if (XCB_RENDER_HAS_GRADIENTS (version))
367 connection->flags |= CAIRO_XCB_RENDER_HAS_GRADIENTS;
368
369 free (version);
370
371 status = _cairo_xcb_connection_parse_xrender_formats (connection, formats);
372 free (formats);
373
374 return status;
375 }
376
377 #if 0
378 static void
379 _cairo_xcb_connection_query_cairo (cairo_xcb_connection_t *connection)
380 {
381 xcb_connection_t *c = connection->xcb_connection;
382 xcb_cairo_query_version_reply_t *version;
383
384 version = xcb_cairo_query_version_reply (c,
385 xcb_cairo_query_version (c, 0, 0),
386 0);
387
388 free (version);
389 }
390 #endif
391
392 #if CAIRO_HAS_XCB_SHM_FUNCTIONS
393 static cairo_bool_t
can_use_shm(cairo_xcb_connection_t * connection)394 can_use_shm (cairo_xcb_connection_t *connection)
395 {
396 cairo_bool_t success = TRUE;
397 xcb_connection_t *c = connection->xcb_connection;
398 xcb_void_cookie_t cookie[2];
399 xcb_generic_error_t *error;
400 int shmid;
401 uint32_t shmseg;
402 void *ptr;
403
404 shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
405 if (shmid == -1)
406 return FALSE;
407
408 ptr = shmat (shmid, NULL, 0);
409 if (ptr == (char *) -1) {
410 shmctl (shmid, IPC_RMID, NULL);
411 return FALSE;
412 }
413
414 shmseg = _cairo_xcb_connection_get_xid (connection);
415 cookie[0] = xcb_shm_attach_checked (c, shmseg, shmid, FALSE);
416 cookie[1] = xcb_shm_detach_checked (c, shmseg);
417 _cairo_xcb_connection_put_xid (connection, shmseg);
418
419 error = xcb_request_check (c, cookie[0]);
420 if (error != NULL)
421 success = FALSE;
422
423 error = xcb_request_check (c, cookie[1]);
424 if (error != NULL)
425 success = FALSE;
426
427 shmctl (shmid, IPC_RMID, NULL);
428 shmdt (ptr);
429
430 return success;
431 }
432
433 static void
_cairo_xcb_connection_query_shm(cairo_xcb_connection_t * connection)434 _cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection)
435 {
436 xcb_connection_t *c = connection->xcb_connection;
437 xcb_shm_query_version_reply_t *version;
438
439 version = xcb_shm_query_version_reply (c, xcb_shm_query_version (c), 0);
440 if (version == NULL)
441 return;
442
443 free (version);
444
445 if (can_use_shm (connection))
446 connection->flags |= CAIRO_XCB_HAS_SHM;
447 }
448 #endif
449
450 #if CAIRO_HAS_XCB_DRM_FUNCTIONS
451 static void
_cairo_xcb_connection_query_dri2(cairo_xcb_connection_t * connection)452 _cairo_xcb_connection_query_dri2 (cairo_xcb_connection_t *connection)
453 {
454 xcb_connection_t *c = connection->xcb_connection;
455 xcb_dri2_query_version_reply_t *version;
456
457 version = xcb_dri2_query_version_reply (c,
458 xcb_dri2_query_version (c,
459 XCB_DRI2_MAJOR_VERSION,
460 XCB_DRI2_MINOR_VERSION),
461 0);
462 if (version == NULL)
463 return;
464
465 free (version);
466
467 connection->flags |= CAIRO_XCB_HAS_DRI2;
468 }
469 #endif
470
471 static cairo_status_t
_device_flush(void * device)472 _device_flush (void *device)
473 {
474 cairo_xcb_connection_t *connection = device;
475 cairo_xcb_screen_t *screen;
476 cairo_status_t status;
477
478 status = cairo_device_acquire (&connection->device);
479 if (unlikely (status))
480 return status;
481
482 CAIRO_MUTEX_LOCK (connection->screens_mutex);
483 cairo_list_foreach_entry (screen, cairo_xcb_screen_t,
484 &connection->screens, link)
485 {
486 if (screen->device != NULL)
487 cairo_device_flush (screen->device);
488 }
489 CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
490
491 xcb_flush (connection->xcb_connection);
492
493 cairo_device_release (&connection->device);
494 return CAIRO_STATUS_SUCCESS;
495 }
496
497 static cairo_bool_t
_xrender_formats_equal(const void * A,const void * B)498 _xrender_formats_equal (const void *A, const void *B)
499 {
500 const cairo_xcb_xrender_format_t *a = A, *b = B;
501 return a->key.hash == b->key.hash;
502 }
503
504 static void
_pluck_xrender_format(void * entry,void * closure)505 _pluck_xrender_format (void *entry,
506 void *closure)
507 {
508 _cairo_hash_table_remove (closure, entry);
509 free (entry);
510 }
511
512 static void
_device_finish(void * device)513 _device_finish (void *device)
514 {
515 cairo_xcb_connection_t *connection = device;
516 cairo_bool_t was_cached = FALSE;
517
518 if (! cairo_list_is_empty (&connection->link)) {
519 CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
520 cairo_list_del (&connection->link);
521 CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
522 was_cached = TRUE;
523 }
524
525 while (! cairo_list_is_empty (&connection->fonts)) {
526 cairo_xcb_font_t *font;
527
528 font = cairo_list_first_entry (&connection->fonts,
529 cairo_xcb_font_t,
530 link);
531 _cairo_xcb_font_finish (font);
532 }
533
534 while (! cairo_list_is_empty (&connection->screens)) {
535 cairo_xcb_screen_t *screen;
536
537 screen = cairo_list_first_entry (&connection->screens,
538 cairo_xcb_screen_t,
539 link);
540 _cairo_xcb_screen_finish (screen);
541 }
542
543 if (connection->has_socket) {
544 /* Send a request so that xcb takes the socket from us, preventing
545 * a later use-after-free on shutdown of the connection.
546 */
547 xcb_no_operation (connection->xcb_connection);
548 }
549
550 if (was_cached)
551 cairo_device_destroy (device);
552 }
553
554 static void
_device_destroy(void * device)555 _device_destroy (void *device)
556 {
557 cairo_xcb_connection_t *connection = device;
558
559 _cairo_hash_table_foreach (connection->xrender_formats,
560 _pluck_xrender_format, connection->xrender_formats);
561 _cairo_hash_table_destroy (connection->xrender_formats);
562
563 _cairo_hash_table_foreach (connection->visual_to_xrender_format,
564 _pluck_xrender_format,
565 connection->visual_to_xrender_format);
566 _cairo_hash_table_destroy (connection->visual_to_xrender_format);
567
568 #if CAIRO_HAS_XCB_SHM_FUNCTIONS
569 _cairo_xcb_connection_shm_mem_pools_fini (connection);
570 #endif
571 _cairo_freepool_fini (&connection->shm_info_freelist);
572
573 _cairo_freepool_fini (&connection->xid_pool);
574
575 CAIRO_MUTEX_FINI (connection->shm_mutex);
576 CAIRO_MUTEX_FINI (connection->screens_mutex);
577
578 free (connection);
579 }
580
581 static const cairo_device_backend_t _cairo_xcb_device_backend = {
582 CAIRO_DEVICE_TYPE_XCB,
583
584 NULL, NULL, /* lock, unlock */
585
586 _device_flush,
587 _device_finish,
588 _device_destroy,
589 };
590
591 cairo_xcb_connection_t *
_cairo_xcb_connection_get(xcb_connection_t * xcb_connection)592 _cairo_xcb_connection_get (xcb_connection_t *xcb_connection)
593 {
594 cairo_xcb_connection_t *connection;
595 const xcb_query_extension_reply_t *ext;
596 cairo_status_t status;
597
598 CAIRO_MUTEX_INITIALIZE ();
599
600 CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
601 if (connections.next == NULL) {
602 /* XXX _cairo_init () */
603 cairo_list_init (&connections);
604 }
605
606 cairo_list_foreach_entry (connection,
607 cairo_xcb_connection_t,
608 &connections,
609 link)
610 {
611 if (connection->xcb_connection == xcb_connection) {
612 /* Maintain MRU order. */
613 if (connections.next != &connection->link)
614 cairo_list_move (&connection->link, &connections);
615
616 goto unlock;
617 }
618 }
619
620 connection = malloc (sizeof (cairo_xcb_connection_t));
621 if (unlikely (connection == NULL))
622 goto unlock;
623
624 _cairo_device_init (&connection->device, &_cairo_xcb_device_backend);
625
626 connection->xcb_connection = xcb_connection;
627 connection->has_socket = FALSE;
628
629 cairo_list_init (&connection->fonts);
630 cairo_list_init (&connection->screens);
631 cairo_list_init (&connection->link);
632 connection->xrender_formats = _cairo_hash_table_create (_xrender_formats_equal);
633 if (connection->xrender_formats == NULL) {
634 CAIRO_MUTEX_FINI (connection->device.mutex);
635 free (connection);
636 connection = NULL;
637 goto unlock;
638 }
639
640 connection->visual_to_xrender_format = _cairo_hash_table_create (_xrender_formats_equal);
641 if (connection->visual_to_xrender_format == NULL) {
642 _cairo_hash_table_destroy (connection->xrender_formats);
643 CAIRO_MUTEX_FINI (connection->device.mutex);
644 free (connection);
645 connection = NULL;
646 goto unlock;
647 }
648
649 cairo_list_init (&connection->free_xids);
650 _cairo_freepool_init (&connection->xid_pool,
651 sizeof (cairo_xcb_xid_t));
652
653 cairo_list_init (&connection->shm_pools);
654 _cairo_freepool_init (&connection->shm_info_freelist,
655 sizeof (cairo_xcb_shm_info_t));
656
657 connection->maximum_request_length =
658 xcb_get_maximum_request_length (xcb_connection);
659
660 CAIRO_MUTEX_INIT (connection->shm_mutex);
661 CAIRO_MUTEX_INIT (connection->screens_mutex);
662
663 CAIRO_MUTEX_LOCK (connection->device.mutex);
664
665 connection->flags = 0;
666
667 xcb_prefetch_extension_data (xcb_connection, &xcb_big_requests_id);
668 xcb_prefetch_extension_data (xcb_connection, &xcb_render_id);
669 #if CAIRO_HAS_XCB_SHM_FUNCTIONS
670 xcb_prefetch_extension_data (xcb_connection, &xcb_shm_id);
671 #endif
672 #if 0
673 xcb_prefetch_extension_data (xcb_connection, &xcb_cairo_id);
674 #endif
675 #if CAIRO_HAS_XCB_DRM_FUNCTIONS
676 xcb_prefetch_extension_data (xcb_connection, &xcb_dri2_id);
677 #endif
678
679 xcb_prefetch_maximum_request_length (xcb_connection);
680
681 connection->root = xcb_get_setup (xcb_connection);
682 connection->render = NULL;
683 ext = xcb_get_extension_data (xcb_connection, &xcb_render_id);
684 if (ext != NULL && ext->present) {
685 status = _cairo_xcb_connection_query_render (connection);
686 if (unlikely (status)) {
687 CAIRO_MUTEX_UNLOCK (connection->device.mutex);
688 _cairo_xcb_connection_destroy (connection);
689 connection = NULL;
690 goto unlock;
691 }
692
693 connection->render = ext;
694 }
695
696 #if 0
697 ext = xcb_get_extension_data (connection, &xcb_cairo_id);
698 if (ext != NULL && ext->present)
699 _cairo_xcb_connection_query_cairo (connection);
700 #endif
701
702 connection->shm = NULL;
703 #if CAIRO_HAS_XCB_SHM_FUNCTIONS
704 ext = xcb_get_extension_data (xcb_connection, &xcb_shm_id);
705 if (ext != NULL && ext->present) {
706 _cairo_xcb_connection_query_shm (connection);
707 connection->shm = ext;
708 }
709 #endif
710
711 connection->dri2 = NULL;
712 #if CAIRO_HAS_XCB_DRM_FUNCTIONS
713 ext = xcb_get_extension_data (xcb_connection, &xcb_dri2_id);
714 if (ext != NULL && ext->present) {
715 _cairo_xcb_connection_query_dri2 (connection);
716 connection->dri2 = ext;
717 }
718 #endif
719
720 CAIRO_MUTEX_UNLOCK (connection->device.mutex);
721
722 cairo_list_add (&connection->link, &connections);
723 unlock:
724 CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
725
726 return connection;
727 }
728
729 xcb_render_pictformat_t
_cairo_xcb_connection_get_xrender_format(cairo_xcb_connection_t * connection,pixman_format_code_t pixman_format)730 _cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection,
731 pixman_format_code_t pixman_format)
732 {
733 cairo_hash_entry_t key;
734 cairo_xcb_xrender_format_t *format;
735
736 key.hash = pixman_format;
737 format = _cairo_hash_table_lookup (connection->xrender_formats, &key);
738 return format ? format->xrender_format : XCB_NONE;
739 }
740
741 xcb_render_pictformat_t
_cairo_xcb_connection_get_xrender_format_for_visual(cairo_xcb_connection_t * connection,const xcb_visualid_t visual)742 _cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection,
743 const xcb_visualid_t visual)
744 {
745 cairo_hash_entry_t key;
746 cairo_xcb_xrender_format_t *format;
747
748 key.hash = visual;
749 format = _cairo_hash_table_lookup (connection->visual_to_xrender_format, &key);
750 return format ? format->xrender_format : XCB_NONE;
751 }
752
753 void
_cairo_xcb_connection_put_xid(cairo_xcb_connection_t * connection,uint32_t xid)754 _cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection,
755 uint32_t xid)
756 {
757 cairo_xcb_xid_t *cache;
758
759 assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
760 cache = _cairo_freepool_alloc (&connection->xid_pool);
761 if (likely (cache != NULL)) {
762 cache->xid = xid;
763 cairo_list_add (&cache->link, &connection->free_xids);
764 }
765 }
766
767 uint32_t
_cairo_xcb_connection_get_xid(cairo_xcb_connection_t * connection)768 _cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection)
769 {
770 uint32_t xid;
771
772 assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
773 if (! cairo_list_is_empty (&connection->free_xids)) {
774 cairo_xcb_xid_t *cache;
775
776 cache = cairo_list_first_entry (&connection->free_xids,
777 cairo_xcb_xid_t,
778 link);
779 xid = cache->xid;
780
781 cairo_list_del (&cache->link);
782 _cairo_freepool_free (&connection->xid_pool, cache);
783 } else {
784 xid = xcb_generate_id (connection->xcb_connection);
785 }
786
787 return xid;
788 }
789
790 static void
_cairo_xcb_return_socket(void * closure)791 _cairo_xcb_return_socket (void *closure)
792 {
793 cairo_xcb_connection_t *connection = closure;
794
795 CAIRO_MUTEX_LOCK (connection->device.mutex);
796 connection->has_socket = FALSE;
797 CAIRO_MUTEX_UNLOCK (connection->device.mutex);
798 }
799
800 cairo_status_t
_cairo_xcb_connection_take_socket(cairo_xcb_connection_t * connection)801 _cairo_xcb_connection_take_socket (cairo_xcb_connection_t *connection)
802 {
803 assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
804
805 if (unlikely (connection->device.status))
806 return connection->device.status;
807
808 if (! connection->has_socket) {
809 if (! xcb_take_socket (connection->xcb_connection,
810 _cairo_xcb_return_socket,
811 connection,
812 0, &connection->seqno))
813 {
814 return connection->device.status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
815 }
816
817 connection->has_socket = TRUE;
818 }
819
820 return CAIRO_STATUS_SUCCESS;
821 }
822
823 /* public (debug) interface */
824
825 void
cairo_xcb_device_debug_cap_xshm_version(cairo_device_t * device,int major_version,int minor_version)826 cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device,
827 int major_version,
828 int minor_version)
829 {
830 cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
831 cairo_status_t status;
832
833 if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
834 status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
835 return;
836 }
837
838 /* clear any flags that are inappropriate for the desired version */
839 if (major_version < 0 && minor_version < 0) {
840 connection->flags &= ~(CAIRO_XCB_HAS_SHM);
841 }
842 }
843
844 void
cairo_xcb_device_debug_cap_xrender_version(cairo_device_t * device,int major_version,int minor_version)845 cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device,
846 int major_version,
847 int minor_version)
848 {
849 cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
850 cairo_status_t status;
851
852 if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
853 status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
854 return;
855 }
856
857 /* clear any flags that are inappropriate for the desired version */
858 if (major_version < 0 && minor_version < 0) {
859 connection->flags &= ~(CAIRO_XCB_HAS_RENDER |
860 CAIRO_XCB_RENDER_HAS_COMPOSITE |
861 CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS |
862 CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
863 CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
864 CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
865 CAIRO_XCB_RENDER_HAS_FILTERS |
866 CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
867 CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
868 CAIRO_XCB_RENDER_HAS_GRADIENTS);
869 } else {
870 xcb_render_query_version_reply_t version;
871
872 version.major_version = major_version;
873 version.minor_version = minor_version;
874
875 if (! XCB_RENDER_HAS_FILL_RECTANGLES (&version))
876 connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
877
878 if (! XCB_RENDER_HAS_TRAPEZOIDS (&version))
879 connection->flags &= ~CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
880
881 if (! XCB_RENDER_HAS_PICTURE_TRANSFORM (&version))
882 connection->flags &= ~CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
883
884 if (! XCB_RENDER_HAS_FILTERS (&version))
885 connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILTERS;
886
887 if (! XCB_RENDER_HAS_PDF_OPERATORS (&version))
888 connection->flags &= ~CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
889
890 if (! XCB_RENDER_HAS_EXTENDED_REPEAT (&version))
891 connection->flags &= ~CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
892
893 if (! XCB_RENDER_HAS_GRADIENTS (&version))
894 connection->flags &= ~CAIRO_XCB_RENDER_HAS_GRADIENTS;
895 }
896 }
897
898 #if 0
899 void
900 cairo_xcb_device_debug_cap_xcairo_version (cairo_device_t *device,
901 int major_version,
902 int minor_version)
903 {
904 cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
905 cairo_status_t status;
906
907 if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
908 status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
909 return;
910 }
911
912 /* clear any flags that are inappropriate for the desired version */
913 if (major_version < 0 && minor_version < 0) {
914 connection->flags &= ~(CAIRO_XCB_HAS_CAIRO);
915 }
916 }
917 #endif
918