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