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 (c) 1990-1994 The Regents of the University of California.
8 * Copyright (c) 1994 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * SCCS: @(#) tkGC.c 1.18 96/02/15 18:53:32
14 */
15
16 #include "tkInt.h"
17
18 /*
19 * One of the following data structures exists for each GC that is
20 * currently active. The structure is indexed with two hash tables,
21 * one based on the values in the graphics context and the other
22 * based on the display and GC identifier.
23 */
24
25 typedef struct {
26 GC gc; /* Graphics context. */
27 Display *display; /* Display to which gc belongs. */
28 int refCount; /* Number of active uses of gc. */
29 Tcl_HashEntry *valueHashPtr;/* Entry in valueTable (needed when deleting
30 * this structure). */
31 } TkGC;
32
33 /*
34 * Hash table to map from a GC's values to a TkGC structure describing
35 * a GC with those values (used by Tk_GetGC).
36 */
37
38 static Tcl_HashTable valueTable;
39 typedef struct {
40 XGCValues values; /* Desired values for GC. */
41 Display *display; /* Display for which GC is valid. */
42 int screenNum; /* screen number of display */
43 int depth; /* and depth for which GC is valid. */
44 } ValueKey;
45
46 /*
47 * Hash table for <display + GC> -> TkGC mapping. This table is used by
48 * Tk_FreeGC.
49 */
50
51 static Tcl_HashTable idTable;
52 typedef struct {
53 Display *display; /* Display for which GC was allocated. */
54 GC gc; /* X's identifier for GC. */
55 } IdKey;
56
57 static int initialized = 0; /* 0 means static structures haven't been
58 * initialized yet. */
59
60 /*
61 * Forward declarations for procedures defined in this file:
62 */
63
64 static void GCInit _ANSI_ARGS_((void));
65
66 /*
67 *----------------------------------------------------------------------
68 *
69 * Tk_GetGC --
70 *
71 * Given a desired set of values for a graphics context, find
72 * a read-only graphics context with the desired values.
73 *
74 * Results:
75 * The return value is the X identifer for the desired graphics
76 * context. The caller should never modify this GC, and should
77 * call Tk_FreeGC when the GC is no longer needed.
78 *
79 * Side effects:
80 * The GC is added to an internal database with a reference count.
81 * For each call to this procedure, there should eventually be a call
82 * to Tk_FreeGC, so that the database can be cleaned up when GC's
83 * aren't needed anymore.
84 *
85 *----------------------------------------------------------------------
86 */
87
88 GC
Tk_GetGC(tkwin,valueMask,valuePtr)89 Tk_GetGC(tkwin, valueMask, valuePtr)
90 Tk_Window tkwin; /* Window in which GC will be used. */
91 register unsigned long valueMask;
92 /* 1 bits correspond to values specified
93 * in *valuesPtr; other values are set
94 * from defaults. */
95 register XGCValues *valuePtr;
96 /* Values are specified here for bits set
97 * in valueMask. */
98 {
99 ValueKey valueKey;
100 IdKey idKey;
101 Tcl_HashEntry *valueHashPtr, *idHashPtr;
102 register TkGC *gcPtr;
103 int new;
104 Drawable d, freeDrawable;
105
106 if (!initialized) {
107 GCInit();
108 }
109
110 /*
111 * Must zero valueKey at start to clear out pad bytes that may be
112 * part of structure on some systems.
113 */
114
115 memset((VOID *) &valueKey, 0, sizeof(valueKey));
116
117 /*
118 * First, check to see if there's already a GC that will work
119 * for this request (exact matches only, sorry).
120 */
121
122 if (valueMask & GCFunction) {
123 valueKey.values.function = valuePtr->function;
124 } else {
125 valueKey.values.function = GXcopy;
126 }
127 if (valueMask & GCPlaneMask) {
128 valueKey.values.plane_mask = valuePtr->plane_mask;
129 } else {
130 valueKey.values.plane_mask = (unsigned) ~0;
131 }
132 if (valueMask & GCForeground) {
133 valueKey.values.foreground = valuePtr->foreground;
134 } else {
135 valueKey.values.foreground = 0;
136 }
137 if (valueMask & GCBackground) {
138 valueKey.values.background = valuePtr->background;
139 } else {
140 valueKey.values.background = 1;
141 }
142 if (valueMask & GCLineWidth) {
143 valueKey.values.line_width = valuePtr->line_width;
144 } else {
145 valueKey.values.line_width = 0;
146 }
147 if (valueMask & GCLineStyle) {
148 valueKey.values.line_style = valuePtr->line_style;
149 } else {
150 valueKey.values.line_style = LineSolid;
151 }
152 if (valueMask & GCCapStyle) {
153 valueKey.values.cap_style = valuePtr->cap_style;
154 } else {
155 valueKey.values.cap_style = CapButt;
156 }
157 if (valueMask & GCJoinStyle) {
158 valueKey.values.join_style = valuePtr->join_style;
159 } else {
160 valueKey.values.join_style = JoinMiter;
161 }
162 if (valueMask & GCFillStyle) {
163 valueKey.values.fill_style = valuePtr->fill_style;
164 } else {
165 valueKey.values.fill_style = FillSolid;
166 }
167 if (valueMask & GCFillRule) {
168 valueKey.values.fill_rule = valuePtr->fill_rule;
169 } else {
170 valueKey.values.fill_rule = EvenOddRule;
171 }
172 if (valueMask & GCArcMode) {
173 valueKey.values.arc_mode = valuePtr->arc_mode;
174 } else {
175 valueKey.values.arc_mode = ArcPieSlice;
176 }
177 if (valueMask & GCTile) {
178 valueKey.values.tile = valuePtr->tile;
179 } else {
180 valueKey.values.tile = None;
181 }
182 if (valueMask & GCStipple) {
183 valueKey.values.stipple = valuePtr->stipple;
184 } else {
185 valueKey.values.stipple = None;
186 }
187 if (valueMask & GCTileStipXOrigin) {
188 valueKey.values.ts_x_origin = valuePtr->ts_x_origin;
189 } else {
190 valueKey.values.ts_x_origin = 0;
191 }
192 if (valueMask & GCTileStipYOrigin) {
193 valueKey.values.ts_y_origin = valuePtr->ts_y_origin;
194 } else {
195 valueKey.values.ts_y_origin = 0;
196 }
197 if (valueMask & GCFont) {
198 valueKey.values.font = valuePtr->font;
199 } else {
200 valueKey.values.font = None;
201 }
202 if (valueMask & GCSubwindowMode) {
203 valueKey.values.subwindow_mode = valuePtr->subwindow_mode;
204 } else {
205 valueKey.values.subwindow_mode = ClipByChildren;
206 }
207 if (valueMask & GCGraphicsExposures) {
208 valueKey.values.graphics_exposures = valuePtr->graphics_exposures;
209 } else {
210 valueKey.values.graphics_exposures = True;
211 }
212 if (valueMask & GCClipXOrigin) {
213 valueKey.values.clip_x_origin = valuePtr->clip_x_origin;
214 } else {
215 valueKey.values.clip_x_origin = 0;
216 }
217 if (valueMask & GCClipYOrigin) {
218 valueKey.values.clip_y_origin = valuePtr->clip_y_origin;
219 } else {
220 valueKey.values.clip_y_origin = 0;
221 }
222 if (valueMask & GCClipMask) {
223 valueKey.values.clip_mask = valuePtr->clip_mask;
224 } else {
225 valueKey.values.clip_mask = None;
226 }
227 if (valueMask & GCDashOffset) {
228 valueKey.values.dash_offset = valuePtr->dash_offset;
229 } else {
230 valueKey.values.dash_offset = 0;
231 }
232 if (valueMask & GCDashList) {
233 valueKey.values.dashes = valuePtr->dashes;
234 } else {
235 valueKey.values.dashes = 4;
236 }
237 valueKey.display = Tk_Display(tkwin);
238 valueKey.screenNum = Tk_ScreenNumber(tkwin);
239 valueKey.depth = Tk_Depth(tkwin);
240 valueHashPtr = Tcl_CreateHashEntry(&valueTable, (char *) &valueKey, &new);
241 if (!new) {
242 gcPtr = (TkGC *) Tcl_GetHashValue(valueHashPtr);
243 gcPtr->refCount++;
244 return gcPtr->gc;
245 }
246
247 /*
248 * No GC is currently available for this set of values. Allocate a
249 * new GC and add a new structure to the database.
250 */
251
252 gcPtr = (TkGC *) ckalloc(sizeof(TkGC));
253
254 /*
255 * Find or make a drawable to use to specify the screen and depth
256 * of the GC. We may have to make a small pixmap, to avoid doing
257 * Tk_MakeWindowExist on the window.
258 */
259
260 freeDrawable = None;
261 if (Tk_WindowId(tkwin) != None) {
262 d = Tk_WindowId(tkwin);
263 } else if (valueKey.depth ==
264 DefaultDepth(valueKey.display, valueKey.screenNum)) {
265 d = RootWindow(valueKey.display, valueKey.screenNum);
266 } else {
267 d = Tk_GetPixmap(valueKey.display,
268 RootWindow(valueKey.display, valueKey.screenNum),
269 1, 1, valueKey.depth);
270 freeDrawable = d;
271 }
272
273 gcPtr->gc = XCreateGC(valueKey.display, d, valueMask, &valueKey.values);
274 gcPtr->display = valueKey.display;
275 gcPtr->refCount = 1;
276 gcPtr->valueHashPtr = valueHashPtr;
277 idKey.display = valueKey.display;
278 idKey.gc = gcPtr->gc;
279 idHashPtr = Tcl_CreateHashEntry(&idTable, (char *) &idKey, &new);
280 if (!new) {
281 panic("GC already registered in Tk_GetGC");
282 }
283 Tcl_SetHashValue(valueHashPtr, gcPtr);
284 Tcl_SetHashValue(idHashPtr, gcPtr);
285 if (freeDrawable != None) {
286 Tk_FreePixmap(valueKey.display, freeDrawable);
287 }
288
289 return gcPtr->gc;
290 }
291
292 /*
293 *----------------------------------------------------------------------
294 *
295 * Tk_FreeGC --
296 *
297 * This procedure is called to release a graphics context allocated by
298 * Tk_GetGC.
299 *
300 * Results:
301 * None.
302 *
303 * Side effects:
304 * The reference count associated with gc is decremented, and
305 * gc is officially deallocated if no-one is using it anymore.
306 *
307 *----------------------------------------------------------------------
308 */
309
310 void
Tk_FreeGC(display,gc)311 Tk_FreeGC(display, gc)
312 Display *display; /* Display for which gc was allocated. */
313 GC gc; /* Graphics context to be released. */
314 {
315 IdKey idKey;
316 Tcl_HashEntry *idHashPtr;
317 register TkGC *gcPtr;
318
319 if (!initialized) {
320 panic("Tk_FreeGC called before Tk_GetGC");
321 }
322
323 idKey.display = display;
324 idKey.gc = gc;
325 idHashPtr = Tcl_FindHashEntry(&idTable, (char *) &idKey);
326 if (idHashPtr == NULL) {
327 panic("Tk_FreeGC received unknown gc argument");
328 }
329 gcPtr = (TkGC *) Tcl_GetHashValue(idHashPtr);
330 gcPtr->refCount--;
331 if (gcPtr->refCount == 0) {
332 Tk_FreeXId(gcPtr->display, (XID) XGContextFromGC(gcPtr->gc));
333 XFreeGC(gcPtr->display, gcPtr->gc);
334 Tcl_DeleteHashEntry(gcPtr->valueHashPtr);
335 Tcl_DeleteHashEntry(idHashPtr);
336 ckfree((char *) gcPtr);
337 }
338 }
339
340 /*
341 *----------------------------------------------------------------------
342 *
343 * GCInit --
344 *
345 * Initialize the structures used for GC management.
346 *
347 * Results:
348 * None.
349 *
350 * Side effects:
351 * Read the code.
352 *
353 *----------------------------------------------------------------------
354 */
355
356 static void
GCInit()357 GCInit()
358 {
359 initialized = 1;
360 Tcl_InitHashTable(&valueTable, sizeof(ValueKey)/sizeof(int));
361 Tcl_InitHashTable(&idTable, sizeof(IdKey)/sizeof(int));
362 }
363