1 /* Efficient caching of Gtk GCs (graphics contexts).
2 Copyright (C) 1993 Free Software Foundation, Inc.
3 Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
5 This file is part of XEmacs.
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
22 /* Synched up with: Not in FSF. */
24 /* Emacs uses a lot of different display attributes; for example, assume
25 that only four fonts are in use (normal, bold, italic, and bold-italic).
26 Then assume that one stipple or background is used for text selections,
27 and another is used for highlighting mousable regions. That makes 16
28 GCs already. Add in the fact that another GC may be needed to display
29 the text cursor in any of those regions, and you've got 32. Add in
30 more fonts, and it keeps increasing exponentially.
32 We used to keep these GCs in a cache of merged (fully qualified) faces.
33 However, a lot of other code in xterm.c used XChangeGC of existing GCs,
34 which is kind of slow and kind of random. Also, managing the face cache
35 was tricky because it was hard to know when a face was no longer visible
36 on the frame -- we had to mark all frames as garbaged whenever a face
37 was changed, which caused an unpleasant amount of flicker (since faces are
38 created/destroyed (= changed) whenever a frame is created/destroyed.
40 So this code maintains a cache at the GC level instead of at the face
41 level. There is an upper limit on the size of the cache, after which we
42 will stop creating GCs and start reusing them (reusing the least-recently-
43 used ones first). So if faces get changed, their GCs will eventually be
44 recycled. Also more sharing of GCs is possible.
46 This code uses hashtables. It could be that, if the cache size is small
47 enough, a linear search might be faster; but I doubt it, since we need
48 `equal' comparisons, not `eq', and I expect that the optimal cache size
51 Written by jwz, 14 jun 93
52 Hacked by William Perry, apr 2000
58 #include "gccache-gtk.h"
60 #define GC_CACHE_SIZE 100
74 struct gc_cache_cell {
76 struct gcv_and_mask gcvm;
77 struct gc_cache_cell *prev, *next;
81 GdkWindow *window; /* used only as arg to XCreateGC */
83 struct gc_cache_cell *head;
84 struct gc_cache_cell *tail;
86 struct hash_table * table;
95 gc_cache_hash (const void *arg)
97 const struct gcv_and_mask *gcvm = (const struct gcv_and_mask *) arg;
98 unsigned long *longs = (unsigned long *) &gcvm->gcv;
99 unsigned long hash = gcvm->mask;
101 /* This could look at the mask and only use the used slots in the
102 hash code. That would win in that we wouldn't have to initialize
103 every slot of the gcv when calling gc_cache_lookup. But we need
104 the hash function to be as fast as possible; some timings should
106 for (i = 0; i < (sizeof (GdkGCValues) / sizeof (unsigned long)); i++)
107 hash = (hash<<1) ^ *longs++;
111 #endif /* GCCACHE_HASH */
114 gc_cache_eql (const void *arg1, const void *arg2)
116 /* See comment in gc_cache_hash */
117 const struct gcv_and_mask *gcvm1 = (const struct gcv_and_mask *) arg1;
118 const struct gcv_and_mask *gcvm2 = (const struct gcv_and_mask *) arg2;
120 return !memcmp(&gcvm1->gcv, &gcvm2->gcv, sizeof(gcvm1->gcv))
121 && gcvm1->mask == gcvm2->mask;
125 make_gc_cache (GtkWidget *widget)
127 struct gc_cache *cache = xnew (struct gc_cache);
128 cache->window = widget->window;
130 cache->head = cache->tail = 0;
131 cache->create_count = cache->delete_count = 0;
134 make_general_hash_table (GC_CACHE_SIZE, gc_cache_hash, gc_cache_eql);
140 free_gc_cache (struct gc_cache *cache)
142 struct gc_cache_cell *rest, *next;
146 gdk_gc_destroy(rest->gc);
152 free_hash_table (cache->table);
158 gc_cache_lookup (struct gc_cache *cache, GdkGCValues *gcv, GdkGCValuesMask mask)
160 struct gc_cache_cell *cell, *next, *prev;
161 struct gcv_and_mask gcvm;
163 if ((!!cache->head) != (!!cache->tail)) ABORT ();
164 if (cache->head && (cache->head->prev || cache->tail->next)) ABORT ();
166 /* Gdk does not have the equivalent of 'None' for the clip_mask, so
167 we need to check it carefully, or gdk_gc_new_with_values will
169 if ((mask & GDK_GC_CLIP_MASK) && !gcv->clip_mask)
171 mask &= ~GDK_GC_CLIP_MASK;
175 gcvm.gcv = *gcv; /* this copies... */
179 if (gethash (&gcvm, cache->table, (const void **) &cell))
181 #else /* !GCCACHE_HASH */
183 cell = cache->tail; /* start at the end (most recently used) */
186 if (gc_cache_eql (&gcvm, &cell->gcvm))
192 /* #### This whole file needs some serious overhauling. */
193 if (!(mask | GDK_GC_TILE) && cell->gcvm.gcv.tile)
195 else if (!(mask | GDK_GC_STIPPLE) && cell->gcvm.gcv.stipple)
200 #endif /* !GCCACHE_HASH */
203 /* Found a cell. Move this cell to the end of the list, so that it
204 will be less likely to be collected than a cell that was accessed
207 if (cell == cache->tail)
212 if (prev) prev->next = next;
213 if (next) next->prev = prev;
214 if (cache->head == cell) cache->head = next;
216 cell->prev = cache->tail;
217 cache->tail->next = cell;
219 if (cache->head == cell) ABORT ();
220 if (cell->next) ABORT ();
221 if (cache->head->prev) ABORT ();
222 if (cache->tail->next) ABORT ();
226 /* else, cache miss. */
228 if (cache->size == GC_CACHE_SIZE)
229 /* Reuse the first cell on the list (least-recently-used).
230 Remove it from the list, and unhash it from the table.
234 cache->head = cell->next;
235 cache->head->prev = 0;
236 if (cache->tail == cell) cache->tail = 0; /* only one */
237 gdk_gc_destroy (cell->gc);
238 cache->delete_count++;
240 remhash (&cell->gcvm, cache->table);
243 else if (cache->size > GC_CACHE_SIZE)
247 /* Allocate a new cell (don't put it in the list or table yet). */
248 cell = xnew (struct gc_cache_cell);
252 /* Now we've got a cell (new or reused). Fill it in. */
253 memcpy (&cell->gcvm.gcv, gcv, sizeof (GdkGCValues));
254 cell->gcvm.mask = mask;
256 /* Put the cell on the end of the list. */
258 cell->prev = cache->tail;
259 if (cache->tail) cache->tail->next = cell;
261 if (! cache->head) cache->head = cell;
263 cache->create_count++;
265 /* Hash it in the table */
266 puthash (&cell->gcvm, cell, cache->table);
269 /* Now make and return the GC. */
270 cell->gc = gdk_gc_new_with_values (cache->window, gcv, mask);
273 assert (cell->gc == gc_cache_lookup (cache, gcv, mask));