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