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