4 * This file maintains a database of color values for the Tk
5 * toolkit, in order to avoid round-trips to the server to
6 * map color names to pixel values.
8 * Copyright 1990 Regents of the University of California
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkColor.c,v 1.15 92/07/14 08:44:49 ouster Exp $ SPRITE (Berkeley)";
26 * A two-level data structure is used to manage the color database.
27 * The top level consists of one entry for each color name that is
28 * currently active, and the bottom level contains one entry for each
29 * pixel value that is still in use. The distinction between
30 * levels is necessary because the same pixel may have several
31 * different names. There are two hash tables, one used to index into
32 * each of the data structures. The name hash table is used when
33 * allocating colors, and the pixel hash table is used when freeing
38 * One of the following data structures is used to keep track of
39 * each color that this module has allocated from the X display
40 * server. These entries are indexed by two hash tables defined
41 * below: nameTable and valueTable.
44 #define COLOR_MAGIC 0xc6140277
46 typedef struct TkColor
{
47 XColor color
; /* Information about this color. */
48 int magic
; /* Used for quick integrity check on this
49 * structure. Must always have the
50 * value COLOR_MAGIC. */
51 Screen
*screen
; /* Screen where this color is valid. Used
53 Colormap colormap
; /* Colormap from which this entry was
55 int refCount
; /* Number of uses of this structure. */
56 Tcl_HashTable
*tablePtr
; /* Hash table that indexes this structure
57 * (needed when deleting structure). */
58 Tcl_HashEntry
*hashPtr
; /* Pointer to hash table entry for this
59 * structure. (for use in deleting entry). */
62 typedef struct VisInfo
{
72 * Hash table for name -> TkColor mapping, and key structure used to
73 * index into that table:
76 static Tcl_HashTable nameTable
;
78 Tk_Uid name
; /* Name of desired color. */
79 Colormap colormap
; /* Colormap from which color will be
81 Display
*display
; /* Display for colormap. */
85 * Hash table for value -> TkColor mapping, and key structure used to
86 * index into that table:
89 static Tcl_HashTable valueTable
;
91 int red
, green
, blue
; /* Values for desired color. */
92 Colormap colormap
; /* Colormap from which color will be
94 Display
*display
; /* Display for colormap. */
98 * Global colormap creation flag
100 char *TK_CreateColormap
= 0;
103 * Hash table for screen -> VisInfo mapping, and key structure used to
104 * index into that table:
107 static Tcl_HashTable screenTable
;
109 static int initialized
= 0; /* 0 means static structures haven't been
110 * initialized yet. */
113 * Forward declarations for procedures defined in this file:
116 static void ColorInit
_ANSI_ARGS_((void));
119 *----------------------------------------------------------------------
123 * Given a string name for a color, map the name to a corresponding
127 * The return value is a pointer to an XColor structure that
128 * indicates the red, blue, and green intensities for the color
129 * given by "name", and also specifies a pixel value to use to
130 * draw in that color in window "tkwin". If an error occurs,
131 * then NULL is returned and an error message will be left in
135 * The color is added to an internal database with a reference count.
136 * For each call to this procedure, there should eventually be a call
137 * to Tk_FreeColor, so that the database is cleaned up when colors
138 * aren't in use anymore.
140 *----------------------------------------------------------------------
144 Tk_GetColor(interp
, tkwin
, colormap
, name
)
145 Tcl_Interp
*interp
; /* Place to leave error message if
146 * color can't be found. */
147 Tk_Window tkwin
; /* Window in which color will be used. */
148 Colormap colormap
; /* Map from which to allocate color. None
149 * means use default. */
150 Tk_Uid name
; /* Name of color to allocated (in form
151 * suitable for passing to XParseColor). */
154 Tcl_HashEntry
*nameHashPtr
;
164 * First, check to see if there's already a mapping for this color
169 if (colormap
== None
) {
170 colormap
= Tk_DefaultColormap(Tk_Screen(tkwin
));
172 nameKey
.colormap
= colormap
;
173 nameKey
.display
= Tk_Display(tkwin
);
174 nameHashPtr
= Tcl_CreateHashEntry(&nameTable
, (char *) &nameKey
, &new);
176 tkColPtr
= (TkColor
*) Tcl_GetHashValue(nameHashPtr
);
177 tkColPtr
->refCount
++;
178 return &tkColPtr
->color
;
182 * The name isn't currently known. Map from the name to a pixel
183 * value. Be tricky here, and call XAllocNamedColor instead of
184 * XParseColor for non-# names: this saves a server round-trip
191 if (XAllocNamedColor(Tk_Display(tkwin
), colormap
, name
,
192 &screen
, &color
) == 0) {
194 Tcl_AppendResult(interp
, "couldn't allocate a color for \"",
195 name
, "\"", (char *) NULL
);
196 Tcl_DeleteHashEntry(nameHashPtr
);
197 return (XColor
*) NULL
;
200 if (XParseColor(Tk_Display(tkwin
), colormap
, name
, &color
) == 0) {
201 Tcl_AppendResult(interp
, "invalid color name \"", name
,
202 "\"", (char *) NULL
);
203 Tcl_DeleteHashEntry(nameHashPtr
);
204 return (XColor
*) NULL
;
206 if (XAllocColor(Tk_Display(tkwin
), colormap
, &color
) == 0) {
212 * Now create a new TkColor structure and add it to nameTable.
215 tkColPtr
= (TkColor
*) ckalloc(sizeof(TkColor
));
216 tkColPtr
->color
= color
;
217 tkColPtr
->magic
= COLOR_MAGIC
;
218 tkColPtr
->screen
= Tk_Screen(tkwin
);
219 tkColPtr
->colormap
= colormap
;
220 tkColPtr
->refCount
= 1;
221 tkColPtr
->tablePtr
= &nameTable
;
222 tkColPtr
->hashPtr
= nameHashPtr
;
223 Tcl_SetHashValue(nameHashPtr
, tkColPtr
);
225 return &tkColPtr
->color
;
229 *----------------------------------------------------------------------
231 * Tk_GetColorByValue --
233 * Given a desired set of red-green-blue intensities for a color,
234 * locate a pixel value to use to draw that color in a given
238 * The return value is a pointer to an XColor structure that
239 * indicates the closest red, blue, and green intensities available
240 * to those specified in colorPtr, and also specifies a pixel
241 * value to use to draw in that color in window "tkwin". If an
242 * error occurs, then NULL is returned and an error message will
243 * be left in interp->result.
246 * The color is added to an internal database with a reference count.
247 * For each call to this procedure, there should eventually be a call
248 * to Tk_FreeColor, so that the database is cleaned up when colors
249 * aren't in use anymore.
251 *----------------------------------------------------------------------
255 Tk_GetColorByValue(interp
, tkwin
, colormap
, colorPtr
)
256 Tcl_Interp
*interp
; /* Place to leave error message if
257 * color can't be found. */
258 Tk_Window tkwin
; /* Window in which color will be used. */
259 Colormap colormap
; /* Map from which to allocate color. None
260 * means use default. */
261 XColor
*colorPtr
; /* Red, green, and blue fields indicate
265 Tcl_HashEntry
*valueHashPtr
;
274 * First, check to see if there's already a mapping for this color
278 valueKey
.red
= colorPtr
->red
;
279 valueKey
.green
= colorPtr
->green
;
280 valueKey
.blue
= colorPtr
->blue
;
281 if (colormap
== None
) {
282 colormap
= Tk_DefaultColormap(Tk_Screen(tkwin
));
284 valueKey
.colormap
= colormap
;
285 valueKey
.display
= Tk_Display(tkwin
);
286 valueHashPtr
= Tcl_CreateHashEntry(&valueTable
, (char *) &valueKey
, &new);
288 tkColPtr
= (TkColor
*) Tcl_GetHashValue(valueHashPtr
);
289 tkColPtr
->refCount
++;
290 return &tkColPtr
->color
;
294 * The name isn't currently known. Find a pixel value for this
295 * color and add a new structure to valueTable.
298 tkColPtr
= (TkColor
*) ckalloc(sizeof(TkColor
));
299 tkColPtr
->color
.red
= valueKey
.red
;
300 tkColPtr
->color
.green
= valueKey
.green
;
301 tkColPtr
->color
.blue
= valueKey
.blue
;
302 if (XAllocColor(Tk_Display(tkwin
), colormap
, &tkColPtr
->color
) == 0) {
303 sprintf(interp
->result
, "couldn't allocate color");
304 Tcl_DeleteHashEntry(valueHashPtr
);
305 ckfree((char *) tkColPtr
);
306 return (XColor
*) NULL
;
308 tkColPtr
->magic
= COLOR_MAGIC
;
309 tkColPtr
->screen
= Tk_Screen(tkwin
);
310 tkColPtr
->colormap
= colormap
;
311 tkColPtr
->refCount
= 1;
312 tkColPtr
->tablePtr
= &valueTable
;
313 tkColPtr
->hashPtr
= valueHashPtr
;
314 Tcl_SetHashValue(valueHashPtr
, tkColPtr
);
316 return &tkColPtr
->color
;
320 *--------------------------------------------------------------
324 * Given a color, return a textual string identifying
328 * If colorPtr was created by Tk_GetColor, then the return
329 * value is the "string" that was used to create it.
330 * Otherwise the return value is a string that could have
331 * been passed to Tk_GetColor to allocate that color. The
332 * storage for the returned string is only guaranteed to
333 * persist up until the next call to this procedure.
338 *--------------------------------------------------------------
342 Tk_NameOfColor(colorPtr
)
343 XColor
*colorPtr
; /* Color whose name is desired. */
345 register TkColor
*tkColPtr
= (TkColor
*) colorPtr
;
346 static char string
[20];
348 if ((tkColPtr
->magic
== COLOR_MAGIC
)
349 && (tkColPtr
->tablePtr
== &nameTable
)) {
350 return ((NameKey
*) tkColPtr
->hashPtr
->key
.words
)->name
;
352 sprintf(string
, "#%4x%4x%4x", colorPtr
->red
, colorPtr
->green
,
358 *----------------------------------------------------------------------
362 * This procedure is called to release a color allocated by
369 * The reference count associated with colorPtr is deleted, and
370 * the color is released to X if there are no remaining uses
373 *----------------------------------------------------------------------
377 Tk_FreeColor(colorPtr
)
378 XColor
*colorPtr
; /* Color to be released. Must have been
379 * allocated by Tk_GetColor or
380 * Tk_GetColorByValue. */
382 register TkColor
*tkColPtr
= (TkColor
*) colorPtr
;
384 Screen
*screen
= tkColPtr
->screen
;
387 * Do a quick sanity check to make sure this color was really
388 * allocated by Tk_GetColor.
391 if (tkColPtr
->magic
!= COLOR_MAGIC
) {
392 panic("Tk_FreeColor called with bogus color");
395 tkColPtr
->refCount
--;
396 if (tkColPtr
->refCount
== 0) {
399 * Careful! Don't free black or white, since this will
400 * make some servers very unhappy.
403 visual
= Tk_DefaultVisual(screen
);
404 if ((visual
->class != StaticGray
) && (visual
->class != StaticColor
)
405 && (tkColPtr
->color
.pixel
!= BlackPixelOfScreen(screen
))
406 && (tkColPtr
->color
.pixel
!= WhitePixelOfScreen(screen
))) {
407 XFreeColors(DisplayOfScreen(screen
), tkColPtr
->colormap
,
408 &tkColPtr
->color
.pixel
, 1, 0L);
410 Tcl_DeleteHashEntry(tkColPtr
->hashPtr
);
412 ckfree((char *) tkColPtr
);
417 *----------------------------------------------------------------------
421 * Initialize the structure used for color management.
429 *----------------------------------------------------------------------
436 Tcl_InitHashTable(&nameTable
, sizeof(NameKey
)/sizeof(int));
437 Tcl_InitHashTable(&valueTable
, sizeof(ValueKey
)/sizeof(int));
438 Tcl_InitHashTable(&screenTable
, TCL_ONE_WORD_KEYS
);
443 Tk_IndexOfScreen(Screen
*screen
)
445 Display
*dpy
= DisplayOfScreen(screen
);
446 int i
, nscreens
= ScreenCount(dpy
);
448 for (i
= 0; i
< nscreens
; i
++) {
449 if (screen
== ScreenOfDisplay(dpy
, i
))
452 return (DefaultScreen(dpy
));
457 Tk_VisInfo(Screen
*screen
)
459 Tcl_HashEntry
*hashPtr
;
461 XVisualInfo vTemplate
;
462 XVisualInfo
*visualList
;
465 int visualsMatched
, scrnum
, new;
471 hashPtr
= Tcl_CreateHashEntry(&screenTable
, (char *) screen
, &new);
473 info
= (VisInfo
*) Tcl_GetHashValue(hashPtr
);
475 info
= (VisInfo
*) ckalloc(sizeof(VisInfo
));
476 info
->screen
= screen
;
478 /* Workaround to support non-default visuals */
480 info
->visual
= XDefaultVisualOfScreen(screen
);
481 info
->depth
= XDefaultDepthOfScreen(screen
);
482 info
->colormap
= XDefaultColormapOfScreen(screen
);
483 info
->gc
= DefaultGCOfScreen(screen
);
485 scrnum
= Tk_IndexOfScreen(screen
);
486 vTemplate
.screen
= scrnum
;
487 vTemplate
.class = TrueColor
;
489 XGetVisualInfo(DisplayOfScreen(screen
),
492 &vTemplate
, &visualsMatched
);
495 if (visualList
!= NULL
) {
497 for (i
= 0; i
< visualsMatched
; i
++) {
498 if (visualList
[i
].depth
> 24)
499 continue; /* Most likely broken */
501 info
->visual
= visualList
[i
].visual
;
502 info
->depth
= visualList
[i
].depth
;
508 if (info
->visual
== NULL
) {
509 info
->visual
= XDefaultVisualOfScreen(screen
);
510 info
->depth
= XDefaultDepthOfScreen(screen
);
511 info
->colormap
= XDefaultColormapOfScreen(screen
);
512 info
->gc
= DefaultGCOfScreen(screen
);
514 if (info
->depth
== 8) {
515 vTemplate
.screen
= scrnum
;
516 vTemplate
.class = PseudoColor
;
518 visualList
= XGetVisualInfo(DisplayOfScreen(screen
),
522 &vTemplate
, &visualsMatched
);
524 if (visualsMatched
> 0) {
525 info
->visual
= visualList
[0].visual
;
526 info
->depth
= visualList
[0].depth
;
532 info
->pixmap
= XCreatePixmap(screen
->display
,
533 RootWindowOfScreen(screen
),
536 if ((TK_CreateColormap
== 0) &&
537 (info
->visual
== DefaultVisualOfScreen(screen
))) {
538 info
->colormap
= DefaultColormapOfScreen(screen
);
539 info
->gc
= DefaultGCOfScreen(screen
);
542 XCreateColormap(screen
->display
,
543 RootWindowOfScreen(screen
),
544 info
->visual
, AllocNone
);
546 XCreateGC(screen
->display
,
547 info
->pixmap
, 0, &values
);
550 XFree((char *)visualList
);
553 Tcl_SetHashValue(hashPtr
, info
);
561 Tk_DefaultDepth(Screen
*screen
)
563 return (Tk_VisInfo(screen
)->depth
);
568 Tk_DefaultVisual(Screen
*screen
)
570 return (Tk_VisInfo(screen
)->visual
);
575 Tk_DefaultColormap(Screen
*screen
)
577 return (Tk_VisInfo(screen
)->colormap
);
582 Tk_DefaultRootWindow(Display
*dpy
)
584 return (DefaultRootWindow(dpy
));
589 Tk_DefaultGC(Screen
*screen
)
591 return (Tk_VisInfo(screen
)->gc
);
596 Tk_DefaultPixmap(Screen
*screen
)
598 return (Tk_VisInfo(screen
)->pixmap
);