Mercurial > hg > xemacs-beta
annotate src/gccache-gtk.c @ 5724:ede80ef92a74
Make soft links in src for module source files, if built in to the executable.
This ensures that those files are built with the same compiler flags as all
other source files.
See these xemacs-beta messages:
<CAHCOHQn+q=Xuwq+y68dvqi7afAP9f-TdB7=8YiZ8VYO816sjHg@mail.gmail.com>
<f5by5ejqiyk.fsf@calexico.inf.ed.ac.uk>
author | Jerry James <james@xemacs.org> |
---|---|
date | Sat, 02 Mar 2013 14:32:37 -0700 |
parents | 308d34e9f07d |
children |
rev | line source |
---|---|
462 | 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. | |
4 | |
5 This file is part of XEmacs. | |
6 | |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5050
diff
changeset
|
7 XEmacs is free software: you can redistribute it and/or modify it |
462 | 8 under the terms of the GNU General Public License as published by the |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5050
diff
changeset
|
9 Free Software Foundation, either version 3 of the License, or (at your |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5050
diff
changeset
|
10 option) any later version. |
462 | 11 |
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 | |
15 for more details. | |
16 | |
17 You should have received a copy of the GNU General Public License | |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5050
diff
changeset
|
18 along with XEmacs. If not, see <http://www.gnu.org/licenses/>. */ |
462 | 19 |
20 /* Synched up with: Not in FSF. */ | |
21 | |
4908
b3ce27ca7647
various fixes related to gtk, redisplay-xlike-inc.c
Ben Wing <ben@xemacs.org>
parents:
2515
diff
changeset
|
22 /* XEmacs uses a lot of different display attributes; for example, assume |
462 | 23 that only four fonts are in use (normal, bold, italic, and bold-italic). |
24 Then assume that one stipple or background is used for text selections, | |
25 and another is used for highlighting mousable regions. That makes 16 | |
26 GCs already. Add in the fact that another GC may be needed to display | |
27 the text cursor in any of those regions, and you've got 32. Add in | |
28 more fonts, and it keeps increasing exponentially. | |
29 | |
30 We used to keep these GCs in a cache of merged (fully qualified) faces. | |
31 However, a lot of other code in xterm.c used XChangeGC of existing GCs, | |
32 which is kind of slow and kind of random. Also, managing the face cache | |
33 was tricky because it was hard to know when a face was no longer visible | |
34 on the frame -- we had to mark all frames as garbaged whenever a face | |
35 was changed, which caused an unpleasant amount of flicker (since faces are | |
36 created/destroyed (= changed) whenever a frame is created/destroyed. | |
37 | |
38 So this code maintains a cache at the GC level instead of at the face | |
39 level. There is an upper limit on the size of the cache, after which we | |
40 will stop creating GCs and start reusing them (reusing the least-recently- | |
41 used ones first). So if faces get changed, their GCs will eventually be | |
42 recycled. Also more sharing of GCs is possible. | |
43 | |
44 This code uses hashtables. It could be that, if the cache size is small | |
45 enough, a linear search might be faster; but I doubt it, since we need | |
46 `equal' comparisons, not `eq', and I expect that the optimal cache size | |
47 will be ~100. | |
48 | |
49 Written by jwz, 14 jun 93 | |
50 Hacked by William Perry, apr 2000 | |
51 */ | |
52 | |
53 #include <config.h> | |
54 #include "lisp.h" | |
4908
b3ce27ca7647
various fixes related to gtk, redisplay-xlike-inc.c
Ben Wing <ben@xemacs.org>
parents:
2515
diff
changeset
|
55 #include "hash.h" |
b3ce27ca7647
various fixes related to gtk, redisplay-xlike-inc.c
Ben Wing <ben@xemacs.org>
parents:
2515
diff
changeset
|
56 |
462 | 57 #include "gccache-gtk.h" |
58 | |
59 #define GC_CACHE_SIZE 100 | |
60 | |
61 #define GCCACHE_HASH | |
62 | |
63 struct gcv_and_mask { | |
64 GdkGCValues gcv; | |
2054 | 65 unsigned long mask; /* contains a GdkGCValuesMask bitmask. */ |
462 | 66 }; |
67 | |
68 struct gc_cache_cell { | |
69 GdkGC *gc; | |
70 struct gcv_and_mask gcvm; | |
71 struct gc_cache_cell *prev, *next; | |
72 }; | |
73 | |
74 struct gc_cache { | |
75 GdkWindow *window; /* used only as arg to XCreateGC */ | |
76 int size; | |
77 struct gc_cache_cell *head; | |
78 struct gc_cache_cell *tail; | |
79 #ifdef GCCACHE_HASH | |
80 struct hash_table * table; | |
81 #endif | |
82 | |
83 int create_count; | |
84 int delete_count; | |
85 }; | |
86 | |
87 #ifdef GCCACHE_HASH | |
2515 | 88 static Hashcode |
462 | 89 gc_cache_hash (const void *arg) |
90 { | |
91 const struct gcv_and_mask *gcvm = (const struct gcv_and_mask *) arg; | |
92 unsigned long *longs = (unsigned long *) &gcvm->gcv; | |
2515 | 93 Hashcode hash = gcvm->mask; |
778 | 94 unsigned int i; |
462 | 95 /* This could look at the mask and only use the used slots in the |
96 hash code. That would win in that we wouldn't have to initialize | |
97 every slot of the gcv when calling gc_cache_lookup. But we need | |
98 the hash function to be as fast as possible; some timings should | |
99 be done. */ | |
100 for (i = 0; i < (sizeof (GdkGCValues) / sizeof (unsigned long)); i++) | |
2515 | 101 hash = (hash << 1) ^ *longs++; |
462 | 102 return hash; |
103 } | |
104 | |
105 #endif /* GCCACHE_HASH */ | |
106 | |
107 static int | |
108 gc_cache_eql (const void *arg1, const void *arg2) | |
109 { | |
110 /* See comment in gc_cache_hash */ | |
111 const struct gcv_and_mask *gcvm1 = (const struct gcv_and_mask *) arg1; | |
112 const struct gcv_and_mask *gcvm2 = (const struct gcv_and_mask *) arg2; | |
113 | |
114 return !memcmp(&gcvm1->gcv, &gcvm2->gcv, sizeof(gcvm1->gcv)) | |
115 && gcvm1->mask == gcvm2->mask; | |
116 } | |
117 | |
118 struct gc_cache * | |
119 make_gc_cache (GtkWidget *widget) | |
120 { | |
121 struct gc_cache *cache = xnew (struct gc_cache); | |
122 cache->window = widget->window; | |
123 cache->size = 0; | |
124 cache->head = cache->tail = 0; | |
125 cache->create_count = cache->delete_count = 0; | |
126 #ifdef GCCACHE_HASH | |
127 cache->table = | |
128 make_general_hash_table (GC_CACHE_SIZE, gc_cache_hash, gc_cache_eql); | |
129 #endif | |
130 return cache; | |
131 } | |
132 | |
133 void | |
134 free_gc_cache (struct gc_cache *cache) | |
135 { | |
136 struct gc_cache_cell *rest, *next; | |
137 rest = cache->head; | |
138 while (rest) | |
139 { | |
140 gdk_gc_destroy(rest->gc); | |
141 next = rest->next; | |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4908
diff
changeset
|
142 xfree (rest); |
462 | 143 rest = next; |
144 } | |
145 #ifdef GCCACHE_HASH | |
146 free_hash_table (cache->table); | |
147 #endif | |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4908
diff
changeset
|
148 xfree (cache); |
462 | 149 } |
150 | |
151 GdkGC * | |
2054 | 152 gc_cache_lookup (struct gc_cache *cache, GdkGCValues *gcv, unsigned long mask) |
462 | 153 { |
154 struct gc_cache_cell *cell, *next, *prev; | |
155 struct gcv_and_mask gcvm; | |
156 | |
5050
6f2158fa75ed
Fix quick-build, use asserts() in place of ABORT()
Ben Wing <ben@xemacs.org>
parents:
4976
diff
changeset
|
157 assert ((!!cache->head) == (!!cache->tail)); |
6f2158fa75ed
Fix quick-build, use asserts() in place of ABORT()
Ben Wing <ben@xemacs.org>
parents:
4976
diff
changeset
|
158 assert (!(cache->head && (cache->head->prev || cache->tail->next))); |
462 | 159 |
160 /* Gdk does not have the equivalent of 'None' for the clip_mask, so | |
161 we need to check it carefully, or gdk_gc_new_with_values will | |
162 coredump */ | |
163 if ((mask & GDK_GC_CLIP_MASK) && !gcv->clip_mask) | |
164 { | |
165 mask &= ~GDK_GC_CLIP_MASK; | |
166 } | |
167 | |
168 gcvm.mask = mask; | |
169 gcvm.gcv = *gcv; /* this copies... */ | |
170 | |
171 #ifdef GCCACHE_HASH | |
172 | |
173 if (gethash (&gcvm, cache->table, (const void **) &cell)) | |
174 | |
175 #else /* !GCCACHE_HASH */ | |
176 | |
177 cell = cache->tail; /* start at the end (most recently used) */ | |
178 while (cell) | |
179 { | |
180 if (gc_cache_eql (&gcvm, &cell->gcvm)) | |
181 break; | |
182 else | |
183 cell = cell->prev; | |
184 } | |
185 | |
186 /* #### This whole file needs some serious overhauling. */ | |
187 if (!(mask | GDK_GC_TILE) && cell->gcvm.gcv.tile) | |
188 cell = 0; | |
189 else if (!(mask | GDK_GC_STIPPLE) && cell->gcvm.gcv.stipple) | |
190 cell = 0; | |
191 | |
192 if (cell) | |
193 | |
194 #endif /* !GCCACHE_HASH */ | |
195 | |
196 { | |
197 /* Found a cell. Move this cell to the end of the list, so that it | |
198 will be less likely to be collected than a cell that was accessed | |
199 less recently. | |
200 */ | |
201 if (cell == cache->tail) | |
202 return cell->gc; | |
203 | |
204 next = cell->next; | |
205 prev = cell->prev; | |
206 if (prev) prev->next = next; | |
207 if (next) next->prev = prev; | |
208 if (cache->head == cell) cache->head = next; | |
209 cell->next = 0; | |
210 cell->prev = cache->tail; | |
211 cache->tail->next = cell; | |
212 cache->tail = cell; | |
5050
6f2158fa75ed
Fix quick-build, use asserts() in place of ABORT()
Ben Wing <ben@xemacs.org>
parents:
4976
diff
changeset
|
213 assert (cache->head != cell); |
6f2158fa75ed
Fix quick-build, use asserts() in place of ABORT()
Ben Wing <ben@xemacs.org>
parents:
4976
diff
changeset
|
214 assert (!cell->next); |
6f2158fa75ed
Fix quick-build, use asserts() in place of ABORT()
Ben Wing <ben@xemacs.org>
parents:
4976
diff
changeset
|
215 assert (!cache->head->prev); |
6f2158fa75ed
Fix quick-build, use asserts() in place of ABORT()
Ben Wing <ben@xemacs.org>
parents:
4976
diff
changeset
|
216 assert (!cache->tail->next); |
462 | 217 return cell->gc; |
218 } | |
219 | |
220 /* else, cache miss. */ | |
221 | |
222 if (cache->size == GC_CACHE_SIZE) | |
223 /* Reuse the first cell on the list (least-recently-used). | |
224 Remove it from the list, and unhash it from the table. | |
225 */ | |
226 { | |
227 cell = cache->head; | |
228 cache->head = cell->next; | |
229 cache->head->prev = 0; | |
230 if (cache->tail == cell) cache->tail = 0; /* only one */ | |
231 gdk_gc_destroy (cell->gc); | |
232 cache->delete_count++; | |
233 #ifdef GCCACHE_HASH | |
234 remhash (&cell->gcvm, cache->table); | |
235 #endif | |
236 } | |
237 else if (cache->size > GC_CACHE_SIZE) | |
2500 | 238 ABORT (); |
462 | 239 else |
240 { | |
241 /* Allocate a new cell (don't put it in the list or table yet). */ | |
242 cell = xnew (struct gc_cache_cell); | |
243 cache->size++; | |
244 } | |
245 | |
246 /* Now we've got a cell (new or reused). Fill it in. */ | |
247 memcpy (&cell->gcvm.gcv, gcv, sizeof (GdkGCValues)); | |
248 cell->gcvm.mask = mask; | |
249 | |
250 /* Put the cell on the end of the list. */ | |
251 cell->next = 0; | |
252 cell->prev = cache->tail; | |
253 if (cache->tail) cache->tail->next = cell; | |
254 cache->tail = cell; | |
255 if (! cache->head) cache->head = cell; | |
256 | |
257 cache->create_count++; | |
258 #ifdef GCCACHE_HASH | |
259 /* Hash it in the table */ | |
260 puthash (&cell->gcvm, cell, cache->table); | |
261 #endif | |
262 | |
263 /* Now make and return the GC. */ | |
2054 | 264 cell->gc = gdk_gc_new_with_values (cache->window, gcv, (GdkGCValuesMask) mask); |
462 | 265 |
266 /* debug */ | |
267 assert (cell->gc == gc_cache_lookup (cache, gcv, mask)); | |
268 | |
269 return cell->gc; | |
270 } |