cache.c revision ecce36be
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 33typedef 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 40static 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. */ 65static int 66pixmap_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 95static int 96has_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 127static connection_cache * 128find_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 180static connection_cache * 181find_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 197const xcb_render_query_version_reply_t * 198xcb_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 206const xcb_render_query_pict_formats_reply_t * 207xcb_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 215int 216xcb_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} 235