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