http://ftp.x.org/contrib/applications/xscreensaver-2.24.tar.gz
[xscreensaver] / utils / visual.c
1 /* xscreensaver, Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998
2  *  by Jamie Zawinski <jwz@netscape.com>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* This file contains some code for intelligently picking the best visual
14    (where "best" is biased in the direction of either: high color counts;
15    or: having writable color cells...)
16  */
17
18 #include "utils.h"
19 #include "resources.h"  /* for get_string_resource() */
20 #include "visual.h"
21
22 #include <X11/Xutil.h>
23
24 extern char *progname;
25
26
27 #ifndef isupper
28 # define isupper(c)  ((c) >= 'A' && (c) <= 'Z')
29 #endif
30 #ifndef _tolower
31 # define _tolower(c)  ((c) - 'A' + 'a')
32 #endif
33
34
35 static Visual *pick_best_visual (Screen *, Bool, Bool);
36 static Visual *pick_mono_visual (Screen *);
37 static Visual *pick_best_visual_of_class (Screen *, int);
38 static Visual *id_to_visual (Screen *, int);
39 static Visual *id_to_visual (Screen *screen, int id);
40
41
42 #define DEFAULT_VISUAL  -1
43 #define BEST_VISUAL     -2
44 #define MONO_VISUAL     -3
45 #define GRAY_VISUAL     -4
46 #define COLOR_VISUAL    -5
47 #define SPECIFIC_VISUAL -6
48
49 Visual *
50 get_visual (Screen *screen, const char *string, Bool prefer_writable_cells,
51             Bool verbose_p)
52 {
53   char *v = (string ? strdup(string) : 0);
54   char c, *tmp;
55   int vclass;
56   unsigned long id;
57   Visual *result = 0;
58
59   if (v)
60     for (tmp = v; *tmp; tmp++)
61       if (isupper (*tmp)) *tmp = _tolower (*tmp);
62
63   if (!v)                                         vclass = BEST_VISUAL;
64   else if (!strcmp (v, "default"))                vclass = DEFAULT_VISUAL;
65   else if (!strcmp (v, "best"))                   vclass = BEST_VISUAL;
66   else if (!strcmp (v, "mono"))                   vclass = MONO_VISUAL;
67   else if (!strcmp (v, "monochrome"))             vclass = MONO_VISUAL;
68   else if (!strcmp (v, "gray"))                   vclass = GRAY_VISUAL;
69   else if (!strcmp (v, "grey"))                   vclass = GRAY_VISUAL;
70   else if (!strcmp (v, "color"))                  vclass = COLOR_VISUAL;
71   else if (!strcmp (v, "staticgray"))             vclass = StaticGray;
72   else if (!strcmp (v, "staticcolor"))            vclass = StaticColor;
73   else if (!strcmp (v, "truecolor"))              vclass = TrueColor;
74   else if (!strcmp (v, "grayscale"))              vclass = GrayScale;
75   else if (!strcmp (v, "greyscale"))              vclass = GrayScale;
76   else if (!strcmp (v, "pseudocolor"))            vclass = PseudoColor;
77   else if (!strcmp (v, "directcolor"))            vclass = DirectColor;
78   else if (1 == sscanf (v, " %ld %c", &id, &c))   vclass = SPECIFIC_VISUAL;
79   else if (1 == sscanf (v, " 0x%lx %c", &id, &c)) vclass = SPECIFIC_VISUAL;
80   else
81     {
82       fprintf (stderr, "%s: unrecognized visual \"%s\".\n", progname, v);
83       vclass = DEFAULT_VISUAL;
84     }
85
86   if (vclass == DEFAULT_VISUAL)
87     result = DefaultVisualOfScreen (screen);
88   else if (vclass == BEST_VISUAL)
89     result = pick_best_visual (screen, prefer_writable_cells, False);
90   else if (vclass == MONO_VISUAL)
91     {
92       result = pick_mono_visual (screen);
93       if (!result && verbose_p)
94         fprintf (stderr, "%s: no monochrome visuals.\n", progname);
95     }
96   else if (vclass == GRAY_VISUAL)
97     {
98       if (prefer_writable_cells)
99         result = pick_best_visual_of_class (screen, GrayScale);
100       if (!result)
101         result = pick_best_visual_of_class (screen, StaticGray);
102       if (!result)
103         result = pick_best_visual_of_class (screen, GrayScale);
104       if (!result && verbose_p)
105         fprintf (stderr, "%s: no GrayScale or StaticGray visuals.\n",
106                  progname);
107     }
108   else if (vclass == COLOR_VISUAL)
109     {
110       int class;
111       /* First see if the default visual will do. */
112       result = DefaultVisualOfScreen (screen);
113       class = visual_class(screen, result);
114       if (class != TrueColor &&
115           class != PseudoColor &&
116           class != DirectColor &&
117           class != StaticColor)
118         result = 0;
119       if (result && visual_depth(screen, result) <= 1)
120         result = 0;
121
122       /* Else, find the best non-default color visual */
123       if (!result)
124         result = pick_best_visual (screen, prefer_writable_cells, True);
125
126       if (!result && verbose_p)
127         fprintf (stderr, "%s: no color visuals.\n", progname);
128     }
129   else if (vclass == SPECIFIC_VISUAL)
130     {
131       result = id_to_visual (screen, id);
132       if (!result && verbose_p)
133         fprintf (stderr, "%s: no visual with id 0x%x.\n", progname,
134                  (unsigned int) id);
135     }
136   else
137     {
138       Visual *visual = pick_best_visual_of_class (screen, vclass);
139       if (visual)
140         result = visual;
141       else if (verbose_p)
142         fprintf (stderr, "%s: no visual of class %s.\n", progname, v);
143     }
144
145   if (v) free (v);
146   return result;
147 }
148
149 Visual *
150 get_visual_resource (Screen *screen, char *name, char *class,
151                      Bool prefer_writable_cells)
152 {
153   char *string = get_string_resource (name, class);
154   Visual *v = get_visual (screen, string, prefer_writable_cells, True);
155   if (string)
156     free(string);
157   if (v)
158     return v;
159   else
160     return DefaultVisualOfScreen (screen);
161 }
162
163
164 static Visual *
165 pick_best_visual (Screen *screen, Bool prefer_writable_cells, Bool color_only)
166 {
167   Visual *visual;
168
169   if (!prefer_writable_cells)
170     {
171       /* If we don't prefer writable cells, then the "best" visual is the one
172          on which we can allocate the largest range and number of colors.
173
174          Therefore, a TrueColor visual which is at least 16 bits deep is best.
175          (The assumption here being that a TrueColor of less than 16 bits is
176          really just a PseudoColor visual with a pre-allocated color cube.)
177
178          The next best thing is a PseudoColor visual of any type.  After that
179          come the non-colormappable visuals, and non-color visuals.
180        */
181       if ((visual = pick_best_visual_of_class (screen, TrueColor)) &&
182           visual_depth (screen, visual) >= 16)
183         return visual;
184     }
185
186 #define TRY_CLASS(CLASS) \
187   if ((visual = pick_best_visual_of_class (screen, CLASS)) && \
188       (!color_only || visual_depth(screen, visual) > 1)) \
189     return visual
190   TRY_CLASS(PseudoColor);
191   TRY_CLASS(TrueColor);
192   TRY_CLASS(DirectColor);
193   TRY_CLASS(StaticColor);
194   if (!color_only)
195     {
196       TRY_CLASS(GrayScale);
197       TRY_CLASS(StaticGray);
198     }
199 #undef TRY_CLASS
200
201   visual = DefaultVisualOfScreen (screen);
202   if (!color_only || visual_depth(screen, visual) > 1)
203     return visual;
204   else
205     return 0;
206 }
207
208 static Visual *
209 pick_mono_visual (Screen *screen)
210 {
211   Display *dpy = DisplayOfScreen (screen);
212   XVisualInfo vi_in, *vi_out;
213   int out_count;
214
215   vi_in.depth = 1;
216   vi_in.screen = screen_number (screen);
217   vi_out = XGetVisualInfo (dpy, (VisualDepthMask | VisualScreenMask),
218                            &vi_in, &out_count);
219   if (vi_out)
220     {
221       Visual *v = (out_count > 0 ? vi_out [0].visual : 0);
222       if (v && vi_out[0].depth != 1)
223         v = 0;
224       XFree ((char *) vi_out);
225       return v;
226     }
227   else
228     return 0;
229 }
230
231
232 static Visual *
233 pick_best_visual_of_class (Screen *screen, int visual_class)
234 {
235   /* The best visual of a class is the one which on which we can allocate
236      the largest range and number of colors, which means the one with the
237      greatest depth and number of cells.
238
239      (But actually, for XDaliClock, all visuals of the same class are
240      probably equivalent - either we have writable cells or we don't.)
241    */
242   Display *dpy = DisplayOfScreen (screen);
243   XVisualInfo vi_in, *vi_out;
244   int out_count;
245
246   vi_in.class = visual_class;
247   vi_in.screen = screen_number (screen);
248   vi_out = XGetVisualInfo (dpy, (VisualClassMask | VisualScreenMask),
249                            &vi_in, &out_count);
250   if (vi_out)
251     {
252       /* choose the 'best' one, if multiple */
253       int i, best;
254       Visual *visual;
255       for (i = 0, best = 0; i < out_count; i++)
256         /* It's better if it's deeper, or if it's the same depth with
257            more cells (does that ever happen?  Well, it could...) */
258         if ((vi_out [i].depth > vi_out [best].depth) ||
259             ((vi_out [i].depth == vi_out [best].depth) &&
260              (vi_out [i].colormap_size > vi_out [best].colormap_size)))
261           best = i;
262       visual = (best < out_count ? vi_out [best].visual : 0);
263       XFree ((char *) vi_out);
264       return visual;
265     }
266   else
267     return 0;
268 }
269
270 static Visual *
271 id_to_visual (Screen *screen, int id)
272 {
273   Display *dpy = DisplayOfScreen (screen);
274   XVisualInfo vi_in, *vi_out;
275   int out_count;
276   vi_in.screen = screen_number (screen);
277   vi_in.visualid = id;
278   vi_out = XGetVisualInfo (dpy, (VisualScreenMask | VisualIDMask),
279                            &vi_in, &out_count);
280   if (vi_out)
281     {
282       Visual *v = vi_out[0].visual;
283       XFree ((char *) vi_out);
284       return v;
285     }
286   return 0;
287 }
288
289 int
290 visual_depth (Screen *screen, Visual *visual)
291 {
292   Display *dpy = DisplayOfScreen (screen);
293   XVisualInfo vi_in, *vi_out;
294   int out_count, d;
295   vi_in.screen = screen_number (screen);
296   vi_in.visualid = XVisualIDFromVisual (visual);
297   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
298                            &vi_in, &out_count);
299   if (! vi_out) abort ();
300   d = vi_out [0].depth;
301   XFree ((char *) vi_out);
302   return d;
303 }
304
305
306 #if 0
307 /* You very probably don't want to be using this.
308    Pixmap depth doesn't refer to the depths of pixmaps, but rather, to
309    the depth of protocol-level on-the-wire pixmap data, that is, XImages.
310    To get this info, you should be looking at XImage->bits_per_pixel
311    instead.  (And allocating the data for your XImage structures by
312    multiplying ximage->bytes_per_line by ximage->height.)
313  */
314 int
315 visual_pixmap_depth (Screen *screen, Visual *visual)
316 {
317   Display *dpy = DisplayOfScreen (screen);
318   int vdepth = visual_depth (screen, visual);
319   int pdepth = vdepth;
320   int i, pfvc = 0;
321   XPixmapFormatValues *pfv = XListPixmapFormats (dpy, &pfvc);
322
323   /* Return the first matching depth in the pixmap formats.  If there are no
324      matching pixmap formats (which shouldn't be able to happen at all) then
325      return the visual depth instead. */
326   for (i = 0; i < pfvc; i++)
327     if (pfv[i].depth == vdepth)
328       {
329         pdepth = pfv[i].bits_per_pixel;
330         break;
331       }
332   if (pfv)
333     XFree (pfv);
334   return pdepth;
335 }
336 #endif /* 0 */
337
338
339 int
340 visual_class (Screen *screen, Visual *visual)
341 {
342   Display *dpy = DisplayOfScreen (screen);
343   XVisualInfo vi_in, *vi_out;
344   int out_count, c;
345   vi_in.screen = screen_number (screen);
346   vi_in.visualid = XVisualIDFromVisual (visual);
347   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
348                            &vi_in, &out_count);
349   if (! vi_out) abort ();
350   c = vi_out [0].class;
351   XFree ((char *) vi_out);
352   return c;
353 }
354
355 Bool
356 has_writable_cells (Screen *screen, Visual *visual)
357 {
358   switch (visual_class (screen, visual))
359     {
360     case GrayScale:     /* Mappable grays. */
361     case PseudoColor:   /* Mappable colors. */
362       return True;
363     case StaticGray:    /* Fixed grays. */
364     case TrueColor:     /* Fixed colors. */
365     case StaticColor:   /* (What's the difference again?) */
366     case DirectColor:   /* DirectColor visuals are like TrueColor, but have
367                            three colormaps - one for each component of RGB.
368                            Screw it. */
369       return False;
370     default:
371       abort();
372     }
373 }
374
375 void
376 describe_visual (FILE *f, Screen *screen, Visual *visual, Bool private_cmap_p)
377 {
378   char n[10];
379   Display *dpy = DisplayOfScreen (screen);
380   XVisualInfo vi_in, *vi_out;
381   int out_count;
382   vi_in.screen = screen_number (screen);
383   vi_in.visualid = XVisualIDFromVisual (visual);
384   vi_out = XGetVisualInfo (dpy, (VisualScreenMask | VisualIDMask),
385                            &vi_in, &out_count);
386   if (! vi_out) abort ();
387   if (private_cmap_p)
388     sprintf(n, "%3d", vi_out->colormap_size);
389   else
390     strcpy(n, "default");
391
392   fprintf (f, "0x%02x (%s depth: %2d, cmap: %s)\n",
393            (unsigned int) vi_out->visualid,
394            (vi_out->class == StaticGray  ? "StaticGray, " :
395             vi_out->class == StaticColor ? "StaticColor," :
396             vi_out->class == TrueColor   ? "TrueColor,  " :
397             vi_out->class == GrayScale   ? "GrayScale,  " :
398             vi_out->class == PseudoColor ? "PseudoColor," :
399             vi_out->class == DirectColor ? "DirectColor," :
400                                            "UNKNOWN:    "),
401            vi_out->depth, n);
402   XFree ((char *) vi_out);
403 }
404
405 int
406 screen_number (Screen *screen)
407 {
408   Display *dpy = DisplayOfScreen (screen);
409   int i;
410   for (i = 0; i < ScreenCount (dpy); i++)
411     if (ScreenOfDisplay (dpy, i) == screen)
412       return i;
413   abort ();
414 }
415
416 int
417 visual_cells (Screen *screen, Visual *visual)
418 {
419   Display *dpy = DisplayOfScreen (screen);
420   XVisualInfo vi_in, *vi_out;
421   int out_count, c;
422   vi_in.screen = screen_number (screen);
423   vi_in.visualid = XVisualIDFromVisual (visual);
424   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
425                            &vi_in, &out_count);
426   if (! vi_out) abort ();
427   c = vi_out [0].colormap_size;
428   XFree ((char *) vi_out);
429   return c;
430 }
431
432 Visual *
433 find_similar_visual(Screen *screen, Visual *old_visual)
434 {
435   Display *dpy = DisplayOfScreen (screen);
436   XVisualInfo vi_in, *vi_out;
437   Visual *result = 0;
438   int out_count;
439
440   vi_in.screen = screen_number (screen);
441   vi_in.class  = visual_class (screen, old_visual);
442   vi_in.depth  = visual_depth (screen, old_visual);
443
444   /* Look for a visual of the same class and depth.
445    */
446   vi_out = XGetVisualInfo (dpy, (VisualScreenMask | VisualClassMask |
447                                  VisualDepthMask),
448                            &vi_in, &out_count);
449   if (vi_out && out_count > 0)
450     result = vi_out[0].visual;
451   if (vi_out) XFree (vi_out);
452   vi_out = 0;
453
454   /* Failing that, look for a visual of the same class.
455    */
456   if (!result)
457     {
458       vi_out = XGetVisualInfo (dpy, (VisualScreenMask | VisualClassMask),
459                                &vi_in, &out_count);
460       if (vi_out && out_count > 0)
461         result = vi_out[0].visual;
462       if (vi_out) XFree (vi_out);
463       vi_out = 0;
464     }
465
466   /* Failing that, return the default visual. */
467   if (!result)
468     result = DefaultVisualOfScreen (screen);
469
470   return result;
471 }