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 *----------------------------------------------------------------------
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). */
155 Tcl_HashEntry
*nameHashPtr
;
165 * First, check to see if there's already a mapping for this color
170 if (colormap
== None
) {
171 colormap
= Tk_DefaultColormap(Tk_Screen(tkwin
));
173 nameKey
.colormap
= colormap
;
174 nameKey
.display
= Tk_Display(tkwin
);
175 nameHashPtr
= Tcl_CreateHashEntry(&nameTable
, (char *) &nameKey
, &new);
177 tkColPtr
= (TkColor
*) Tcl_GetHashValue(nameHashPtr
);
178 tkColPtr
->refCount
++;
179 return &tkColPtr
->color
;
183 * The name isn't currently known. Map from the name to a pixel
184 * value. Be tricky here, and call XAllocNamedColor instead of
185 * XParseColor for non-# names: this saves a server round-trip
192 if (XAllocNamedColor(Tk_Display(tkwin
), colormap
, name
,
193 &screen
, &color
) == 0) {
195 Tcl_AppendResult(interp
, "couldn't allocate a color for \"",
196 name
, "\"", (char *) NULL
);
197 Tcl_DeleteHashEntry(nameHashPtr
);
198 return (XColor
*) NULL
;
201 if (XParseColor(Tk_Display(tkwin
), colormap
, name
, &color
) == 0) {
202 Tcl_AppendResult(interp
, "invalid color name \"", name
,
203 "\"", (char *) NULL
);
204 Tcl_DeleteHashEntry(nameHashPtr
);
205 return (XColor
*) NULL
;
207 if (XAllocColor(Tk_Display(tkwin
), colormap
, &color
) == 0) {
213 * Now create a new TkColor structure and add it to nameTable.
216 tkColPtr
= (TkColor
*) ckalloc(sizeof(TkColor
));
217 tkColPtr
->color
= color
;
218 tkColPtr
->magic
= COLOR_MAGIC
;
219 tkColPtr
->screen
= Tk_Screen(tkwin
);
220 tkColPtr
->colormap
= colormap
;
221 tkColPtr
->refCount
= 1;
222 tkColPtr
->tablePtr
= &nameTable
;
223 tkColPtr
->hashPtr
= nameHashPtr
;
224 Tcl_SetHashValue(nameHashPtr
, tkColPtr
);
226 return &tkColPtr
->color
;
230 *----------------------------------------------------------------------
232 * Tk_GetColorByValue --
234 * Given a desired set of red-green-blue intensities for a color,
235 * locate a pixel value to use to draw that color in a given
239 * The return value is a pointer to an XColor structure that
240 * indicates the closest red, blue, and green intensities available
241 * to those specified in colorPtr, and also specifies a pixel
242 * value to use to draw in that color in window "tkwin". If an
243 * error occurs, then NULL is returned and an error message will
244 * be left in interp->result.
247 * The color is added to an internal database with a reference count.
248 * For each call to this procedure, there should eventually be a call
249 * to Tk_FreeColor, so that the database is cleaned up when colors
250 * aren't in use anymore.
252 *----------------------------------------------------------------------
257 Tcl_Interp
*interp
, /* Place to leave error message if
258 * color can't be found. */
259 Tk_Window tkwin
, /* Window in which color will be used. */
260 Colormap colormap
, /* Map from which to allocate color. None
261 * means use default. */
262 XColor
*colorPtr
/* Red, green, and blue fields indicate
267 Tcl_HashEntry
*valueHashPtr
;
276 * First, check to see if there's already a mapping for this color
280 valueKey
.red
= colorPtr
->red
;
281 valueKey
.green
= colorPtr
->green
;
282 valueKey
.blue
= colorPtr
->blue
;
283 if (colormap
== None
) {
284 colormap
= Tk_DefaultColormap(Tk_Screen(tkwin
));
286 valueKey
.colormap
= colormap
;
287 valueKey
.display
= Tk_Display(tkwin
);
288 valueHashPtr
= Tcl_CreateHashEntry(&valueTable
, (char *) &valueKey
, &new);
290 tkColPtr
= (TkColor
*) Tcl_GetHashValue(valueHashPtr
);
291 tkColPtr
->refCount
++;
292 return &tkColPtr
->color
;
296 * The name isn't currently known. Find a pixel value for this
297 * color and add a new structure to valueTable.
300 tkColPtr
= (TkColor
*) ckalloc(sizeof(TkColor
));
301 tkColPtr
->color
.red
= valueKey
.red
;
302 tkColPtr
->color
.green
= valueKey
.green
;
303 tkColPtr
->color
.blue
= valueKey
.blue
;
304 if (XAllocColor(Tk_Display(tkwin
), colormap
, &tkColPtr
->color
) == 0) {
305 sprintf(interp
->result
, "couldn't allocate color");
306 Tcl_DeleteHashEntry(valueHashPtr
);
307 ckfree((char *) tkColPtr
);
308 return (XColor
*) NULL
;
310 tkColPtr
->magic
= COLOR_MAGIC
;
311 tkColPtr
->screen
= Tk_Screen(tkwin
);
312 tkColPtr
->colormap
= colormap
;
313 tkColPtr
->refCount
= 1;
314 tkColPtr
->tablePtr
= &valueTable
;
315 tkColPtr
->hashPtr
= valueHashPtr
;
316 Tcl_SetHashValue(valueHashPtr
, tkColPtr
);
318 return &tkColPtr
->color
;
322 *--------------------------------------------------------------
326 * Given a color, return a textual string identifying
330 * If colorPtr was created by Tk_GetColor, then the return
331 * value is the "string" that was used to create it.
332 * Otherwise the return value is a string that could have
333 * been passed to Tk_GetColor to allocate that color. The
334 * storage for the returned string is only guaranteed to
335 * persist up until the next call to this procedure.
340 *--------------------------------------------------------------
345 XColor
*colorPtr
/* Color whose name is desired. */
348 register TkColor
*tkColPtr
= (TkColor
*) colorPtr
;
349 static char string
[20];
351 if ((tkColPtr
->magic
== COLOR_MAGIC
)
352 && (tkColPtr
->tablePtr
== &nameTable
)) {
353 return ((NameKey
*) tkColPtr
->hashPtr
->key
.words
)->name
;
355 sprintf(string
, "#%4x%4x%4x", colorPtr
->red
, colorPtr
->green
,
361 *----------------------------------------------------------------------
365 * This procedure is called to release a color allocated by
372 * The reference count associated with colorPtr is deleted, and
373 * the color is released to X if there are no remaining uses
376 *----------------------------------------------------------------------
381 XColor
*colorPtr
/* Color to be released. Must have been
382 * allocated by Tk_GetColor or
383 * Tk_GetColorByValue. */
386 register TkColor
*tkColPtr
= (TkColor
*) colorPtr
;
388 Screen
*screen
= tkColPtr
->screen
;
391 * Do a quick sanity check to make sure this color was really
392 * allocated by Tk_GetColor.
395 if (tkColPtr
->magic
!= COLOR_MAGIC
) {
396 panic("Tk_FreeColor called with bogus color");
399 tkColPtr
->refCount
--;
400 if (tkColPtr
->refCount
== 0) {
403 * Careful! Don't free black or white, since this will
404 * make some servers very unhappy.
407 visual
= Tk_DefaultVisual(screen
);
408 if ((visual
->class != StaticGray
) && (visual
->class != StaticColor
)
409 && (tkColPtr
->color
.pixel
!= BlackPixelOfScreen(screen
))
410 && (tkColPtr
->color
.pixel
!= WhitePixelOfScreen(screen
))) {
411 XFreeColors(DisplayOfScreen(screen
), tkColPtr
->colormap
,
412 &tkColPtr
->color
.pixel
, 1, 0L);
414 Tcl_DeleteHashEntry(tkColPtr
->hashPtr
);
416 ckfree((char *) tkColPtr
);
421 *----------------------------------------------------------------------
425 * Initialize the structure used for color management.
433 *----------------------------------------------------------------------
440 Tcl_InitHashTable(&nameTable
, sizeof(NameKey
)/sizeof(int));
441 Tcl_InitHashTable(&valueTable
, sizeof(ValueKey
)/sizeof(int));
442 Tcl_InitHashTable(&screenTable
, TCL_ONE_WORD_KEYS
);
447 Tk_IndexOfScreen(Screen
*screen
)
449 Display
*dpy
= DisplayOfScreen(screen
);
450 int i
, nscreens
= ScreenCount(dpy
);
452 for (i
= 0; i
< nscreens
; i
++) {
453 if (screen
== ScreenOfDisplay(dpy
, i
))
456 return (DefaultScreen(dpy
));
461 Tk_VisInfo(Screen
*screen
)
463 Tcl_HashEntry
*hashPtr
;
465 XVisualInfo vTemplate
;
466 XVisualInfo
*visualList
;
469 int visualsMatched
, scrnum
, new;
475 hashPtr
= Tcl_CreateHashEntry(&screenTable
, (char *) screen
, &new);
477 info
= (VisInfo
*) Tcl_GetHashValue(hashPtr
);
479 info
= (VisInfo
*) ckalloc(sizeof(VisInfo
));
480 info
->screen
= screen
;
482 /* Workaround to support non-default visuals */
484 info
->visual
= XDefaultVisualOfScreen(screen
);
485 info
->depth
= XDefaultDepthOfScreen(screen
);
486 info
->colormap
= XDefaultColormapOfScreen(screen
);
487 info
->gc
= DefaultGCOfScreen(screen
);
489 scrnum
= Tk_IndexOfScreen(screen
);
490 vTemplate
.screen
= scrnum
;
491 vTemplate
.class = TrueColor
;
493 XGetVisualInfo(DisplayOfScreen(screen
),
496 &vTemplate
, &visualsMatched
);
499 if (visualList
!= NULL
) {
501 for (i
= 0; i
< visualsMatched
; i
++) {
502 if (visualList
[i
].depth
> 24)
503 continue; /* Most likely broken */
505 info
->visual
= visualList
[i
].visual
;
506 info
->depth
= visualList
[i
].depth
;
512 if (info
->visual
== NULL
) {
513 info
->visual
= XDefaultVisualOfScreen(screen
);
514 info
->depth
= XDefaultDepthOfScreen(screen
);
515 info
->colormap
= XDefaultColormapOfScreen(screen
);
516 info
->gc
= DefaultGCOfScreen(screen
);
518 if (info
->depth
== 8) {
519 vTemplate
.screen
= scrnum
;
520 vTemplate
.class = PseudoColor
;
522 visualList
= XGetVisualInfo(DisplayOfScreen(screen
),
526 &vTemplate
, &visualsMatched
);
528 if (visualsMatched
> 0) {
529 info
->visual
= visualList
[0].visual
;
530 info
->depth
= visualList
[0].depth
;
536 info
->pixmap
= XCreatePixmap(screen
->display
,
537 RootWindowOfScreen(screen
),
540 if ((TK_CreateColormap
== 0) &&
541 (info
->visual
== DefaultVisualOfScreen(screen
))) {
542 info
->colormap
= DefaultColormapOfScreen(screen
);
543 info
->gc
= DefaultGCOfScreen(screen
);
546 XCreateColormap(screen
->display
,
547 RootWindowOfScreen(screen
),
548 info
->visual
, AllocNone
);
550 XCreateGC(screen
->display
,
551 info
->pixmap
, 0, &values
);
554 XFree((char *)visualList
);
557 Tcl_SetHashValue(hashPtr
, info
);
565 Tk_DefaultDepth(Screen
*screen
)
567 return (Tk_VisInfo(screen
)->depth
);
572 Tk_DefaultVisual(Screen
*screen
)
574 return (Tk_VisInfo(screen
)->visual
);
579 Tk_DefaultColormap(Screen
*screen
)
581 return (Tk_VisInfo(screen
)->colormap
);
586 Tk_DefaultRootWindow(Display
*dpy
)
588 return (DefaultRootWindow(dpy
));
593 Tk_DefaultGC(Screen
*screen
)
595 return (Tk_VisInfo(screen
)->gc
);
600 Tk_DefaultPixmap(Screen
*screen
)
602 return (Tk_VisInfo(screen
)->pixmap
);