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