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