1
/* Cairo - a vector graphics library with display and print output
2
 *
3
 * Copyright © 2008 Chris Wilson
4
 * Copyright © 2009 Intel Corporation
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it either under the terms of the GNU Lesser General Public
8
 * License version 2.1 as published by the Free Software Foundation
9
 * (the "LGPL") or, at your option, under the terms of the Mozilla
10
 * Public License Version 1.1 (the "MPL"). If you do not alter this
11
 * notice, a recipient may use your version of this file under either
12
 * the MPL or the LGPL.
13
 *
14
 * You should have received a copy of the LGPL along with this library
15
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17
 * You should have received a copy of the MPL along with this library
18
 * in the file COPYING-MPL-1.1
19
 *
20
 * The contents of this file are subject to the Mozilla Public License
21
 * Version 1.1 (the "License"); you may not use this file except in
22
 * compliance with the License. You may obtain a copy of the License at
23
 * http://www.mozilla.org/MPL/
24
 *
25
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27
 * the specific language governing rights and limitations.
28
 *
29
 * Authors:
30
 *    Chris Wilson <chris@chris-wilson.co.uk>
31
 */
32

            
33
#include "cairoint.h"
34

            
35
#include "cairo-xcb-private.h"
36
#include "cairo-list-inline.h"
37

            
38
#include "cairo-fontconfig-private.h"
39

            
40
static void
41
_cairo_xcb_init_screen_font_options (cairo_xcb_screen_t *screen)
42
{
43
    cairo_xcb_resources_t res;
44
    cairo_antialias_t antialias;
45
    cairo_subpixel_order_t subpixel_order;
46
    cairo_lcd_filter_t lcd_filter;
47
    cairo_hint_style_t hint_style;
48

            
49
    _cairo_xcb_resources_get (screen, &res);
50

            
51
    /* the rest of the code in this function is copied from
52
       _cairo_xlib_init_screen_font_options in cairo-xlib-screen.c */
53

            
54
    if (res.xft_hinting) {
55
	switch (res.xft_hintstyle) {
56
	case FC_HINT_NONE:
57
	    hint_style = CAIRO_HINT_STYLE_NONE;
58
	    break;
59
	case FC_HINT_SLIGHT:
60
	    hint_style = CAIRO_HINT_STYLE_SLIGHT;
61
	    break;
62
	case FC_HINT_MEDIUM:
63
	    hint_style = CAIRO_HINT_STYLE_MEDIUM;
64
	    break;
65
	case FC_HINT_FULL:
66
	    hint_style = CAIRO_HINT_STYLE_FULL;
67
	    break;
68
	default:
69
	    hint_style = CAIRO_HINT_STYLE_DEFAULT;
70
	}
71
    } else {
72
	hint_style = CAIRO_HINT_STYLE_NONE;
73
    }
74

            
75
    switch (res.xft_rgba) {
76
    case FC_RGBA_RGB:
77
	subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
78
	break;
79
    case FC_RGBA_BGR:
80
	subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
81
	break;
82
    case FC_RGBA_VRGB:
83
	subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
84
	break;
85
    case FC_RGBA_VBGR:
86
	subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
87
	break;
88
    case FC_RGBA_UNKNOWN:
89
    case FC_RGBA_NONE:
90
    default:
91
	subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
92
    }
93

            
94
    switch (res.xft_lcdfilter) {
95
    case FC_LCD_NONE:
96
	lcd_filter = CAIRO_LCD_FILTER_NONE;
97
	break;
98
    case FC_LCD_DEFAULT:
99
	lcd_filter = CAIRO_LCD_FILTER_FIR5;
100
	break;
101
    case FC_LCD_LIGHT:
102
	lcd_filter = CAIRO_LCD_FILTER_FIR3;
103
	break;
104
    case FC_LCD_LEGACY:
105
	lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
106
	break;
107
    default:
108
	lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
109
	break;
110
    }
111

            
112
    if (res.xft_antialias) {
113
	if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT)
114
	    antialias = CAIRO_ANTIALIAS_GRAY;
115
	else
116
	    antialias = CAIRO_ANTIALIAS_SUBPIXEL;
117
    } else {
118
	antialias = CAIRO_ANTIALIAS_NONE;
119
    }
120

            
121
    cairo_font_options_set_hint_style (&screen->font_options, hint_style);
122
    cairo_font_options_set_antialias (&screen->font_options, antialias);
123
    cairo_font_options_set_subpixel_order (&screen->font_options, subpixel_order);
124
    _cairo_font_options_set_lcd_filter (&screen->font_options, lcd_filter);
125
    cairo_font_options_set_hint_metrics (&screen->font_options, CAIRO_HINT_METRICS_ON);
126
}
127

            
128
struct pattern_cache_entry {
129
    cairo_cache_entry_t key;
130
    cairo_xcb_screen_t *screen;
131
    cairo_pattern_union_t pattern;
132
    cairo_surface_t *picture;
133
};
134

            
135
void
136
4
_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen)
137
{
138
    int i;
139

            
140
4
    CAIRO_MUTEX_LOCK (screen->connection->screens_mutex);
141
4
    cairo_list_del (&screen->link);
142
4
    CAIRO_MUTEX_UNLOCK (screen->connection->screens_mutex);
143

            
144
7
    while (! cairo_list_is_empty (&screen->surfaces)) {
145
	cairo_surface_t *surface;
146

            
147
3
	surface = &cairo_list_first_entry (&screen->surfaces,
148
					   cairo_xcb_surface_t,
149
					   link)->base;
150

            
151
3
	cairo_surface_finish (surface);
152
    }
153

            
154
4
    while (! cairo_list_is_empty (&screen->pictures)) {
155
	cairo_surface_t *surface;
156

            
157
	surface = &cairo_list_first_entry (&screen->pictures,
158
					   cairo_xcb_picture_t,
159
					   link)->base;
160

            
161
	cairo_surface_finish (surface);
162
    }
163

            
164
4
    for (i = 0; i < screen->solid_cache_size; i++)
165
	cairo_surface_destroy (screen->solid_cache[i].picture);
166

            
167
16
    for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
168
12
	cairo_surface_destroy (screen->stock_colors[i]);
169

            
170
20
    for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
171
16
	if (screen->gc_depths[i] != 0)
172
	    xcb_free_gc (screen->connection->xcb_connection, screen->gc[i]);
173
    }
174

            
175
4
    _cairo_cache_fini (&screen->linear_pattern_cache);
176
4
    _cairo_cache_fini (&screen->radial_pattern_cache);
177
4
    _cairo_freelist_fini (&screen->pattern_cache_entry_freelist);
178

            
179
4
    free (screen);
180
4
}
181

            
182
static cairo_bool_t
183
_linear_pattern_cache_entry_equal (const void *A, const void *B)
184
{
185
    const struct pattern_cache_entry *a = A, *b = B;
186

            
187
    return _cairo_linear_pattern_equal (&a->pattern.gradient.linear,
188
					&b->pattern.gradient.linear);
189
}
190

            
191
static cairo_bool_t
192
_radial_pattern_cache_entry_equal (const void *A, const void *B)
193
{
194
    const struct pattern_cache_entry *a = A, *b = B;
195

            
196
    return _cairo_radial_pattern_equal (&a->pattern.gradient.radial,
197
					&b->pattern.gradient.radial);
198
}
199

            
200
static void
201
_pattern_cache_entry_destroy (void *closure)
202
{
203
    struct pattern_cache_entry *entry = closure;
204

            
205
    _cairo_pattern_fini (&entry->pattern.base);
206
    cairo_surface_destroy (entry->picture);
207
    _cairo_freelist_free (&entry->screen->pattern_cache_entry_freelist, entry);
208
}
209

            
210
4
static int _get_screen_index(cairo_xcb_connection_t *xcb_connection,
211
			     xcb_screen_t *xcb_screen)
212
{
213
4
    int idx = 0;
214
4
    xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_connection->root);
215
4
    for (; iter.rem; xcb_screen_next(&iter), idx++)
216
4
	if (iter.data->root == xcb_screen->root)
217
4
	    return idx;
218

            
219
    ASSERT_NOT_REACHED;
220
}
221

            
222
cairo_xcb_screen_t *
223
4
_cairo_xcb_screen_get (xcb_connection_t *xcb_connection,
224
		       xcb_screen_t *xcb_screen)
225
{
226
    cairo_xcb_connection_t *connection;
227
    cairo_xcb_screen_t *screen;
228
    cairo_status_t status;
229
    int screen_idx;
230
    int i;
231

            
232
4
    connection = _cairo_xcb_connection_get (xcb_connection);
233
4
    if (unlikely (connection == NULL))
234
	return NULL;
235

            
236
4
    CAIRO_MUTEX_LOCK (connection->screens_mutex);
237

            
238
4
    cairo_list_foreach_entry (screen,
239
			      cairo_xcb_screen_t,
240
			      &connection->screens,
241
			      link)
242
    {
243
	if (screen->xcb_screen == xcb_screen) {
244
	    /* Maintain list in MRU order */
245
	    if (&screen->link != connection->screens.next)
246
		cairo_list_move (&screen->link, &connection->screens);
247

            
248
	    goto unlock;
249
	}
250
    }
251

            
252
4
    screen = _cairo_calloc (sizeof (cairo_xcb_screen_t));
253
4
    if (unlikely (screen == NULL))
254
	goto unlock;
255

            
256
4
    screen_idx = _get_screen_index(connection, xcb_screen);
257

            
258
4
    screen->connection = connection;
259
4
    screen->xcb_screen = xcb_screen;
260
4
    screen->has_font_options = FALSE;
261
4
    screen->subpixel_order = connection->subpixel_orders[screen_idx];
262

            
263
4
    _cairo_freelist_init (&screen->pattern_cache_entry_freelist,
264
			  sizeof (struct pattern_cache_entry));
265
4
    cairo_list_init (&screen->link);
266
4
    cairo_list_init (&screen->surfaces);
267
4
    cairo_list_init (&screen->pictures);
268

            
269
4
    memset (screen->gc_depths, 0, sizeof (screen->gc_depths));
270
4
    memset (screen->gc, 0, sizeof (screen->gc));
271

            
272
4
    screen->solid_cache_size = 0;
273
16
    for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
274
12
	screen->stock_colors[i] = NULL;
275

            
276
4
    status = _cairo_cache_init (&screen->linear_pattern_cache,
277
				_linear_pattern_cache_entry_equal,
278
				NULL,
279
				_pattern_cache_entry_destroy,
280
				16);
281
4
    if (unlikely (status))
282
	goto error_screen;
283

            
284
4
    status = _cairo_cache_init (&screen->radial_pattern_cache,
285
				_radial_pattern_cache_entry_equal,
286
				NULL,
287
				_pattern_cache_entry_destroy,
288
				4);
289
4
    if (unlikely (status))
290
	goto error_linear;
291

            
292
4
    cairo_list_add (&screen->link, &connection->screens);
293

            
294
4
unlock:
295
4
    CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
296

            
297
4
    return screen;
298

            
299
error_linear:
300
    _cairo_cache_fini (&screen->linear_pattern_cache);
301
error_screen:
302
    CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
303
    free (screen);
304

            
305
    return NULL;
306
}
307

            
308
static xcb_gcontext_t
309
_create_gc (cairo_xcb_screen_t *screen,
310
	    xcb_drawable_t drawable)
311
{
312
    uint32_t values[] = { 0 };
313

            
314
    return _cairo_xcb_connection_create_gc (screen->connection, drawable,
315
					    XCB_GC_GRAPHICS_EXPOSURES,
316
					    values);
317
}
318

            
319
xcb_gcontext_t
320
_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen,
321
			  xcb_drawable_t drawable,
322
			  int depth)
323
{
324
    int i;
325

            
326
    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
327

            
328
    for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
329
	if (screen->gc_depths[i] == depth) {
330
	    screen->gc_depths[i] = 0;
331
	    return screen->gc[i];
332
	}
333
    }
334

            
335
    return _create_gc (screen, drawable);
336
}
337

            
338
void
339
_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc)
340
{
341
    int i;
342

            
343
    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
344

            
345
    for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
346
	if (screen->gc_depths[i] == 0)
347
	    break;
348
    }
349

            
350
    if (i == ARRAY_LENGTH (screen->gc)) {
351
	/* perform random substitution to ensure fair caching over depths */
352
	i = rand () % ARRAY_LENGTH (screen->gc);
353
	xcb_free_gc (screen->connection->xcb_connection, screen->gc[i]);
354
    }
355

            
356
    screen->gc[i] = gc;
357
    screen->gc_depths[i] = depth;
358
}
359

            
360
cairo_status_t
361
_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen,
362
					const cairo_linear_pattern_t *linear,
363
					cairo_surface_t *picture)
364
{
365
    struct pattern_cache_entry *entry;
366
    cairo_status_t status;
367

            
368
    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
369

            
370
    entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
371
    if (unlikely (entry == NULL))
372
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
373

            
374
    entry->key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
375
    entry->key.size = 1;
376

            
377
    status = _cairo_pattern_init_copy (&entry->pattern.base, &linear->base.base);
378
    if (unlikely (status)) {
379
	_cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
380
	return status;
381
    }
382

            
383
    entry->picture = cairo_surface_reference (picture);
384
    entry->screen = screen;
385

            
386
    status = _cairo_cache_insert (&screen->linear_pattern_cache,
387
				  &entry->key);
388
    if (unlikely (status)) {
389
	cairo_surface_destroy (picture);
390
	_cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
391
	return status;
392
    }
393

            
394
    return CAIRO_STATUS_SUCCESS;
395
}
396

            
397
cairo_surface_t *
398
_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen,
399
					 const cairo_linear_pattern_t *linear)
400
{
401
    cairo_surface_t *picture = NULL;
402
    struct pattern_cache_entry tmpl;
403
    struct pattern_cache_entry *entry;
404

            
405
    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
406

            
407
    tmpl.key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
408
    _cairo_pattern_init_static_copy (&tmpl.pattern.base, &linear->base.base);
409

            
410
    entry = _cairo_cache_lookup (&screen->linear_pattern_cache, &tmpl.key);
411
    if (entry != NULL)
412
	picture = cairo_surface_reference (entry->picture);
413

            
414
    return picture;
415
}
416

            
417
cairo_status_t
418
_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen,
419
					const cairo_radial_pattern_t *radial,
420
					cairo_surface_t *picture)
421
{
422
    struct pattern_cache_entry *entry;
423
    cairo_status_t status;
424

            
425
    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
426

            
427
    entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
428
    if (unlikely (entry == NULL))
429
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
430

            
431
    entry->key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
432
    entry->key.size = 1;
433

            
434
    status = _cairo_pattern_init_copy (&entry->pattern.base, &radial->base.base);
435
    if (unlikely (status)) {
436
	_cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
437
	return status;
438
    }
439

            
440
    entry->picture = cairo_surface_reference (picture);
441
    entry->screen = screen;
442

            
443
    status = _cairo_cache_insert (&screen->radial_pattern_cache, &entry->key);
444
    if (unlikely (status)) {
445
	cairo_surface_destroy (picture);
446
	_cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
447
	return status;
448
    }
449

            
450
    return CAIRO_STATUS_SUCCESS;
451
}
452

            
453
cairo_surface_t *
454
_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen,
455
					 const cairo_radial_pattern_t *radial)
456
{
457
    cairo_surface_t *picture = NULL;
458
    struct pattern_cache_entry tmpl;
459
    struct pattern_cache_entry *entry;
460

            
461
    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
462

            
463
    tmpl.key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
464
    _cairo_pattern_init_static_copy (&tmpl.pattern.base, &radial->base.base);
465

            
466
    entry = _cairo_cache_lookup (&screen->radial_pattern_cache, &tmpl.key);
467
    if (entry != NULL)
468
	picture = cairo_surface_reference (entry->picture);
469

            
470
    return picture;
471
}
472

            
473
cairo_font_options_t *
474
_cairo_xcb_screen_get_font_options (cairo_xcb_screen_t *screen)
475
{
476
    if (! screen->has_font_options) {
477
	_cairo_font_options_init_default (&screen->font_options);
478
	_cairo_font_options_set_round_glyph_positions (&screen->font_options, CAIRO_ROUND_GLYPH_POS_ON);
479

            
480
	/* XXX: This is disabled because something seems to be merging
481
	   font options incorrectly for xcb.  This effectively reverts
482
	   the changes brought in git e691d242, and restores ~150 tests
483
	   to resume passing.  See mailing list archives for Sep 17,
484
	   2014 for more discussion. */
485
	if (0 && ! _cairo_xcb_connection_acquire (screen->connection)) {
486
	    _cairo_xcb_init_screen_font_options (screen);
487
	    _cairo_xcb_connection_release (screen->connection);
488
	}
489

            
490
	screen->has_font_options = TRUE;
491
    }
492

            
493
    return &screen->font_options;
494
}