Tizen 2.0 Release
[framework/graphics/cairo.git] / src / cairo-xcb-screen.c
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 struct pattern_cache_entry {
39     cairo_cache_entry_t key;
40     cairo_xcb_screen_t *screen;
41     cairo_pattern_union_t pattern;
42     cairo_surface_t *picture;
43 };
44
45 void
46 _cairo_xcb_screen_finish (cairo_xcb_screen_t *screen)
47 {
48     int i;
49
50     CAIRO_MUTEX_LOCK (screen->connection->screens_mutex);
51     cairo_list_del (&screen->link);
52     CAIRO_MUTEX_UNLOCK (screen->connection->screens_mutex);
53
54     while (! cairo_list_is_empty (&screen->surfaces)) {
55         cairo_surface_t *surface;
56
57         surface = &cairo_list_first_entry (&screen->surfaces,
58                                            cairo_xcb_surface_t,
59                                            link)->base;
60
61         cairo_surface_finish (surface);
62     }
63
64     while (! cairo_list_is_empty (&screen->pictures)) {
65         cairo_surface_t *surface;
66
67         surface = &cairo_list_first_entry (&screen->pictures,
68                                            cairo_xcb_picture_t,
69                                            link)->base;
70
71         cairo_surface_finish (surface);
72     }
73
74     for (i = 0; i < screen->solid_cache_size; i++)
75         cairo_surface_destroy (screen->solid_cache[i].picture);
76
77     for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
78         cairo_surface_destroy (screen->stock_colors[i]);
79
80     for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
81         if (screen->gc_depths[i] != 0)
82             _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]);
83     }
84
85     _cairo_cache_fini (&screen->linear_pattern_cache);
86     _cairo_cache_fini (&screen->radial_pattern_cache);
87     _cairo_freelist_fini (&screen->pattern_cache_entry_freelist);
88
89     free (screen);
90 }
91
92 static cairo_bool_t
93 _linear_pattern_cache_entry_equal (const void *A, const void *B)
94 {
95     const struct pattern_cache_entry *a = A, *b = B;
96
97     return _cairo_linear_pattern_equal (&a->pattern.gradient.linear,
98                                         &b->pattern.gradient.linear);
99 }
100
101 static cairo_bool_t
102 _radial_pattern_cache_entry_equal (const void *A, const void *B)
103 {
104     const struct pattern_cache_entry *a = A, *b = B;
105
106     return _cairo_radial_pattern_equal (&a->pattern.gradient.radial,
107                                         &b->pattern.gradient.radial);
108 }
109
110 static void
111 _pattern_cache_entry_destroy (void *closure)
112 {
113     struct pattern_cache_entry *entry = closure;
114
115     _cairo_pattern_fini (&entry->pattern.base);
116     cairo_surface_destroy (entry->picture);
117     _cairo_freelist_free (&entry->screen->pattern_cache_entry_freelist, entry);
118 }
119
120 cairo_xcb_screen_t *
121 _cairo_xcb_screen_get (xcb_connection_t *xcb_connection,
122                        xcb_screen_t *xcb_screen)
123 {
124     cairo_xcb_connection_t *connection;
125     cairo_xcb_screen_t *screen;
126     cairo_status_t status;
127     int i;
128
129     connection = _cairo_xcb_connection_get (xcb_connection);
130     if (unlikely (connection == NULL))
131         return NULL;
132
133     CAIRO_MUTEX_LOCK (connection->screens_mutex);
134
135     cairo_list_foreach_entry (screen,
136                               cairo_xcb_screen_t,
137                               &connection->screens,
138                               link)
139     {
140         if (screen->xcb_screen == xcb_screen) {
141             /* Maintain list in MRU order */
142             if (&screen->link != connection->screens.next)
143                 cairo_list_move (&screen->link, &connection->screens);
144
145             goto unlock;
146         }
147     }
148
149     screen = malloc (sizeof (cairo_xcb_screen_t));
150     if (unlikely (screen == NULL))
151         goto unlock;
152
153     screen->connection = connection;
154     screen->xcb_screen = xcb_screen;
155
156     _cairo_freelist_init (&screen->pattern_cache_entry_freelist,
157                           sizeof (struct pattern_cache_entry));
158     cairo_list_init (&screen->link);
159     cairo_list_init (&screen->surfaces);
160     cairo_list_init (&screen->pictures);
161
162     memset (screen->gc_depths, 0, sizeof (screen->gc_depths));
163     memset (screen->gc, 0, sizeof (screen->gc));
164
165     screen->solid_cache_size = 0;
166     for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
167         screen->stock_colors[i] = NULL;
168
169     status = _cairo_cache_init (&screen->linear_pattern_cache,
170                                 _linear_pattern_cache_entry_equal,
171                                 NULL,
172                                 _pattern_cache_entry_destroy,
173                                 16);
174     if (unlikely (status))
175         goto error_screen;
176
177     status = _cairo_cache_init (&screen->radial_pattern_cache,
178                                 _radial_pattern_cache_entry_equal,
179                                 NULL,
180                                 _pattern_cache_entry_destroy,
181                                 4);
182     if (unlikely (status))
183         goto error_linear;
184
185     cairo_list_add (&screen->link, &connection->screens);
186
187 unlock:
188     CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
189
190     return screen;
191
192 error_linear:
193     _cairo_cache_fini (&screen->linear_pattern_cache);
194 error_screen:
195     CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
196     free (screen);
197
198     return NULL;
199 }
200
201 static xcb_gcontext_t
202 _create_gc (cairo_xcb_screen_t *screen,
203             xcb_drawable_t drawable)
204 {
205     uint32_t values[] = { 0 };
206
207     return _cairo_xcb_connection_create_gc (screen->connection, drawable,
208                                             XCB_GC_GRAPHICS_EXPOSURES,
209                                             values);
210 }
211
212 xcb_gcontext_t
213 _cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen,
214                           xcb_drawable_t drawable,
215                           int depth)
216 {
217     int i;
218
219     assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
220
221     for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
222         if (screen->gc_depths[i] == depth) {
223             screen->gc_depths[i] = 0;
224             return screen->gc[i];
225         }
226     }
227
228     return _create_gc (screen, drawable);
229 }
230
231 void
232 _cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc)
233 {
234     int i;
235
236     assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
237
238     for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
239         if (screen->gc_depths[i] == 0)
240             break;
241     }
242
243     if (i == ARRAY_LENGTH (screen->gc)) {
244         /* perform random substitution to ensure fair caching over depths */
245         i = rand () % ARRAY_LENGTH (screen->gc);
246         _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]);
247     }
248
249     screen->gc[i] = gc;
250     screen->gc_depths[i] = depth;
251 }
252
253 cairo_status_t
254 _cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen,
255                                         const cairo_linear_pattern_t *linear,
256                                         cairo_surface_t *picture)
257 {
258     struct pattern_cache_entry *entry;
259     cairo_status_t status;
260
261     assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
262
263     entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
264     if (unlikely (entry == NULL))
265         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
266
267     entry->key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
268     entry->key.size = 1;
269
270     status = _cairo_pattern_init_copy (&entry->pattern.base, &linear->base.base);
271     if (unlikely (status)) {
272         _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
273         return status;
274     }
275
276     entry->picture = cairo_surface_reference (picture);
277     entry->screen = screen;
278
279     status = _cairo_cache_insert (&screen->linear_pattern_cache,
280                                   &entry->key);
281     if (unlikely (status)) {
282         cairo_surface_destroy (picture);
283         _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
284         return status;
285     }
286
287     return CAIRO_STATUS_SUCCESS;
288 }
289
290 cairo_surface_t *
291 _cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen,
292                                          const cairo_linear_pattern_t *linear)
293 {
294     cairo_surface_t *picture = NULL;
295     struct pattern_cache_entry tmpl;
296     struct pattern_cache_entry *entry;
297
298     assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
299
300     tmpl.key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
301     _cairo_pattern_init_static_copy (&tmpl.pattern.base, &linear->base.base);
302
303     entry = _cairo_cache_lookup (&screen->linear_pattern_cache, &tmpl.key);
304     if (entry != NULL)
305         picture = cairo_surface_reference (entry->picture);
306
307     return picture;
308 }
309
310 cairo_status_t
311 _cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen,
312                                         const cairo_radial_pattern_t *radial,
313                                         cairo_surface_t *picture)
314 {
315     struct pattern_cache_entry *entry;
316     cairo_status_t status;
317
318     assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
319
320     entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
321     if (unlikely (entry == NULL))
322         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
323
324     entry->key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
325     entry->key.size = 1;
326
327     status = _cairo_pattern_init_copy (&entry->pattern.base, &radial->base.base);
328     if (unlikely (status)) {
329         _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
330         return status;
331     }
332
333     entry->picture = cairo_surface_reference (picture);
334     entry->screen = screen;
335
336     status = _cairo_cache_insert (&screen->radial_pattern_cache, &entry->key);
337     if (unlikely (status)) {
338         cairo_surface_destroy (picture);
339         _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
340         return status;
341     }
342
343     return CAIRO_STATUS_SUCCESS;
344 }
345
346 cairo_surface_t *
347 _cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen,
348                                          const cairo_radial_pattern_t *radial)
349 {
350     cairo_surface_t *picture = NULL;
351     struct pattern_cache_entry tmpl;
352     struct pattern_cache_entry *entry;
353
354     assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
355
356     tmpl.key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
357     _cairo_pattern_init_static_copy (&tmpl.pattern.base, &radial->base.base);
358
359     entry = _cairo_cache_lookup (&screen->radial_pattern_cache, &tmpl.key);
360     if (entry != NULL)
361         picture = cairo_surface_reference (entry->picture);
362
363     return picture;
364 }