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