Commit | Line | Data |
---|---|---|
6a5fa4e0 MG |
1 | /* |
2 | * tkGC.c -- | |
3 | * | |
4 | * This file maintains a database of read-only graphics contexts | |
5 | * for the Tk toolkit, in order to allow GC's to be shared. | |
6 | * | |
7 | * Copyright 1990 Regents of the University of California | |
8 | * Permission to use, copy, modify, and distribute this | |
9 | * software and its documentation for any purpose and without | |
10 | * fee is hereby granted, provided that the above copyright | |
11 | * notice appear in all copies. The University of California | |
12 | * makes no representations about the suitability of this | |
13 | * software for any purpose. It is provided "as is" without | |
14 | * express or implied warranty. | |
15 | */ | |
16 | ||
17 | #ifndef lint | |
18 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkGC.c,v 1.9 92/05/13 08:48:45 ouster Exp $ SPRITE (Berkeley)"; | |
19 | #endif /* not lint */ | |
20 | ||
21 | #include "tkconfig.h" | |
22 | #include "tk.h" | |
23 | ||
24 | /* | |
25 | * One of the following data structures exists for each GC that is | |
26 | * currently active. The structure is indexed with two hash tables, | |
27 | * one based on font name and one based on XFontStruct address. | |
28 | */ | |
29 | ||
30 | typedef struct { | |
31 | GC gc; /* Graphics context. */ | |
32 | Display *display; /* Display to which gc belongs. */ | |
33 | int refCount; /* Number of active uses of gc. */ | |
34 | Tcl_HashEntry *valueHashPtr;/* Entry in valueTable (needed when deleting | |
35 | * this structure). */ | |
36 | } TkGC; | |
37 | ||
38 | /* | |
39 | * Hash table to map from a GC's values to a TkGC structure describing | |
40 | * a GC with those values (used by Tk_GetGC). | |
41 | */ | |
42 | ||
43 | static Tcl_HashTable valueTable; | |
44 | typedef struct { | |
45 | XGCValues values; /* Desired values for GC. */ | |
46 | Screen *screen; /* Screen for which GC is valid. */ | |
47 | } ValueKey; | |
48 | ||
49 | /* | |
50 | * Hash table for GC -> TkGC mapping. This table is indexed by the | |
51 | * GC identifier, and is used by Tk_FreeGC. | |
52 | */ | |
53 | ||
54 | static Tcl_HashTable idTable; | |
55 | ||
56 | static int initialized = 0; /* 0 means static structures haven't been | |
57 | * initialized yet. */ | |
58 | ||
59 | /* | |
60 | * Forward declarations for procedures defined in this file: | |
61 | */ | |
62 | ||
63 | static void GCInit _ANSI_ARGS_((void)); | |
64 | \f | |
65 | /* | |
66 | *---------------------------------------------------------------------- | |
67 | * | |
68 | * Tk_GetGC -- | |
69 | * | |
70 | * Given a desired set of values for a graphics context, find | |
71 | * a read-only graphics context with the desired values. | |
72 | * | |
73 | * Results: | |
74 | * The return value is the X identifer for the desired graphics | |
75 | * context. The caller should never modify this GC, and should | |
76 | * call Tk_FreeGC when the GC is no longer needed. | |
77 | * | |
78 | * Side effects: | |
79 | * The GC is added to an internal database with a reference count. | |
80 | * For each call to this procedure, there should eventually be a call | |
81 | * to Tk_FreeGC, so that the database can be cleaned up when GC's | |
82 | * aren't needed anymore. | |
83 | * | |
84 | *---------------------------------------------------------------------- | |
85 | */ | |
86 | ||
87 | GC | |
88 | Tk_GetGC(tkwin, valueMask, valuePtr) | |
89 | Tk_Window tkwin; /* Window in which GC will be used. */ | |
90 | register unsigned long valueMask; | |
91 | /* 1 bits correspond to values specified | |
92 | * in *valuesPtr; other values are set | |
93 | * from defaults. */ | |
94 | register XGCValues *valuePtr; | |
95 | /* Values are specified here for bits set | |
96 | * in valueMask. */ | |
97 | { | |
98 | ValueKey key; | |
99 | Tcl_HashEntry *valueHashPtr, *idHashPtr; | |
100 | register TkGC *gcPtr; | |
101 | int new; | |
102 | ||
103 | if (!initialized) { | |
104 | GCInit(); | |
105 | } | |
106 | ||
107 | /* | |
108 | * Must zero key at start to clear out pad bytes that may be | |
109 | * part of structure on some systems. | |
110 | */ | |
111 | ||
112 | memset((VOID *) &key, 0, sizeof(key)); | |
113 | ||
114 | /* | |
115 | * First, check to see if there's already a GC that will work | |
116 | * for this request (exact matches only, sorry). | |
117 | */ | |
118 | ||
119 | if (valueMask & GCFunction) { | |
120 | key.values.function = valuePtr->function; | |
121 | } else { | |
122 | key.values.function = GXcopy; | |
123 | } | |
124 | if (valueMask & GCPlaneMask) { | |
125 | key.values.plane_mask = valuePtr->plane_mask; | |
126 | } else { | |
127 | key.values.plane_mask = ~0; | |
128 | } | |
129 | if (valueMask & GCForeground) { | |
130 | key.values.foreground = valuePtr->foreground; | |
131 | } else { | |
132 | key.values.foreground = 0; | |
133 | } | |
134 | if (valueMask & GCBackground) { | |
135 | key.values.background = valuePtr->background; | |
136 | } else { | |
137 | key.values.background = 1; | |
138 | } | |
139 | if (valueMask & GCLineWidth) { | |
140 | key.values.line_width = valuePtr->line_width; | |
141 | } else { | |
142 | key.values.line_width = 0; | |
143 | } | |
144 | if (valueMask & GCLineStyle) { | |
145 | key.values.line_style = valuePtr->line_style; | |
146 | } else { | |
147 | key.values.line_style = LineSolid; | |
148 | } | |
149 | if (valueMask & GCCapStyle) { | |
150 | key.values.cap_style = valuePtr->cap_style; | |
151 | } else { | |
152 | key.values.cap_style = CapButt; | |
153 | } | |
154 | if (valueMask & GCJoinStyle) { | |
155 | key.values.join_style = valuePtr->join_style; | |
156 | } else { | |
157 | key.values.join_style = JoinMiter; | |
158 | } | |
159 | if (valueMask & GCFillStyle) { | |
160 | key.values.fill_style = valuePtr->fill_style; | |
161 | } else { | |
162 | key.values.fill_style = FillSolid; | |
163 | } | |
164 | if (valueMask & GCFillRule) { | |
165 | key.values.fill_rule = valuePtr->fill_rule; | |
166 | } else { | |
167 | key.values.fill_rule = EvenOddRule; | |
168 | } | |
169 | if (valueMask & GCArcMode) { | |
170 | key.values.arc_mode = valuePtr->arc_mode; | |
171 | } else { | |
172 | key.values.arc_mode = ArcPieSlice; | |
173 | } | |
174 | if (valueMask & GCTile) { | |
175 | key.values.tile = valuePtr->tile; | |
176 | } else { | |
177 | key.values.tile = None; | |
178 | } | |
179 | if (valueMask & GCStipple) { | |
180 | key.values.stipple = valuePtr->stipple; | |
181 | } else { | |
182 | key.values.stipple = None; | |
183 | } | |
184 | if (valueMask & GCTileStipXOrigin) { | |
185 | key.values.ts_x_origin = valuePtr->ts_x_origin; | |
186 | } else { | |
187 | key.values.ts_x_origin = 0; | |
188 | } | |
189 | if (valueMask & GCTileStipYOrigin) { | |
190 | key.values.ts_y_origin = valuePtr->ts_y_origin; | |
191 | } else { | |
192 | key.values.ts_y_origin = 0; | |
193 | } | |
194 | if (valueMask & GCFont) { | |
195 | key.values.font = valuePtr->font; | |
196 | } else { | |
197 | key.values.font = None; | |
198 | } | |
199 | if (valueMask & GCSubwindowMode) { | |
200 | key.values.subwindow_mode = valuePtr->subwindow_mode; | |
201 | } else { | |
202 | key.values.subwindow_mode = ClipByChildren; | |
203 | } | |
204 | if (valueMask & GCGraphicsExposures) { | |
205 | key.values.graphics_exposures = valuePtr->graphics_exposures; | |
206 | } else { | |
207 | key.values.graphics_exposures = True; | |
208 | } | |
209 | if (valueMask & GCClipXOrigin) { | |
210 | key.values.clip_x_origin = valuePtr->clip_x_origin; | |
211 | } else { | |
212 | key.values.clip_x_origin = 0; | |
213 | } | |
214 | if (valueMask & GCClipYOrigin) { | |
215 | key.values.clip_y_origin = valuePtr->clip_y_origin; | |
216 | } else { | |
217 | key.values.clip_y_origin = 0; | |
218 | } | |
219 | if (valueMask & GCClipMask) { | |
220 | key.values.clip_mask = valuePtr->clip_mask; | |
221 | } else { | |
222 | key.values.clip_mask = None; | |
223 | } | |
224 | if (valueMask & GCDashOffset) { | |
225 | key.values.dash_offset = valuePtr->dash_offset; | |
226 | } else { | |
227 | key.values.dash_offset = 0; | |
228 | } | |
229 | if (valueMask & GCDashList) { | |
230 | key.values.dashes = valuePtr->dashes; | |
231 | } else { | |
232 | key.values.dashes = 4; | |
233 | } | |
234 | key.screen = Tk_Screen(tkwin); | |
235 | valueHashPtr = Tcl_CreateHashEntry(&valueTable, (char *) &key, &new); | |
236 | if (!new) { | |
237 | gcPtr = (TkGC *) Tcl_GetHashValue(valueHashPtr); | |
238 | gcPtr->refCount++; | |
239 | return gcPtr->gc; | |
240 | } | |
241 | ||
242 | /* | |
243 | * No GC is currently available for this set of values. Allocate a | |
244 | * new GC and add a new structure to the database. | |
245 | */ | |
246 | ||
247 | gcPtr = (TkGC *) ckalloc(sizeof(TkGC)); | |
248 | #if 0 | |
249 | gcPtr->gc = XCreateGC(Tk_Display(tkwin), | |
250 | RootWindowOfScreen(Tk_Screen(tkwin)), | |
251 | valueMask, &key.values); | |
252 | #else | |
253 | gcPtr->gc = XCreateGC(Tk_Display(tkwin), | |
254 | Tk_DefaultPixmap(Tk_Screen(tkwin)), | |
255 | valueMask, &key.values); | |
256 | #endif | |
257 | gcPtr->display = Tk_Display(tkwin); | |
258 | gcPtr->refCount = 1; | |
259 | gcPtr->valueHashPtr = valueHashPtr; | |
260 | idHashPtr = Tcl_CreateHashEntry(&idTable, (char *) gcPtr->gc, &new); | |
261 | if (!new) { | |
262 | panic("GC already registered in Tk_GetGC"); | |
263 | } | |
264 | Tcl_SetHashValue(valueHashPtr, gcPtr); | |
265 | Tcl_SetHashValue(idHashPtr, gcPtr); | |
266 | return gcPtr->gc; | |
267 | } | |
268 | \f | |
269 | /* | |
270 | *---------------------------------------------------------------------- | |
271 | * | |
272 | * Tk_FreeGC -- | |
273 | * | |
274 | * This procedure is called to release a font allocated by | |
275 | * Tk_GetGC. | |
276 | * | |
277 | * Results: | |
278 | * None. | |
279 | * | |
280 | * Side effects: | |
281 | * The reference count associated with gc is decremented, and | |
282 | * gc is officially deallocated if no-one is using it anymore. | |
283 | * | |
284 | *---------------------------------------------------------------------- | |
285 | */ | |
286 | ||
287 | void | |
288 | Tk_FreeGC(gc) | |
289 | GC gc; /* Graphics context to be released. */ | |
290 | { | |
291 | Tcl_HashEntry *idHashPtr; | |
292 | register TkGC *gcPtr; | |
293 | ||
294 | if (!initialized) { | |
295 | panic("Tk_FreeGC called before Tk_GetGC"); | |
296 | } | |
297 | ||
298 | idHashPtr = Tcl_FindHashEntry(&idTable, (char *) gc); | |
299 | if (idHashPtr == NULL) { | |
300 | panic("Tk_FreeGC received unknown gc argument"); | |
301 | } | |
302 | gcPtr = (TkGC *) Tcl_GetHashValue(idHashPtr); | |
303 | gcPtr->refCount--; | |
304 | if (gcPtr->refCount == 0) { | |
305 | XFreeGC(gcPtr->display, gcPtr->gc); | |
306 | Tcl_DeleteHashEntry(gcPtr->valueHashPtr); | |
307 | Tcl_DeleteHashEntry(idHashPtr); | |
308 | ckfree((char *) gcPtr); | |
309 | } | |
310 | } | |
311 | \f | |
312 | /* | |
313 | *---------------------------------------------------------------------- | |
314 | * | |
315 | * GCInit -- | |
316 | * | |
317 | * Initialize the structures used for GC management. | |
318 | * | |
319 | * Results: | |
320 | * None. | |
321 | * | |
322 | * Side effects: | |
323 | * Read the code. | |
324 | * | |
325 | *---------------------------------------------------------------------- | |
326 | */ | |
327 | ||
328 | static void | |
329 | GCInit() | |
330 | { | |
331 | initialized = 1; | |
332 | Tcl_InitHashTable(&valueTable, sizeof(ValueKey)/sizeof(int)); | |
333 | Tcl_InitHashTable(&idTable, TCL_ONE_WORD_KEYS); | |
334 | } |