1/* 2 * tkWinColor.c -- 3 * 4 * Functions to map color names to system color values. 5 * 6 * Copyright (c) 1995 Sun Microsystems, Inc. 7 * Copyright (c) 1994 Software Research Associates, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution 10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id: tkWinColor.c,v 1.6 2000/07/06 03:17:44 mo Exp $ 13 */ 14 15#include "tkWinInt.h" 16#include "tkColor.h" 17 18/* 19 * The following structure is used to keep track of each color that is 20 * allocated by this module. 21 */ 22 23typedef struct WinColor { 24 TkColor info; /* Generic color information. */ 25 int index; /* Index for GetSysColor(), -1 if color 26 * is not a "live" system color. */ 27} WinColor; 28 29/* 30 * The sysColors array contains the names and index values for the 31 * Windows indirect system color names. In use, all of the names 32 * will have the string "System" prepended, but we omit it in the table 33 * to save space. 34 */ 35 36typedef struct { 37 char *name; 38 int index; 39} SystemColorEntry; 40 41 42static SystemColorEntry sysColors[] = { 43 "3dDarkShadow", COLOR_3DDKSHADOW, 44 "3dLight", COLOR_3DLIGHT, 45 "ActiveBorder", COLOR_ACTIVEBORDER, 46 "ActiveCaption", COLOR_ACTIVECAPTION, 47 "AppWorkspace", COLOR_APPWORKSPACE, 48 "Background", COLOR_BACKGROUND, 49 "ButtonFace", COLOR_BTNFACE, 50 "ButtonHighlight", COLOR_BTNHIGHLIGHT, 51 "ButtonShadow", COLOR_BTNSHADOW, 52 "ButtonText", COLOR_BTNTEXT, 53 "CaptionText", COLOR_CAPTIONTEXT, 54 "DisabledText", COLOR_GRAYTEXT, 55 "GrayText", COLOR_GRAYTEXT, 56 "Highlight", COLOR_HIGHLIGHT, 57 "HighlightText", COLOR_HIGHLIGHTTEXT, 58 "InactiveBorder", COLOR_INACTIVEBORDER, 59 "InactiveCaption", COLOR_INACTIVECAPTION, 60 "InactiveCaptionText", COLOR_INACTIVECAPTIONTEXT, 61 "InfoBackground", COLOR_INFOBK, 62 "InfoText", COLOR_INFOTEXT, 63 "Menu", COLOR_MENU, 64 "MenuText", COLOR_MENUTEXT, 65 "Scrollbar", COLOR_SCROLLBAR, 66 "Window", COLOR_WINDOW, 67 "WindowFrame", COLOR_WINDOWFRAME, 68 "WindowText", COLOR_WINDOWTEXT, 69 NULL, 0 70}; 71 72typedef struct ThreadSpecificData { 73 int ncolors; 74} ThreadSpecificData; 75static Tcl_ThreadDataKey dataKey; 76 77/* 78 * Forward declarations for functions defined later in this file. 79 */ 80 81static int FindSystemColor _ANSI_ARGS_((const char *name, 82 XColor *colorPtr, int *indexPtr)); 83 84/* 85 *---------------------------------------------------------------------- 86 * 87 * FindSystemColor -- 88 * 89 * This routine finds the color entry that corresponds to the 90 * specified color. 91 * 92 * Results: 93 * Returns non-zero on success. The RGB values of the XColor 94 * will be initialized to the proper values on success. 95 * 96 * Side effects: 97 * None. 98 * 99 *---------------------------------------------------------------------- 100 */ 101 102static int 103FindSystemColor(name, colorPtr, indexPtr) 104 const char *name; /* Color name. */ 105 XColor *colorPtr; /* Where to store results. */ 106 int *indexPtr; /* Out parameter to store color index. */ 107{ 108 int l, u, r, i; 109 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 110 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 111 112 /* 113 * Count the number of elements in the color array if we haven't 114 * done so yet. 115 */ 116 117 if (tsdPtr->ncolors == 0) { 118 SystemColorEntry *ePtr; 119 int version; 120 121 version = LOBYTE(LOWORD(GetVersion())); 122 for (ePtr = sysColors; ePtr->name != NULL; ePtr++) { 123 if (version < 4) { 124 if (ePtr->index == COLOR_3DDKSHADOW) { 125 ePtr->index = COLOR_BTNSHADOW; 126 } else if (ePtr->index == COLOR_3DLIGHT) { 127 ePtr->index = COLOR_BTNHIGHLIGHT; 128 } 129 } 130 tsdPtr->ncolors++; 131 } 132 } 133 134 /* 135 * Perform a binary search on the sorted array of colors. 136 */ 137 138 l = 0; 139 u = tsdPtr->ncolors - 1; 140 while (l <= u) { 141 i = (l + u) / 2; 142 r = strcasecmp(name, sysColors[i].name); 143 if (r == 0) { 144 break; 145 } else if (r < 0) { 146 u = i-1; 147 } else { 148 l = i+1; 149 } 150 } 151 if (l > u) { 152 return 0; 153 } 154 155 *indexPtr = sysColors[i].index; 156 colorPtr->pixel = GetSysColor(sysColors[i].index); 157 /* 158 * x257 is (value<<8 + value) to get the properly bit shifted 159 * and padded value. [Bug: 4919] 160 */ 161 colorPtr->red = GetRValue(colorPtr->pixel) * 257; 162 colorPtr->green = GetGValue(colorPtr->pixel) * 257; 163 colorPtr->blue = GetBValue(colorPtr->pixel) * 257; 164 colorPtr->flags = DoRed|DoGreen|DoBlue; 165 colorPtr->pad = 0; 166 return 1; 167} 168 169/* 170 *---------------------------------------------------------------------- 171 * 172 * TkpGetColor -- 173 * 174 * Allocate a new TkColor for the color with the given name. 175 * 176 * Results: 177 * Returns a newly allocated TkColor, or NULL on failure. 178 * 179 * Side effects: 180 * May invalidate the colormap cache associated with tkwin upon 181 * allocating a new colormap entry. Allocates a new TkColor 182 * structure. 183 * 184 *---------------------------------------------------------------------- 185 */ 186 187TkColor * 188TkpGetColor(tkwin, name) 189 Tk_Window tkwin; /* Window in which color will be used. */ 190 Tk_Uid name; /* Name of color to allocated (in form 191 * suitable for passing to XParseColor). */ 192{ 193 WinColor *winColPtr; 194 XColor color; 195 int index = -1; /* -1 indicates that this is not an indirect 196 * sytem color. */ 197 198 /* 199 * Check to see if it is a system color or an X color string. If the 200 * color is found, allocate a new WinColor and store the XColor and the 201 * system color index. 202 */ 203 204 if (((strncasecmp(name, "system", 6) == 0) 205 && FindSystemColor(name+6, &color, &index)) 206 || XParseColor(Tk_Display(tkwin), Tk_Colormap(tkwin), name, 207 &color)) { 208 winColPtr = (WinColor *) ckalloc(sizeof(WinColor)); 209 winColPtr->info.color = color; 210 winColPtr->index = index; 211 212 XAllocColor(Tk_Display(tkwin), Tk_Colormap(tkwin), 213 &winColPtr->info.color); 214 return (TkColor *) winColPtr; 215 } 216 return (TkColor *) NULL; 217} 218 219/* 220 *---------------------------------------------------------------------- 221 * 222 * TkpGetColorByValue -- 223 * 224 * Given a desired set of red-green-blue intensities for a color, 225 * locate a pixel value to use to draw that color in a given 226 * window. 227 * 228 * Results: 229 * The return value is a pointer to an TkColor structure that 230 * indicates the closest red, blue, and green intensities available 231 * to those specified in colorPtr, and also specifies a pixel 232 * value to use to draw in that color. 233 * 234 * Side effects: 235 * May invalidate the colormap cache for the specified window. 236 * Allocates a new TkColor structure. 237 * 238 *---------------------------------------------------------------------- 239 */ 240 241TkColor * 242TkpGetColorByValue(tkwin, colorPtr) 243 Tk_Window tkwin; /* Window in which color will be used. */ 244 XColor *colorPtr; /* Red, green, and blue fields indicate 245 * desired color. */ 246{ 247 WinColor *tkColPtr = (WinColor *) ckalloc(sizeof(WinColor)); 248 249 tkColPtr->info.color.red = colorPtr->red; 250 tkColPtr->info.color.green = colorPtr->green; 251 tkColPtr->info.color.blue = colorPtr->blue; 252 tkColPtr->info.color.pixel = 0; 253 tkColPtr->index = -1; 254 XAllocColor(Tk_Display(tkwin), Tk_Colormap(tkwin), &tkColPtr->info.color); 255 return (TkColor *) tkColPtr; 256} 257 258/* 259 *---------------------------------------------------------------------- 260 * 261 * TkpFreeColor -- 262 * 263 * Release the specified color back to the system. 264 * 265 * Results: 266 * None 267 * 268 * Side effects: 269 * Invalidates the colormap cache for the colormap associated with 270 * the given color. 271 * 272 *---------------------------------------------------------------------- 273 */ 274 275void 276TkpFreeColor(tkColPtr) 277 TkColor *tkColPtr; /* Color to be released. Must have been 278 * allocated by TkpGetColor or 279 * TkpGetColorByValue. */ 280{ 281 Screen *screen = tkColPtr->screen; 282 283 XFreeColors(DisplayOfScreen(screen), tkColPtr->colormap, 284 &tkColPtr->color.pixel, 1, 0L); 285} 286 287/* 288 *---------------------------------------------------------------------- 289 * 290 * TkWinIndexOfColor -- 291 * 292 * Given a color, return the system color index that was used 293 * to create the color. 294 * 295 * Results: 296 * If the color was allocated using a system indirect color name, 297 * then the corresponding GetSysColor() index is returned. 298 * Otherwise, -1 is returned. 299 * 300 * Side effects: 301 * None. 302 * 303 *---------------------------------------------------------------------- 304 */ 305 306int 307TkWinIndexOfColor(colorPtr) 308 XColor *colorPtr; 309{ 310 register WinColor *winColPtr = (WinColor *) colorPtr; 311 if (winColPtr->info.magic == COLOR_MAGIC) { 312 return winColPtr->index; 313 } 314 return -1; 315} 316 317/* 318 *---------------------------------------------------------------------- 319 * 320 * XAllocColor -- 321 * 322 * Find the closest available color to the specified XColor. 323 * 324 * Results: 325 * Updates the color argument and returns 1 on success. Otherwise 326 * returns 0. 327 * 328 * Side effects: 329 * Allocates a new color in the palette. 330 * 331 *---------------------------------------------------------------------- 332 */ 333 334int 335XAllocColor(display, colormap, color) 336 Display* display; 337 Colormap colormap; 338 XColor* color; 339{ 340 TkWinColormap *cmap = (TkWinColormap *) colormap; 341 PALETTEENTRY entry, closeEntry; 342 HDC dc = GetDC(NULL); 343 344 entry.peRed = (color->red) >> 8; 345 entry.peGreen = (color->green) >> 8; 346 entry.peBlue = (color->blue) >> 8; 347 entry.peFlags = 0; 348 349 if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) { 350 unsigned long sizePalette = GetDeviceCaps(dc, SIZEPALETTE); 351 UINT newPixel, closePixel; 352 int new, refCount; 353 Tcl_HashEntry *entryPtr; 354 UINT index; 355 356 /* 357 * Find the nearest existing palette entry. 358 */ 359 360 newPixel = RGB(entry.peRed, entry.peGreen, entry.peBlue); 361 index = GetNearestPaletteIndex(cmap->palette, newPixel); 362 GetPaletteEntries(cmap->palette, index, 1, &closeEntry); 363 closePixel = RGB(closeEntry.peRed, closeEntry.peGreen, 364 closeEntry.peBlue); 365 366 /* 367 * If this is not a duplicate, allocate a new entry. Note that 368 * we may get values for index that are above the current size 369 * of the palette. This happens because we don't shrink the size of 370 * the palette object when we deallocate colors so there may be 371 * stale values that match in the upper slots. We should ignore 372 * those values and just put the new color in as if the colors 373 * had not matched. 374 */ 375 376 if ((index >= cmap->size) || (newPixel != closePixel)) { 377 if (cmap->size == sizePalette) { 378 color->red = closeEntry.peRed * 257; 379 color->green = closeEntry.peGreen * 257; 380 color->blue = closeEntry.peBlue * 257; 381 entry = closeEntry; 382 if (index >= cmap->size) { 383 OutputDebugString("XAllocColor: Colormap is bigger than we thought"); 384 } 385 } else { 386 cmap->size++; 387 ResizePalette(cmap->palette, cmap->size); 388 SetPaletteEntries(cmap->palette, cmap->size - 1, 1, &entry); 389 } 390 } 391 392 color->pixel = PALETTERGB(entry.peRed, entry.peGreen, entry.peBlue); 393 entryPtr = Tcl_CreateHashEntry(&cmap->refCounts, 394 (char *) color->pixel, &new); 395 if (new) { 396 refCount = 1; 397 } else { 398 refCount = ((int) Tcl_GetHashValue(entryPtr)) + 1; 399 } 400 Tcl_SetHashValue(entryPtr, (ClientData)refCount); 401 } else { 402 403 /* 404 * Determine what color will actually be used on non-colormap systems. 405 */ 406 407 color->pixel = GetNearestColor(dc, 408 RGB(entry.peRed, entry.peGreen, entry.peBlue)); 409 color->red = GetRValue(color->pixel) * 257; 410 color->green = GetGValue(color->pixel) * 257; 411 color->blue = GetBValue(color->pixel) * 257; 412 } 413 414 ReleaseDC(NULL, dc); 415 return 1; 416} 417 418/* 419 *---------------------------------------------------------------------- 420 * 421 * XFreeColors -- 422 * 423 * Deallocate a block of colors. 424 * 425 * Results: 426 * None. 427 * 428 * Side effects: 429 * Removes entries for the current palette and compacts the 430 * remaining set. 431 * 432 *---------------------------------------------------------------------- 433 */ 434 435void 436XFreeColors(display, colormap, pixels, npixels, planes) 437 Display* display; 438 Colormap colormap; 439 unsigned long* pixels; 440 int npixels; 441 unsigned long planes; 442{ 443 TkWinColormap *cmap = (TkWinColormap *) colormap; 444 COLORREF cref; 445 UINT count, index, refCount; 446 int i; 447 PALETTEENTRY entry, *entries; 448 Tcl_HashEntry *entryPtr; 449 HDC dc = GetDC(NULL); 450 451 /* 452 * We don't have to do anything for non-palette devices. 453 */ 454 455 if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) { 456 457 /* 458 * This is really slow for large values of npixels. 459 */ 460 461 for (i = 0; i < npixels; i++) { 462 entryPtr = Tcl_FindHashEntry(&cmap->refCounts, 463 (char *) pixels[i]); 464 if (!entryPtr) { 465 panic("Tried to free a color that isn't allocated."); 466 } 467 refCount = (int) Tcl_GetHashValue(entryPtr) - 1; 468 if (refCount == 0) { 469 cref = pixels[i] & 0x00ffffff; 470 index = GetNearestPaletteIndex(cmap->palette, cref); 471 GetPaletteEntries(cmap->palette, index, 1, &entry); 472 if (cref == RGB(entry.peRed, entry.peGreen, entry.peBlue)) { 473 count = cmap->size - index; 474 entries = (PALETTEENTRY *) ckalloc(sizeof(PALETTEENTRY) 475 * count); 476 GetPaletteEntries(cmap->palette, index+1, count, entries); 477 SetPaletteEntries(cmap->palette, index, count, entries); 478 ckfree((char *) entries); 479 cmap->size--; 480 } else { 481 panic("Tried to free a color that isn't allocated."); 482 } 483 Tcl_DeleteHashEntry(entryPtr); 484 } else { 485 Tcl_SetHashValue(entryPtr, (ClientData)refCount); 486 } 487 } 488 } 489 ReleaseDC(NULL, dc); 490} 491 492/* 493 *---------------------------------------------------------------------- 494 * 495 * XCreateColormap -- 496 * 497 * Allocate a new colormap. 498 * 499 * Results: 500 * Returns a newly allocated colormap. 501 * 502 * Side effects: 503 * Allocates an empty palette and color list. 504 * 505 *---------------------------------------------------------------------- 506 */ 507 508Colormap 509XCreateColormap(display, w, visual, alloc) 510 Display* display; 511 Window w; 512 Visual* visual; 513 int alloc; 514{ 515 char logPalBuf[sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)]; 516 LOGPALETTE *logPalettePtr; 517 PALETTEENTRY *entryPtr; 518 TkWinColormap *cmap; 519 Tcl_HashEntry *hashPtr; 520 int new; 521 UINT i; 522 HPALETTE sysPal; 523 524 /* 525 * Allocate a starting palette with all of the reserved colors. 526 */ 527 528 logPalettePtr = (LOGPALETTE *) logPalBuf; 529 logPalettePtr->palVersion = 0x300; 530 sysPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE); 531 logPalettePtr->palNumEntries = GetPaletteEntries(sysPal, 0, 256, 532 logPalettePtr->palPalEntry); 533 534 cmap = (TkWinColormap *) ckalloc(sizeof(TkWinColormap)); 535 cmap->size = logPalettePtr->palNumEntries; 536 cmap->stale = 0; 537 cmap->palette = CreatePalette(logPalettePtr); 538 539 /* 540 * Add hash entries for each of the static colors. 541 */ 542 543 Tcl_InitHashTable(&cmap->refCounts, TCL_ONE_WORD_KEYS); 544 for (i = 0; i < logPalettePtr->palNumEntries; i++) { 545 entryPtr = logPalettePtr->palPalEntry + i; 546 hashPtr = Tcl_CreateHashEntry(&cmap->refCounts, (char*) PALETTERGB( 547 entryPtr->peRed, entryPtr->peGreen, entryPtr->peBlue), &new); 548 Tcl_SetHashValue(hashPtr, (ClientData)1); 549 } 550 551 return (Colormap)cmap; 552} 553 554/* 555 *---------------------------------------------------------------------- 556 * 557 * XFreeColormap -- 558 * 559 * Frees the resources associated with the given colormap. 560 * 561 * Results: 562 * None. 563 * 564 * Side effects: 565 * Deletes the palette associated with the colormap. Note that 566 * the palette must not be selected into a device context when 567 * this occurs. 568 * 569 *---------------------------------------------------------------------- 570 */ 571 572void 573XFreeColormap(display, colormap) 574 Display* display; 575 Colormap colormap; 576{ 577 TkWinColormap *cmap = (TkWinColormap *) colormap; 578 if (!DeleteObject(cmap->palette)) { 579 panic("Unable to free colormap, palette is still selected."); 580 } 581 Tcl_DeleteHashTable(&cmap->refCounts); 582 ckfree((char *) cmap); 583} 584 585/* 586 *---------------------------------------------------------------------- 587 * 588 * TkWinSelectPalette -- 589 * 590 * This function sets up the specified device context with a 591 * given palette. If the palette is stale, it realizes it in 592 * the background unless the palette is the current global 593 * palette. 594 * 595 * Results: 596 * Returns the previous palette selected into the device context. 597 * 598 * Side effects: 599 * May change the system palette. 600 * 601 *---------------------------------------------------------------------- 602 */ 603 604HPALETTE 605TkWinSelectPalette(dc, colormap) 606 HDC dc; 607 Colormap colormap; 608{ 609 TkWinColormap *cmap = (TkWinColormap *) colormap; 610 HPALETTE oldPalette; 611 612 oldPalette = SelectPalette(dc, cmap->palette, 613 (cmap->palette == TkWinGetSystemPalette()) ? FALSE : TRUE); 614 RealizePalette(dc); 615 return oldPalette; 616} 617