1 /*
2  * tkColor.c --
3  *
4  *	This file maintains a database of color values for the Tk toolkit, in
5  *	order to avoid round-trips to the server to map color names to pixel
6  *	values.
7  *
8  * Copyright © 1990-1994 The Regents of the University of California.
9  * Copyright © 1994-1997 Sun Microsystems, Inc.
10  *
11  * See the file "license.terms" for information on usage and redistribution of
12  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  */
14 
15 #include "tkInt.h"
16 #include "tkColor.h"
17 
18 /*
19  * Structures of the following following type are used as keys for
20  * colorValueTable (in TkDisplay).
21  */
22 
23 typedef struct {
24     int red, green, blue;	/* Values for desired color. */
25     Colormap colormap;		/* Colormap from which color will be
26 				 * allocated. */
27     Display *display;		/* Display for colormap. */
28 } ValueKey;
29 
30 /*
31  * The structure below is used to allocate thread-local data.
32  */
33 
34 typedef struct {
35     char rgbString[20];		/* */
36 } ThreadSpecificData;
37 static Tcl_ThreadDataKey dataKey;
38 
39 /*
40  * Forward declarations for functions defined in this file:
41  */
42 
43 static void		ColorInit(TkDisplay *dispPtr);
44 static void		DupColorObjProc(Tcl_Obj *srcObjPtr,Tcl_Obj *dupObjPtr);
45 static void		FreeColorObj(Tcl_Obj *objPtr);
46 static void		FreeColorObjProc(Tcl_Obj *objPtr);
47 static void		InitColorObj(Tcl_Obj *objPtr);
48 
49 /*
50  * The following structure defines the implementation of the "color" Tcl
51  * object, which maps a string color name to a TkColor object. The ptr1 field
52  * of the Tcl_Obj points to a TkColor object.
53  */
54 
55 const Tcl_ObjType tkColorObjType = {
56     "color",			/* name */
57     FreeColorObjProc,		/* freeIntRepProc */
58     DupColorObjProc,		/* dupIntRepProc */
59     NULL,			/* updateStringProc */
60     NULL			/* setFromAnyProc */
61 };
62 
63 /*
64  *----------------------------------------------------------------------
65  *
66  * Tk_AllocColorFromObj --
67  *
68  *	Given a Tcl_Obj *, map the value to a corresponding XColor structure
69  *	based on the tkwin given.
70  *
71  * Results:
72  *	The return value is a pointer to an XColor structure that indicates
73  *	the red, blue, and green intensities for the color given by the string
74  *	in objPtr, and also specifies a pixel value to use to draw in that
75  *	color. If an error occurs, NULL is returned and an error message will
76  *	be left in interp's result (unless interp is NULL).
77  *
78  * Side effects:
79  *	The color is added to an internal database with a reference count. For
80  *	each call to this function, there should eventually be a call to
81  *	Tk_FreeColorFromObj so that the database is cleaned up when colors
82  *	aren't in use anymore.
83  *
84  *----------------------------------------------------------------------
85  */
86 
87 XColor *
Tk_AllocColorFromObj(Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * objPtr)88 Tk_AllocColorFromObj(
89     Tcl_Interp *interp,		/* Used only for error reporting. If NULL,
90 				 * then no messages are provided. */
91     Tk_Window tkwin,		/* Window in which the color will be used.*/
92     Tcl_Obj *objPtr)		/* Object that describes the color; string
93 				 * value is a color name such as "red" or
94 				 * "#ff0000".*/
95 {
96     TkColor *tkColPtr;
97 
98     if (objPtr->typePtr != &tkColorObjType) {
99 	InitColorObj(objPtr);
100     }
101     tkColPtr = (TkColor *) objPtr->internalRep.twoPtrValue.ptr1;
102 
103     /*
104      * If the object currently points to a TkColor, see if it's the one we
105      * want. If so, increment its reference count and return.
106      */
107 
108     if (tkColPtr != NULL) {
109 	if (tkColPtr->resourceRefCount == 0) {
110 	    /*
111 	     * This is a stale reference: it refers to a TkColor that's no
112 	     * longer in use. Clear the reference.
113 	     */
114 
115 	    FreeColorObj(objPtr);
116 	    tkColPtr = NULL;
117 	} else if ((Tk_Screen(tkwin) == tkColPtr->screen)
118 		&& (Tk_Colormap(tkwin) == tkColPtr->colormap)) {
119 	    tkColPtr->resourceRefCount++;
120 	    return (XColor *) tkColPtr;
121 	}
122     }
123 
124     /*
125      * The object didn't point to the TkColor that we wanted. Search the list
126      * of TkColors with the same name to see if one of the other TkColors is
127      * the right one.
128      */
129 
130     if (tkColPtr != NULL) {
131 	TkColor *firstColorPtr = (TkColor *)Tcl_GetHashValue(tkColPtr->hashPtr);
132 
133 	FreeColorObj(objPtr);
134 	for (tkColPtr = firstColorPtr; tkColPtr != NULL;
135 		tkColPtr = tkColPtr->nextPtr) {
136 	    if ((Tk_Screen(tkwin) == tkColPtr->screen)
137 		    && (Tk_Colormap(tkwin) == tkColPtr->colormap)) {
138 		tkColPtr->resourceRefCount++;
139 		tkColPtr->objRefCount++;
140 		objPtr->internalRep.twoPtrValue.ptr1 = tkColPtr;
141 		return (XColor *) tkColPtr;
142 	    }
143 	}
144     }
145 
146     /*
147      * Still no luck. Call Tk_GetColor to allocate a new TkColor object.
148      */
149 
150     tkColPtr = (TkColor *) Tk_GetColor(interp, tkwin, Tcl_GetString(objPtr));
151     objPtr->internalRep.twoPtrValue.ptr1 = tkColPtr;
152     if (tkColPtr != NULL) {
153 	tkColPtr->objRefCount++;
154     }
155     return (XColor *) tkColPtr;
156 }
157 
158 /*
159  *----------------------------------------------------------------------
160  *
161  * Tk_GetColor --
162  *
163  *	Given a string name for a color, map the name to a corresponding
164  *	XColor structure.
165  *
166  * Results:
167  *	The return value is a pointer to an XColor structure that indicates
168  *	the red, blue, and green intensities for the color given by "name",
169  *	and also specifies a pixel value to use to draw in that color. If an
170  *	error occurs, NULL is returned and an error message will be left in
171  *	the interp's result.
172  *
173  * Side effects:
174  *	The color is added to an internal database with a reference count. For
175  *	each call to this function, there should eventually be a call to
176  *	Tk_FreeColor so that the database is cleaned up when colors aren't in
177  *	use anymore.
178  *
179  *----------------------------------------------------------------------
180  */
181 
182 XColor *
Tk_GetColor(Tcl_Interp * interp,Tk_Window tkwin,Tk_Uid name)183 Tk_GetColor(
184     Tcl_Interp *interp,		/* Place to leave error message if color can't
185 				 * be found. */
186     Tk_Window tkwin,		/* Window in which color will be used. */
187     Tk_Uid name)		/* Name of color to be allocated (in form
188 				 * suitable for passing to XParseColor). */
189 {
190     Tcl_HashEntry *nameHashPtr;
191     int isNew;
192     TkColor *tkColPtr;
193     TkColor *existingColPtr;
194     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
195 
196     if (!dispPtr->colorInit) {
197 	ColorInit(dispPtr);
198     }
199 
200     /*
201      * First, check to see if there's already a mapping for this color name.
202      */
203 
204     nameHashPtr = Tcl_CreateHashEntry(&dispPtr->colorNameTable, name, &isNew);
205     if (!isNew) {
206 	existingColPtr = (TkColor *)Tcl_GetHashValue(nameHashPtr);
207 	for (tkColPtr = existingColPtr; tkColPtr != NULL;
208 		tkColPtr = tkColPtr->nextPtr) {
209 	    if ((tkColPtr->screen == Tk_Screen(tkwin))
210 		    && (Tk_Colormap(tkwin) == tkColPtr->colormap)) {
211 		tkColPtr->resourceRefCount++;
212 		return &tkColPtr->color;
213 	    }
214 	}
215     } else {
216 	existingColPtr = NULL;
217     }
218 
219     /*
220      * The name isn't currently known. Map from the name to a pixel value.
221      */
222 
223     tkColPtr = TkpGetColor(tkwin, name);
224     if (tkColPtr == NULL) {
225 	if (interp != NULL) {
226 	    if (*name == '#') {
227 		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
228 			"invalid color name \"%s\"", name));
229 		Tcl_SetErrorCode(interp, "TK", "VALUE", "COLOR", NULL);
230 	    } else {
231 		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
232 			"unknown color name \"%s\"", name));
233 		Tcl_SetErrorCode(interp, "TK", "LOOKUP", "COLOR", name, NULL);
234 	    }
235 	}
236 	if (isNew) {
237 	    Tcl_DeleteHashEntry(nameHashPtr);
238 	}
239 	return NULL;
240     }
241 
242     /*
243      * Now create a new TkColor structure and add it to colorNameTable (in
244      * TkDisplay).
245      */
246 
247     tkColPtr->magic = COLOR_MAGIC;
248     tkColPtr->gc = NULL;
249     tkColPtr->screen = Tk_Screen(tkwin);
250     tkColPtr->colormap = Tk_Colormap(tkwin);
251     tkColPtr->visual = Tk_Visual(tkwin);
252     tkColPtr->resourceRefCount = 1;
253     tkColPtr->objRefCount = 0;
254     tkColPtr->type = TK_COLOR_BY_NAME;
255     tkColPtr->hashPtr = nameHashPtr;
256     tkColPtr->nextPtr = existingColPtr;
257     Tcl_SetHashValue(nameHashPtr, tkColPtr);
258 
259     return &tkColPtr->color;
260 }
261 
262 /*
263  *----------------------------------------------------------------------
264  *
265  * Tk_GetColorByValue --
266  *
267  *	Given a desired set of red-green-blue intensities for a color, locate
268  *	a pixel value to use to draw that color in a given window.
269  *
270  * Results:
271  *	The return value is a pointer to an XColor structure that indicates
272  *	the closest red, blue, and green intensities available to those
273  *	specified in colorPtr, and also specifies a pixel value to use to draw
274  *	in that color.
275  *
276  * Side effects:
277  *	The color is added to an internal database with a reference count. For
278  *	each call to this function, there should eventually be a call to
279  *	Tk_FreeColor, so that the database is cleaned up when colors aren't in
280  *	use anymore.
281  *
282  *----------------------------------------------------------------------
283  */
284 
285 XColor *
Tk_GetColorByValue(Tk_Window tkwin,XColor * colorPtr)286 Tk_GetColorByValue(
287     Tk_Window tkwin,		/* Window where color will be used. */
288     XColor *colorPtr)		/* Red, green, and blue fields indicate
289 				 * desired color. */
290 {
291     ValueKey valueKey;
292     Tcl_HashEntry *valueHashPtr;
293     int isNew;
294     TkColor *tkColPtr;
295     Display *display = Tk_Display(tkwin);
296     TkDisplay *dispPtr = TkGetDisplay(display);
297 
298     if (!dispPtr->colorInit) {
299 	ColorInit(dispPtr);
300     }
301 
302     /*
303      * First, check to see if there's already a mapping for this color name.
304      * Must clear the structure first; it's not tightly packed on 64-bit
305      * systems. [Bug 2911570]
306      */
307 
308     memset(&valueKey, 0, sizeof(ValueKey));
309     valueKey.red = colorPtr->red;
310     valueKey.green = colorPtr->green;
311     valueKey.blue = colorPtr->blue;
312     valueKey.colormap = Tk_Colormap(tkwin);
313     valueKey.display = display;
314     valueHashPtr = Tcl_CreateHashEntry(&dispPtr->colorValueTable,
315 	    (char *) &valueKey, &isNew);
316     if (!isNew) {
317 	tkColPtr = (TkColor *)Tcl_GetHashValue(valueHashPtr);
318 	tkColPtr->resourceRefCount++;
319 	return &tkColPtr->color;
320     }
321 
322     /*
323      * The name isn't currently known. Find a pixel value for this color and
324      * add a new structure to colorValueTable (in TkDisplay).
325      */
326 
327     tkColPtr = TkpGetColorByValue(tkwin, colorPtr);
328     tkColPtr->magic = COLOR_MAGIC;
329     tkColPtr->gc = NULL;
330     tkColPtr->screen = Tk_Screen(tkwin);
331     tkColPtr->colormap = valueKey.colormap;
332     tkColPtr->visual = Tk_Visual(tkwin);
333     tkColPtr->resourceRefCount = 1;
334     tkColPtr->objRefCount = 0;
335     tkColPtr->type = TK_COLOR_BY_VALUE;
336     tkColPtr->hashPtr = valueHashPtr;
337     tkColPtr->nextPtr = NULL;
338     Tcl_SetHashValue(valueHashPtr, tkColPtr);
339     return &tkColPtr->color;
340 }
341 
342 /*
343  *--------------------------------------------------------------
344  *
345  * Tk_NameOfColor --
346  *
347  *	Given a color, return a textual string identifying the color.
348  *
349  * Results:
350  *	If colorPtr was created by Tk_GetColor, then the return value is the
351  *	"string" that was used to create it. Otherwise the return value is a
352  *	string that could have been passed to Tk_GetColor to allocate that
353  *	color. The storage for the returned string is only guaranteed to
354  *	persist up until the next call to this function.
355  *
356  * Side effects:
357  *	None.
358  *
359  *--------------------------------------------------------------
360  */
361 
362 const char *
Tk_NameOfColor(XColor * colorPtr)363 Tk_NameOfColor(
364     XColor *colorPtr)		/* Color whose name is desired. */
365 {
366     TkColor *tkColPtr = (TkColor *) colorPtr;
367 
368     if (tkColPtr->magic==COLOR_MAGIC && tkColPtr->type==TK_COLOR_BY_NAME) {
369 	return tkColPtr->hashPtr->key.string;
370     } else {
371 	ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
372 		Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
373 
374 	sprintf(tsdPtr->rgbString, "#%04x%04x%04x", colorPtr->red,
375 		colorPtr->green, colorPtr->blue);
376 
377 	/*
378 	 * If the string has the form #RSRSTUTUVWVW (where equal letters
379 	 * denote equal hexdigits) then this is equivalent to #RSTUVW. Then
380 	 * output the shorter form.
381 	 */
382 
383 	if ((tsdPtr->rgbString[1] == tsdPtr->rgbString[3])
384 		&& (tsdPtr->rgbString[2] == tsdPtr->rgbString[4])
385 		&& (tsdPtr->rgbString[5] == tsdPtr->rgbString[7])
386 		&& (tsdPtr->rgbString[6] == tsdPtr->rgbString[8])
387 		&& (tsdPtr->rgbString[9] == tsdPtr->rgbString[11])
388 		&& (tsdPtr->rgbString[10] == tsdPtr->rgbString[12])) {
389 	    tsdPtr->rgbString[3] = tsdPtr->rgbString[5];
390 	    tsdPtr->rgbString[4] = tsdPtr->rgbString[6];
391 	    tsdPtr->rgbString[5] = tsdPtr->rgbString[9];
392 	    tsdPtr->rgbString[6] = tsdPtr->rgbString[10];
393 	    tsdPtr->rgbString[7] = '\0';
394 	}
395 	return tsdPtr->rgbString;
396     }
397 }
398 
399 /*
400  *----------------------------------------------------------------------
401  *
402  * Tk_GCForColor --
403  *
404  *	Given a color allocated from this module, this function returns a GC
405  *	that can be used for simple drawing with that color.
406  *
407  * Results:
408  *	The return value is a GC with color set as its foreground color and
409  *	all other fields defaulted. This GC is only valid as long as the color
410  *	exists; it is freed automatically when the last reference to the color
411  *	is freed.
412  *
413  * Side effects:
414  *	None.
415  *
416  *----------------------------------------------------------------------
417  */
418 
419 GC
Tk_GCForColor(XColor * colorPtr,Drawable drawable)420 Tk_GCForColor(
421     XColor *colorPtr,		/* Color for which a GC is desired. Must have
422 				 * been allocated by Tk_GetColor. */
423     Drawable drawable)		/* Drawable in which the color will be used
424 				 * (must have same screen and depth as the one
425 				 * for which the color was allocated). */
426 {
427     TkColor *tkColPtr = (TkColor *) colorPtr;
428     XGCValues gcValues;
429 
430     /*
431      * Do a quick sanity check to make sure this color was really allocated by
432      * Tk_GetColor.
433      */
434 
435     if (tkColPtr->magic != COLOR_MAGIC) {
436 	Tcl_Panic("Tk_GCForColor called with bogus color");
437     }
438 
439     if (tkColPtr->gc == NULL) {
440 	gcValues.foreground = tkColPtr->color.pixel;
441 	tkColPtr->gc = XCreateGC(DisplayOfScreen(tkColPtr->screen), drawable,
442 		GCForeground, &gcValues);
443     }
444     return tkColPtr->gc;
445 }
446 
447 /*
448  *----------------------------------------------------------------------
449  *
450  * Tk_FreeColor --
451  *
452  *	This function is called to release a color allocated by Tk_GetColor.
453  *
454  * Results:
455  *	None.
456  *
457  * Side effects:
458  *	The reference count associated with colorPtr is deleted, and the color
459  *	is released to X if there are no remaining uses for it.
460  *
461  *----------------------------------------------------------------------
462  */
463 
464 void
Tk_FreeColor(XColor * colorPtr)465 Tk_FreeColor(
466     XColor *colorPtr)		/* Color to be released. Must have been
467 				 * allocated by Tk_GetColor or
468 				 * Tk_GetColorByValue. */
469 {
470     TkColor *tkColPtr = (TkColor *) colorPtr;
471     Screen *screen = tkColPtr->screen;
472     TkColor *prevPtr;
473 
474     /*
475      * Do a quick sanity check to make sure this color was really allocated by
476      * Tk_GetColor.
477      */
478 
479     if (tkColPtr->magic != COLOR_MAGIC) {
480 	Tcl_Panic("Tk_FreeColor called with bogus color");
481     }
482 
483     if (tkColPtr->resourceRefCount-- > 1) {
484 	return;
485     }
486 
487     /*
488      * This color is no longer being actively used, so free the color
489      * resources associated with it and remove it from the hash table. No
490      * longer any objects referencing it.
491      */
492 
493     if (tkColPtr->gc != NULL) {
494 	XFreeGC(DisplayOfScreen(screen), tkColPtr->gc);
495 	tkColPtr->gc = NULL;
496     }
497     TkpFreeColor(tkColPtr);
498 
499     prevPtr = (TkColor *)Tcl_GetHashValue(tkColPtr->hashPtr);
500     if (prevPtr == tkColPtr) {
501 	if (tkColPtr->nextPtr == NULL) {
502 	    Tcl_DeleteHashEntry(tkColPtr->hashPtr);
503 	} else {
504 	    Tcl_SetHashValue(tkColPtr->hashPtr, tkColPtr->nextPtr);
505 	}
506     } else {
507 	while (prevPtr->nextPtr != tkColPtr) {
508 	    prevPtr = prevPtr->nextPtr;
509 	}
510 	prevPtr->nextPtr = tkColPtr->nextPtr;
511     }
512 
513     /*
514      * Free the TkColor structure if there are no objects referencing it.
515      * However, if there are objects referencing it then keep the structure
516      * around; it will get freed when the last reference is cleared
517      */
518 
519     if (tkColPtr->objRefCount == 0) {
520 	ckfree(tkColPtr);
521     }
522 }
523 
524 /*
525  *----------------------------------------------------------------------
526  *
527  * Tk_FreeColorFromObj --
528  *
529  *	This function is called to release a color allocated by
530  *	Tk_AllocColorFromObj. It does not throw away the Tcl_Obj *; it only
531  *	gets rid of the hash table entry for this color and clears the cached
532  *	value that is normally stored in the object.
533  *
534  * Results:
535  *	None.
536  *
537  * Side effects:
538  *	The reference count associated with the color represented by objPtr is
539  *	decremented, and the color is released to X if there are no remaining
540  *	uses for it.
541  *
542  *----------------------------------------------------------------------
543  */
544 
545 void
Tk_FreeColorFromObj(Tk_Window tkwin,Tcl_Obj * objPtr)546 Tk_FreeColorFromObj(
547     Tk_Window tkwin,		/* The window this color lives in. Needed for
548 				 * the screen and colormap values. */
549     Tcl_Obj *objPtr)		/* The Tcl_Obj * to be freed. */
550 {
551     Tk_FreeColor(Tk_GetColorFromObj(tkwin, objPtr));
552     FreeColorObj(objPtr);
553 }
554 
555 /*
556  *---------------------------------------------------------------------------
557  *
558  * FreeColorObjProc, FreeColorObj --
559  *
560  *	This proc is called to release an object reference to a color. Called
561  *	when the object's internal rep is released or when the cached tkColPtr
562  *	needs to be changed.
563  *
564  * Results:
565  *	None.
566  *
567  * Side effects:
568  *	The object reference count is decremented. When both it and the hash
569  *	ref count go to zero, the color's resources are released.
570  *
571  *---------------------------------------------------------------------------
572  */
573 
574 static void
FreeColorObjProc(Tcl_Obj * objPtr)575 FreeColorObjProc(
576     Tcl_Obj *objPtr)		/* The object we are releasing. */
577 {
578     FreeColorObj(objPtr);
579     objPtr->typePtr = NULL;
580 }
581 
582 static void
FreeColorObj(Tcl_Obj * objPtr)583 FreeColorObj(
584     Tcl_Obj *objPtr)		/* The object we are releasing. */
585 {
586     TkColor *tkColPtr = (TkColor *)objPtr->internalRep.twoPtrValue.ptr1;
587 
588     if (tkColPtr != NULL) {
589 	if ((tkColPtr->objRefCount-- <= 1)
590 		&& (tkColPtr->resourceRefCount == 0)) {
591 	    ckfree(tkColPtr);
592 	}
593 	objPtr->internalRep.twoPtrValue.ptr1 = NULL;
594     }
595 }
596 
597 /*
598  *---------------------------------------------------------------------------
599  *
600  * DupColorObjProc --
601  *
602  *	When a cached color object is duplicated, this is called to update the
603  *	internal reps.
604  *
605  * Results:
606  *	None.
607  *
608  * Side effects:
609  *	The color's objRefCount is incremented and the internal rep of the
610  *	copy is set to point to it.
611  *
612  *---------------------------------------------------------------------------
613  */
614 
615 static void
DupColorObjProc(Tcl_Obj * srcObjPtr,Tcl_Obj * dupObjPtr)616 DupColorObjProc(
617     Tcl_Obj *srcObjPtr,		/* The object we are copying from. */
618     Tcl_Obj *dupObjPtr)		/* The object we are copying to. */
619 {
620     TkColor *tkColPtr = (TkColor *)srcObjPtr->internalRep.twoPtrValue.ptr1;
621 
622     dupObjPtr->typePtr = srcObjPtr->typePtr;
623     dupObjPtr->internalRep.twoPtrValue.ptr1 = tkColPtr;
624 
625     if (tkColPtr != NULL) {
626 	tkColPtr->objRefCount++;
627     }
628 }
629 
630 /*
631  *----------------------------------------------------------------------
632  *
633  * Tk_GetColorFromObj --
634  *
635  *	Returns the color referred to by a Tcl object. The color must already
636  *	have been allocated via a call to Tk_AllocColorFromObj or Tk_GetColor.
637  *
638  * Results:
639  *	Returns the XColor * that matches the tkwin and the string rep of
640  *	objPtr.
641  *
642  * Side effects:
643  *	If the object is not already a color, the conversion will free any old
644  *	internal representation.
645  *
646  *----------------------------------------------------------------------
647  */
648 
649 XColor *
Tk_GetColorFromObj(Tk_Window tkwin,Tcl_Obj * objPtr)650 Tk_GetColorFromObj(
651     Tk_Window tkwin,		/* The window in which the color will be
652 				 * used. */
653     Tcl_Obj *objPtr)		/* String value contains the name of the
654 				 * desired color. */
655 {
656     TkColor *tkColPtr;
657     Tcl_HashEntry *hashPtr;
658     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
659 
660     if (objPtr->typePtr != &tkColorObjType) {
661 	InitColorObj(objPtr);
662     }
663 
664     /*
665      * First check to see if the internal representation of the object is
666      * defined and is a color that is valid for the current screen and color
667      * map. If it is, we are done.
668      */
669 
670     tkColPtr = (TkColor *)objPtr->internalRep.twoPtrValue.ptr1;
671     if ((tkColPtr != NULL)
672 	    && (tkColPtr->resourceRefCount > 0)
673 	    && (Tk_Screen(tkwin) == tkColPtr->screen)
674 	    && (Tk_Colormap(tkwin) == tkColPtr->colormap)) {
675 	/*
676 	 * The object already points to the right TkColor structure. Just
677 	 * return it.
678 	 */
679 
680 	return (XColor *) tkColPtr;
681     }
682 
683     /*
684      * If we reach this point, it means that the TkColor structure that we
685      * have cached in the internal representation is not valid for the current
686      * screen and colormap. But there is a list of other TkColor structures
687      * attached to the TkDisplay. Walk this list looking for the right TkColor
688      * structure.
689      */
690 
691     hashPtr = Tcl_FindHashEntry(&dispPtr->colorNameTable,
692 	    Tcl_GetString(objPtr));
693     if (hashPtr == NULL) {
694 	goto error;
695     }
696     for (tkColPtr = (TkColor *)Tcl_GetHashValue(hashPtr);
697 	    (tkColPtr != NULL); tkColPtr = tkColPtr->nextPtr) {
698 	if ((Tk_Screen(tkwin) == tkColPtr->screen)
699 		&& (Tk_Colormap(tkwin) == tkColPtr->colormap)) {
700 	    FreeColorObj(objPtr);
701 	    objPtr->internalRep.twoPtrValue.ptr1 = tkColPtr;
702 	    tkColPtr->objRefCount++;
703 	    return (XColor *) tkColPtr;
704 	}
705     }
706 
707   error:
708     Tcl_Panic("Tk_GetColorFromObj called with non-existent color!");
709     /*
710      * The following code isn't reached; it's just there to please compilers.
711      */
712     return NULL;
713 }
714 
715 /*
716  *----------------------------------------------------------------------
717  *
718  * InitColorObj --
719  *
720  *	Bookeeping function to change an objPtr to a color type.
721  *
722  * Results:
723  *	None.
724  *
725  * Side effects:
726  *	The old internal rep of the object is freed. The object's type is set
727  *	to color with a NULL TkColor pointer (the pointer will be set later by
728  *	either Tk_AllocColorFromObj or Tk_GetColorFromObj).
729  *
730  *----------------------------------------------------------------------
731  */
732 
733 static void
InitColorObj(Tcl_Obj * objPtr)734 InitColorObj(
735     Tcl_Obj *objPtr)		/* The object to convert. */
736 {
737     const Tcl_ObjType *typePtr;
738 
739     /*
740      * Free the old internalRep before setting the new one.
741      */
742 
743     Tcl_GetString(objPtr);
744     typePtr = objPtr->typePtr;
745     if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
746 	typePtr->freeIntRepProc(objPtr);
747     }
748     objPtr->typePtr = &tkColorObjType;
749     objPtr->internalRep.twoPtrValue.ptr1 = NULL;
750 }
751 
752 /*
753  *----------------------------------------------------------------------
754  *
755  * ColorInit --
756  *
757  *	Initialize the structure used for color management.
758  *
759  * Results:
760  *	None.
761  *
762  * Side effects:
763  *	Read the code.
764  *
765  *----------------------------------------------------------------------
766  */
767 
768 static void
ColorInit(TkDisplay * dispPtr)769 ColorInit(
770     TkDisplay *dispPtr)
771 {
772     if (!dispPtr->colorInit) {
773 	dispPtr->colorInit = 1;
774 	Tcl_InitHashTable(&dispPtr->colorNameTable, TCL_STRING_KEYS);
775 	Tcl_InitHashTable(&dispPtr->colorValueTable,
776 		sizeof(ValueKey)/sizeof(int));
777     }
778 }
779 
780 /*
781  *----------------------------------------------------------------------
782  *
783  * TkDebugColor --
784  *
785  *	This function returns debugging information about a color.
786  *
787  * Results:
788  *	The return value is a list with one sublist for each TkColor
789  *	corresponding to "name". Each sublist has two elements that contain
790  *	the resourceRefCount and objRefCount fields from the TkColor
791  *	structure.
792  *
793  * Side effects:
794  *	None.
795  *
796  *----------------------------------------------------------------------
797  */
798 
799 Tcl_Obj *
TkDebugColor(Tk_Window tkwin,const char * name)800 TkDebugColor(
801     Tk_Window tkwin,		/* The window in which the color will be used
802 				 * (not currently used). */
803     const char *name)		/* Name of the desired color. */
804 {
805     Tcl_HashEntry *hashPtr;
806     Tcl_Obj *resultPtr;
807     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
808 
809     resultPtr = Tcl_NewObj();
810     hashPtr = Tcl_FindHashEntry(&dispPtr->colorNameTable, name);
811     if (hashPtr != NULL) {
812 	TkColor *tkColPtr = (TkColor *)Tcl_GetHashValue(hashPtr);
813 
814 	if (tkColPtr == NULL) {
815 	    Tcl_Panic("TkDebugColor found empty hash table entry");
816 	}
817 	for ( ; (tkColPtr != NULL); tkColPtr = tkColPtr->nextPtr) {
818 	    Tcl_Obj *objPtr = Tcl_NewObj();
819 
820 	    Tcl_ListObjAppendElement(NULL, objPtr,
821 		    Tcl_NewWideIntObj(tkColPtr->resourceRefCount));
822 	    Tcl_ListObjAppendElement(NULL, objPtr,
823 		    Tcl_NewWideIntObj(tkColPtr->objRefCount));
824 	    Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
825 	}
826     }
827     return resultPtr;
828 }
829 
830 #ifndef _WIN32
831 
832 /* This function is not necessary for Win32,
833  * since XParseColor already does the right thing */
834 
835 #undef XParseColor
836 
837 const char *const tkWebColors[20] = {
838     /* 'a' */ "qua\0#0000ffffffff",
839     /* 'b' */ NULL,
840     /* 'c' */ "rimson\0#dcdc14143c3c",
841     /* 'd' */ NULL,
842     /* 'e' */ NULL,
843     /* 'f' */ "uchsia\0#ffff0000ffff",
844     /* 'g' */ "reen\0#000080800000",
845     /* 'h' */ NULL,
846     /* 'i' */ "ndigo\0#4b4b00008282",
847     /* 'j' */ NULL,
848     /* 'k' */ NULL,
849     /* 'l' */ "ime\0#0000ffff0000",
850     /* 'm' */ "aroon\0#808000000000",
851     /* 'n' */ NULL,
852     /* 'o' */ "live\0#808080800000",
853     /* 'p' */ "urple\0#808000008080",
854     /* 'q' */ NULL,
855     /* 'r' */ NULL,
856     /* 's' */ "ilver\0#c0c0c0c0c0c0",
857     /* 't' */ "eal\0#000080808080"
858 };
859 
860 Status
TkParseColor(Display * display,Colormap map,const char * name,XColor * color)861 TkParseColor(
862     Display *display,		/* The display */
863     Colormap map,			/* Color map */
864     const char *name,     /* String to be parsed */
865     XColor *color)
866 {
867     char buf[14];
868     if (*name == '#') {
869 	buf[0] = '#'; buf[13] = '\0';
870 	if (!*(++name) || !*(++name) || !*(++name)) {
871 	    /* Not at least 3 hex digits, so invalid */
872 	return 0;
873 	} else if (!*(++name)) {
874 	    /* Exactly 3 hex digits */
875 	    buf[9] = buf[10] = buf[11] = buf[12] = *(--name);
876 	    buf[5] = buf[6] = buf[7] = buf[8] = *(--name);
877 	    buf[1] = buf[2] = buf[3] = buf[4] = *(--name);
878 	    name = buf;
879 	} else if (!*(++name)	|| !*(++name)) {
880 	    /* Not at least 6 hex digits, so invalid */
881 	    return 0;
882 	} else if (!*(++name)) {
883 	    /* Exactly 6 hex digits */
884 	    buf[10] = buf[12] = *(--name);
885 	    buf[9] = buf[11] = *(--name);
886 	    buf[6] = buf[8] = *(--name);
887 	    buf[5] = buf[7] = *(--name);
888 	    buf[2] = buf[4] = *(--name);
889 	    buf[1] = buf[3] = *(--name);
890 	    name = buf;
891 	} else if (!*(++name) || !*(++name)) {
892 	    /* Not at least 9 hex digits, so invalid */
893 	    return 0;
894 	} else if (!*(++name)) {
895 	    /* Exactly 9 hex digits */
896 	    buf[11] = *(--name);
897 	    buf[10] = *(--name);
898 	    buf[9] = buf[12] = *(--name);
899 	    buf[7] = *(--name);
900 	    buf[6] = *(--name);
901 	    buf[5] = buf[8] = *(--name);
902 	    buf[3] = *(--name);
903 	    buf[2] = *(--name);
904 	    buf[1] = buf[4] = *(--name);
905 	    name = buf;
906 	} else if (!*(++name) || !*(++name) || *(++name)) {
907 	    /* Not exactly 12 hex digits, so invalid */
908 	    return 0;
909 	} else {
910 	    name -= 13;
911 	}
912 	goto done;
913     } else if (((*name - 'A') & 0xdf) < sizeof(tkWebColors)/sizeof(tkWebColors[0])) {
914 	if (!((name[0] - 'G') & 0xdf) && !((name[1] - 'R') & 0xdf)
915 		&& !((name[2] - 'A') & 0xdb) && !((name[3] - 'Y') & 0xdf)
916 		&& !name[4]) {
917 	    name = "#808080808080";
918 	    goto done;
919 	} else {
920 	    const char *p = tkWebColors[((*name - 'A') & 0x1f)];
921 	    if (p) {
922 		const char *q = name;
923 		while (!((*p - *(++q)) & 0xdf)) {
924 		    if (!*p++) {
925 			name = p;
926 			goto done;
927 		    }
928 		}
929 	    }
930 	}
931     }
932     if (strlen(name) > 99) {
933 	return 0;
934     }
935 done:
936     return XParseColor(display, map, name, color);
937 }
938 #endif /* _WIN32 */
939 /*
940  * Local Variables:
941  * mode: c
942  * c-basic-offset: 4
943  * fill-column: 78
944  * End:
945  */
946