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