1/* 2 * tkVisual.c -- 3 * 4 * This file contains library procedures for allocating and freeing 5 * visuals and colormaps. This code is based on a prototype 6 * implementation by Paul Mackerras. 7 * 8 * Copyright (c) 1994 The Regents of the University of California. 9 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 10 * 11 * See the file "license.terms" for information on usage and redistribution of 12 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id$ 15 */ 16 17#include "tkInt.h" 18 19/* 20 * The table below maps from symbolic names for visual classes to the 21 * associated X class symbols. 22 */ 23 24typedef struct VisualDictionary { 25 char *name; /* Textual name of class. */ 26 int minLength; /* Minimum # characters that must be specified 27 * for an unambiguous match. */ 28 int class; /* X symbol for class. */ 29} VisualDictionary; 30static VisualDictionary visualNames[] = { 31 {"best", 1, 0}, 32 {"directcolor", 2, DirectColor}, 33 {"grayscale", 1, GrayScale}, 34 {"greyscale", 1, GrayScale}, 35 {"pseudocolor", 1, PseudoColor}, 36 {"staticcolor", 7, StaticColor}, 37 {"staticgray", 7, StaticGray}, 38 {"staticgrey", 7, StaticGray}, 39 {"truecolor", 1, TrueColor}, 40 {NULL, 0, 0}, 41}; 42 43/* 44 * One of the following structures exists for each distinct non-default 45 * colormap allocated for a display by Tk_GetColormap. 46 */ 47 48struct TkColormap { 49 Colormap colormap; /* X's identifier for the colormap. */ 50 Visual *visual; /* Visual for which colormap was allocated. */ 51 int refCount; /* How many uses of the colormap are still 52 * outstanding (calls to Tk_GetColormap minus 53 * calls to Tk_FreeColormap). */ 54 int shareable; /* 0 means this colormap was allocated by a 55 * call to Tk_GetColormap with "new", implying 56 * that the window wants it all for itself. 1 57 * means that the colormap was allocated as a 58 * default for a particular visual, so it can 59 * be shared. */ 60 struct TkColormap *nextPtr; /* Next in list of colormaps for this display, 61 * or NULL for end of list. */ 62}; 63 64/* 65 *---------------------------------------------------------------------- 66 * 67 * Tk_GetVisual -- 68 * 69 * Given a string identifying a particular kind of visual, this procedure 70 * returns a visual and depth that matches the specification. 71 * 72 * Results: 73 * The return value is normally a pointer to a visual. If an error 74 * occurred in looking up the visual, NULL is returned and an error 75 * message is left in the interp's result. The depth of the visual is 76 * returned to *depthPtr under normal returns. If colormapPtr is 77 * non-NULL, then this procedure also finds a suitable colormap for use 78 * with the visual in tkwin, and it returns that colormap in *colormapPtr 79 * unless an error occurs. 80 * 81 * Side effects: 82 * A new colormap may be allocated. 83 * 84 *---------------------------------------------------------------------- 85 */ 86 87Visual * 88Tk_GetVisual( 89 Tcl_Interp *interp, /* Interpreter to use for error reporting. */ 90 Tk_Window tkwin, /* Window in which visual will be used. */ 91 CONST char *string, /* String describing visual. See manual entry 92 * for details. */ 93 int *depthPtr, /* The depth of the returned visual is stored 94 * here. */ 95 Colormap *colormapPtr) /* If non-NULL, then a suitable colormap for 96 * visual is placed here. This colormap must 97 * eventually be freed by calling 98 * Tk_FreeColormap. */ 99{ 100 Tk_Window tkwin2; 101 XVisualInfo template, *visInfoList, *bestPtr; 102 long mask; 103 Visual *visual; 104 ptrdiff_t length; 105 int c, numVisuals, prio, bestPrio, i; 106 CONST char *p; 107 VisualDictionary *dictPtr; 108 TkColormap *cmapPtr; 109 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 110 111 /* 112 * Parse string and set up a template for use in searching for an 113 * appropriate visual. 114 */ 115 116 c = string[0]; 117 if (c == '.') { 118 /* 119 * The string must be a window name. If the window is on the same 120 * screen as tkwin, then just use its visual. Otherwise use the 121 * information about the visual as a template for the search. 122 */ 123 124 tkwin2 = Tk_NameToWindow(interp, string, tkwin); 125 if (tkwin2 == NULL) { 126 return NULL; 127 } 128 visual = Tk_Visual(tkwin2); 129 if (Tk_Screen(tkwin) == Tk_Screen(tkwin2)) { 130 *depthPtr = Tk_Depth(tkwin2); 131 if (colormapPtr != NULL) { 132 /* 133 * Use the colormap from the other window too (but be sure to 134 * increment its reference count if it's one of the ones 135 * allocated here). 136 */ 137 138 *colormapPtr = Tk_Colormap(tkwin2); 139 for (cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL; 140 cmapPtr = cmapPtr->nextPtr) { 141 if (cmapPtr->colormap == *colormapPtr) { 142 cmapPtr->refCount += 1; 143 break; 144 } 145 } 146 } 147 return visual; 148 } 149 template.depth = Tk_Depth(tkwin2); 150 template.class = visual->class; 151 template.red_mask = visual->red_mask; 152 template.green_mask = visual->green_mask; 153 template.blue_mask = visual->blue_mask; 154 template.colormap_size = visual->map_entries; 155 template.bits_per_rgb = visual->bits_per_rgb; 156 mask = VisualDepthMask|VisualClassMask|VisualRedMaskMask 157 |VisualGreenMaskMask|VisualBlueMaskMask|VisualColormapSizeMask 158 |VisualBitsPerRGBMask; 159 } else if ((c == 0) || ((c == 'd') && (string[1] != 0) 160 && (strncmp(string, "default", strlen(string)) == 0))) { 161 /* 162 * Use the default visual for the window's screen. 163 */ 164 165 if (colormapPtr != NULL) { 166 *colormapPtr = DefaultColormapOfScreen(Tk_Screen(tkwin)); 167 } 168 *depthPtr = DefaultDepthOfScreen(Tk_Screen(tkwin)); 169 return DefaultVisualOfScreen(Tk_Screen(tkwin)); 170 } else if (isdigit(UCHAR(c))) { 171 int visualId; 172 173 /* 174 * This is a visual ID. 175 */ 176 177 if (Tcl_GetInt(interp, string, &visualId) == TCL_ERROR) { 178 Tcl_ResetResult(interp); 179 Tcl_AppendResult(interp, "bad X identifier for visual: \"", 180 string, "\"", NULL); 181 return NULL; 182 } 183 template.visualid = visualId; 184 mask = VisualIDMask; 185 } else { 186 /* 187 * Parse the string into a class name (or "best") optionally followed 188 * by whitespace and a depth. 189 */ 190 191 for (p = string; *p != 0; p++) { 192 if (isspace(UCHAR(*p)) || isdigit(UCHAR(*p))) { 193 break; 194 } 195 } 196 length = p - string; 197 template.class = -1; 198 for (dictPtr = visualNames; dictPtr->name != NULL; dictPtr++) { 199 if ((dictPtr->name[0] == c) && (length >= dictPtr->minLength) 200 && (strncmp(string, dictPtr->name, 201 (size_t) length) == 0)) { 202 template.class = dictPtr->class; 203 break; 204 } 205 } 206 if (template.class == -1) { 207 Tcl_AppendResult(interp, "unknown or ambiguous visual name \"", 208 string, "\": class must be ", NULL); 209 for (dictPtr = visualNames; dictPtr->name != NULL; dictPtr++) { 210 Tcl_AppendResult(interp, dictPtr->name, ", ", NULL); 211 } 212 Tcl_AppendResult(interp, "or default", NULL); 213 return NULL; 214 } 215 while (isspace(UCHAR(*p))) { 216 p++; 217 } 218 if (*p == 0) { 219 template.depth = 10000; 220 } else { 221 if (Tcl_GetInt(interp, p, &template.depth) != TCL_OK) { 222 return NULL; 223 } 224 } 225 if (c == 'b') { 226 mask = 0; 227 } else { 228 mask = VisualClassMask; 229 } 230 } 231 232 /* 233 * Find all visuals that match the template we've just created, and return 234 * an error if there are none that match. 235 */ 236 237 template.screen = Tk_ScreenNumber(tkwin); 238 mask |= VisualScreenMask; 239 visInfoList = XGetVisualInfo(Tk_Display(tkwin), mask, &template, 240 &numVisuals); 241 if (visInfoList == NULL) { 242 Tcl_SetResult(interp, "couldn't find an appropriate visual", 243 TCL_STATIC); 244 return NULL; 245 } 246 247 /* 248 * Search through the visuals that were returned to find the best one. 249 * The choice is based on the following criteria, in decreasing order of 250 * importance: 251 * 252 * 1. Depth: choose a visual with exactly the desired depth, else one with 253 * more bits than requested but as few bits as possible, else one with 254 * fewer bits but as many as possible. 255 * 2. Class: some visual classes are more desirable than others; pick the 256 * visual with the most desirable class. 257 * 3. Default: the default visual for the screen gets preference over 258 * other visuals, all else being equal. 259 */ 260 261 bestPrio = 0; 262 bestPtr = NULL; 263 for (i = 0; i < numVisuals; i++) { 264 switch (visInfoList[i].class) { 265 case DirectColor: 266 prio = 5; break; 267 case GrayScale: 268 prio = 1; break; 269 case PseudoColor: 270 prio = 7; break; 271 case StaticColor: 272 prio = 3; break; 273 case StaticGray: 274 prio = 1; break; 275 case TrueColor: 276 prio = 5; break; 277 default: 278 prio = 0; break; 279 } 280 if (visInfoList[i].visual 281 == DefaultVisualOfScreen(Tk_Screen(tkwin))) { 282 prio++; 283 } 284 if (bestPtr == NULL) { 285 goto newBest; 286 } 287 if (visInfoList[i].depth < bestPtr->depth) { 288 if (visInfoList[i].depth >= template.depth) { 289 goto newBest; 290 } 291 } else if (visInfoList[i].depth > bestPtr->depth) { 292 if (bestPtr->depth < template.depth) { 293 goto newBest; 294 } 295 } else { 296 if (prio > bestPrio) { 297 goto newBest; 298 } 299 } 300 continue; 301 302 newBest: 303 bestPtr = &visInfoList[i]; 304 bestPrio = prio; 305 } 306 *depthPtr = bestPtr->depth; 307 visual = bestPtr->visual; 308 XFree((char *) visInfoList); 309 310 /* 311 * If we need to find a colormap for this visual, do it now. If the visual 312 * is the default visual for the screen, then use the default colormap. 313 * Otherwise search for an existing colormap that's shareable. If all else 314 * fails, create a new colormap. 315 */ 316 317 if (colormapPtr != NULL) { 318 if (visual == DefaultVisualOfScreen(Tk_Screen(tkwin))) { 319 *colormapPtr = DefaultColormapOfScreen(Tk_Screen(tkwin)); 320 } else { 321 for (cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL; 322 cmapPtr = cmapPtr->nextPtr) { 323 if (cmapPtr->shareable && (cmapPtr->visual == visual)) { 324 *colormapPtr = cmapPtr->colormap; 325 cmapPtr->refCount += 1; 326 goto done; 327 } 328 } 329 cmapPtr = (TkColormap *) ckalloc(sizeof(TkColormap)); 330 cmapPtr->colormap = XCreateColormap(Tk_Display(tkwin), 331 RootWindowOfScreen(Tk_Screen(tkwin)), visual, 332 AllocNone); 333 cmapPtr->visual = visual; 334 cmapPtr->refCount = 1; 335 cmapPtr->shareable = 1; 336 cmapPtr->nextPtr = dispPtr->cmapPtr; 337 dispPtr->cmapPtr = cmapPtr; 338 *colormapPtr = cmapPtr->colormap; 339 } 340 } 341 342 done: 343 return visual; 344} 345 346/* 347 *---------------------------------------------------------------------- 348 * 349 * Tk_GetColormap -- 350 * 351 * Given a string identifying a colormap, this procedure finds an 352 * appropriate colormap. 353 * 354 * Results: 355 * The return value is normally the X resource identifier for the 356 * colormap. If an error occurs, None is returned and an error message is 357 * placed in the interp's result. 358 * 359 * Side effects: 360 * A reference count is incremented for the colormap, so Tk_FreeColormap 361 * must eventually be called exactly once for each call to 362 * Tk_GetColormap. 363 * 364 *---------------------------------------------------------------------- 365 */ 366 367Colormap 368Tk_GetColormap( 369 Tcl_Interp *interp, /* Interpreter to use for error reporting. */ 370 Tk_Window tkwin, /* Window where colormap will be used. */ 371 CONST char *string) /* String that identifies colormap: either 372 * "new" or the name of another window. */ 373{ 374 Colormap colormap; 375 TkColormap *cmapPtr; 376 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 377 Tk_Window other; 378 379 /* 380 * Allocate a new colormap, if that's what is wanted. 381 */ 382 383 if (strcmp(string, "new") == 0) { 384 cmapPtr = (TkColormap *) ckalloc(sizeof(TkColormap)); 385 cmapPtr->colormap = XCreateColormap(Tk_Display(tkwin), 386 RootWindowOfScreen(Tk_Screen(tkwin)), Tk_Visual(tkwin), 387 AllocNone); 388 cmapPtr->visual = Tk_Visual(tkwin); 389 cmapPtr->refCount = 1; 390 cmapPtr->shareable = 0; 391 cmapPtr->nextPtr = dispPtr->cmapPtr; 392 dispPtr->cmapPtr = cmapPtr; 393 return cmapPtr->colormap; 394 } 395 396 /* 397 * Use a colormap from an existing window. It must have the same visual as 398 * tkwin (which means, among other things, that the other window must be 399 * on the same screen). 400 */ 401 402 other = Tk_NameToWindow(interp, string, tkwin); 403 if (other == NULL) { 404 return None; 405 } 406 if (Tk_Screen(other) != Tk_Screen(tkwin)) { 407 Tcl_AppendResult(interp, "can't use colormap for ", string, 408 ": not on same screen", NULL); 409 return None; 410 } 411 if (Tk_Visual(other) != Tk_Visual(tkwin)) { 412 Tcl_AppendResult(interp, "can't use colormap for ", string, 413 ": incompatible visuals", NULL); 414 return None; 415 } 416 colormap = Tk_Colormap(other); 417 418 /* 419 * If the colormap was a special one allocated by code in this file, 420 * increment its reference count. 421 */ 422 423 for (cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL; 424 cmapPtr = cmapPtr->nextPtr) { 425 if (cmapPtr->colormap == colormap) { 426 cmapPtr->refCount += 1; 427 } 428 } 429 return colormap; 430} 431 432/* 433 *---------------------------------------------------------------------- 434 * 435 * Tk_FreeColormap -- 436 * 437 * This procedure is called to release a colormap that was previously 438 * allocated by Tk_GetColormap. 439 * 440 * Results: 441 * None. 442 * 443 * Side effects: 444 * The colormap's reference count is decremented. If this was the last 445 * reference to the colormap, then the colormap is freed. 446 * 447 *---------------------------------------------------------------------- 448 */ 449 450void 451Tk_FreeColormap( 452 Display *display, /* Display for which colormap was 453 * allocated. */ 454 Colormap colormap) /* Colormap that is no longer needed. Must 455 * have been returned by previous call to 456 * Tk_GetColormap, or preserved by a previous 457 * call to Tk_PreserveColormap. */ 458{ 459 TkDisplay *dispPtr; 460 TkColormap *cmapPtr, *prevPtr; 461 462 /* 463 * Find Tk's information about the display, then see if this colormap is a 464 * non-default one (if it's a default one, there won't be an entry for it 465 * in the display's list). 466 */ 467 468 dispPtr = TkGetDisplay(display); 469 if (dispPtr == NULL) { 470 Tcl_Panic("unknown display passed to Tk_FreeColormap"); 471 } 472 for (prevPtr = NULL, cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL; 473 prevPtr = cmapPtr, cmapPtr = cmapPtr->nextPtr) { 474 if (cmapPtr->colormap == colormap) { 475 cmapPtr->refCount -= 1; 476 if (cmapPtr->refCount == 0) { 477 XFreeColormap(display, colormap); 478 if (prevPtr == NULL) { 479 dispPtr->cmapPtr = cmapPtr->nextPtr; 480 } else { 481 prevPtr->nextPtr = cmapPtr->nextPtr; 482 } 483 ckfree((char *) cmapPtr); 484 } 485 return; 486 } 487 } 488} 489 490/* 491 *---------------------------------------------------------------------- 492 * 493 * Tk_PreserveColormap -- 494 * 495 * This procedure is called to indicate to Tk that the specified colormap 496 * is being referenced from another location and should not be freed 497 * until all extra references are eliminated. The colormap must have been 498 * returned by Tk_GetColormap. 499 * 500 * Results: 501 * None. 502 * 503 * Side effects: 504 * The colormap's reference count is incremented, so Tk_FreeColormap must 505 * eventually be called exactly once for each call to 506 * Tk_PreserveColormap. 507 * 508 *---------------------------------------------------------------------- 509 */ 510 511void 512Tk_PreserveColormap( 513 Display *display, /* Display for which colormap was 514 * allocated. */ 515 Colormap colormap) /* Colormap that should be preserved. */ 516{ 517 TkDisplay *dispPtr; 518 TkColormap *cmapPtr; 519 520 /* 521 * Find Tk's information about the display, then see if this colormap is a 522 * non-default one (if it's a default one, there won't be an entry for it 523 * in the display's list). 524 */ 525 526 dispPtr = TkGetDisplay(display); 527 if (dispPtr == NULL) { 528 Tcl_Panic("unknown display passed to Tk_PreserveColormap"); 529 } 530 for (cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL; 531 cmapPtr = cmapPtr->nextPtr) { 532 if (cmapPtr->colormap == colormap) { 533 cmapPtr->refCount += 1; 534 return; 535 } 536 } 537} 538 539/* 540 * Local Variables: 541 * mode: c 542 * c-basic-offset: 4 543 * fill-column: 78 544 * End: 545 */ 546