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