X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=utils%2Fvisual.c;h=89b4f33e69f3d7af84fc2a5931ae594c1916a47e;hb=aa75c7476aeaa84cf3abc192b376a8b03c325213;hp=cc8b5729cfb17751f4e9aae1110184c632dfe1f7;hpb=ccbc9f87eb59497b23bd0424ee1ed20ad7c7db54;p=xscreensaver diff --git a/utils/visual.c b/utils/visual.c index cc8b5729..89b4f33e 100644 --- a/utils/visual.c +++ b/utils/visual.c @@ -1,5 +1,4 @@ -/* xscreensaver, Copyright (c) 1993, 1994, 1995 - * Jamie Zawinski +/* xscreensaver, Copyright (c) 1993-2014 by Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -11,25 +10,23 @@ */ /* This file contains some code for intelligently picking the best visual - (where "best" is biased in the direction of high color counts...) + (where "best" is biased in the direction of either: high color counts; + or: having writable color cells...) */ -#if __STDC__ -#include -#include -#include -#endif +#include "utils.h" +#include "resources.h" /* for get_string_resource() */ +#include "visual.h" -#include -#include +#include +#ifndef HAVE_ANDROID #include - -#if __STDC__ -# define P(x)x #else -#define P(x)() +#include "../android/android-visual.h" #endif +extern char *progname; + #ifndef isupper # define isupper(c) ((c) >= 'A' && (c) <= 'Z') #endif @@ -37,115 +34,222 @@ # define _tolower(c) ((c) - 'A' + 'a') #endif -extern char *progname; -extern char *get_string_resource P((char *, char *)); -static Visual *pick_best_visual P ((Screen *)); -static Visual *pick_best_visual_of_class P((Screen *, int)); -static Visual *id_to_visual P((Screen *, int)); -static int screen_number P((Screen *)); -static Visual *id_to_visual P((Screen *screen, int id)); -int get_visual_depth P((Display *dpy, Visual *visual)); +static Visual *pick_best_visual (Screen *, Bool, Bool); +static Visual *pick_mono_visual (Screen *); +static Visual *pick_best_visual_of_class (Screen *, int); +static Visual *pick_best_gl_visual (Screen *); #define DEFAULT_VISUAL -1 #define BEST_VISUAL -2 -#define SPECIFIC_VISUAL -3 +#define MONO_VISUAL -3 +#define GRAY_VISUAL -4 +#define COLOR_VISUAL -5 +#define GL_VISUAL -6 +#define SPECIFIC_VISUAL -7 Visual * -get_visual_resource (dpy, name, class) - Display *dpy; - char *name, *class; +get_visual (Screen *screen, const char *string, Bool prefer_writable_cells, + Bool verbose_p) { - Screen *screen = DefaultScreenOfDisplay (dpy); - char c, *v = get_string_resource (name, class); - char *tmp; + char *v = (string ? strdup(string) : 0); + char c, *tmp; int vclass; unsigned long id; + Visual *result = 0; if (v) for (tmp = v; *tmp; tmp++) if (isupper (*tmp)) *tmp = _tolower (*tmp); - if (!v) vclass = BEST_VISUAL; + if (!v || !*v) vclass = BEST_VISUAL; else if (!strcmp (v, "default")) vclass = DEFAULT_VISUAL; else if (!strcmp (v, "best")) vclass = BEST_VISUAL; + else if (!strcmp (v, "mono")) vclass = MONO_VISUAL; + else if (!strcmp (v, "monochrome")) vclass = MONO_VISUAL; + else if (!strcmp (v, "gray")) vclass = GRAY_VISUAL; + else if (!strcmp (v, "grey")) vclass = GRAY_VISUAL; + else if (!strcmp (v, "color")) vclass = COLOR_VISUAL; + else if (!strcmp (v, "gl")) vclass = GL_VISUAL; else if (!strcmp (v, "staticgray")) vclass = StaticGray; else if (!strcmp (v, "staticcolor")) vclass = StaticColor; else if (!strcmp (v, "truecolor")) vclass = TrueColor; else if (!strcmp (v, "grayscale")) vclass = GrayScale; + else if (!strcmp (v, "greyscale")) vclass = GrayScale; else if (!strcmp (v, "pseudocolor")) vclass = PseudoColor; else if (!strcmp (v, "directcolor")) vclass = DirectColor; - else if (1 == sscanf (v, " %ld %c", &id, &c)) vclass = SPECIFIC_VISUAL; + else if (1 == sscanf (v, " %lu %c", &id, &c)) vclass = SPECIFIC_VISUAL; else if (1 == sscanf (v, " 0x%lx %c", &id, &c)) vclass = SPECIFIC_VISUAL; else { fprintf (stderr, "%s: unrecognized visual \"%s\".\n", progname, v); vclass = DEFAULT_VISUAL; } - if (v) free (v); if (vclass == DEFAULT_VISUAL) - return DefaultVisualOfScreen (screen); + result = DefaultVisualOfScreen (screen); else if (vclass == BEST_VISUAL) - return pick_best_visual (screen); + result = pick_best_visual (screen, prefer_writable_cells, False); + else if (vclass == MONO_VISUAL) + { + result = pick_mono_visual (screen); + if (!result && verbose_p) + fprintf (stderr, "%s: no monochrome visuals.\n", progname); + } + else if (vclass == GRAY_VISUAL) + { + if (prefer_writable_cells) + result = pick_best_visual_of_class (screen, GrayScale); + if (!result) + result = pick_best_visual_of_class (screen, StaticGray); + if (!result) + result = pick_best_visual_of_class (screen, GrayScale); + if (!result && verbose_p) + fprintf (stderr, "%s: no GrayScale or StaticGray visuals.\n", + progname); + } + else if (vclass == COLOR_VISUAL) + { + int class; + /* First see if the default visual will do. */ + result = DefaultVisualOfScreen (screen); + class = visual_class(screen, result); + if (class != TrueColor && + class != PseudoColor && + class != DirectColor && + class != StaticColor) + result = 0; + if (result && visual_depth(screen, result) <= 1) + result = 0; + + /* Else, find the best non-default color visual */ + if (!result) + result = pick_best_visual (screen, prefer_writable_cells, True); + + if (!result && verbose_p) + fprintf (stderr, "%s: no color visuals.\n", progname); + } + else if (vclass == GL_VISUAL) + { + Visual *visual = pick_best_gl_visual (screen); + if (visual) + result = visual; + else if (verbose_p) + fprintf (stderr, "%s: no visual suitable for GL.\n", progname); + } else if (vclass == SPECIFIC_VISUAL) { - Visual *visual = id_to_visual (screen, id); - if (visual) return visual; - fprintf (stderr, "%s: no visual with id 0x%x.\n", progname, - (unsigned int) id); - return DefaultVisualOfScreen (screen); + result = id_to_visual (screen, id); + if (!result && verbose_p) + fprintf (stderr, "%s: no visual with id 0x%x.\n", progname, + (unsigned int) id); } else { Visual *visual = pick_best_visual_of_class (screen, vclass); - if (visual) return visual; - fprintf (stderr, "%s: no visual of class %s.\n", progname, v); - return DefaultVisualOfScreen (screen); + if (visual) + result = visual; + else if (verbose_p) + fprintf (stderr, "%s: no visual of class %s.\n", progname, v); } + + if (v) free (v); + return result; } -static Visual * -pick_best_visual (screen) - Screen *screen; +Visual * +get_visual_resource (Screen *screen, char *name, char *class, + Bool prefer_writable_cells) { - /* The "best" visual is the one on which we can allocate the largest - range and number of colors. + char *string = get_string_resource (DisplayOfScreen (screen), name, class); + Visual *v = get_visual (screen, string, prefer_writable_cells, True); + if (string) + free(string); + if (v) + return v; + else + return DefaultVisualOfScreen (screen); +} - Therefore, a TrueColor visual which is at least 16 bits deep is best. - (The assumption here being that a TrueColor of less than 16 bits is - really just a PseudoColor visual with a pre-allocated color cube.) - The next best thing is a PseudoColor visual of any type. After that - come the non-colormappable visuals, and non-color visuals. - */ - Display *dpy = DisplayOfScreen (screen); +static Visual * +pick_best_visual (Screen *screen, Bool prefer_writable_cells, Bool color_only) +{ Visual *visual; - if ((visual = pick_best_visual_of_class (screen, TrueColor)) && - get_visual_depth (dpy, visual) >= 16) - return visual; - if ((visual = pick_best_visual_of_class (screen, PseudoColor))) - return visual; - if ((visual = pick_best_visual_of_class (screen, TrueColor))) - return visual; - if ((visual = pick_best_visual_of_class (screen, DirectColor))) - return visual; - if ((visual = pick_best_visual_of_class (screen, GrayScale))) - return visual; - if ((visual = pick_best_visual_of_class (screen, StaticGray))) + + if (!prefer_writable_cells) + { + /* If we don't prefer writable cells, then the "best" visual is the one + on which we can allocate the largest range and number of colors. + + Therefore, a TrueColor visual which is at least 16 bits deep is best. + (The assumption here being that a TrueColor of less than 16 bits is + really just a PseudoColor visual with a pre-allocated color cube.) + + The next best thing is a PseudoColor visual of any type. After that + come the non-colormappable visuals, and non-color visuals. + */ + if ((visual = pick_best_visual_of_class (screen, TrueColor)) && + visual_depth (screen, visual) >= 16) + return visual; + } + +#define TRY_CLASS(CLASS) \ + if ((visual = pick_best_visual_of_class (screen, CLASS)) && \ + (!color_only || visual_depth(screen, visual) > 1)) \ + return visual + TRY_CLASS(PseudoColor); + TRY_CLASS(TrueColor); + TRY_CLASS(DirectColor); + TRY_CLASS(StaticColor); + if (!color_only) + { + TRY_CLASS(GrayScale); + TRY_CLASS(StaticGray); + } +#undef TRY_CLASS + + visual = DefaultVisualOfScreen (screen); + if (!color_only || visual_depth(screen, visual) > 1) return visual; - return DefaultVisualOfScreen (screen); + else + return 0; +} + +static Visual * +pick_mono_visual (Screen *screen) +{ + Display *dpy = DisplayOfScreen (screen); + XVisualInfo vi_in, *vi_out; + int out_count; + + vi_in.depth = 1; + vi_in.screen = screen_number (screen); + vi_out = XGetVisualInfo (dpy, (VisualDepthMask | VisualScreenMask), + &vi_in, &out_count); + if (vi_out) + { + Visual *v = (out_count > 0 ? vi_out [0].visual : 0); + if (v && vi_out[0].depth != 1) + v = 0; + XFree ((char *) vi_out); + return v; + } + else + return 0; } + static Visual * -pick_best_visual_of_class (screen, visual_class) - Screen *screen; - int visual_class; +pick_best_visual_of_class (Screen *screen, int visual_class) { /* The best visual of a class is the one which on which we can allocate the largest range and number of colors, which means the one with the greatest depth and number of cells. + + (But actually, for XDaliClock, all visuals of the same class are + probably equivalent - either we have writable cells or we don't.) */ Display *dpy = DisplayOfScreen (screen); XVisualInfo vi_in, *vi_out; @@ -160,14 +264,15 @@ pick_best_visual_of_class (screen, visual_class) /* choose the 'best' one, if multiple */ int i, best; Visual *visual; - for (i = 0, best = 0; i < out_count; i++) +/* for (i = 0, best = 0; i < out_count; i++) */ + for (i = out_count-1, best = i; i >= 0; i--) /* go backwards */ /* It's better if it's deeper, or if it's the same depth with more cells (does that ever happen? Well, it could...) */ if ((vi_out [i].depth > vi_out [best].depth) || ((vi_out [i].depth == vi_out [best].depth) && (vi_out [i].colormap_size > vi_out [best].colormap_size))) best = i; - visual = vi_out [best].visual; + visual = (best < out_count ? vi_out [best].visual : 0); XFree ((char *) vi_out); return visual; } @@ -176,9 +281,66 @@ pick_best_visual_of_class (screen, visual_class) } static Visual * -id_to_visual (screen, id) - Screen *screen; - int id; +pick_best_gl_visual (Screen *screen) +{ + /* The best visual for GL is a TrueColor visual that is half as deep as + the screen. If such a thing doesn't exist, then TrueColor is best. + Failing that, the deepest available color visual is best. + + Compare this function to get_gl_visual() in visual-gl.c. + This function tries to find the best GL visual using Xlib calls, + whereas that function does the same thing using GLX calls. + */ + Display *dpy = DisplayOfScreen (screen); + XVisualInfo vi_in, *vi_out; + int out_count; + Visual *result = 0; + + int ndepths = 0; + int *depths = XListDepths (dpy, screen_number (screen), &ndepths); + int screen_depth = (depths && ndepths) ? depths[ndepths - 1] : 0; + XFree (depths); + + vi_in.class = TrueColor; + vi_in.screen = screen_number (screen); + vi_in.depth = screen_depth / 2; + vi_out = XGetVisualInfo (dpy, (VisualClassMask | VisualScreenMask | + VisualDepthMask), + &vi_in, &out_count); + if (out_count > 0) + result = vi_out[0].visual; + + if (vi_out) + XFree ((char *) vi_out); + + if (!result && screen_depth > 24) + { + /* If it's a 32-deep screen and we didn't find a depth-16 visual, + see if there's a depth-12 visual. */ + vi_in.class = TrueColor; + vi_in.screen = screen_number (screen); + vi_in.depth = 12; + vi_out = XGetVisualInfo (dpy, (VisualClassMask | VisualScreenMask | + VisualDepthMask), + &vi_in, &out_count); + if (out_count > 0) + result = vi_out[0].visual; + } + + if (!result) + /* No half-depth TrueColor? Ok, try for any TrueColor (the deepest.) */ + result = pick_best_visual_of_class (screen, TrueColor); + + if (!result) + /* No TrueColor? Ok, try for anything. */ + result = pick_best_visual (screen, False, False); + + return result; +} + + +Visual * +id_to_visual (Screen *screen, int id) { Display *dpy = DisplayOfScreen (screen); XVisualInfo vi_in, *vi_out; @@ -197,13 +359,12 @@ id_to_visual (screen, id) } int -get_visual_depth (dpy, visual) - Display *dpy; - Visual *visual; +visual_depth (Screen *screen, Visual *visual) { + Display *dpy = DisplayOfScreen (screen); XVisualInfo vi_in, *vi_out; int out_count, d; - vi_in.screen = DefaultScreen (dpy); + vi_in.screen = screen_number (screen); vi_in.visualid = XVisualIDFromVisual (visual); vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask, &vi_in, &out_count); @@ -214,14 +375,46 @@ get_visual_depth (dpy, visual) } +#if 0 +/* You very probably don't want to be using this. + Pixmap depth doesn't refer to the depths of pixmaps, but rather, to + the depth of protocol-level on-the-wire pixmap data, that is, XImages. + To get this info, you should be looking at XImage->bits_per_pixel + instead. (And allocating the data for your XImage structures by + multiplying ximage->bytes_per_line by ximage->height.) + */ int -get_visual_class (dpy, visual) - Display *dpy; - Visual *visual; +visual_pixmap_depth (Screen *screen, Visual *visual) { + Display *dpy = DisplayOfScreen (screen); + int vdepth = visual_depth (screen, visual); + int pdepth = vdepth; + int i, pfvc = 0; + XPixmapFormatValues *pfv = XListPixmapFormats (dpy, &pfvc); + + /* Return the first matching depth in the pixmap formats. If there are no + matching pixmap formats (which shouldn't be able to happen at all) then + return the visual depth instead. */ + for (i = 0; i < pfvc; i++) + if (pfv[i].depth == vdepth) + { + pdepth = pfv[i].bits_per_pixel; + break; + } + if (pfv) + XFree (pfv); + return pdepth; +} +#endif /* 0 */ + + +int +visual_class (Screen *screen, Visual *visual) +{ + Display *dpy = DisplayOfScreen (screen); XVisualInfo vi_in, *vi_out; int out_count, c; - vi_in.screen = DefaultScreen (dpy); + vi_in.screen = screen_number (screen); vi_in.visualid = XVisualIDFromVisual (visual); vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask, &vi_in, &out_count); @@ -231,13 +424,31 @@ get_visual_class (dpy, visual) return c; } +Bool +has_writable_cells (Screen *screen, Visual *visual) +{ + switch (visual_class (screen, visual)) + { + case GrayScale: /* Mappable grays. */ + case PseudoColor: /* Mappable colors. */ + case DirectColor: /* Like TrueColor, but with three colormaps: + one each for red, green, and blue. */ + return True; + case StaticGray: /* Fixed grays. */ + case TrueColor: /* Fixed colors. */ + case StaticColor: /* Like PseudoColor with an unmodifiable colormap. */ + return False; + default: + abort(); + return False; + } +} + void -describe_visual (f, dpy, visual) - FILE *f; - Display *dpy; - Visual *visual; +describe_visual (FILE *f, Screen *screen, Visual *visual, Bool private_cmap_p) { - Screen *screen = DefaultScreenOfDisplay (dpy); + char n[10]; + Display *dpy = DisplayOfScreen (screen); XVisualInfo vi_in, *vi_out; int out_count; vi_in.screen = screen_number (screen); @@ -245,7 +456,12 @@ describe_visual (f, dpy, visual) vi_out = XGetVisualInfo (dpy, (VisualScreenMask | VisualIDMask), &vi_in, &out_count); if (! vi_out) abort (); - fprintf (f, "0x%02x (%s depth: %2d, cmap: %3d)\n", + if (private_cmap_p) + sprintf(n, "%3d", vi_out->colormap_size); + else + strcpy(n, "default"); + + fprintf (f, "0x%02x (%s depth: %2d, cmap: %s)\n", (unsigned int) vi_out->visualid, (vi_out->class == StaticGray ? "StaticGray, " : vi_out->class == StaticColor ? "StaticColor," : @@ -254,13 +470,12 @@ describe_visual (f, dpy, visual) vi_out->class == PseudoColor ? "PseudoColor," : vi_out->class == DirectColor ? "DirectColor," : "UNKNOWN: "), - vi_out->depth, vi_out->colormap_size /*, vi_out->bits_per_rgb*/); + vi_out->depth, n); XFree ((char *) vi_out); } -static int -screen_number (screen) - Screen *screen; +int +screen_number (Screen *screen) { Display *dpy = DisplayOfScreen (screen); int i; @@ -268,16 +483,16 @@ screen_number (screen) if (ScreenOfDisplay (dpy, i) == screen) return i; abort (); + return 0; } int -visual_cells (dpy, visual) - Display *dpy; - Visual *visual; +visual_cells (Screen *screen, Visual *visual) { + Display *dpy = DisplayOfScreen (screen); XVisualInfo vi_in, *vi_out; int out_count, c; - vi_in.screen = DefaultScreen (dpy); + vi_in.screen = screen_number (screen); vi_in.visualid = XVisualIDFromVisual (visual); vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask, &vi_in, &out_count); @@ -286,3 +501,89 @@ visual_cells (dpy, visual) XFree ((char *) vi_out); return c; } + +Visual * +find_similar_visual(Screen *screen, Visual *old_visual) +{ + Display *dpy = DisplayOfScreen (screen); + XVisualInfo vi_in, *vi_out; + Visual *result = 0; + int out_count; + + vi_in.screen = screen_number (screen); + vi_in.class = visual_class (screen, old_visual); + vi_in.depth = visual_depth (screen, old_visual); + + /* Look for a visual of the same class and depth. + */ + vi_out = XGetVisualInfo (dpy, (VisualScreenMask | VisualClassMask | + VisualDepthMask), + &vi_in, &out_count); + if (vi_out && out_count > 0) + result = vi_out[0].visual; + if (vi_out) XFree (vi_out); + vi_out = 0; + + /* Failing that, look for a visual of the same class. + */ + if (!result) + { + vi_out = XGetVisualInfo (dpy, (VisualScreenMask | VisualClassMask), + &vi_in, &out_count); + if (vi_out && out_count > 0) + result = vi_out[0].visual; + if (vi_out) XFree (vi_out); + vi_out = 0; + } + + /* Failing that, return the default visual. */ + if (!result) + result = DefaultVisualOfScreen (screen); + + return result; +} + + +int +get_bits_per_pixel(Display *dpy, int depth) +{ + unsigned i = 0; + int count, result; + XPixmapFormatValues *formats = XListPixmapFormats(dpy, &count); + + /* XCreateImage calls _XGetBitsPerPixel to figure this out, but that function + is private to Xlib. + + For some reason, _XGetBitsPerPixel tries a hard-coded list of depths if + it doesn't find a matching pixmap format, but I (Dave Odell) couldn't + find any justification for this in the X11 spec. And the XFree86 CVS + repository doesn't quite go back far enough to shed any light on what + the deal is with that. + http://cvsweb.xfree86.org/cvsweb/xc/lib/X11/ImUtil.c + + The hard-coded list apparently was added between X11R5 and X11R6. + See . + */ + + if (!formats) return 0; + + for (;;) + { + if (i == (unsigned)count) + { + result = 0; + break; + } + + if (formats[i].depth == depth) + { + result = formats[i].bits_per_pixel; + break; + } + + ++i; + } + + XFree (formats); + return result; +}