1/*
2 * tkVisual.c --
3 *
4 *	This file contains library procedures for allocating and freeing
5 *	visuals and colormaps. This code is based on a prototype
6 *	implementation by Paul Mackerras.
7 *
8 * Copyright (c) 1994 The Regents of the University of California.
9 * Copyright (c) 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 * RCS: @(#) $Id$
15 */
16
17#include "tkInt.h"
18
19/*
20 * The table below maps from symbolic names for visual classes to the
21 * associated X class symbols.
22 */
23
24typedef struct VisualDictionary {
25    char *name;			/* Textual name of class. */
26    int minLength;		/* Minimum # characters that must be specified
27				 * for an unambiguous match. */
28    int class;			/* X symbol for class. */
29} VisualDictionary;
30static VisualDictionary visualNames[] = {
31    {"best",		1,	0},
32    {"directcolor",	2,	DirectColor},
33    {"grayscale",	1,	GrayScale},
34    {"greyscale",	1,	GrayScale},
35    {"pseudocolor",	1,	PseudoColor},
36    {"staticcolor",	7,	StaticColor},
37    {"staticgray",	7,	StaticGray},
38    {"staticgrey",	7,	StaticGray},
39    {"truecolor",	1,	TrueColor},
40    {NULL,		0,	0},
41};
42
43/*
44 * One of the following structures exists for each distinct non-default
45 * colormap allocated for a display by Tk_GetColormap.
46 */
47
48struct TkColormap {
49    Colormap colormap;		/* X's identifier for the colormap. */
50    Visual *visual;		/* Visual for which colormap was allocated. */
51    int refCount;		/* How many uses of the colormap are still
52				 * outstanding (calls to Tk_GetColormap minus
53				 * calls to Tk_FreeColormap). */
54    int shareable;		/* 0 means this colormap was allocated by a
55				 * call to Tk_GetColormap with "new", implying
56				 * that the window wants it all for itself.  1
57				 * means that the colormap was allocated as a
58				 * default for a particular visual, so it can
59				 * be shared. */
60    struct TkColormap *nextPtr;	/* Next in list of colormaps for this display,
61				 * or NULL for end of list. */
62};
63
64/*
65 *----------------------------------------------------------------------
66 *
67 * Tk_GetVisual --
68 *
69 *	Given a string identifying a particular kind of visual, this procedure
70 *	returns a visual and depth that matches the specification.
71 *
72 * Results:
73 *	The return value is normally a pointer to a visual. If an error
74 *	occurred in looking up the visual, NULL is returned and an error
75 *	message is left in the interp's result. The depth of the visual is
76 *	returned to *depthPtr under normal returns. If colormapPtr is
77 *	non-NULL, then this procedure also finds a suitable colormap for use
78 *	with the visual in tkwin, and it returns that colormap in *colormapPtr
79 *	unless an error occurs.
80 *
81 * Side effects:
82 *	A new colormap may be allocated.
83 *
84 *----------------------------------------------------------------------
85 */
86
87Visual *
88Tk_GetVisual(
89    Tcl_Interp *interp,		/* Interpreter to use for error reporting. */
90    Tk_Window tkwin,		/* Window in which visual will be used. */
91    CONST char *string,		/* String describing visual. See manual entry
92				 * for details. */
93    int *depthPtr,		/* The depth of the returned visual is stored
94				 * here. */
95    Colormap *colormapPtr)	/* If non-NULL, then a suitable colormap for
96				 * visual is placed here. This colormap must
97				 * eventually be freed by calling
98				 * Tk_FreeColormap. */
99{
100    Tk_Window tkwin2;
101    XVisualInfo template, *visInfoList, *bestPtr;
102    long mask;
103    Visual *visual;
104    ptrdiff_t length;
105    int c, numVisuals, prio, bestPrio, i;
106    CONST char *p;
107    VisualDictionary *dictPtr;
108    TkColormap *cmapPtr;
109    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
110
111    /*
112     * Parse string and set up a template for use in searching for an
113     * appropriate visual.
114     */
115
116    c = string[0];
117    if (c == '.') {
118	/*
119	 * The string must be a window name. If the window is on the same
120	 * screen as tkwin, then just use its visual. Otherwise use the
121	 * information about the visual as a template for the search.
122	 */
123
124	tkwin2 = Tk_NameToWindow(interp, string, tkwin);
125	if (tkwin2 == NULL) {
126	    return NULL;
127	}
128	visual = Tk_Visual(tkwin2);
129	if (Tk_Screen(tkwin) == Tk_Screen(tkwin2)) {
130	    *depthPtr = Tk_Depth(tkwin2);
131	    if (colormapPtr != NULL) {
132		/*
133		 * Use the colormap from the other window too (but be sure to
134		 * increment its reference count if it's one of the ones
135		 * allocated here).
136		 */
137
138		*colormapPtr = Tk_Colormap(tkwin2);
139		for (cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL;
140			cmapPtr = cmapPtr->nextPtr) {
141		    if (cmapPtr->colormap == *colormapPtr) {
142			cmapPtr->refCount += 1;
143			break;
144		    }
145		}
146	    }
147	    return visual;
148	}
149	template.depth = Tk_Depth(tkwin2);
150	template.class = visual->class;
151	template.red_mask = visual->red_mask;
152	template.green_mask = visual->green_mask;
153	template.blue_mask = visual->blue_mask;
154	template.colormap_size = visual->map_entries;
155	template.bits_per_rgb = visual->bits_per_rgb;
156	mask = VisualDepthMask|VisualClassMask|VisualRedMaskMask
157		|VisualGreenMaskMask|VisualBlueMaskMask|VisualColormapSizeMask
158		|VisualBitsPerRGBMask;
159    } else if ((c == 0) || ((c == 'd') && (string[1] != 0)
160	    && (strncmp(string, "default", strlen(string)) == 0))) {
161	/*
162	 * Use the default visual for the window's screen.
163	 */
164
165	if (colormapPtr != NULL) {
166	    *colormapPtr = DefaultColormapOfScreen(Tk_Screen(tkwin));
167	}
168	*depthPtr = DefaultDepthOfScreen(Tk_Screen(tkwin));
169	return DefaultVisualOfScreen(Tk_Screen(tkwin));
170    } else if (isdigit(UCHAR(c))) {
171	int visualId;
172
173	/*
174	 * This is a visual ID.
175	 */
176
177	if (Tcl_GetInt(interp, string, &visualId) == TCL_ERROR) {
178	    Tcl_ResetResult(interp);
179	    Tcl_AppendResult(interp, "bad X identifier for visual: \"",
180		    string, "\"", NULL);
181	    return NULL;
182	}
183	template.visualid = visualId;
184	mask = VisualIDMask;
185    } else {
186	/*
187	 * Parse the string into a class name (or "best") optionally followed
188	 * by whitespace and a depth.
189	 */
190
191	for (p = string; *p != 0; p++) {
192	    if (isspace(UCHAR(*p)) || isdigit(UCHAR(*p))) {
193		break;
194	    }
195	}
196	length = p - string;
197	template.class = -1;
198	for (dictPtr = visualNames; dictPtr->name != NULL; dictPtr++) {
199	    if ((dictPtr->name[0] == c) && (length >= dictPtr->minLength)
200		    && (strncmp(string, dictPtr->name,
201		    (size_t) length) == 0)) {
202		template.class = dictPtr->class;
203		break;
204	    }
205	}
206	if (template.class == -1) {
207	    Tcl_AppendResult(interp, "unknown or ambiguous visual name \"",
208		    string, "\": class must be ", NULL);
209	    for (dictPtr = visualNames; dictPtr->name != NULL; dictPtr++) {
210		Tcl_AppendResult(interp, dictPtr->name, ", ", NULL);
211	    }
212	    Tcl_AppendResult(interp, "or default", NULL);
213	    return NULL;
214	}
215	while (isspace(UCHAR(*p))) {
216	    p++;
217	}
218	if (*p == 0) {
219	    template.depth = 10000;
220	} else {
221	    if (Tcl_GetInt(interp, p, &template.depth) != TCL_OK) {
222		return NULL;
223	    }
224	}
225	if (c == 'b') {
226	    mask = 0;
227	} else {
228	    mask = VisualClassMask;
229	}
230    }
231
232    /*
233     * Find all visuals that match the template we've just created, and return
234     * an error if there are none that match.
235     */
236
237    template.screen = Tk_ScreenNumber(tkwin);
238    mask |= VisualScreenMask;
239    visInfoList = XGetVisualInfo(Tk_Display(tkwin), mask, &template,
240	    &numVisuals);
241    if (visInfoList == NULL) {
242	Tcl_SetResult(interp, "couldn't find an appropriate visual",
243		TCL_STATIC);
244	return NULL;
245    }
246
247    /*
248     * Search through the visuals that were returned to find the best one.
249     * The choice is based on the following criteria, in decreasing order of
250     * importance:
251     *
252     * 1. Depth: choose a visual with exactly the desired depth, else one with
253     *	  more bits than requested but as few bits as possible, else one with
254     *	  fewer bits but as many as possible.
255     * 2. Class: some visual classes are more desirable than others; pick the
256     *    visual with the most desirable class.
257     * 3. Default: the default visual for the screen gets preference over
258     *    other visuals, all else being equal.
259     */
260
261    bestPrio = 0;
262    bestPtr = NULL;
263    for (i = 0; i < numVisuals; i++) {
264	switch (visInfoList[i].class) {
265	case DirectColor:
266	    prio = 5; break;
267	case GrayScale:
268	    prio = 1; break;
269	case PseudoColor:
270	    prio = 7; break;
271	case StaticColor:
272	    prio = 3; break;
273	case StaticGray:
274	    prio = 1; break;
275	case TrueColor:
276	    prio = 5; break;
277	default:
278	    prio = 0; break;
279	}
280	if (visInfoList[i].visual
281		== DefaultVisualOfScreen(Tk_Screen(tkwin))) {
282	    prio++;
283	}
284	if (bestPtr == NULL) {
285	    goto newBest;
286	}
287	if (visInfoList[i].depth < bestPtr->depth) {
288	    if (visInfoList[i].depth >= template.depth) {
289		goto newBest;
290	    }
291	} else if (visInfoList[i].depth > bestPtr->depth) {
292	    if (bestPtr->depth < template.depth) {
293		goto newBest;
294	    }
295	} else {
296	    if (prio > bestPrio) {
297		goto newBest;
298	    }
299	}
300	continue;
301
302    newBest:
303	bestPtr = &visInfoList[i];
304	bestPrio = prio;
305    }
306    *depthPtr = bestPtr->depth;
307    visual = bestPtr->visual;
308    XFree((char *) visInfoList);
309
310    /*
311     * If we need to find a colormap for this visual, do it now. If the visual
312     * is the default visual for the screen, then use the default colormap.
313     * Otherwise search for an existing colormap that's shareable. If all else
314     * fails, create a new colormap.
315     */
316
317    if (colormapPtr != NULL) {
318	if (visual == DefaultVisualOfScreen(Tk_Screen(tkwin))) {
319	    *colormapPtr = DefaultColormapOfScreen(Tk_Screen(tkwin));
320	} else {
321	    for (cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL;
322		    cmapPtr = cmapPtr->nextPtr) {
323		if (cmapPtr->shareable && (cmapPtr->visual == visual)) {
324		    *colormapPtr = cmapPtr->colormap;
325		    cmapPtr->refCount += 1;
326		    goto done;
327		}
328	    }
329	    cmapPtr = (TkColormap *) ckalloc(sizeof(TkColormap));
330	    cmapPtr->colormap = XCreateColormap(Tk_Display(tkwin),
331		    RootWindowOfScreen(Tk_Screen(tkwin)), visual,
332		    AllocNone);
333	    cmapPtr->visual = visual;
334	    cmapPtr->refCount = 1;
335	    cmapPtr->shareable = 1;
336	    cmapPtr->nextPtr = dispPtr->cmapPtr;
337	    dispPtr->cmapPtr = cmapPtr;
338	    *colormapPtr = cmapPtr->colormap;
339	}
340    }
341
342  done:
343    return visual;
344}
345
346/*
347 *----------------------------------------------------------------------
348 *
349 * Tk_GetColormap --
350 *
351 *	Given a string identifying a colormap, this procedure finds an
352 *	appropriate colormap.
353 *
354 * Results:
355 *	The return value is normally the X resource identifier for the
356 *	colormap. If an error occurs, None is returned and an error message is
357 *	placed in the interp's result.
358 *
359 * Side effects:
360 *	A reference count is incremented for the colormap, so Tk_FreeColormap
361 *	must eventually be called exactly once for each call to
362 *	Tk_GetColormap.
363 *
364 *----------------------------------------------------------------------
365 */
366
367Colormap
368Tk_GetColormap(
369    Tcl_Interp *interp,		/* Interpreter to use for error reporting. */
370    Tk_Window tkwin,		/* Window where colormap will be used. */
371    CONST char *string)		/* String that identifies colormap: either
372				 * "new" or the name of another window. */
373{
374    Colormap colormap;
375    TkColormap *cmapPtr;
376    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
377    Tk_Window other;
378
379    /*
380     * Allocate a new colormap, if that's what is wanted.
381     */
382
383    if (strcmp(string, "new") == 0) {
384	cmapPtr = (TkColormap *) ckalloc(sizeof(TkColormap));
385	cmapPtr->colormap = XCreateColormap(Tk_Display(tkwin),
386		RootWindowOfScreen(Tk_Screen(tkwin)), Tk_Visual(tkwin),
387		AllocNone);
388	cmapPtr->visual = Tk_Visual(tkwin);
389	cmapPtr->refCount = 1;
390	cmapPtr->shareable = 0;
391	cmapPtr->nextPtr = dispPtr->cmapPtr;
392	dispPtr->cmapPtr = cmapPtr;
393	return cmapPtr->colormap;
394    }
395
396    /*
397     * Use a colormap from an existing window. It must have the same visual as
398     * tkwin (which means, among other things, that the other window must be
399     * on the same screen).
400     */
401
402    other = Tk_NameToWindow(interp, string, tkwin);
403    if (other == NULL) {
404	return None;
405    }
406    if (Tk_Screen(other) != Tk_Screen(tkwin)) {
407	Tcl_AppendResult(interp, "can't use colormap for ", string,
408		": not on same screen", NULL);
409	return None;
410    }
411    if (Tk_Visual(other) != Tk_Visual(tkwin)) {
412	Tcl_AppendResult(interp, "can't use colormap for ", string,
413		": incompatible visuals", NULL);
414	return None;
415    }
416    colormap = Tk_Colormap(other);
417
418    /*
419     * If the colormap was a special one allocated by code in this file,
420     * increment its reference count.
421     */
422
423    for (cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL;
424	    cmapPtr = cmapPtr->nextPtr) {
425	if (cmapPtr->colormap == colormap) {
426	    cmapPtr->refCount += 1;
427	}
428    }
429    return colormap;
430}
431
432/*
433 *----------------------------------------------------------------------
434 *
435 * Tk_FreeColormap --
436 *
437 *	This procedure is called to release a colormap that was previously
438 *	allocated by Tk_GetColormap.
439 *
440 * Results:
441 *	None.
442 *
443 * Side effects:
444 *	The colormap's reference count is decremented. If this was the last
445 *	reference to the colormap, then the colormap is freed.
446 *
447 *----------------------------------------------------------------------
448 */
449
450void
451Tk_FreeColormap(
452    Display *display,		/* Display for which colormap was
453				 * allocated. */
454    Colormap colormap)		/* Colormap that is no longer needed. Must
455				 * have been returned by previous call to
456				 * Tk_GetColormap, or preserved by a previous
457				 * call to Tk_PreserveColormap. */
458{
459    TkDisplay *dispPtr;
460    TkColormap *cmapPtr, *prevPtr;
461
462    /*
463     * Find Tk's information about the display, then see if this colormap is a
464     * non-default one (if it's a default one, there won't be an entry for it
465     * in the display's list).
466     */
467
468    dispPtr = TkGetDisplay(display);
469    if (dispPtr == NULL) {
470	Tcl_Panic("unknown display passed to Tk_FreeColormap");
471    }
472    for (prevPtr = NULL, cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL;
473	    prevPtr = cmapPtr, cmapPtr = cmapPtr->nextPtr) {
474	if (cmapPtr->colormap == colormap) {
475	    cmapPtr->refCount -= 1;
476	    if (cmapPtr->refCount == 0) {
477		XFreeColormap(display, colormap);
478		if (prevPtr == NULL) {
479		    dispPtr->cmapPtr = cmapPtr->nextPtr;
480		} else {
481		    prevPtr->nextPtr = cmapPtr->nextPtr;
482		}
483		ckfree((char *) cmapPtr);
484	    }
485	    return;
486	}
487    }
488}
489
490/*
491 *----------------------------------------------------------------------
492 *
493 * Tk_PreserveColormap --
494 *
495 *	This procedure is called to indicate to Tk that the specified colormap
496 *	is being referenced from another location and should not be freed
497 *	until all extra references are eliminated. The colormap must have been
498 *	returned by Tk_GetColormap.
499 *
500 * Results:
501 *	None.
502 *
503 * Side effects:
504 *	The colormap's reference count is incremented, so Tk_FreeColormap must
505 *	eventually be called exactly once for each call to
506 *	Tk_PreserveColormap.
507 *
508 *----------------------------------------------------------------------
509 */
510
511void
512Tk_PreserveColormap(
513    Display *display,		/* Display for which colormap was
514				 * allocated. */
515    Colormap colormap)		/* Colormap that should be preserved. */
516{
517    TkDisplay *dispPtr;
518    TkColormap *cmapPtr;
519
520    /*
521     * Find Tk's information about the display, then see if this colormap is a
522     * non-default one (if it's a default one, there won't be an entry for it
523     * in the display's list).
524     */
525
526    dispPtr = TkGetDisplay(display);
527    if (dispPtr == NULL) {
528	Tcl_Panic("unknown display passed to Tk_PreserveColormap");
529    }
530    for (cmapPtr = dispPtr->cmapPtr; cmapPtr != NULL;
531	    cmapPtr = cmapPtr->nextPtr) {
532	if (cmapPtr->colormap == colormap) {
533	    cmapPtr->refCount += 1;
534	    return;
535	}
536    }
537}
538
539/*
540 * Local Variables:
541 * mode: c
542 * c-basic-offset: 4
543 * fill-column: 78
544 * End:
545 */
546