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-inline.h"
39

            
40
#include <xcb/xcbext.h>
41
#include <xcb/bigreq.h>
42
#include <errno.h>
43

            
44
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
45
#include <sys/ipc.h>
46
#include <sys/shm.h>
47
#include <xcb/shm.h>
48
#endif
49

            
50
typedef struct _cairo_xcb_xrender_format {
51
    cairo_hash_entry_t key;
52
    xcb_render_pictformat_t xrender_format;
53
} cairo_xcb_xrender_format_t;
54

            
55
typedef struct _cairo_xcb_xid {
56
    cairo_list_t link;
57
    uint32_t xid;
58
} cairo_xcb_xid_t;
59

            
60
#define XCB_RENDER_AT_LEAST(V, major, minor)	\
61
	(((V)->major_version > major) ||			\
62
	 (((V)->major_version == major) && ((V)->minor_version >= minor)))
63

            
64
#define XCB_RENDER_HAS_CREATE_PICTURE(surface)		XCB_RENDER_AT_LEAST((surface), 0, 0)
65
#define XCB_RENDER_HAS_COMPOSITE(surface)		XCB_RENDER_AT_LEAST((surface), 0, 0)
66
#define XCB_RENDER_HAS_COMPOSITE_TEXT(surface)		XCB_RENDER_AT_LEAST((surface), 0, 0)
67

            
68
#define XCB_RENDER_HAS_FILL_RECTANGLES(surface)		XCB_RENDER_AT_LEAST((surface), 0, 1)
69

            
70
#define XCB_RENDER_HAS_DISJOINT(surface)		XCB_RENDER_AT_LEAST((surface), 0, 2)
71
#define XCB_RENDER_HAS_CONJOINT(surface)		XCB_RENDER_AT_LEAST((surface), 0, 2)
72

            
73
#define XCB_RENDER_HAS_TRAPEZOIDS(surface)		XCB_RENDER_AT_LEAST((surface), 0, 4)
74
#define XCB_RENDER_HAS_TRIANGLES(surface)		XCB_RENDER_AT_LEAST((surface), 0, 4)
75
#define XCB_RENDER_HAS_TRISTRIP(surface)		XCB_RENDER_AT_LEAST((surface), 0, 4)
76
#define XCB_RENDER_HAS_TRIFAN(surface)			XCB_RENDER_AT_LEAST((surface), 0, 4)
77

            
78
#define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface)	XCB_RENDER_AT_LEAST((surface), 0, 6)
79
#define XCB_RENDER_HAS_FILTERS(surface)			XCB_RENDER_AT_LEAST((surface), 0, 6)
80
#define XCB_RENDER_HAS_FILTER_GOOD(surface) FALSE
81
#define XCB_RENDER_HAS_FILTER_BEST(surface) FALSE
82
#define XCB_RENDER_HAS_SUBPIXEL_ORDER(surface)		XCB_RENDER_AT_LEAST((surface), 0, 6)
83

            
84
#define XCB_RENDER_HAS_EXTENDED_REPEAT(surface)	XCB_RENDER_AT_LEAST((surface), 0, 10)
85
#define XCB_RENDER_HAS_GRADIENTS(surface)	XCB_RENDER_AT_LEAST((surface), 0, 10)
86

            
87
#define XCB_RENDER_HAS_PDF_OPERATORS(surface)	XCB_RENDER_AT_LEAST((surface), 0, 11)
88

            
89
static cairo_list_t connections;
90

            
91
static cairo_status_t
92
4
_cairo_xcb_connection_find_visual_formats (cairo_xcb_connection_t *connection,
93
					  const xcb_render_query_pict_formats_reply_t *formats)
94
{
95
    xcb_render_pictscreen_iterator_t screens;
96
    xcb_render_pictdepth_iterator_t depths;
97
    xcb_render_pictvisual_iterator_t visuals;
98

            
99
4
    for (screens = xcb_render_query_pict_formats_screens_iterator (formats);
100
8
	 screens.rem;
101
4
	 xcb_render_pictscreen_next (&screens))
102
    {
103
4
	for (depths = xcb_render_pictscreen_depths_iterator (screens.data);
104
28
	     depths.rem;
105
24
	     xcb_render_pictdepth_next (&depths))
106
	{
107
24
	    for (visuals = xcb_render_pictdepth_visuals_iterator (depths.data);
108
36
		 visuals.rem;
109
12
		 xcb_render_pictvisual_next (&visuals))
110
	    {
111
		cairo_xcb_xrender_format_t *f;
112
		cairo_status_t status;
113

            
114
12
		f = _cairo_calloc (sizeof (cairo_xcb_xrender_format_t));
115
12
		if (unlikely (f == NULL))
116
		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
117

            
118
12
		f->key.hash = visuals.data->visual;
119
12
		f->xrender_format = visuals.data->format;
120
12
		status = _cairo_hash_table_insert (connection->visual_to_xrender_format,
121
						   &f->key);
122
12
		if (unlikely (status))
123
		    return status;
124
	    }
125
	}
126
    }
127

            
128
4
    return CAIRO_STATUS_SUCCESS;
129
}
130

            
131
#if 0
132
static xcb_format_t *
133
find_format_for_depth (const xcb_setup_t *setup, uint8_t depth)
134
{
135
    xcb_format_t *fmt = xcb_setup_pixmap_formats (setup);
136
    xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length (setup);
137

            
138
    for (; fmt != fmtend; ++fmt)
139
	if (fmt->depth == depth)
140
	    return fmt;
141

            
142
    return 0;
143
}
144
#endif
145

            
146
static cairo_status_t
147
4
_cairo_xcb_connection_parse_xrender_formats (cairo_xcb_connection_t *connection,
148
					     const xcb_render_query_pict_formats_reply_t *formats)
149
{
150
    xcb_render_pictforminfo_iterator_t i;
151
    cairo_status_t status;
152

            
153
4
    for (i = xcb_render_query_pict_formats_formats_iterator (formats);
154
100
	 i.rem;
155
96
	 xcb_render_pictforminfo_next (&i))
156
    {
157
	cairo_format_masks_t masks;
158
	pixman_format_code_t pixman_format;
159

            
160
96
	if (i.data->type != XCB_RENDER_PICT_TYPE_DIRECT)
161
	    continue;
162

            
163
96
	masks.alpha_mask =
164
96
	    (unsigned long) i.data->direct.alpha_mask << i.data->direct.alpha_shift;
165
96
	masks.red_mask =
166
96
	    (unsigned long) i.data->direct.red_mask << i.data->direct.red_shift;
167
96
	masks.green_mask =
168
96
	    (unsigned long) i.data->direct.green_mask << i.data->direct.green_shift;
169
96
	masks.blue_mask =
170
96
	    (unsigned long) i.data->direct.blue_mask << i.data->direct.blue_shift;
171
96
	masks.bpp = i.data->depth;
172

            
173
96
	if (_pixman_format_from_masks (&masks, &pixman_format)) {
174
	    cairo_hash_entry_t key;
175

            
176
88
	    key.hash = pixman_format;
177
88
	    if (! _cairo_hash_table_lookup (connection->xrender_formats, &key)) {
178
		cairo_xcb_xrender_format_t *f;
179

            
180
88
		f = _cairo_calloc (sizeof (cairo_xcb_xrender_format_t));
181
88
		if (unlikely (f == NULL))
182
		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
183

            
184
88
		f->key.hash = pixman_format;
185
88
		f->xrender_format = i.data->id;
186
88
		status = _cairo_hash_table_insert (connection->xrender_formats,
187
						   &f->key);
188
88
		if (unlikely (status))
189
		    return status;
190

            
191
#if 0
192
		printf ("xrender %x -> (%lx, %lx, %lx, %lx, %d) %x [%d, %d]\n",
193
			i.data->id,
194
			masks.alpha_mask,
195
			masks.red_mask,
196
			masks.green_mask,
197
			masks.blue_mask,
198
			masks.bpp,
199
			pixman_format,
200
			PIXMAN_FORMAT_DEPTH(pixman_format),
201
			PIXMAN_FORMAT_BPP(pixman_format));
202
#endif
203
	    }
204
	}
205
    }
206

            
207
4
    status = _cairo_xcb_connection_find_visual_formats (connection, formats);
208
4
    if (unlikely (status))
209
	return status;
210

            
211
4
    connection->standard_formats[CAIRO_FORMAT_A1] =
212
4
	_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a1);
213

            
214
4
    connection->standard_formats[CAIRO_FORMAT_A8] =
215
4
	_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8);
216

            
217
4
    connection->standard_formats[CAIRO_FORMAT_RGB24] =
218
4
	_cairo_xcb_connection_get_xrender_format (connection,
219
						  PIXMAN_FORMAT (24,
220
								 PIXMAN_TYPE_ARGB,
221
								 0, 8, 8, 8));
222
4
    if (connection->standard_formats[CAIRO_FORMAT_RGB24] == XCB_NONE) {
223
	connection->standard_formats[CAIRO_FORMAT_RGB24] =
224
	    _cairo_xcb_connection_get_xrender_format (connection,
225
						      PIXMAN_FORMAT (24, PIXMAN_TYPE_ABGR,
226
								     0, 8, 8, 8));
227
    }
228

            
229
4
    connection->standard_formats[CAIRO_FORMAT_ARGB32] =
230
4
	_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8r8g8b8);
231
4
    if (connection->standard_formats[CAIRO_FORMAT_ARGB32] == XCB_NONE) {
232
	connection->standard_formats[CAIRO_FORMAT_ARGB32] =
233
	    _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8b8g8r8);
234
    }
235

            
236
4
    return CAIRO_STATUS_SUCCESS;
237
}
238

            
239
/*
240
 * We require support for depth 1, 8, 24 and 32 pixmaps
241
 */
242
#define DEPTH_MASK(d)	(1 << ((d) - 1))
243
#define REQUIRED_DEPTHS	(DEPTH_MASK(1) | \
244
			 DEPTH_MASK(8) | \
245
			 DEPTH_MASK(24) | \
246
			 DEPTH_MASK(32))
247
static cairo_bool_t
248
pixmap_depths_usable (cairo_xcb_connection_t *connection,
249
		      uint32_t missing,
250
		      xcb_drawable_t root)
251
{
252
    xcb_connection_t *c = connection->xcb_connection;
253
    xcb_void_cookie_t create_cookie[32];
254
    xcb_pixmap_t pixmap;
255
    cairo_bool_t success = TRUE;
256
    int depth, i, j;
257

            
258
    pixmap = xcb_generate_id (connection->xcb_connection);
259

            
260
    for (depth = 1, i = 0; depth <= 32; depth++) {
261
	if (missing & DEPTH_MASK(depth)) {
262
	    create_cookie[i] = xcb_create_pixmap_checked (c, depth, pixmap, root, 1, 1);
263
	    xcb_free_pixmap (c, pixmap);
264
	    if (!create_cookie[i].sequence) {
265
		success = FALSE;
266
		break;
267
	    }
268
	    i++;
269
	}
270
    }
271

            
272
    for (j = 0; j < i; j++) {
273
	xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[j]);
274
	success &= create_error == NULL;
275
	free (create_error);
276
    }
277

            
278
    return success;
279
}
280

            
281
static cairo_bool_t
282
4
has_required_depths (cairo_xcb_connection_t *connection)
283
{
284
    xcb_screen_iterator_t screens;
285

            
286
4
    for (screens = xcb_setup_roots_iterator (connection->root);
287
8
	 screens.rem;
288
4
	 xcb_screen_next (&screens))
289
    {
290
	xcb_depth_iterator_t depths;
291
4
	uint32_t missing = REQUIRED_DEPTHS;
292

            
293
4
	for (depths = xcb_screen_allowed_depths_iterator (screens.data);
294
28
	     depths.rem;
295
24
	     xcb_depth_next (&depths))
296
	{
297
24
	    missing &= ~DEPTH_MASK (depths.data->depth);
298
	}
299
4
	if (missing == 0)
300
4
	    continue;
301

            
302
	/*
303
	 * Ok, this is ugly.  It should be sufficient at this
304
	 * point to just return false, but Xinerama is broken at
305
	 * this point and only advertises depths which have an
306
	 * associated visual.  Of course, the other depths still
307
	 * work, but the only way to find out is to try them.
308
	 */
309
	if (! pixmap_depths_usable (connection, missing, screens.data->root))
310
	    return FALSE;
311
    }
312

            
313
4
    return TRUE;
314
}
315

            
316
static xcb_render_query_version_reply_t *
317
4
_render_restrict_env(xcb_render_query_version_reply_t *version)
318
{
319
    const char *env;
320

            
321
4
    if (version == NULL)
322
	return NULL;
323

            
324
4
    env = getenv ("CAIRO_DEBUG");
325
4
    if (env != NULL)
326
	env = strstr (env, "xrender-version=");
327
4
    if (env != NULL) {
328
	int max_render_major, max_render_minor;
329

            
330
	env += sizeof ("xrender-version=") - 1;
331
	if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2)
332
	    max_render_major = max_render_minor = -1;
333

            
334
	if (max_render_major < 0 || max_render_minor < 0) {
335
	    free (version);
336
	    return NULL;
337
	}
338

            
339
	if (max_render_major < (int) version->major_version ||
340
	    (max_render_major == (int) version->major_version &&
341
	     max_render_minor < (int) version->minor_version))
342
	{
343
	    version->major_version = max_render_major;
344
	    version->minor_version = max_render_minor;
345
	}
346
    }
347

            
348
4
    return version;
349
}
350

            
351
static cairo_status_t
352
4
_cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection)
353
{
354
4
    xcb_connection_t *c = connection->xcb_connection;
355
    xcb_render_query_version_cookie_t version_cookie;
356
    xcb_render_query_pict_formats_cookie_t formats_cookie;
357
    xcb_render_query_version_reply_t *version;
358
    xcb_render_query_pict_formats_reply_t *formats;
359
    cairo_status_t status;
360
    cairo_bool_t present;
361

            
362
4
    version_cookie = xcb_render_query_version (c, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION);
363
4
    formats_cookie = xcb_render_query_pict_formats (c);
364

            
365
4
    present = has_required_depths (connection);
366
4
    version = xcb_render_query_version_reply (c, version_cookie, 0);
367
4
    formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0);
368

            
369
4
    version = _render_restrict_env (version);
370

            
371
4
    if (! present || version == NULL || formats == NULL) {
372
	free (version);
373
	free (formats);
374
	return CAIRO_STATUS_SUCCESS;
375
    }
376

            
377
    /* always true if the extension is present (i.e. >= 0.0) */
378
4
    connection->flags |= CAIRO_XCB_HAS_RENDER;
379
4
    connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE;
380
4
    connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS;
381

            
382
4
    if (XCB_RENDER_HAS_FILL_RECTANGLES (version))
383
4
	connection->flags |= CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
384

            
385
4
    if (XCB_RENDER_HAS_TRAPEZOIDS (version))
386
4
	connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
387

            
388
4
    if (XCB_RENDER_HAS_PICTURE_TRANSFORM (version))
389
4
	connection->flags |= CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
390

            
391
4
    if (XCB_RENDER_HAS_FILTERS (version))
392
4
	connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS;
393

            
394
    if (XCB_RENDER_HAS_FILTER_GOOD (version))
395
	connection->flags |= CAIRO_XCB_RENDER_HAS_FILTER_GOOD;
396

            
397
    if (XCB_RENDER_HAS_FILTER_BEST (version))
398
	connection->flags |= CAIRO_XCB_RENDER_HAS_FILTER_BEST;
399

            
400
4
    if (XCB_RENDER_HAS_PDF_OPERATORS (version))
401
4
	connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
402

            
403
4
    if (XCB_RENDER_HAS_EXTENDED_REPEAT (version))
404
4
	connection->flags |= CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
405

            
406
4
    if (XCB_RENDER_HAS_GRADIENTS (version))
407
4
	connection->flags |= CAIRO_XCB_RENDER_HAS_GRADIENTS;
408

            
409
4
    if (XCB_RENDER_HAS_SUBPIXEL_ORDER (version)) {
410
	uint32_t screen;
411
4
	uint32_t *subpixel = xcb_render_query_pict_formats_subpixels(formats);
412

            
413
	/* The spec explicitly allows to have too few entries in the reply... */
414
8
	for (screen = 0; screen < formats->num_subpixel && screen < connection->root->roots_len; screen++)
415
4
	    connection->subpixel_orders[screen] = subpixel[screen];
416
    }
417

            
418
4
    free (version);
419

            
420
4
    status = _cairo_xcb_connection_parse_xrender_formats (connection, formats);
421
4
    free (formats);
422

            
423
4
    return status;
424
}
425

            
426
#if 0
427
static void
428
_cairo_xcb_connection_query_cairo (cairo_xcb_connection_t *connection)
429
{
430
    xcb_connection_t *c = connection->xcb_connection;
431
    xcb_cairo_query_version_reply_t *version;
432

            
433
    version = xcb_cairo_query_version_reply (c,
434
					     xcb_cairo_query_version (c, 0, 0),
435
					     0);
436

            
437
    free (version);
438
}
439
#endif
440

            
441
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
442
static cairo_bool_t
443
4
can_use_shm (cairo_xcb_connection_t *connection)
444
{
445
4
    cairo_bool_t success = TRUE;
446
4
    xcb_connection_t *c = connection->xcb_connection;
447
    xcb_void_cookie_t cookie[2];
448
    xcb_generic_error_t *error;
449
    int shmid;
450
    uint32_t shmseg;
451
    void *ptr;
452

            
453
4
    shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
454
4
    if (shmid == -1)
455
	return FALSE;
456

            
457
4
    ptr = shmat (shmid, NULL, 0);
458
4
    if (ptr == (char *) -1) {
459
	shmctl (shmid, IPC_RMID, NULL);
460
	return FALSE;
461
    }
462

            
463
4
    shmseg = xcb_generate_id (connection->xcb_connection);
464
4
    cookie[0] = xcb_shm_attach_checked (c, shmseg, shmid, FALSE);
465
4
    cookie[1] = xcb_shm_detach_checked (c, shmseg);
466

            
467
4
    error = xcb_request_check (c, cookie[0]);
468
4
    if (error != NULL)
469
	success = FALSE;
470

            
471
4
    error = xcb_request_check (c, cookie[1]);
472
4
    if (error != NULL)
473
	success = FALSE;
474

            
475
4
    shmctl (shmid, IPC_RMID, NULL);
476
4
    shmdt (ptr);
477

            
478
4
    return success;
479
}
480

            
481
static void
482
4
_cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection)
483
{
484
4
    xcb_connection_t *c = connection->xcb_connection;
485
    xcb_shm_query_version_reply_t *version;
486

            
487
4
    version = xcb_shm_query_version_reply (c, xcb_shm_query_version (c), 0);
488
4
    if (version == NULL)
489
	return;
490

            
491
4
    free (version);
492

            
493
4
    if (can_use_shm (connection))
494
4
	connection->flags |= CAIRO_XCB_HAS_SHM;
495
}
496
#endif
497

            
498
static cairo_status_t
499
4
_device_flush (void *device)
500
{
501
4
    cairo_xcb_connection_t *connection = device;
502
    cairo_status_t status;
503

            
504
4
    status = cairo_device_acquire (&connection->device);
505
4
    if (unlikely (status))
506
	return status;
507

            
508
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
509
4
    _cairo_xcb_connection_shm_mem_pools_flush (connection);
510
#endif
511

            
512
4
    xcb_flush (connection->xcb_connection);
513

            
514
4
    cairo_device_release (&connection->device);
515
4
    return CAIRO_STATUS_SUCCESS;
516
}
517

            
518
static void
519
100
_pluck_xrender_format (void *entry,
520
		       void *closure)
521
{
522
100
    _cairo_hash_table_remove (closure, entry);
523
100
    free (entry);
524
100
}
525

            
526
static void
527
4
_device_finish (void *device)
528
{
529
4
    cairo_xcb_connection_t *connection = device;
530
4
    cairo_bool_t was_cached = FALSE;
531

            
532
4
    if (! cairo_list_is_empty (&connection->link)) {
533
4
	CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
534
4
	cairo_list_del (&connection->link);
535
4
	CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
536
4
	was_cached = TRUE;
537
    }
538

            
539
4
    while (! cairo_list_is_empty (&connection->fonts)) {
540
	cairo_xcb_font_t *font;
541

            
542
	font = cairo_list_first_entry (&connection->fonts,
543
				       cairo_xcb_font_t,
544
				       link);
545
	_cairo_xcb_font_close (font);
546
    }
547

            
548
8
    while (! cairo_list_is_empty (&connection->screens)) {
549
	cairo_xcb_screen_t *screen;
550

            
551
4
	screen = cairo_list_first_entry (&connection->screens,
552
					 cairo_xcb_screen_t,
553
					 link);
554
4
	_cairo_xcb_screen_finish (screen);
555
    }
556

            
557
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
558
    /* _cairo_xcb_screen_finish finishes surfaces. If any of those surfaces had
559
     * a fallback image, we might have done a SHM PutImage. */
560
4
    _cairo_xcb_connection_shm_mem_pools_flush (connection);
561
#endif
562

            
563
4
    if (was_cached)
564
4
	cairo_device_destroy (device);
565
4
}
566

            
567
static void
568
4
_device_destroy (void *device)
569
{
570
4
    cairo_xcb_connection_t *connection = device;
571

            
572
4
    _cairo_hash_table_foreach (connection->xrender_formats,
573
4
			       _pluck_xrender_format, connection->xrender_formats);
574
4
    _cairo_hash_table_destroy (connection->xrender_formats);
575

            
576
4
    _cairo_hash_table_foreach (connection->visual_to_xrender_format,
577
			       _pluck_xrender_format,
578
4
			       connection->visual_to_xrender_format);
579
4
    _cairo_hash_table_destroy (connection->visual_to_xrender_format);
580

            
581
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
582
4
    _cairo_xcb_connection_shm_mem_pools_fini (connection);
583
#endif
584
4
    _cairo_freepool_fini (&connection->shm_info_freelist);
585

            
586
4
    CAIRO_MUTEX_FINI (connection->shm_mutex);
587
4
    CAIRO_MUTEX_FINI (connection->screens_mutex);
588

            
589
4
    free (connection->subpixel_orders);
590
4
    free (connection);
591
4
}
592

            
593
static const cairo_device_backend_t _cairo_xcb_device_backend = {
594
    CAIRO_DEVICE_TYPE_XCB,
595

            
596
    NULL, NULL, /* lock, unlock */
597

            
598
    _device_flush,
599
    _device_finish,
600
    _device_destroy,
601
};
602

            
603
cairo_xcb_connection_t *
604
4
_cairo_xcb_connection_get (xcb_connection_t *xcb_connection)
605
{
606
    cairo_xcb_connection_t *connection;
607
    const xcb_query_extension_reply_t *ext;
608
    cairo_status_t status;
609

            
610
    CAIRO_MUTEX_INITIALIZE ();
611

            
612
4
    CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
613
4
    if (connections.next == NULL) {
614
	/* XXX _cairo_init () */
615
4
	cairo_list_init (&connections);
616
    }
617

            
618
4
    cairo_list_foreach_entry (connection,
619
			      cairo_xcb_connection_t,
620
			      &connections,
621
			      link)
622
    {
623
	if (connection->xcb_connection == xcb_connection) {
624
	    /* Maintain MRU order. */
625
	    if (connections.next != &connection->link)
626
		cairo_list_move (&connection->link, &connections);
627

            
628
	    goto unlock;
629
	}
630
    }
631

            
632
4
    connection = _cairo_calloc (sizeof (cairo_xcb_connection_t));
633
4
    if (unlikely (connection == NULL))
634
	goto unlock;
635

            
636
4
    _cairo_device_init (&connection->device, &_cairo_xcb_device_backend);
637

            
638
4
    connection->xcb_connection = xcb_connection;
639

            
640
4
    cairo_list_init (&connection->fonts);
641
4
    cairo_list_init (&connection->screens);
642
4
    cairo_list_init (&connection->link);
643
4
    connection->xrender_formats = _cairo_hash_table_create (NULL);
644
4
    if (connection->xrender_formats == NULL) {
645
	CAIRO_MUTEX_FINI (connection->device.mutex);
646
	free (connection);
647
	connection = NULL;
648
	goto unlock;
649
    }
650

            
651
4
    connection->visual_to_xrender_format = _cairo_hash_table_create (NULL);
652
4
    if (connection->visual_to_xrender_format == NULL) {
653
	_cairo_hash_table_destroy (connection->xrender_formats);
654
	CAIRO_MUTEX_FINI (connection->device.mutex);
655
	free (connection);
656
	connection = NULL;
657
	goto unlock;
658
    }
659

            
660
4
    cairo_list_init (&connection->shm_pools);
661
4
    cairo_list_init (&connection->shm_pending);
662
4
    _cairo_freepool_init (&connection->shm_info_freelist,
663
			  sizeof (cairo_xcb_shm_info_t));
664

            
665
4
    connection->maximum_request_length =
666
4
	xcb_get_maximum_request_length (xcb_connection);
667

            
668
4
    CAIRO_MUTEX_INIT (connection->shm_mutex);
669
4
    CAIRO_MUTEX_INIT (connection->screens_mutex);
670

            
671
4
    CAIRO_MUTEX_LOCK (connection->device.mutex);
672

            
673
4
    connection->flags = 0;
674
4
    connection->force_precision = -1;
675

            
676
4
    xcb_prefetch_extension_data (xcb_connection, &xcb_big_requests_id);
677
4
    xcb_prefetch_extension_data (xcb_connection, &xcb_render_id);
678
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
679
4
    xcb_prefetch_extension_data (xcb_connection, &xcb_shm_id);
680
#endif
681
#if 0
682
    xcb_prefetch_extension_data (xcb_connection, &xcb_cairo_id);
683
#endif
684

            
685
4
    xcb_prefetch_maximum_request_length (xcb_connection);
686

            
687
4
    connection->root = xcb_get_setup (xcb_connection);
688
4
    connection->render = NULL;
689
4
    connection->subpixel_orders = _cairo_calloc_ab (connection->root->roots_len,
690
                                                 sizeof(*connection->subpixel_orders));
691
4
    if (unlikely (connection->subpixel_orders == NULL)) {
692
	CAIRO_MUTEX_UNLOCK (connection->device.mutex);
693
	_cairo_xcb_connection_destroy (connection);
694
	connection = NULL;
695
	goto unlock;
696
    }
697

            
698
4
    ext = xcb_get_extension_data (xcb_connection, &xcb_render_id);
699
4
    if (ext != NULL && ext->present) {
700
4
	status = _cairo_xcb_connection_query_render (connection);
701
4
	if (unlikely (status)) {
702
	    CAIRO_MUTEX_UNLOCK (connection->device.mutex);
703
	    _cairo_xcb_connection_destroy (connection);
704
	    connection = NULL;
705
	    goto unlock;
706
	}
707

            
708
4
	connection->render = ext;
709
    }
710

            
711
#if 0
712
    ext = xcb_get_extension_data (connection, &xcb_cairo_id);
713
    if (ext != NULL && ext->present)
714
	_cairo_xcb_connection_query_cairo (connection);
715
#endif
716

            
717
4
    connection->shm = NULL;
718
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
719
4
    ext = xcb_get_extension_data (xcb_connection, &xcb_shm_id);
720
4
    if (ext != NULL && ext->present) {
721
4
	_cairo_xcb_connection_query_shm (connection);
722
4
	connection->shm = ext;
723
    }
724
#endif
725

            
726
4
    connection->original_flags = connection->flags;
727

            
728
4
    CAIRO_MUTEX_UNLOCK (connection->device.mutex);
729

            
730
4
    cairo_list_add (&connection->link, &connections);
731
4
unlock:
732
4
    CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
733

            
734
4
    return connection;
735
}
736

            
737
xcb_render_pictformat_t
738
16
_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection,
739
					  pixman_format_code_t pixman_format)
740
{
741
    cairo_hash_entry_t key;
742
    cairo_xcb_xrender_format_t *format;
743

            
744
16
    key.hash = pixman_format;
745
16
    format = _cairo_hash_table_lookup (connection->xrender_formats, &key);
746
16
    return format ? format->xrender_format : XCB_NONE;
747
}
748

            
749
xcb_render_pictformat_t
750
_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection,
751
						     const xcb_visualid_t visual)
752
{
753
    cairo_hash_entry_t key;
754
    cairo_xcb_xrender_format_t *format;
755

            
756
    key.hash = visual;
757
    format = _cairo_hash_table_lookup (connection->visual_to_xrender_format, &key);
758
    return format ? format->xrender_format : XCB_NONE;
759
}
760

            
761
/**
762
 * cairo_xcb_device_get_connection:
763
 * @device: a #cairo_device_t for the XCB backend
764
 *
765
 * Get the connection for the XCB device.
766
 *
767
 * Returns: the #xcb_connection_t for the connection
768
 *
769
 * Since: 1.12
770
 **/
771
xcb_connection_t *
772
cairo_xcb_device_get_connection (cairo_device_t *device)
773
{
774
    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB)
775
	    return NULL;
776

            
777
    return ((cairo_xcb_connection_t *)device)->xcb_connection;
778
}
779

            
780
/* public (debug) interface */
781

            
782
/**
783
 * cairo_xcb_device_debug_cap_xshm_version:
784
 * @device: a #cairo_device_t for the XCB backend
785
 * @major_version: major version to restrict to
786
 * @minor_version: minor version to restrict to
787
 *
788
 * Restricts all future XCB surfaces for this devices to the specified version
789
 * of the SHM extension. This function exists solely for debugging purpose.
790
 * It let's you find out how cairo would behave with an older version of
791
 * the SHM extension.
792
 *
793
 * Use the special values -1 and -1 for disabling the SHM extension.
794
 *
795
 * Since: 1.12
796
 **/
797
void
798
cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device,
799
                                         int major_version,
800
                                         int minor_version)
801
{
802
    cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
803

            
804
    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
805
	cairo_status_t status;
806

            
807
	status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
808
	(void) status;
809
	return;
810
    }
811

            
812
    /* First reset all the SHM flags to their original value. This works
813
     * because we only ever clear bits after the connection was created.
814
     */
815
    connection->flags |= (connection->original_flags & CAIRO_XCB_SHM_MASK);
816

            
817
    /* clear any flags that are inappropriate for the desired version */
818
    if (major_version < 0 && minor_version < 0) {
819
	connection->flags &= ~(CAIRO_XCB_HAS_SHM);
820
    }
821
}
822

            
823
/**
824
 * cairo_xcb_device_debug_cap_xrender_version:
825
 * @device: a #cairo_device_t for the XCB backend
826
 * @major_version: major version to restrict to
827
 * @minor_version: minor version to restrict to
828
 *
829
 * Restricts all future XCB surfaces for this devices to the specified version
830
 * of the RENDER extension. This function exists solely for debugging purpose.
831
 * It let's you find out how cairo would behave with an older version of
832
 * the RENDER extension.
833
 *
834
 * Use the special values -1 and -1 for disabling the RENDER extension.
835
 *
836
 * Since: 1.12
837
 **/
838
void
839
cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device,
840
                                            int major_version,
841
                                            int minor_version)
842
{
843
    cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
844

            
845
    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
846
	cairo_status_t status;
847

            
848
	status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
849
	(void) status;
850
	return;
851
    }
852

            
853
    /* First reset all the RENDER flags to their original value. This works
854
     * because we only ever clear bits after the connection was created.
855
     */
856
    connection->flags |= (connection->original_flags & CAIRO_XCB_RENDER_MASK);
857

            
858
    /* clear any flags that are inappropriate for the desired version */
859
    if (major_version < 0 && minor_version < 0) {
860
	connection->flags &= ~(CAIRO_XCB_HAS_RENDER |
861
			       CAIRO_XCB_RENDER_HAS_COMPOSITE |
862
			       CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS |
863
			       CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
864
			       CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
865
			       CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
866
			       CAIRO_XCB_RENDER_HAS_FILTERS |
867
			       CAIRO_XCB_RENDER_HAS_FILTER_GOOD |
868
			       CAIRO_XCB_RENDER_HAS_FILTER_BEST |
869
			       CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
870
			       CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
871
			       CAIRO_XCB_RENDER_HAS_GRADIENTS);
872
    } else {
873
	xcb_render_query_version_reply_t version;
874

            
875
	version.major_version = major_version;
876
	version.minor_version = minor_version;
877

            
878
	if (! XCB_RENDER_HAS_FILL_RECTANGLES (&version))
879
	    connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
880

            
881
	if (! XCB_RENDER_HAS_TRAPEZOIDS (&version))
882
	    connection->flags &= ~CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
883

            
884
	if (! XCB_RENDER_HAS_PICTURE_TRANSFORM (&version))
885
	    connection->flags &= ~CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
886

            
887
	if (! XCB_RENDER_HAS_FILTERS (&version))
888
	    connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILTERS;
889

            
890
	if (! XCB_RENDER_HAS_PDF_OPERATORS (&version))
891
	    connection->flags &= ~CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
892

            
893
	if (! XCB_RENDER_HAS_EXTENDED_REPEAT (&version))
894
	    connection->flags &= ~CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
895

            
896
	if (! XCB_RENDER_HAS_GRADIENTS (&version))
897
	    connection->flags &= ~CAIRO_XCB_RENDER_HAS_GRADIENTS;
898
    }
899
}
900

            
901
/**
902
 * cairo_xcb_device_debug_set_precision:
903
 * @device: a #cairo_device_t for the XCB backend
904
 * @precision: the precision to use
905
 *
906
 * Render supports two modes of precision when rendering trapezoids. Set
907
 * the precision to the desired mode.
908
 *
909
 * Since: 1.12
910
 **/
911
void
912
cairo_xcb_device_debug_set_precision (cairo_device_t *device,
913
				      int precision)
914
{
915
    if (device == NULL || device->status)
916
	return;
917
    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
918
	cairo_status_t status;
919

            
920
	status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
921
	(void) status;
922
	return;
923
    }
924

            
925
    ((cairo_xcb_connection_t *) device)->force_precision = precision;
926
}
927

            
928
/**
929
 * cairo_xcb_device_debug_get_precision:
930
 * @device: a #cairo_device_t for the XCB backend
931
 *
932
 * Get the Xrender precision mode.
933
 *
934
 * Returns: the render precision mode
935
 *
936
 * Since: 1.12
937
 **/
938
int
939
cairo_xcb_device_debug_get_precision (cairo_device_t *device)
940
{
941
    if (device == NULL || device->status)
942
	return -1;
943
    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
944
	cairo_status_t status;
945

            
946
	status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
947
	(void) status;
948
	return -1;
949
    }
950

            
951
    return ((cairo_xcb_connection_t *) device)->force_precision;
952
}