2 * Theme engine resource cache.
4 * Copyright (c) 2004, Joe English
8 * Tk maintains reference counts for fonts, colors, and images,
9 * and deallocates them when the reference count goes to zero.
10 * With the theme engine, resources are allocated right before
11 * drawing an element and released immediately after.
12 * This causes a severe performance penalty, and on PseudoColor
13 * visuals it causes colormap cycling as colormap entries are
14 * released and reused.
16 * Solution: Acquire fonts, colors, and objects from a
17 * resource cache instead of directly from Tk; the cache
18 * holds a semipermanent reference to the resource to keep
19 * it from being deallocated.
21 * The plumbing and control flow here is quite contorted;
22 * it would be better to address this problem in the core instead.
24 * @@@ BUGS/TODO: Need distinct caches for each combination
25 * of display, visual, and colormap.
27 * @@@ Colormap flashing on PseudoColor visuals is still possible,
28 * but this will be a transient effect.
34 struct Ttk_ResourceCache_ {
35 Tcl_Interp *interp; /* Interpreter for error reporting */
36 Tk_Window tkwin; /* Cache window. */
37 Tcl_HashTable fontTable; /* Entries: Tcl_Obj* holding FontObjs */
38 Tcl_HashTable colorTable; /* Entries: Tcl_Obj* holding ColorObjs */
39 Tcl_HashTable borderTable; /* Entries: Tcl_Obj* holding BorderObjs */
40 Tcl_HashTable imageTable; /* Entries: Tk_Images */
42 Tcl_HashTable namedColors; /* Entries: RGB values as Tcl_StringObjs */
46 * Ttk_CreateResourceCache --
47 * Initialize a new resource cache.
49 Ttk_ResourceCache Ttk_CreateResourceCache(Tcl_Interp *interp)
51 Ttk_ResourceCache cache = ckalloc(sizeof(*cache));
53 cache->tkwin = NULL; /* initialized later */
54 cache->interp = interp;
55 Tcl_InitHashTable(&cache->fontTable, TCL_STRING_KEYS);
56 Tcl_InitHashTable(&cache->colorTable, TCL_STRING_KEYS);
57 Tcl_InitHashTable(&cache->borderTable, TCL_STRING_KEYS);
58 Tcl_InitHashTable(&cache->imageTable, TCL_STRING_KEYS);
59 Tcl_InitHashTable(&cache->namedColors, TCL_STRING_KEYS);
66 * Release references to all cached resources.
68 static void Ttk_ClearCache(Ttk_ResourceCache cache)
70 Tcl_HashSearch search;
71 Tcl_HashEntry *entryPtr;
76 entryPtr = Tcl_FirstHashEntry(&cache->fontTable, &search);
77 while (entryPtr != NULL) {
78 Tcl_Obj *fontObj = Tcl_GetHashValue(entryPtr);
80 Tk_FreeFontFromObj(cache->tkwin, fontObj);
81 Tcl_DecrRefCount(fontObj);
83 entryPtr = Tcl_NextHashEntry(&search);
85 Tcl_DeleteHashTable(&cache->fontTable);
86 Tcl_InitHashTable(&cache->fontTable, TCL_STRING_KEYS);
91 entryPtr = Tcl_FirstHashEntry(&cache->colorTable, &search);
92 while (entryPtr != NULL) {
93 Tcl_Obj *colorObj = Tcl_GetHashValue(entryPtr);
95 Tk_FreeColorFromObj(cache->tkwin, colorObj);
96 Tcl_DecrRefCount(colorObj);
98 entryPtr = Tcl_NextHashEntry(&search);
100 Tcl_DeleteHashTable(&cache->colorTable);
101 Tcl_InitHashTable(&cache->colorTable, TCL_STRING_KEYS);
106 entryPtr = Tcl_FirstHashEntry(&cache->borderTable, &search);
107 while (entryPtr != NULL) {
108 Tcl_Obj *borderObj = Tcl_GetHashValue(entryPtr);
110 Tk_Free3DBorderFromObj(cache->tkwin, borderObj);
111 Tcl_DecrRefCount(borderObj);
113 entryPtr = Tcl_NextHashEntry(&search);
115 Tcl_DeleteHashTable(&cache->borderTable);
116 Tcl_InitHashTable(&cache->borderTable, TCL_STRING_KEYS);
121 entryPtr = Tcl_FirstHashEntry(&cache->imageTable, &search);
122 while (entryPtr != NULL) {
123 Tk_Image image = Tcl_GetHashValue(entryPtr);
127 entryPtr = Tcl_NextHashEntry(&search);
129 Tcl_DeleteHashTable(&cache->imageTable);
130 Tcl_InitHashTable(&cache->imageTable, TCL_STRING_KEYS);
136 * Ttk_FreeResourceCache --
137 * Release references to all cached resources, delete the cache.
140 void Ttk_FreeResourceCache(Ttk_ResourceCache cache)
142 Tcl_HashEntry *entryPtr;
143 Tcl_HashSearch search;
145 Ttk_ClearCache(cache);
147 Tcl_DeleteHashTable(&cache->colorTable);
148 Tcl_DeleteHashTable(&cache->fontTable);
149 Tcl_DeleteHashTable(&cache->imageTable);
154 entryPtr = Tcl_FirstHashEntry(&cache->namedColors, &search);
155 while (entryPtr != NULL) {
156 Tcl_Obj *colorNameObj = Tcl_GetHashValue(entryPtr);
157 Tcl_DecrRefCount(colorNameObj);
158 entryPtr = Tcl_NextHashEntry(&search);
160 Tcl_DeleteHashTable(&cache->namedColors);
166 * CacheWinEventHandler --
167 * Detect when the cache window is destroyed, clear cache.
169 static void CacheWinEventHandler(ClientData clientData, XEvent *eventPtr)
171 Ttk_ResourceCache cache = clientData;
173 if (eventPtr->type != DestroyNotify) {
176 Tk_DeleteEventHandler(cache->tkwin, StructureNotifyMask,
177 CacheWinEventHandler, clientData);
178 Ttk_ClearCache(cache);
184 * Specify the cache window if not already set.
185 * @@@ SHOULD: use separate caches for each combination
186 * @@@ of display, visual, and colormap.
188 static void InitCacheWindow(Ttk_ResourceCache cache, Tk_Window tkwin)
190 if (cache->tkwin == NULL) {
191 cache->tkwin = tkwin;
192 Tk_CreateEventHandler(tkwin, StructureNotifyMask,
193 CacheWinEventHandler, cache);
198 * Ttk_RegisterNamedColor --
199 * Specify an RGB triplet as a named color.
200 * Overrides any previous named color specification.
202 void Ttk_RegisterNamedColor(
203 Ttk_ResourceCache cache,
204 const char *colorName,
208 Tcl_HashEntry *entryPtr;
210 Tcl_Obj *colorNameObj;
212 sprintf(nameBuf, "#%04X%04X%04X",
213 colorPtr->red, colorPtr->green, colorPtr->blue);
214 colorNameObj = Tcl_NewStringObj(nameBuf, -1);
215 Tcl_IncrRefCount(colorNameObj);
217 entryPtr = Tcl_CreateHashEntry(&cache->namedColors, colorName, &newEntry);
219 Tcl_Obj *oldColor = Tcl_GetHashValue(entryPtr);
220 Tcl_DecrRefCount(oldColor);
223 Tcl_SetHashValue(entryPtr, colorNameObj);
227 * CheckNamedColor(objPtr) --
228 * If objPtr is a registered color name, return a Tcl_Obj *
229 * containing the registered color value specification.
230 * Otherwise, return the input argument.
232 static Tcl_Obj *CheckNamedColor(Ttk_ResourceCache cache, Tcl_Obj *objPtr)
234 Tcl_HashEntry *entryPtr =
235 Tcl_FindHashEntry(&cache->namedColors, Tcl_GetString(objPtr));
236 if (entryPtr) { /* Use named color instead */
237 objPtr = Tcl_GetHashValue(entryPtr);
243 * Template for allocation routines:
245 typedef void *(*Allocator)(Tcl_Interp *, Tk_Window, Tcl_Obj *);
247 static Tcl_Obj *Ttk_Use(
249 Tcl_HashTable *table,
255 Tcl_HashEntry *entryPtr =
256 Tcl_CreateHashEntry(table,Tcl_GetString(objPtr),&newEntry);
260 return Tcl_GetHashValue(entryPtr);
263 cacheObj = Tcl_DuplicateObj(objPtr);
264 Tcl_IncrRefCount(cacheObj);
266 if (allocate(interp, tkwin, cacheObj)) {
267 Tcl_SetHashValue(entryPtr, cacheObj);
270 Tcl_DecrRefCount(cacheObj);
271 Tcl_SetHashValue(entryPtr, NULL);
272 Tcl_BackgroundException(interp, TCL_ERROR);
279 * Acquire a font from the cache.
281 Tcl_Obj *Ttk_UseFont(Ttk_ResourceCache cache, Tk_Window tkwin, Tcl_Obj *objPtr)
283 InitCacheWindow(cache, tkwin);
284 return Ttk_Use(cache->interp,
285 &cache->fontTable,(Allocator)Tk_AllocFontFromObj, tkwin, objPtr);
290 * Acquire a color from the cache.
292 Tcl_Obj *Ttk_UseColor(Ttk_ResourceCache cache, Tk_Window tkwin, Tcl_Obj *objPtr)
294 objPtr = CheckNamedColor(cache, objPtr);
295 InitCacheWindow(cache, tkwin);
296 return Ttk_Use(cache->interp,
297 &cache->colorTable,(Allocator)Tk_AllocColorFromObj, tkwin, objPtr);
302 * Acquire a Tk_3DBorder from the cache.
304 Tcl_Obj *Ttk_UseBorder(
305 Ttk_ResourceCache cache, Tk_Window tkwin, Tcl_Obj *objPtr)
307 objPtr = CheckNamedColor(cache, objPtr);
308 InitCacheWindow(cache, tkwin);
309 return Ttk_Use(cache->interp,
310 &cache->borderTable,(Allocator)Tk_Alloc3DBorderFromObj, tkwin, objPtr);
313 /* NullImageChanged --
314 * Tk_ImageChangedProc for Ttk_UseImage
317 static void NullImageChanged(ClientData clientData,
318 int x, int y, int width, int height, int imageWidth, int imageHeight)
323 * Acquire a Tk_Image from the cache.
325 Tk_Image Ttk_UseImage(Ttk_ResourceCache cache, Tk_Window tkwin, Tcl_Obj *objPtr)
327 const char *imageName = Tcl_GetString(objPtr);
329 Tcl_HashEntry *entryPtr =
330 Tcl_CreateHashEntry(&cache->imageTable,imageName,&newEntry);
333 InitCacheWindow(cache, tkwin);
336 return Tcl_GetHashValue(entryPtr);
339 image = Tk_GetImage(cache->interp, tkwin, imageName, NullImageChanged,0);
340 Tcl_SetHashValue(entryPtr, image);
343 Tcl_BackgroundException(cache->interp, TCL_ERROR);