96acbe8f2295bb2a4b17288db23347c737a7e8be
[framework/uifw/xorg/xcb/xcb-util.git] / renderutil / cache.c
1 /* Copyright © 2006 Jamey Sharp.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a
4  * copy of this software and associated documentation files (the "Software"),
5  * to deal in the Software without restriction, including without limitation
6  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7  * and/or sell copies of the Software, and to permit persons to whom the
8  * Software is furnished to do so, subject to the following conditions:
9  * 
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  * 
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
17  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  * 
20  * Except as contained in this notice, the names of the authors or their
21  * institutions shall not be used in advertising or otherwise to promote the
22  * sale, use or other dealings in this Software without prior written
23  * authorization from the authors.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 #include "xcb_renderutil.h"
30 #include <stdlib.h>
31 #include <pthread.h>
32
33 typedef struct connection_cache {
34     struct connection_cache      *next;         /* keep a linked list */
35     xcb_connection_t                *c;         /* which display this is */
36     xcb_render_query_version_reply_t     *version;
37     xcb_render_query_pict_formats_reply_t *formats;
38 } connection_cache;
39
40 static struct {
41     pthread_mutex_t   lock;
42     connection_cache *head;           /* start of the list */
43     connection_cache *cur;            /* most recently used */
44 } connections = { PTHREAD_MUTEX_INITIALIZER };
45
46 /*
47  * If the server is missing support for any of the required depths on
48  * any screen, tell the application that Render is not present.
49  */
50
51 #define DEPTH_MASK(d)   (1 << ((d) - 1))
52     
53 /*
54  * Render requires support for depth 1, 4, 8, 24 and 32 pixmaps
55  */
56
57 #define REQUIRED_DEPTHS (DEPTH_MASK(1) | \
58                          DEPTH_MASK(4) | \
59                          DEPTH_MASK(8) | \
60                          DEPTH_MASK(24) | \
61                          DEPTH_MASK(32))
62
63 /* Test each depth not explicitly advertised to see if pixmap creation
64  * succeeds: if it does, that depth is usable. */
65 static int
66 pixmap_depths_usable (xcb_connection_t *c, uint32_t missing, xcb_pixmap_t pixmap, xcb_drawable_t root)
67 {
68     xcb_void_cookie_t create_cookie[32] = { { 0 } };
69     xcb_void_cookie_t free_cookie[32]   = { { 0 } };
70     int d;
71     int success = 1;
72     for (d = 1; d <= 32; d++)
73         if (missing & DEPTH_MASK(d))
74         {
75             create_cookie[d - 1] = xcb_create_pixmap_checked (c, d, pixmap, root, 1, 1);
76             free_cookie[d - 1] = xcb_free_pixmap_checked (c, pixmap);
77             if (!create_cookie[d - 1].sequence || !free_cookie[d - 1].sequence)
78             {
79                 success = 0;
80                 break;
81             }
82         }
83     for (d = 0; d < 32; d++)
84         if (create_cookie[d].sequence || free_cookie[d].sequence)
85         {
86             xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[d]);
87             xcb_generic_error_t *free_error = xcb_request_check (c, free_cookie[d]);
88             success = success && !create_error;
89             free(create_error);
90             free(free_error);
91         }
92     return success;
93 }
94
95 static int
96 has_required_depths (xcb_connection_t *c)
97 {
98     xcb_screen_iterator_t screens;
99     xcb_pixmap_t pixmap = { -1 };
100     for (screens = xcb_setup_roots_iterator(xcb_get_setup(c)); screens.rem; xcb_screen_next(&screens))
101     {
102         xcb_depth_iterator_t depths;
103         uint32_t missing = REQUIRED_DEPTHS;
104         xcb_drawable_t root;
105
106         for (depths = xcb_screen_allowed_depths_iterator(screens.data); depths.rem; xcb_depth_next(&depths))
107             missing &= ~DEPTH_MASK(depths.data->depth);
108         if (!missing)
109             continue;
110
111         /*
112          * Ok, this is ugly.  It should be sufficient at this
113          * point to just return false, but Xinerama is broken at
114          * this point and only advertises depths which have an
115          * associated visual.  Of course, the other depths still
116          * work, but the only way to find out is to try them.
117          */
118         if (pixmap == -1)
119             pixmap = xcb_generate_id(c);
120         root = screens.data->root;
121         if (!pixmap_depths_usable (c, missing, pixmap, root))
122             return 0;
123     }
124     return 1;
125 }
126
127 static connection_cache *
128 find_or_create_display (xcb_connection_t *c)
129 {
130     connection_cache *info;
131     xcb_render_query_version_cookie_t version_cookie;
132     xcb_render_query_pict_formats_cookie_t formats_cookie;
133     int present;
134
135     /*
136      * look for display in list
137      */
138     for (info = connections.head; info; info = info->next)
139         if (info->c == c) {
140             connections.cur = info;     /* cache most recently used */
141             return info;
142         }
143
144     /*
145      * don't already have this display: add it.
146      */
147     info = malloc (sizeof (connection_cache));
148     if (!info)
149         return NULL;
150     info->c = c;
151
152     version_cookie = xcb_render_query_version(c, 0, 10);
153     formats_cookie = xcb_render_query_pict_formats(c);
154     xcb_flush(c);
155     present = has_required_depths (c);
156     info->version = xcb_render_query_version_reply(c, version_cookie, 0);
157     info->formats = xcb_render_query_pict_formats_reply(c, formats_cookie, 0);
158
159     if (!present || !info->version || !info->formats)
160     {
161         free(info->version);
162         info->version = 0;
163         free(info->formats);
164         info->formats = 0;
165     }
166     /* Check for the lack of sub-pixel data */
167     else if (info->version->major_version == 0 && info->version->minor_version < 6)
168         info->formats->num_subpixel = 0;
169
170     /*
171      * now, chain it onto the list
172      */
173     info->next = connections.head;
174     connections.head = info;
175     connections.cur = info;
176
177     return info;
178 }
179
180 static connection_cache *
181 find_display (xcb_connection_t *c)
182 {
183     connection_cache *info;
184
185     /*
186      * see if this was the most recently accessed display
187      */
188     if ((info = connections.cur) && info->c == c) 
189         return info;
190
191     pthread_mutex_lock(&connections.lock);
192     info = find_or_create_display (c);
193     pthread_mutex_unlock(&connections.lock);
194     return info;
195 }
196
197 const xcb_render_query_version_reply_t *
198 xcb_render_util_query_version (xcb_connection_t *c)
199 {
200     connection_cache *info = find_display (c);
201     if (!info)
202         return 0;
203     return info->version;
204 }
205
206 const xcb_render_query_pict_formats_reply_t *
207 xcb_render_util_query_formats (xcb_connection_t *c)
208 {
209     connection_cache *info = find_display (c);
210     if (!info)
211         return 0;
212     return info->formats;
213 }
214
215 int
216 xcb_render_util_disconnect (xcb_connection_t *c)
217 {
218     connection_cache **prev, *cur = NULL;
219     pthread_mutex_lock(&connections.lock);
220     for(prev = &connections.head; *prev; prev = &(*prev)->next)
221         if((*prev)->c == c)
222         {
223             cur = *prev;
224             *prev = cur->next;
225             if(cur == connections.cur)
226                 connections.cur = NULL;  /* flush cache */
227             free(cur->version);
228             free(cur->formats);
229             free(cur);
230             break;
231         }
232     pthread_mutex_unlock(&connections.lock);
233     return cur != NULL;
234 }