OSDN Git Service

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