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