Commit | Line | Data |
---|---|---|
6a5fa4e0 MG |
1 | /* |
2 | * tkColor.c -- | |
3 | * | |
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. | |
7 | * | |
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. | |
16 | */ | |
17 | ||
18 | #ifndef lint | |
19 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkColor.c,v 1.15 92/07/14 08:44:49 ouster Exp $ SPRITE (Berkeley)"; | |
20 | #endif /* not lint */ | |
21 | ||
22 | #include "tkconfig.h" | |
23 | #include "tk.h" | |
24 | ||
25 | /* | |
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 | |
34 | * colors. | |
35 | */ | |
36 | ||
37 | /* | |
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. | |
42 | */ | |
43 | ||
44 | #define COLOR_MAGIC 0xc6140277 | |
45 | ||
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 | |
52 | * to delete it. */ | |
53 | Colormap colormap; /* Colormap from which this entry was | |
54 | * allocated. */ | |
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). */ | |
60 | } TkColor; | |
61 | ||
62 | typedef struct VisInfo { | |
63 | Visual *visual; | |
64 | Screen *screen; | |
65 | Colormap colormap; | |
66 | int depth; | |
67 | Pixmap pixmap; | |
68 | GC gc; | |
69 | } VisInfo; | |
70 | ||
71 | /* | |
72 | * Hash table for name -> TkColor mapping, and key structure used to | |
73 | * index into that table: | |
74 | */ | |
75 | ||
76 | static Tcl_HashTable nameTable; | |
77 | typedef struct { | |
78 | Tk_Uid name; /* Name of desired color. */ | |
79 | Colormap colormap; /* Colormap from which color will be | |
80 | * allocated. */ | |
81 | Display *display; /* Display for colormap. */ | |
82 | } NameKey; | |
83 | ||
84 | /* | |
85 | * Hash table for value -> TkColor mapping, and key structure used to | |
86 | * index into that table: | |
87 | */ | |
88 | ||
89 | static Tcl_HashTable valueTable; | |
90 | typedef struct { | |
91 | int red, green, blue; /* Values for desired color. */ | |
92 | Colormap colormap; /* Colormap from which color will be | |
93 | * allocated. */ | |
94 | Display *display; /* Display for colormap. */ | |
95 | } ValueKey; | |
96 | ||
97 | /* | |
98 | * Global colormap creation flag | |
99 | */ | |
100 | char *TK_CreateColormap = 0; | |
101 | ||
102 | /* | |
103 | * Hash table for screen -> VisInfo mapping, and key structure used to | |
104 | * index into that table: | |
105 | */ | |
106 | ||
107 | static Tcl_HashTable screenTable; | |
108 | ||
109 | static int initialized = 0; /* 0 means static structures haven't been | |
110 | * initialized yet. */ | |
111 | ||
112 | /* | |
113 | * Forward declarations for procedures defined in this file: | |
114 | */ | |
115 | ||
116 | static void ColorInit _ANSI_ARGS_((void)); | |
117 | \f | |
118 | /* | |
119 | *---------------------------------------------------------------------- | |
120 | * | |
121 | * Tk_GetColor -- | |
122 | * | |
123 | * Given a string name for a color, map the name to a corresponding | |
124 | * XColor structure. | |
125 | * | |
126 | * Results: | |
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 | |
132 | * interp->result. | |
133 | * | |
134 | * Side effects: | |
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. | |
139 | * | |
140 | *---------------------------------------------------------------------- | |
141 | */ | |
142 | ||
143 | XColor * | |
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). */ | |
152 | { | |
153 | NameKey nameKey; | |
154 | Tcl_HashEntry *nameHashPtr; | |
155 | int new; | |
156 | TkColor *tkColPtr; | |
157 | XColor color; | |
158 | ||
159 | if (!initialized) { | |
160 | ColorInit(); | |
161 | } | |
162 | ||
163 | /* | |
164 | * First, check to see if there's already a mapping for this color | |
165 | * name. | |
166 | */ | |
167 | ||
168 | nameKey.name = name; | |
169 | if (colormap == None) { | |
170 | colormap = Tk_DefaultColormap(Tk_Screen(tkwin)); | |
171 | } | |
172 | nameKey.colormap = colormap; | |
173 | nameKey.display = Tk_Display(tkwin); | |
174 | nameHashPtr = Tcl_CreateHashEntry(&nameTable, (char *) &nameKey, &new); | |
175 | if (!new) { | |
176 | tkColPtr = (TkColor *) Tcl_GetHashValue(nameHashPtr); | |
177 | tkColPtr->refCount++; | |
178 | return &tkColPtr->color; | |
179 | } | |
180 | ||
181 | /* | |
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 | |
185 | * for those names. | |
186 | */ | |
187 | ||
188 | if (*name != '#') { | |
189 | XColor screen; | |
190 | ||
191 | if (XAllocNamedColor(Tk_Display(tkwin), colormap, name, | |
192 | &screen, &color) == 0) { | |
193 | allocFailed: | |
194 | Tcl_AppendResult(interp, "couldn't allocate a color for \"", | |
195 | name, "\"", (char *) NULL); | |
196 | Tcl_DeleteHashEntry(nameHashPtr); | |
197 | return (XColor *) NULL; | |
198 | } | |
199 | } else { | |
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; | |
205 | } | |
206 | if (XAllocColor(Tk_Display(tkwin), colormap, &color) == 0) { | |
207 | goto allocFailed; | |
208 | } | |
209 | } | |
210 | ||
211 | /* | |
212 | * Now create a new TkColor structure and add it to nameTable. | |
213 | */ | |
214 | ||
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); | |
224 | ||
225 | return &tkColPtr->color; | |
226 | } | |
227 | \f | |
228 | /* | |
229 | *---------------------------------------------------------------------- | |
230 | * | |
231 | * Tk_GetColorByValue -- | |
232 | * | |
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 | |
235 | * window. | |
236 | * | |
237 | * Results: | |
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. | |
244 | * | |
245 | * Side effects: | |
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. | |
250 | * | |
251 | *---------------------------------------------------------------------- | |
252 | */ | |
253 | ||
254 | XColor * | |
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 | |
262 | * desired color. */ | |
263 | { | |
264 | ValueKey valueKey; | |
265 | Tcl_HashEntry *valueHashPtr; | |
266 | int new; | |
267 | TkColor *tkColPtr; | |
268 | ||
269 | if (!initialized) { | |
270 | ColorInit(); | |
271 | } | |
272 | ||
273 | /* | |
274 | * First, check to see if there's already a mapping for this color | |
275 | * name. | |
276 | */ | |
277 | ||
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)); | |
283 | } | |
284 | valueKey.colormap = colormap; | |
285 | valueKey.display = Tk_Display(tkwin); | |
286 | valueHashPtr = Tcl_CreateHashEntry(&valueTable, (char *) &valueKey, &new); | |
287 | if (!new) { | |
288 | tkColPtr = (TkColor *) Tcl_GetHashValue(valueHashPtr); | |
289 | tkColPtr->refCount++; | |
290 | return &tkColPtr->color; | |
291 | } | |
292 | ||
293 | /* | |
294 | * The name isn't currently known. Find a pixel value for this | |
295 | * color and add a new structure to valueTable. | |
296 | */ | |
297 | ||
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; | |
307 | } | |
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); | |
315 | ||
316 | return &tkColPtr->color; | |
317 | } | |
318 | \f | |
319 | /* | |
320 | *-------------------------------------------------------------- | |
321 | * | |
322 | * Tk_NameOfColor -- | |
323 | * | |
324 | * Given a color, return a textual string identifying | |
325 | * the color. | |
326 | * | |
327 | * Results: | |
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. | |
334 | * | |
335 | * Side effects: | |
336 | * None. | |
337 | * | |
338 | *-------------------------------------------------------------- | |
339 | */ | |
340 | ||
341 | char * | |
342 | Tk_NameOfColor(colorPtr) | |
343 | XColor *colorPtr; /* Color whose name is desired. */ | |
344 | { | |
345 | register TkColor *tkColPtr = (TkColor *) colorPtr; | |
346 | static char string[20]; | |
347 | ||
348 | if ((tkColPtr->magic == COLOR_MAGIC) | |
349 | && (tkColPtr->tablePtr == &nameTable)) { | |
350 | return ((NameKey *) tkColPtr->hashPtr->key.words)->name; | |
351 | } | |
352 | sprintf(string, "#%4x%4x%4x", colorPtr->red, colorPtr->green, | |
353 | colorPtr->blue); | |
354 | return string; | |
355 | } | |
356 | \f | |
357 | /* | |
358 | *---------------------------------------------------------------------- | |
359 | * | |
360 | * Tk_FreeColor -- | |
361 | * | |
362 | * This procedure is called to release a color allocated by | |
363 | * Tk_GetColor. | |
364 | * | |
365 | * Results: | |
366 | * None. | |
367 | * | |
368 | * Side effects: | |
369 | * The reference count associated with colorPtr is deleted, and | |
370 | * the color is released to X if there are no remaining uses | |
371 | * for it. | |
372 | * | |
373 | *---------------------------------------------------------------------- | |
374 | */ | |
375 | ||
376 | void | |
377 | Tk_FreeColor(colorPtr) | |
378 | XColor *colorPtr; /* Color to be released. Must have been | |
379 | * allocated by Tk_GetColor or | |
380 | * Tk_GetColorByValue. */ | |
381 | { | |
382 | register TkColor *tkColPtr = (TkColor *) colorPtr; | |
383 | Visual *visual; | |
384 | Screen *screen = tkColPtr->screen; | |
385 | ||
386 | /* | |
387 | * Do a quick sanity check to make sure this color was really | |
388 | * allocated by Tk_GetColor. | |
389 | */ | |
390 | ||
391 | if (tkColPtr->magic != COLOR_MAGIC) { | |
392 | panic("Tk_FreeColor called with bogus color"); | |
393 | } | |
394 | ||
395 | tkColPtr->refCount--; | |
396 | if (tkColPtr->refCount == 0) { | |
397 | ||
398 | /* | |
399 | * Careful! Don't free black or white, since this will | |
400 | * make some servers very unhappy. | |
401 | */ | |
402 | ||
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); | |
409 | } | |
410 | Tcl_DeleteHashEntry(tkColPtr->hashPtr); | |
411 | tkColPtr->magic = 0; | |
412 | ckfree((char *) tkColPtr); | |
413 | } | |
414 | } | |
415 | \f | |
416 | /* | |
417 | *---------------------------------------------------------------------- | |
418 | * | |
419 | * ColorInit -- | |
420 | * | |
421 | * Initialize the structure used for color management. | |
422 | * | |
423 | * Results: | |
424 | * None. | |
425 | * | |
426 | * Side effects: | |
427 | * Read the code. | |
428 | * | |
429 | *---------------------------------------------------------------------- | |
430 | */ | |
431 | ||
432 | static void | |
433 | ColorInit() | |
434 | { | |
435 | initialized = 1; | |
436 | Tcl_InitHashTable(&nameTable, sizeof(NameKey)/sizeof(int)); | |
437 | Tcl_InitHashTable(&valueTable, sizeof(ValueKey)/sizeof(int)); | |
438 | Tcl_InitHashTable(&screenTable, TCL_ONE_WORD_KEYS); | |
439 | } | |
440 | ||
441 | ||
442 | int | |
443 | Tk_IndexOfScreen(Screen *screen) | |
444 | { | |
445 | Display *dpy = DisplayOfScreen(screen); | |
446 | int i, nscreens = ScreenCount(dpy); | |
447 | ||
448 | for (i = 0; i < nscreens; i++) { | |
449 | if (screen == ScreenOfDisplay(dpy, i)) | |
450 | return (i); | |
451 | } | |
452 | return (DefaultScreen(dpy)); | |
453 | } | |
454 | ||
455 | ||
456 | VisInfo * | |
457 | Tk_VisInfo(Screen *screen) | |
458 | { | |
459 | Tcl_HashEntry *hashPtr; | |
460 | VisInfo *info; | |
461 | XVisualInfo vTemplate; | |
462 | XVisualInfo *visualList; | |
463 | Visual *visual; | |
464 | XGCValues values; | |
465 | int visualsMatched, scrnum, new; | |
466 | ||
467 | if (!initialized) { | |
468 | ColorInit(); | |
469 | } | |
470 | ||
471 | hashPtr = Tcl_CreateHashEntry(&screenTable, (char *) screen, &new); | |
472 | if (!new) { | |
473 | info = (VisInfo *) Tcl_GetHashValue(hashPtr); | |
474 | } else { | |
475 | info = (VisInfo *) ckalloc(sizeof(VisInfo)); | |
476 | info->screen = screen; | |
477 | ||
478 | /* Workaround to support non-default visuals */ | |
479 | #if 0 | |
480 | info->visual = XDefaultVisualOfScreen(screen); | |
481 | info->depth = XDefaultDepthOfScreen(screen); | |
482 | info->colormap = XDefaultColormapOfScreen(screen); | |
483 | info->gc = DefaultGCOfScreen(screen); | |
484 | #else | |
485 | scrnum = Tk_IndexOfScreen(screen); | |
486 | vTemplate.screen = scrnum; | |
487 | vTemplate.depth = 8; | |
488 | vTemplate.class = PseudoColor; | |
489 | visualList = | |
490 | XGetVisualInfo(DisplayOfScreen(screen), | |
491 | VisualScreenMask | | |
492 | VisualDepthMask | | |
493 | VisualClassMask, | |
494 | &vTemplate, &visualsMatched); | |
495 | if (visualsMatched > 0) { | |
496 | info->visual = visualList[0].visual; | |
497 | info->depth = 8; | |
498 | info->pixmap = XCreatePixmap(screen->display, | |
499 | RootWindowOfScreen(screen), | |
500 | 1, 1, 8); | |
501 | //fprintf(stderr, "TK_CreateColormap %d\n", TK_CreateColormap); | |
502 | if ((TK_CreateColormap == 0) && | |
503 | (info->visual == DefaultVisualOfScreen(screen))) { | |
504 | info->colormap = DefaultColormapOfScreen(screen); | |
505 | info->gc = DefaultGCOfScreen(screen); | |
506 | } else { | |
507 | info->colormap = | |
508 | XCreateColormap(screen->display, | |
509 | RootWindowOfScreen(screen), | |
510 | info->visual, AllocNone); | |
511 | info->gc = | |
512 | XCreateGC(screen->display, | |
513 | info->pixmap, 0, &values); | |
514 | } | |
515 | } else { | |
516 | info->visual = XDefaultVisualOfScreen(screen); | |
517 | info->depth = XDefaultDepthOfScreen(screen); | |
518 | info->pixmap = XCreatePixmap(screen->display, | |
519 | RootWindowOfScreen(screen), | |
520 | 1, 1, info->depth); | |
521 | info->colormap = XDefaultColormapOfScreen(screen); | |
522 | info->gc = DefaultGCOfScreen(screen); | |
523 | } | |
524 | ||
525 | XFree((char *)visualList); | |
526 | #endif | |
527 | ||
528 | Tcl_SetHashValue(hashPtr, info); | |
529 | } | |
530 | ||
531 | return (info); | |
532 | } | |
533 | ||
534 | ||
535 | int | |
536 | Tk_DefaultDepth(Screen *screen) | |
537 | { | |
538 | return (Tk_VisInfo(screen)->depth); | |
539 | } | |
540 | ||
541 | ||
542 | Visual * | |
543 | Tk_DefaultVisual(Screen *screen) | |
544 | { | |
545 | return (Tk_VisInfo(screen)->visual); | |
546 | } | |
547 | ||
548 | ||
549 | Colormap | |
550 | Tk_DefaultColormap(Screen *screen) | |
551 | { | |
552 | return (Tk_VisInfo(screen)->colormap); | |
553 | } | |
554 | ||
555 | ||
556 | Window | |
557 | Tk_DefaultRootWindow(Display *dpy) | |
558 | { | |
559 | return (DefaultRootWindow(dpy)); | |
560 | } | |
561 | ||
562 | ||
563 | GC | |
564 | Tk_DefaultGC(Screen *screen) | |
565 | { | |
566 | return (Tk_VisInfo(screen)->gc); | |
567 | } | |
568 | ||
569 | ||
570 | Pixmap | |
571 | Tk_DefaultPixmap(Screen *screen) | |
572 | { | |
573 | return (Tk_VisInfo(screen)->pixmap); | |
574 | } | |
575 | ||
576 |