All files imported without modification from `xscreensaver-5.45.tar.gz`.
See file headers for license details.
--- /dev/null
+/* -*- mode: c; tab-width: 4; fill-column: 128 -*- */
+/* vi: set ts=4 tw=128: */
+
+/*
+aligned_malloc.c, Copyright (c) 2014 Dave Odell <dmo2118@gmail.com>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation. No representations are made about the suitability of this
+software for any purpose. It is provided "as is" without express or
+implied warranty.
+*/
+
+#include "aligned_malloc.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <assert.h>
+#include <errno.h>
+
+/* aligned_alloc() (C11) or posix_memalign() (POSIX) are other possibilities
+ for aligned_malloc().
+ */
+
+int aligned_malloc(void **ptr, unsigned alignment, size_t size)
+{
+ void *block_start;
+ ptrdiff_t align1 = alignment - 1;
+
+ assert(alignment && !(alignment & (alignment - 1))); /* alignment must be a power of two. */
+
+ size += sizeof(void *) + align1;
+ block_start = malloc(size);
+ if(!block_start)
+ return ENOMEM;
+ *ptr = (void *)(((ptrdiff_t)block_start + sizeof(void *) + align1) & ~align1);
+ ((void **)(*ptr))[-1] = block_start;
+ return 0;
+}
+
+void aligned_free(void *ptr)
+{
+ if(ptr)
+ free(((void **)(ptr))[-1]);
+}
--- /dev/null
+/* -*- mode: c; tab-width: 4; fill-column: 128 -*- */
+/* vi: set ts=4 tw=128: */
+
+/*
+aligned_malloc.h, Copyright (c) 2014 Dave Odell <dmo2118@gmail.com>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation. No representations are made about the suitability of this
+software for any purpose. It is provided "as is" without express or
+implied warranty.
+*/
+
+#ifndef __ALIGNED_MALLOC_H__
+#define __ALIGNED_MALLOC_H__
+
+#include <stdlib.h>
+
+ /* This can't simply be named posix_memalign, since the real thing uses
+ free(), but this one can't. */
+ int aligned_malloc(void **ptr, unsigned alignment, size_t size);
+ void aligned_free(void *);
+
+#endif /* __ALIGNED_MALLOC_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 2006
+ * Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* Beauty is only skin deep, unless you've got an alpha channel. */
+
+
+#include "utils.h"
+#include "alpha.h"
+#include "visual.h"
+#include "hsv.h"
+#include "yarandom.h"
+#include "resources.h"
+
+#include <X11/Xutil.h>
+
+#ifndef countof
+# define countof(x) (sizeof(*(x))/sizeof((x)))
+#endif
+
+
+/* I don't believe this fucking language doesn't have builtin exponentiation.
+ I further can't believe that the fucking ^ character means fucking XOR!! */
+static int
+i_exp (int i, int j)
+{
+ int k = 1;
+ while (j--) k *= i;
+ return k;
+}
+
+
+static void
+merge_colors (int argc, XColor **argv, XColor *into_color, int mask,
+ Bool additive_p)
+{
+ int j;
+ *into_color = *argv [0];
+ into_color->pixel |= mask;
+
+ for (j = 1; j < argc; j++)
+ {
+# define SHORT_INC(x,y) (x = ((((x)+(y)) > 0xFFFF) ? 0xFFFF : ((x)+(y))))
+# define SHORT_DEC(x,y) (x = ((((x)-(y)) < 0) ? 0 : ((x)-(y))))
+ if (additive_p)
+ {
+ SHORT_INC (into_color->red, argv[j]->red);
+ SHORT_INC (into_color->green, argv[j]->green);
+ SHORT_INC (into_color->blue, argv[j]->blue);
+ }
+ else
+ {
+ SHORT_DEC (into_color->red, argv[j]->red);
+ SHORT_DEC (into_color->green, argv[j]->green);
+ SHORT_DEC (into_color->blue, argv[j]->blue);
+ }
+# undef SHORT_INC
+# undef SHORT_DEC
+ }
+}
+
+static void
+permute_colors (XColor *pcolors, XColor *colors,
+ int count,
+ unsigned long *plane_masks,
+ Bool additive_p)
+{
+ int out = 0;
+ int max = i_exp (2, count);
+ if (count > 31) abort ();
+ for (out = 1; out < max; out++)
+ {
+ XColor *argv [32];
+ int this_mask = 0;
+ int argc = 0;
+ int bit;
+ for (bit = 0; bit < 32; bit++)
+ if (out & (1<<bit))
+ {
+ argv [argc++] = &pcolors [bit];
+ this_mask |= plane_masks [bit];
+ }
+ merge_colors (argc, argv, &colors [out-1], this_mask, additive_p);
+ }
+}
+
+
+static int
+allocate_color_planes (Display *dpy, Colormap cmap,
+ int nplanes, unsigned long *plane_masks,
+ unsigned long *base_pixel_ret)
+{
+ while (nplanes > 1 &&
+ !XAllocColorCells (dpy, cmap, False, plane_masks, nplanes,
+ base_pixel_ret, 1))
+ nplanes--;
+
+ return nplanes;
+}
+
+
+static void
+initialize_transparency_colormap (Display *dpy, Colormap cmap,
+ int nplanes,
+ unsigned long base_pixel,
+ unsigned long *plane_masks,
+ XColor *colors,
+ Bool additive_p)
+{
+ int i;
+ int total_colors = i_exp (2, nplanes);
+ XColor *all_colors = (XColor *) calloc (total_colors, sizeof (XColor));
+
+ for (i = 0; i < nplanes; i++)
+ colors[i].pixel = base_pixel | plane_masks [i];
+ permute_colors (colors, all_colors, nplanes, plane_masks, additive_p);
+
+ /* clone the default background of the window into our "base" pixel */
+ all_colors [total_colors - 1].pixel =
+ get_pixel_resource (dpy, cmap, "background", "Background");
+ XQueryColor (dpy, cmap, &all_colors [total_colors - 1]);
+ all_colors [total_colors - 1].pixel = base_pixel;
+
+ for (i = 0; i < total_colors; i++)
+ all_colors[i].flags = DoRed|DoGreen|DoBlue;
+ XStoreColors (dpy, cmap, all_colors, total_colors);
+ XFree ((XPointer) all_colors);
+}
+
+
+Bool
+allocate_alpha_colors (Screen *screen, Visual *visual, Colormap cmap,
+ int *nplanesP, Bool additive_p,
+ unsigned long **plane_masks,
+ unsigned long *base_pixelP)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XColor *colors;
+ int nplanes = *nplanesP;
+ int i;
+
+ if (!has_writable_cells (screen, visual))
+ cmap = 0;
+
+ if (!cmap) /* A TrueColor visual, or similar. */
+ {
+ int depth = visual_depth (screen, visual);
+ unsigned long masks;
+ XVisualInfo vi_in, *vi_out;
+
+ /* Find out which bits the R, G, and B components actually occupy
+ on this visual. */
+ vi_in.screen = screen_number (screen);
+ vi_in.visualid = XVisualIDFromVisual (visual);
+ vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
+ &vi_in, &i);
+ if (! vi_out) abort ();
+ masks = vi_out[0].red_mask | vi_out[0].green_mask | vi_out[0].blue_mask;
+ XFree ((char *) vi_out);
+
+ if (nplanes > depth)
+ nplanes = depth;
+ *nplanesP = nplanes;
+ *base_pixelP = 0;
+ *plane_masks = (unsigned long *) calloc(sizeof(unsigned long), nplanes);
+
+ /* Pick the planar values randomly, but constrain them to fall within
+ the bit positions of the R, G, and B fields. */
+ for (i = 0; i < nplanes; i++)
+ (*plane_masks)[i] = random() & masks;
+
+ }
+ else /* A PseudoColor visual, or similar. */
+ {
+ if (nplanes > 31) nplanes = 31;
+ *plane_masks = (unsigned long *) malloc(sizeof(unsigned long) * nplanes);
+
+ nplanes = allocate_color_planes (dpy, cmap, nplanes, *plane_masks,
+ base_pixelP);
+ *nplanesP = nplanes;
+
+ if (nplanes <= 1)
+ {
+ free(*plane_masks);
+ *plane_masks = 0;
+ return False;
+ }
+
+ colors = (XColor *) calloc (nplanes, sizeof (XColor));
+ for (i = 0; i < nplanes; i++)
+ {
+ /* pick the base colors. If we are in subtractive mode, pick higher
+ intensities. */
+ hsv_to_rgb (random () % 360,
+ frand (1.0),
+ frand (0.5) + (additive_p ? 0.2 : 0.5),
+ &colors[i].red,
+ &colors[i].green,
+ &colors[i].blue);
+ }
+ initialize_transparency_colormap (dpy, cmap, nplanes,
+ *base_pixelP, *plane_masks, colors,
+ additive_p);
+ XFree ((XPointer) colors);
+ }
+ return True;
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997
+ * Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __XSCREENSAVER_ALPHA_H__
+#define __XSCREENSAVER_ALPHA_H__
+
+extern Bool allocate_alpha_colors (Screen *screen, Visual *visual,
+ Colormap cmap,
+ int *nplanesP, Bool additive_p,
+ unsigned long **plane_masks,
+ unsigned long *base_pixelP);
+
+#endif /* __XSCREENSAVER_ALPHA_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1997-2018 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* This file contains some utility routines for randomly picking the colors
+ to hack the screen with.
+ */
+
+#include "utils.h"
+#include "hsv.h"
+#include "yarandom.h"
+#include "visual.h"
+#include "colors.h"
+
+extern char *progname;
+
+void
+free_colors (Screen *screen, Colormap cmap, XColor *colors, int ncolors)
+{
+ Display *dpy = screen ? DisplayOfScreen (screen) : 0;
+ int i;
+ if (ncolors > 0)
+ {
+ unsigned long *pixels = (unsigned long *)
+ malloc(sizeof(*pixels) * ncolors);
+ for (i = 0; i < ncolors; i++)
+ pixels[i] = colors[i].pixel;
+ XFreeColors (dpy, cmap, pixels, ncolors, 0L);
+ free(pixels);
+ }
+}
+
+
+void
+allocate_writable_colors (Screen *screen, Colormap cmap,
+ unsigned long *pixels, int *ncolorsP)
+{
+ Display *dpy = screen ? DisplayOfScreen (screen) : 0;
+ int desired = *ncolorsP;
+ int got = 0;
+ int requested = desired;
+ unsigned long *new_pixels = pixels;
+
+ *ncolorsP = 0;
+ while (got < desired
+ && requested > 0)
+ {
+ if (desired - got < requested)
+ requested = desired - got;
+
+ if (XAllocColorCells (dpy, cmap, False, 0, 0, new_pixels, requested))
+ {
+ /* Got all the pixels we asked for. */
+ new_pixels += requested;
+ got += requested;
+ }
+ else
+ {
+ /* We didn't get all/any of the pixels we asked for. This time, ask
+ for half as many. (If we do get all that we ask for, we ask for
+ the same number again next time, so we only do O(log(n)) server
+ roundtrips.)
+ */
+ requested = requested / 2;
+ }
+ }
+ *ncolorsP += got;
+}
+
+
+static void
+complain (int wanted_colors, int got_colors,
+ Bool wanted_writable, Bool got_writable)
+{
+ if (got_colors > wanted_colors - 10)
+ /* don't bother complaining if we're within ten pixels. */
+ return;
+
+ if (wanted_writable && !got_writable)
+ fprintf (stderr,
+ "%s: wanted %d writable colors; got %d read-only colors.\n",
+ progname, wanted_colors, got_colors);
+ else
+ fprintf (stderr, "%s: wanted %d%s colors; got %d.\n",
+ progname, wanted_colors, (got_writable ? " writable" : ""),
+ got_colors);
+}
+
+
+
+void
+make_color_ramp (Screen *screen, Visual *visual, Colormap cmap,
+ int h1, double s1, double v1, /* 0-360, 0-1.0, 0-1.0 */
+ int h2, double s2, double v2, /* 0-360, 0-1.0, 0-1.0 */
+ XColor *colors, int *ncolorsP,
+ Bool closed_p,
+ Bool allocate_p,
+ Bool *writable_pP)
+{
+ Display *dpy = screen ? DisplayOfScreen (screen) : 0;
+ Bool verbose_p = True; /* argh. */
+ int i;
+ int total_ncolors = *ncolorsP;
+ int ncolors, wanted;
+ Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
+ double dh, ds, dv; /* deltas */
+
+ wanted = total_ncolors;
+ if (closed_p)
+ wanted = (wanted / 2) + 1;
+
+ /* If this visual doesn't support writable cells, don't bother trying.
+ */
+ if (wanted_writable && !has_writable_cells(screen, visual))
+ *writable_pP = False;
+
+ AGAIN:
+ ncolors = total_ncolors;
+
+ memset (colors, 0, (*ncolorsP) * sizeof(*colors));
+
+ if (closed_p)
+ ncolors = (ncolors / 2) + 1;
+
+ /* Note: unlike other routines in this module, this function assumes that
+ if h1 and h2 are more than 180 degrees apart, then the desired direction
+ is always from h1 to h2 (rather than the shorter path.) make_uniform
+ depends on this.
+ */
+ dh = ((double)h2 - (double)h1) / ncolors;
+ ds = (s2 - s1) / ncolors;
+ dv = (v2 - v1) / ncolors;
+
+ for (i = 0; i < ncolors; i++)
+ {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ hsv_to_rgb ((int) (h1 + (i*dh)), (s1 + (i*ds)), (v1 + (i*dv)),
+ &colors[i].red, &colors[i].green, &colors[i].blue);
+ }
+
+ if (closed_p)
+ for (i = ncolors; i < *ncolorsP; i++)
+ colors[i] = colors[(*ncolorsP)-i];
+
+ if (!allocate_p)
+ return;
+
+ if (writable_pP && *writable_pP)
+ {
+ unsigned long *pixels = (unsigned long *)
+ malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
+
+ /* allocate_writable_colors() won't do here, because we need exactly this
+ number of cells, or the color sequence we've chosen won't fit. */
+ if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
+ {
+ free(pixels);
+ goto FAIL;
+ }
+
+ for (i = 0; i < *ncolorsP; i++)
+ colors[i].pixel = pixels[i];
+ free (pixels);
+
+ XStoreColors (dpy, cmap, colors, *ncolorsP);
+ }
+ else
+ {
+ for (i = 0; i < *ncolorsP; i++)
+ {
+ XColor color;
+ color = colors[i];
+ if (XAllocColor (dpy, cmap, &color))
+ {
+ colors[i].pixel = color.pixel;
+ }
+ else
+ {
+ free_colors (screen, cmap, colors, i);
+ goto FAIL;
+ }
+ }
+ }
+
+ goto WARN;
+
+ FAIL:
+ /* we weren't able to allocate all the colors we wanted;
+ decrease the requested number and try again.
+ */
+ total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
+ total_ncolors > 100 ? total_ncolors - 10 :
+ total_ncolors > 75 ? total_ncolors - 5 :
+ total_ncolors > 25 ? total_ncolors - 3 :
+ total_ncolors > 10 ? total_ncolors - 2 :
+ total_ncolors > 2 ? total_ncolors - 1 :
+ 0);
+ *ncolorsP = total_ncolors;
+ ncolors = total_ncolors;
+ if (total_ncolors > 0)
+ goto AGAIN;
+
+ WARN:
+
+ if (verbose_p &&
+ /* don't warn if we got 0 writable colors -- probably TrueColor. */
+ (ncolors != 0 || !wanted_writable))
+ complain (wanted, ncolors, wanted_writable,
+ (wanted_writable && writable_pP && *writable_pP));
+}
+
+
+#define MAXPOINTS 50 /* yeah, so I'm lazy */
+
+
+static void
+make_color_path (Screen *screen, Visual *visual, Colormap cmap,
+ int npoints, int *h, double *s, double *v,
+ XColor *colors, int *ncolorsP,
+ Bool allocate_p,
+ Bool *writable_pP)
+{
+ Display *dpy = screen ? DisplayOfScreen (screen) : 0;
+ int i, j, k;
+ int total_ncolors = *ncolorsP;
+
+ int ncolors[MAXPOINTS]; /* number of pixels per edge */
+ double dh[MAXPOINTS]; /* distance between pixels, per edge (0 - 360.0) */
+ double ds[MAXPOINTS]; /* distance between pixels, per edge (0 - 1.0) */
+ double dv[MAXPOINTS]; /* distance between pixels, per edge (0 - 1.0) */
+
+ if (npoints == 0)
+ {
+ *ncolorsP = 0;
+ return;
+ }
+ else if (npoints == 2) /* using make_color_ramp() will be faster */
+ {
+ make_color_ramp (screen, visual, cmap,
+ h[0], s[0], v[0], h[1], s[1], v[1],
+ colors, ncolorsP,
+ True, /* closed_p */
+ allocate_p, writable_pP);
+ return;
+ }
+ else if (npoints >= MAXPOINTS)
+ {
+ npoints = MAXPOINTS-1;
+ }
+
+ AGAIN:
+
+ {
+ double DH[MAXPOINTS]; /* Distance between H values in the shortest
+ direction around the circle, that is, the
+ distance between 10 and 350 is 20.
+ (Range is 0 - 360.0.)
+ */
+ double edge[MAXPOINTS]; /* lengths of edges in unit HSV space. */
+ double ratio[MAXPOINTS]; /* proportions of the edges (total 1.0) */
+ double circum = 0;
+ double one_point_oh = 0; /* (debug) */
+
+ for (i = 0; i < npoints; i++)
+ {
+ int j = (i+1) % npoints;
+ double d = ((double) (h[i] - h[j])) / 360;
+ if (d < 0) d = -d;
+ if (d > 0.5) d = 0.5 - (d - 0.5);
+ DH[i] = d;
+ }
+
+ for (i = 0; i < npoints; i++)
+ {
+ int j = (i+1) % npoints;
+ edge[i] = sqrt((DH[i] * DH[j]) +
+ ((s[j] - s[i]) * (s[j] - s[i])) +
+ ((v[j] - v[i]) * (v[j] - v[i])));
+ circum += edge[i];
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "\ncolors:");
+ for (i=0; i < npoints; i++)
+ fprintf(stderr, " (%d, %.3f, %.3f)", h[i], s[i], v[i]);
+ fprintf(stderr, "\nlengths:");
+ for (i=0; i < npoints; i++)
+ fprintf(stderr, " %.3f", edge[i]);
+#endif /* DEBUG */
+
+ if (circum < 0.0001)
+ goto FAIL;
+
+ for (i = 0; i < npoints; i++)
+ {
+ ratio[i] = edge[i] / circum;
+ one_point_oh += ratio[i];
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "\nratios:");
+ for (i=0; i < npoints; i++)
+ fprintf(stderr, " %.3f", ratio[i]);
+#endif /* DEBUG */
+
+ if (one_point_oh < 0.99999 || one_point_oh > 1.00001)
+ abort();
+
+ /* space the colors evenly along the circumference -- that means that the
+ number of pixels on a edge is proportional to the length of that edge
+ (relative to the lengths of the other edges.)
+ */
+ for (i = 0; i < npoints; i++)
+ ncolors[i] = total_ncolors * ratio[i];
+
+
+#ifdef DEBUG
+ fprintf(stderr, "\npixels:");
+ for (i=0; i < npoints; i++)
+ fprintf(stderr, " %d", ncolors[i]);
+ fprintf(stderr, " (%d)\n", total_ncolors);
+#endif /* DEBUG */
+
+ for (i = 0; i < npoints; i++)
+ {
+ int j = (i+1) % npoints;
+
+ if (ncolors[i] > 0)
+ {
+ dh[i] = 360 * (DH[i] / ncolors[i]);
+ ds[i] = (s[j] - s[i]) / ncolors[i];
+ dv[i] = (v[j] - v[i]) / ncolors[i];
+ }
+ }
+ }
+
+ memset (colors, 0, (*ncolorsP) * sizeof(*colors));
+
+ k = 0;
+ for (i = 0; i < npoints; i++)
+ {
+ int distance = h[(i+1) % npoints] - h[i];
+ int direction = (distance >= 0 ? -1 : 1);
+
+ if (distance <= 180 && distance >= -180)
+ direction = -direction;
+
+#ifdef DEBUG
+ fprintf (stderr, "point %d: %3d %.2f %.2f\n",
+ i, h[i], s[i], v[i]);
+ fprintf(stderr, " h[i]=%d dh[i]=%.2f ncolors[i]=%d\n",
+ h[i], dh[i], ncolors[i]);
+#endif /* DEBUG */
+ for (j = 0; j < ncolors[i]; j++, k++)
+ {
+ double hh = (h[i] + (j * dh[i] * direction));
+ if (hh < 0) hh += 360;
+ else if (hh > 360) hh -= 0;
+ colors[k].flags = DoRed|DoGreen|DoBlue;
+ hsv_to_rgb ((int)
+ hh,
+ (s[i] + (j * ds[i])),
+ (v[i] + (j * dv[i])),
+ &colors[k].red, &colors[k].green, &colors[k].blue);
+#ifdef DEBUG
+ fprintf (stderr, "point %d+%d: %.2f %.2f %.2f %04X %04X %04X\n",
+ i, j,
+ hh,
+ (s[i] + (j * ds[i])),
+ (v[i] + (j * dv[i])),
+ colors[k].red, colors[k].green, colors[k].blue);
+#endif /* DEBUG */
+ }
+ }
+
+ /* Floating-point round-off can make us decide to use fewer colors. */
+ if (k < *ncolorsP)
+ {
+ /* We used to just return the smaller set of colors, but that meant
+ that after re-generating the color map repeatedly, the number of
+ colors in use would tend toward 0, which not only looked bad but
+ also often caused crashes. So instead, just duplicate the last
+ color to pad things out. */
+# if 0
+ *ncolorsP = k;
+ if (k <= 0)
+ return;
+# else
+ if (k <= 0)
+ return;
+ for (i = k; i < *ncolorsP; i++)
+ /* #### Should duplicate the allocation of the color cell here
+ to avoid a double-color-free on PseudoColor, but it's 2018
+ and I don't care, */
+ colors[i] = colors[i-1];
+# endif
+ }
+
+ if (!allocate_p)
+ return;
+
+ if (writable_pP && *writable_pP)
+ {
+ unsigned long *pixels = (unsigned long *)
+ malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
+
+ /* allocate_writable_colors() won't do here, because we need exactly this
+ number of cells, or the color sequence we've chosen won't fit. */
+ if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
+ {
+ free(pixels);
+ goto FAIL;
+ }
+
+ for (i = 0; i < *ncolorsP; i++)
+ colors[i].pixel = pixels[i];
+ free (pixels);
+
+ XStoreColors (dpy, cmap, colors, *ncolorsP);
+ }
+ else
+ {
+ for (i = 0; i < *ncolorsP; i++)
+ {
+ XColor color;
+ color = colors[i];
+ if (XAllocColor (dpy, cmap, &color))
+ {
+ colors[i].pixel = color.pixel;
+ }
+ else
+ {
+ free_colors (screen, cmap, colors, i);
+ goto FAIL;
+ }
+ }
+ }
+
+ return;
+
+ FAIL:
+ /* we weren't able to allocate all the colors we wanted;
+ decrease the requested number and try again.
+ */
+ total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
+ total_ncolors > 100 ? total_ncolors - 10 :
+ total_ncolors > 75 ? total_ncolors - 5 :
+ total_ncolors > 25 ? total_ncolors - 3 :
+ total_ncolors > 10 ? total_ncolors - 2 :
+ total_ncolors > 2 ? total_ncolors - 1 :
+ 0);
+ *ncolorsP = total_ncolors;
+ if (total_ncolors > 0)
+ goto AGAIN;
+}
+
+
+void
+make_color_loop (Screen *screen, Visual *visual, Colormap cmap,
+ int h0, double s0, double v0, /* 0-360, 0-1.0, 0-1.0 */
+ int h1, double s1, double v1, /* 0-360, 0-1.0, 0-1.0 */
+ int h2, double s2, double v2, /* 0-360, 0-1.0, 0-1.0 */
+ XColor *colors, int *ncolorsP,
+ Bool allocate_p,
+ Bool *writable_pP)
+{
+ Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
+
+ int h[3];
+ double s[3], v[3];
+ h[0] = h0; h[1] = h1; h[2] = h2;
+ s[0] = s0; s[1] = s1; s[2] = s2;
+ v[0] = v0; v[1] = v1; v[2] = v2;
+
+ /* If this visual doesn't support writable cells, don't bother trying.
+ */
+ if (wanted_writable && !has_writable_cells(screen, visual))
+ *writable_pP = False;
+
+ make_color_path (screen, visual, cmap,
+ 3, h, s, v,
+ colors, ncolorsP,
+ allocate_p, writable_pP);
+}
+
+
+void
+make_smooth_colormap (Screen *screen, Visual *visual, Colormap cmap,
+ XColor *colors, int *ncolorsP,
+ Bool allocate_p,
+ Bool *writable_pP,
+ Bool verbose_p)
+{
+ int npoints;
+ int ncolors = *ncolorsP;
+ Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
+ int i;
+ int h[MAXPOINTS];
+ double s[MAXPOINTS];
+ double v[MAXPOINTS];
+ double total_s = 0;
+ double total_v = 0;
+ int loop = 0;
+
+ if (*ncolorsP <= 0) return;
+
+ {
+ int n = random() % 20;
+ if (n <= 5) npoints = 2; /* 30% of the time */
+ else if (n <= 15) npoints = 3; /* 50% of the time */
+ else if (n <= 18) npoints = 4; /* 15% of the time */
+ else npoints = 5; /* 5% of the time */
+ }
+
+ REPICK_ALL_COLORS:
+ for (i = 0; i < npoints; i++)
+ {
+ REPICK_THIS_COLOR:
+ if (++loop > 10000) abort();
+ h[i] = random() % 360;
+ s[i] = frand(1.0);
+ v[i] = frand(0.8) + 0.2;
+
+ /* Make sure that no two adjascent colors are *too* close together.
+ If they are, try again.
+ */
+ if (i > 0)
+ {
+ int j = (i+1 == npoints) ? 0 : (i-1);
+ double hi = ((double) h[i]) / 360;
+ double hj = ((double) h[j]) / 360;
+ double dh = hj - hi;
+ double distance;
+ if (dh < 0) dh = -dh;
+ if (dh > 0.5) dh = 0.5 - (dh - 0.5);
+ distance = sqrt ((dh * dh) +
+ ((s[j] - s[i]) * (s[j] - s[i])) +
+ ((v[j] - v[i]) * (v[j] - v[i])));
+ if (distance < 0.2)
+ goto REPICK_THIS_COLOR;
+ }
+ total_s += s[i];
+ total_v += v[i];
+ }
+
+ /* If the average saturation or intensity are too low, repick the colors,
+ so that we don't end up with a black-and-white or too-dark map.
+ */
+ if (total_s / npoints < 0.2)
+ goto REPICK_ALL_COLORS;
+ if (total_v / npoints < 0.3)
+ goto REPICK_ALL_COLORS;
+
+ /* If this visual doesn't support writable cells, don't bother trying.
+ */
+ if (wanted_writable && !has_writable_cells(screen, visual))
+ *writable_pP = False;
+
+ RETRY_NON_WRITABLE:
+ make_color_path (screen, visual, cmap, npoints, h, s, v, colors, &ncolors,
+ allocate_p, writable_pP);
+
+ /* If we tried for writable cells and got none, try for non-writable. */
+ if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
+ {
+ *writable_pP = False;
+ goto RETRY_NON_WRITABLE;
+ }
+
+ if (verbose_p)
+ complain(*ncolorsP, ncolors, wanted_writable,
+ wanted_writable && *writable_pP);
+
+ *ncolorsP = ncolors;
+}
+
+
+void
+make_uniform_colormap (Screen *screen, Visual *visual, Colormap cmap,
+ XColor *colors, int *ncolorsP,
+ Bool allocate_p,
+ Bool *writable_pP,
+ Bool verbose_p)
+{
+ int ncolors = *ncolorsP;
+ Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
+
+ double S = ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
+ double V = ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
+
+ if (*ncolorsP <= 0) return;
+
+ /* If this visual doesn't support writable cells, don't bother trying. */
+ if (wanted_writable && !has_writable_cells(screen, visual))
+ *writable_pP = False;
+
+ RETRY_NON_WRITABLE:
+ make_color_ramp(screen, visual, cmap,
+ 0, S, V,
+ 359, S, V,
+ colors, &ncolors,
+ False, allocate_p, writable_pP);
+
+ /* If we tried for writable cells and got none, try for non-writable. */
+ if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
+ {
+ ncolors = *ncolorsP;
+ *writable_pP = False;
+ goto RETRY_NON_WRITABLE;
+ }
+
+ if (verbose_p)
+ complain(*ncolorsP, ncolors, wanted_writable,
+ wanted_writable && *writable_pP);
+
+ *ncolorsP = ncolors;
+}
+
+
+void
+make_random_colormap (Screen *screen, Visual *visual, Colormap cmap,
+ XColor *colors, int *ncolorsP,
+ Bool bright_p,
+ Bool allocate_p,
+ Bool *writable_pP,
+ Bool verbose_p)
+{
+ Display *dpy = screen ? DisplayOfScreen (screen) : 0;
+ Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
+ int ncolors = *ncolorsP;
+ int i;
+
+ if (*ncolorsP <= 0) return;
+
+ /* If this visual doesn't support writable cells, don't bother trying. */
+ if (wanted_writable && !has_writable_cells(screen, visual))
+ *writable_pP = False;
+
+ RETRY_ALL:
+ for (i = 0; i < ncolors; i++)
+ {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ if (bright_p)
+ {
+ int H = random() % 360; /* range 0-360 */
+ double S = ((double) (random()%70) + 30)/100.0; /* range 30%-100% */
+ double V = ((double) (random()%34) + 66)/100.0; /* range 66%-100% */
+ hsv_to_rgb (H, S, V,
+ &colors[i].red, &colors[i].green, &colors[i].blue);
+ }
+ else
+ {
+ colors[i].red = random() % 0xFFFF;
+ colors[i].green = random() % 0xFFFF;
+ colors[i].blue = random() % 0xFFFF;
+ }
+ }
+
+ /* If there are a small number of colors, make sure at least the first
+ two contrast well.
+ */
+ if (!bright_p && ncolors <= 4)
+ {
+ int h0, h1;
+ double s0, s1, v0, v1;
+ rgb_to_hsv (colors[0].red, colors[0].green, colors[0].blue, &h0,&s0,&v0);
+ rgb_to_hsv (colors[1].red, colors[1].green, colors[1].blue, &h1,&s1,&v1);
+ if (fabs (v1-v0) < 0.5)
+ goto RETRY_ALL;
+ }
+
+ if (!allocate_p)
+ return;
+
+ RETRY_NON_WRITABLE:
+ if (writable_pP && *writable_pP)
+ {
+ unsigned long *pixels = (unsigned long *)
+ malloc(sizeof(*pixels) * (ncolors + 1));
+
+ allocate_writable_colors (screen, cmap, pixels, &ncolors);
+ if (ncolors > 0)
+ for (i = 0; i < ncolors; i++)
+ colors[i].pixel = pixels[i];
+ free (pixels);
+ if (ncolors > 0)
+ XStoreColors (dpy, cmap, colors, ncolors);
+ }
+ else
+ {
+ for (i = 0; i < ncolors; i++)
+ {
+ XColor color;
+ color = colors[i];
+ if (!XAllocColor (dpy, cmap, &color))
+ break;
+ colors[i].pixel = color.pixel;
+ }
+ ncolors = i;
+ }
+
+ /* If we tried for writable cells and got none, try for non-writable. */
+ if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
+ {
+ ncolors = *ncolorsP;
+ *writable_pP = False;
+ goto RETRY_NON_WRITABLE;
+ }
+
+ if (verbose_p)
+ complain(*ncolorsP, ncolors, wanted_writable,
+ wanted_writable && *writable_pP);
+
+ *ncolorsP = ncolors;
+}
+
+
+void
+rotate_colors (Screen *screen, Colormap cmap,
+ XColor *colors, int ncolors, int distance)
+{
+ Display *dpy = screen ? DisplayOfScreen (screen) : 0;
+ int i;
+ XColor *colors2;
+ if (ncolors < 2) return;
+ colors2 = (XColor *) malloc(sizeof(*colors2) * ncolors);
+ distance = distance % ncolors;
+ for (i = 0; i < ncolors; i++)
+ {
+ int j = i - distance;
+ if (j >= ncolors) j -= ncolors;
+ if (j < 0) j += ncolors;
+ colors2[i] = colors[j];
+ colors2[i].pixel = colors[i].pixel;
+ }
+ XStoreColors (dpy, cmap, colors2, ncolors);
+ XFlush(dpy);
+ memcpy(colors, colors2, sizeof(*colors) * ncolors);
+ free(colors2);
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-2013 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __COLORS_H__
+#define __COLORS_H__
+
+/* Like XFreeColors, but works on `XColor *' instead of `unsigned long *'
+ */
+extern void free_colors (Screen *, Colormap, XColor *, int ncolors);
+
+
+/* Allocates writable, non-contiguous color cells. The number requested is
+ passed in *ncolorsP, and the number actually allocated is returned there.
+ (Unlike XAllocColorCells(), this will allocate as many as it can, instead
+ of failing if they can't all be allocated.)
+ */
+extern void allocate_writable_colors (Screen *, Colormap,
+ unsigned long *pixels, int *ncolorsP);
+
+
+/* Generates a sequence of colors evenly spaced between the given pair
+ of HSV coordinates.
+
+ If closed_p is true, the colors will go from the first point to the
+ second then back to the first.
+
+ If allocate_p is true, the colors will be allocated from the map;
+ if enough colors can't be allocated, we will try for less, and the
+ result will be returned to ncolorsP.
+
+ If writable_p is true, writable color cells will be allocated;
+ otherwise, read-only cells will be allocated.
+
+ If allocate_p is false, screen and cmap are unused (OpenGL usage).
+ */
+extern void make_color_ramp (Screen *, Visual *, Colormap,
+ int h1, double s1, double v1,
+ int h2, double s2, double v2,
+ XColor *colors, int *ncolorsP,
+ Bool closed_p,
+ Bool allocate_p,
+ Bool *writable_pP);
+
+/* Generates a sequence of colors evenly spaced around the triangle
+ indicated by the thee HSV coordinates.
+
+ If allocate_p is true, the colors will be allocated from the map;
+ if enough colors can't be allocated, we will try for less, and the
+ result will be returned to ncolorsP.
+
+ If writable_p is true, writable color cells will be allocated;
+ otherwise, read-only cells will be allocated.
+
+ If allocate_p is false, screen, visual and cmap are unused (OpenGL usage).
+ */
+extern void make_color_loop (Screen *, Visual *, Colormap,
+ int h1, double s1, double v1,
+ int h2, double s2, double v2,
+ int h3, double s3, double v3,
+ XColor *colors, int *ncolorsP,
+ Bool allocate_p,
+ Bool *writable_pP);
+
+
+/* Allocates a hopefully-interesting colormap, which will be a closed loop
+ without any sudden transitions.
+
+ If allocate_p is true, the colors will be allocated from the map;
+ if enough colors can't be allocated, we will try for less, and the
+ result will be returned to ncolorsP. An error message will be
+ printed on stderr (if verbose_p).
+
+ If *writable_pP is true, writable color cells will be allocated;
+ otherwise, read-only cells will be allocated. If no writable cells
+ cannot be allocated, we will try to allocate unwritable cells
+ instead, and print a message on stderr to that effect (if verbose_p).
+
+ If allocate_p is false, screen, visual and cmap are unused (OpenGL usage).
+ */
+extern void make_smooth_colormap (Screen *, Visual *, Colormap,
+ XColor *colors, int *ncolorsP,
+ Bool allocate_p,
+ Bool *writable_pP,
+ Bool verbose_p);
+
+/* Allocates a uniform colormap which touches each hue of the spectrum,
+ evenly spaced. The saturation and intensity are chosen randomly, but
+ will be high enough to be visible.
+
+ If allocate_p is true, the colors will be allocated from the map;
+ if enough colors can't be allocated, we will try for less, and the
+ result will be returned to ncolorsP. An error message will be
+ printed on stderr (if verbose_p).
+
+ If *writable_pP is true, writable color cells will be allocated;
+ otherwise, read-only cells will be allocated. If no writable cells
+ cannot be allocated, we will try to allocate unwritable cells
+ instead, and print a message on stderr to that effect (if verbose_p).
+
+ If allocate_p is false, screen, visual and cmap are unused (OpenGL usage).
+ */
+extern void make_uniform_colormap (Screen *, Visual *, Colormap,
+ XColor *colors, int *ncolorsP,
+ Bool allocate_p,
+ Bool *writable_pP,
+ Bool verbose_p);
+
+/* Allocates a random colormap (the colors are unrelated to one another.)
+ If `bright_p' is false, the colors will be completely random; if it is
+ true, all of the colors will be bright enough to see on a black background.
+
+ If allocate_p is true, the colors will be allocated from the map;
+ if enough colors can't be allocated, we will try for less, and the
+ result will be returned to ncolorsP. An error message will be
+ printed on stderr (if verbose_p).
+
+ If *writable_pP is true, writable color cells will be allocated;
+ otherwise, read-only cells will be allocated. If no writable cells
+ cannot be allocated, we will try to allocate unwritable cells
+ instead, and print a message on stderr to that effect (if verbose_p).
+
+ If allocate_p is false, screen, visual and cmap are unused (OpenGL usage).
+ */
+extern void make_random_colormap (Screen *, Visual *, Colormap,
+ XColor *colors, int *ncolorsP,
+ Bool bright_p,
+ Bool allocate_p,
+ Bool *writable_pP,
+ Bool verbose_p);
+
+
+/* Assuming that the array of colors indicates the current state of a set
+ of writable color cells, this rotates the contents of the array by
+ `distance' steps, moving the colors of cell N to cell (N - distance).
+ */
+extern void rotate_colors (Screen *, Colormap,
+ XColor *, int ncolors, int distance);
+
+#endif /* __COLORS_H__ */
--- /dev/null
+/* erase.c: Erase the screen in various more or less interesting ways.
+ * Copyright (c) 1997-2008 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Portions (c) 1997 by Johannes Keukelaar <johannes@nada.kth.se>:
+ * Permission to use in any way granted. Provided "as is" without expressed
+ * or implied warranty. NO WARRANTY, NO EXPRESSION OF SUITABILITY FOR ANY
+ * PURPOSE. (I.e.: Use in any way, but at your own risk!)
+ */
+
+#include "utils.h"
+#include "yarandom.h"
+#include "usleep.h"
+#include "resources.h"
+#include "erase.h"
+#include <sys/time.h> /* for gettimeofday() */
+
+extern char *progname;
+
+#undef countof
+#define countof(x) (sizeof(x)/sizeof(*(x)))
+
+typedef void (*Eraser) (eraser_state *);
+
+struct eraser_state {
+ Display *dpy;
+ Window window;
+ GC fg_gc, bg_gc;
+ int width, height;
+ Eraser fn;
+
+ double start_time, stop_time;
+ double ratio, prev_ratio;
+
+ /* data for random_lines, venetian, random_squares */
+ Bool horiz_p;
+ Bool flip_p;
+ int nlines, *lines;
+
+ /* data for triple_wipe, quad_wipe */
+ Bool flip_x, flip_y;
+
+ /* data for circle_wipe, three_circle_wipe */
+ int start;
+
+ /* data for random_squares */
+ int cols;
+
+ /* data for fizzle */
+ unsigned short *fizzle_rnd;
+
+};
+
+
+static double
+double_time (void)
+{
+ struct timeval now;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(&now, &tzp);
+# else
+ gettimeofday(&now);
+# endif
+
+ return (now.tv_sec + ((double) now.tv_usec * 0.000001));
+}
+
+
+static void
+random_lines (eraser_state *st)
+{
+ int i;
+
+ if (! st->lines) /* first time */
+ {
+ st->horiz_p = (random() & 1);
+ st->nlines = (st->horiz_p ? st->height : st->width);
+ st->lines = (int *) calloc (st->nlines, sizeof(*st->lines));
+
+ for (i = 0; i < st->nlines; i++) /* every line */
+ st->lines[i] = i;
+
+ for (i = 0; i < st->nlines; i++) /* shuffle */
+ {
+ int t, r;
+ t = st->lines[i];
+ r = random() % st->nlines;
+ st->lines[i] = st->lines[r];
+ st->lines[r] = t;
+ }
+ }
+
+ for (i = st->nlines * st->prev_ratio;
+ i < st->nlines * st->ratio;
+ i++)
+ {
+ if (st->horiz_p)
+ XDrawLine (st->dpy, st->window, st->bg_gc,
+ 0, st->lines[i], st->width, st->lines[i]);
+ else
+ XDrawLine (st->dpy, st->window, st->bg_gc,
+ st->lines[i], 0, st->lines[i], st->height);
+ }
+
+ if (st->ratio >= 1.0)
+ {
+ free (st->lines);
+ st->lines = 0;
+ }
+}
+
+
+static void
+venetian (eraser_state *st)
+{
+ int i;
+ if (st->ratio == 0.0)
+ {
+ int j = 0;
+ st->horiz_p = (random() & 1);
+ st->flip_p = (random() & 1);
+ st->nlines = (st->horiz_p ? st->height : st->width);
+ st->lines = (int *) calloc (st->nlines, sizeof(*st->lines));
+
+ for (i = 0; i < st->nlines * 2; i++)
+ {
+ int line = ((i / 16) * 16) - ((i % 16) * 15);
+ if (line >= 0 && line < st->nlines)
+ st->lines[j++] = (st->flip_p ? st->nlines - line : line);
+ }
+ }
+
+
+ for (i = st->nlines * st->prev_ratio;
+ i < st->nlines * st->ratio;
+ i++)
+ {
+ if (st->horiz_p)
+ XDrawLine (st->dpy, st->window, st->bg_gc,
+ 0, st->lines[i], st->width, st->lines[i]);
+ else
+ XDrawLine (st->dpy, st->window, st->bg_gc,
+ st->lines[i], 0, st->lines[i], st->height);
+ }
+
+ if (st->ratio >= 1.0)
+ {
+ free (st->lines);
+ st->lines = 0;
+ }
+}
+
+
+static void
+triple_wipe (eraser_state *st)
+{
+ int i;
+ if (st->ratio == 0.0)
+ {
+ st->flip_x = random() & 1;
+ st->flip_y = random() & 1;
+ st->nlines = st->width + (st->height / 2);
+ st->lines = (int *) calloc (st->nlines, sizeof(*st->lines));
+
+ for (i = 0; i < st->width / 2; i++)
+ st->lines[i] = i * 2 + st->height;
+ for (i = 0; i < st->height / 2; i++)
+ st->lines[i + st->width / 2] = i*2;
+ for (i = 0; i < st->width / 2; i++)
+ st->lines[i + st->width / 2 + st->height / 2] =
+ st->width - i * 2 - (st->width % 2 ? 0 : 1) + st->height;
+ }
+
+ for (i = st->nlines * st->prev_ratio;
+ i < st->nlines * st->ratio;
+ i++)
+ {
+ int x, y, x2, y2;
+
+ if (st->lines[i] < st->height)
+ x = 0, y = st->lines[i], x2 = st->width, y2 = y;
+ else
+ x = st->lines[i]-st->height, y = 0, x2 = x, y2 = st->height;
+
+ if (st->flip_x)
+ x = st->width - x, x2 = st->width - x2;
+ if (st->flip_y)
+ y = st->height - y, y2 = st->height - y2;
+
+ XDrawLine (st->dpy, st->window, st->bg_gc, x, y, x2, y2);
+ }
+
+ if (st->ratio >= 1.0)
+ {
+ free (st->lines);
+ st->lines = 0;
+ }
+}
+
+
+static void
+quad_wipe (eraser_state *st)
+{
+ int i;
+ if (st->ratio == 0.0)
+ {
+ st->flip_x = random() & 1;
+ st->flip_y = random() & 1;
+ st->nlines = st->width + st->height;
+ st->lines = (int *) calloc (st->nlines, sizeof(*st->lines));
+
+ for (i = 0; i < st->nlines/4; i++)
+ {
+ st->lines[i*4] = i*2;
+ st->lines[i*4+1] = st->height - i*2 - (st->height % 2 ? 0 : 1);
+ st->lines[i*4+2] = st->height + i*2;
+ st->lines[i*4+3] = st->height + st->width - i*2
+ - (st->width % 2 ? 0 : 1);
+ }
+ }
+
+ for (i = st->nlines * st->prev_ratio;
+ i < st->nlines * st->ratio;
+ i++)
+ {
+ int x, y, x2, y2;
+ if (st->lines[i] < st->height)
+ x = 0, y = st->lines[i], x2 = st->width, y2 = y;
+ else
+ x = st->lines[i] - st->height, y = 0, x2 = x, y2 = st->height;
+
+ if (st->flip_x)
+ x = st->width-x, x2 = st->width-x2;
+ if (st->flip_y)
+ y = st->height-y, y2 = st->height-y2;
+
+ XDrawLine (st->dpy, st->window, st->bg_gc, x, y, x2, y2);
+ }
+
+ if (st->ratio >= 1.0)
+ {
+ free (st->lines);
+ st->lines = 0;
+ }
+}
+
+
+static void
+circle_wipe (eraser_state *st)
+{
+ int rad = (st->width > st->height ? st->width : st->height);
+ int max = 360 * 64;
+ int th, oth;
+
+ if (st->ratio == 0.0)
+ {
+ st->flip_p = random() & 1;
+ st->start = random() % max;
+ }
+
+ th = max * st->ratio;
+ oth = max * st->prev_ratio;
+ if (st->flip_p)
+ {
+ th = max - th;
+ oth = max - oth;
+ }
+ XFillArc (st->dpy, st->window, st->bg_gc,
+ (st->width / 2) - rad,
+ (st->height / 2) - rad,
+ rad*2, rad*2,
+ (st->start + oth) % max,
+ th-oth);
+}
+
+
+static void
+three_circle_wipe (eraser_state *st)
+{
+ int rad = (st->width > st->height ? st->width : st->height);
+ int max = 360 * 64;
+ int th, oth;
+ int i;
+
+ if (st->ratio == 0.0)
+ st->start = random() % max;
+
+ th = max/6 * st->ratio;
+ oth = max/6 * st->prev_ratio;
+
+ for (i = 0; i < 3; i++)
+ {
+ int off = i * max / 3;
+ XFillArc (st->dpy, st->window, st->bg_gc,
+ (st->width / 2) - rad,
+ (st->height / 2) - rad,
+ rad*2, rad*2,
+ (st->start + off + oth) % max,
+ th-oth);
+ XFillArc (st->dpy, st->window, st->bg_gc,
+ (st->width / 2) - rad,
+ (st->height / 2) - rad,
+ rad*2, rad*2,
+ (st->start + off - oth) % max,
+ oth-th);
+ }
+}
+
+
+static void
+squaretate (eraser_state *st)
+{
+ int max = ((st->width > st->height ? st->width : st->height) * 2);
+ XPoint points [3];
+ int i = max * st->ratio;
+
+ if (st->ratio == 0.0)
+ st->flip_p = random() & 1;
+
+# define DRAW() \
+ if (st->flip_p) { \
+ points[0].x = st->width - points[0].x; \
+ points[1].x = st->width - points[1].x; \
+ points[2].x = st->width - points[2].x; \
+ } \
+ XFillPolygon (st->dpy, st->window, st->bg_gc, \
+ points, 3, Convex, CoordModeOrigin)
+
+ points[0].x = 0;
+ points[0].y = 0;
+ points[1].x = st->width;
+ points[1].y = 0;
+ points[2].x = 0;
+ points[2].y = points[0].y + ((i * st->height) / max);
+ DRAW();
+
+ points[0].x = 0;
+ points[0].y = 0;
+ points[1].x = 0;
+ points[1].y = st->height;
+ points[2].x = ((i * st->width) / max);
+ points[2].y = st->height;
+ DRAW();
+
+ points[0].x = st->width;
+ points[0].y = st->height;
+ points[1].x = 0;
+ points[1].y = st->height;
+ points[2].x = st->width;
+ points[2].y = st->height - ((i * st->height) / max);
+ DRAW();
+
+ points[0].x = st->width;
+ points[0].y = st->height;
+ points[1].x = st->width;
+ points[1].y = 0;
+ points[2].x = st->width - ((i * st->width) / max);
+ points[2].y = 0;
+ DRAW();
+# undef DRAW
+}
+
+
+static void
+fizzle (eraser_state *st)
+{
+ const double overshoot = 1.0625;
+
+ unsigned int x, y, i;
+ const unsigned int size = 256;
+ unsigned short *rnd;
+ XPoint *points;
+ unsigned int npoints =
+ (unsigned int)(size * size * st->ratio * overshoot) -
+ (unsigned int)(size * size * st->prev_ratio * overshoot);
+
+ if (st->ratio >= 1.0)
+ {
+ free (st->fizzle_rnd);
+ st->fizzle_rnd = NULL;
+ return;
+ }
+
+ if (! st->fizzle_rnd)
+ {
+ unsigned int chunks =
+ ((st->width + size - 1) / size) * ((st->height + size - 1) / size);
+ unsigned int i;
+
+ st->fizzle_rnd =
+ (unsigned short *) calloc (sizeof(unsigned short), chunks);
+
+ if (! st->fizzle_rnd)
+ return;
+
+ for (i = 0; i != chunks; i++)
+ st->fizzle_rnd[i] = NRAND(0x10000) | 1; /* Seed can't be 0. */
+ }
+
+ points = (XPoint *) malloc ((npoints + 1) * sizeof(*points));
+ if (! points) return;
+
+ rnd = st->fizzle_rnd;
+
+ for (y = 0; y < st->height; y += 256)
+ {
+ for (x = 0; x < st->width; x += 256)
+ {
+ unsigned int need0 = 0;
+ unsigned short r = *rnd;
+ for (i = 0; i != npoints; i++)
+ {
+ points[i].x = r % size + x;
+ points[i].y = (r >> 8) % size + y;
+
+ /* Xorshift. This has a period of 2^16, which exactly matches
+ the number of pixels in each 256x256 chunk.
+
+ Other shift constants are possible, but it's hard to say
+ which constants are best: a 2^16 period PRNG will never score
+ well on BigCrush.
+ */
+ r = (r ^ (r << 3)) & 0xffff;
+ r = r ^ (r >> 5);
+ r = (r ^ (r << 11)) & 0xffff;
+ need0 |= (r == 0x8080); /* Can be anything, really. */
+ }
+
+ if (need0)
+ {
+ points[npoints].x = x;
+ points[npoints].y = y;
+ }
+
+ XDrawPoints (st->dpy, st->window, st->bg_gc,
+ points, npoints + need0, CoordModeOrigin);
+ *rnd = r;
+ rnd++;
+ }
+ }
+ free (points);
+}
+
+
+static void
+spiral (eraser_state *st)
+{
+ int max_radius = (st->width > st->height ? st->width : st->height) * 0.7;
+ int loops = 10;
+ float max_th = M_PI * 2 * loops;
+ int i;
+ int steps = 360 * loops / 4;
+ float off;
+
+ if (st->ratio == 0.0)
+ {
+ st->flip_p = random() & 1;
+ st->start = random() % 360;
+ }
+
+ off = st->start * M_PI / 180;
+
+ for (i = steps * st->prev_ratio;
+ i < steps * st->ratio;
+ i++)
+ {
+ float th1 = i * max_th / steps;
+ float th2 = (i+1) * max_th / steps;
+ int r1 = i * max_radius / steps;
+ int r2 = (i+1) * max_radius / steps;
+ XPoint points[3];
+
+ if (st->flip_p)
+ {
+ th1 = max_th - th1;
+ th2 = max_th - th2;
+ }
+
+ points[0].x = st->width / 2;
+ points[0].y = st->height / 2;
+ points[1].x = points[0].x + r1 * cos (off + th1);
+ points[1].y = points[0].y + r1 * sin (off + th1);
+ points[2].x = points[0].x + r2 * cos (off + th2);
+ points[2].y = points[0].y + r2 * sin (off + th2);
+/* XFillRectangle(st->dpy, st->window, st->fg_gc,0,0,st->width, st->height);*/
+ XFillPolygon (st->dpy, st->window, st->bg_gc,
+ points, 3, Convex, CoordModeOrigin);
+ }
+}
+
+
+static void
+random_squares (eraser_state *st)
+{
+ int i, size, rows;
+
+ if (st->ratio == 0.0)
+ {
+ st->cols = 10 + random() % 30;
+ size = st->width / st->cols;
+ rows = (size ? (st->height / size) : 0) + 1;
+ st->nlines = st->cols * rows;
+ st->lines = (int *) calloc (st->nlines, sizeof(*st->lines));
+
+ for (i = 0; i < st->nlines; i++) /* every square */
+ st->lines[i] = i;
+
+ for (i = 0; i < st->nlines; i++) /* shuffle */
+ {
+ int t, r;
+ t = st->lines[i];
+ r = random() % st->nlines;
+ st->lines[i] = st->lines[r];
+ st->lines[r] = t;
+ }
+ }
+
+ size = st->width / st->cols;
+ rows = (size ? (st->height / size) : 0) + 1;
+
+ for (i = st->nlines * st->prev_ratio;
+ i < st->nlines * st->ratio;
+ i++)
+ {
+ int x = st->lines[i] % st->cols;
+ int y = st->lines[i] / st->cols;
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ st->width * x / st->cols,
+ st->height * y / rows,
+ size+1, size+1);
+ }
+
+ if (st->ratio >= 1.0)
+ {
+ free (st->lines);
+ st->lines = 0;
+ }
+}
+
+
+/* I first saw something like this, albeit in reverse, in an early Tetris
+ implementation for the Mac.
+ -- Torbjörn Andersson <torbjorn@dev.eurotime.se>
+ */
+static void
+slide_lines (eraser_state *st)
+{
+ int max = st->width * 1.1;
+ int nlines = 40;
+ int h = st->height / nlines;
+ int y, step;
+ int tick = 0;
+
+ if (h < 10)
+ h = 10;
+
+ step = (max * st->ratio) - (max * st->prev_ratio);
+ if (step <= 0)
+ step = 1;
+
+ for (y = 0; y < st->height; y += h)
+ {
+ if (st->width <= step)
+ ;
+ else if (tick & 1)
+ {
+ XCopyArea (st->dpy, st->window, st->window, st->fg_gc,
+ 0, y, st->width-step, h, step, y);
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ 0, y, step, h);
+ }
+ else
+ {
+ XCopyArea (st->dpy, st->window, st->window, st->fg_gc,
+ step, y, st->width-step, h, 0, y);
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ st->width-step, y, step, h);
+ }
+
+ tick++;
+ }
+}
+
+
+/* from Frederick Roeber <roeber@xigo.com> */
+static void
+losira (eraser_state *st)
+{
+ double mode1 = 0.55;
+ double mode2 = mode1 + 0.30;
+ double mode3 = 1.0;
+ int radius = 10;
+
+ if (st->ratio < mode1) /* squeeze from the sides */
+ {
+ double ratio = st->ratio / mode1;
+ double prev_ratio = st->prev_ratio / mode1;
+ int max = st->width / 2;
+ int step = (max * ratio) - (max * prev_ratio);
+
+ if (step <= 0)
+ step = 1;
+
+ /* pull from left */
+ XCopyArea (st->dpy, st->window, st->window, st->fg_gc,
+ 0, 0, max - step, st->height, step, 0);
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ 0, 0, max * ratio, st->height);
+
+ /* pull from right */
+ XCopyArea (st->dpy, st->window, st->window, st->fg_gc,
+ max+step, 0, max - step, st->height, max, 0);
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ max + max*(1-ratio), 0, max, st->height);
+
+ /* expand white from center */
+ XFillRectangle (st->dpy, st->window, st->fg_gc,
+ max - (radius * ratio), 0,
+ radius * ratio * 2, st->height);
+ }
+ else if (st->ratio < mode2) /* squeeze from the top/bottom */
+ {
+ double ratio = (st->ratio - mode1) / (mode2 - mode1);
+ int max = st->height / 2;
+
+ /* fill middle */
+ XFillRectangle (st->dpy, st->window, st->fg_gc,
+ st->width/2 - radius,
+ max * ratio,
+ radius*2, st->height * (1 - ratio));
+
+ /* fill left and right */
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ 0, 0, st->width/2 - radius, st->height);
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ st->width/2 + radius, 0, st->width/2, st->height);
+
+ /* fill top and bottom */
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ 0, 0, st->width, max * ratio);
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ 0, st->height - (max * ratio),
+ st->width, max);
+
+ /* cap top */
+ XFillArc (st->dpy, st->window, st->fg_gc,
+ st->width/2 - radius,
+ max * ratio - radius,
+ radius*2, radius*2,
+ 0, 180*64);
+
+ /* cap bottom */
+ XFillArc (st->dpy, st->window, st->fg_gc,
+ st->width/2 - radius,
+ st->height - (max * ratio + radius),
+ radius*2, radius*2,
+ 180*64, 180*64);
+ }
+ else if (st->ratio < mode3) /* starburst */
+ {
+ double ratio = (st->ratio - mode2) / (mode3 - mode2);
+ double r2 = ratio * radius * 4;
+ XArc arc[9];
+ int i;
+ int angle = 360*64/countof(arc);
+
+ for (i = 0; i < countof(arc); i++)
+ {
+ double th;
+ arc[i].angle1 = angle * i;
+ arc[i].angle2 = angle;
+ arc[i].width = radius*2 * (1 + ratio);
+ arc[i].height = radius*2 * (1 + ratio);
+ arc[i].x = st->width / 2 - radius;
+ arc[i].y = st->height / 2 - radius;
+
+ th = ((arc[i].angle1 + (arc[i].angle2 / 2)) / 64.0 / 180 * M_PI);
+
+ arc[i].x += r2 * cos (th);
+ arc[i].y -= r2 * sin (th);
+ }
+
+ XFillRectangle (st->dpy, st->window, st->bg_gc,
+ 0, 0, st->width, st->height);
+ XFillArcs (st->dpy, st->window, st->fg_gc, arc, countof(arc));
+ }
+}
+
+
+static Eraser erasers[] = {
+ random_lines,
+ venetian,
+ triple_wipe,
+ quad_wipe,
+ circle_wipe,
+ three_circle_wipe,
+ squaretate,
+ fizzle,
+ spiral,
+ random_squares,
+ slide_lines,
+ losira,
+};
+
+
+static eraser_state *
+eraser_init (Display *dpy, Window window)
+{
+ eraser_state *st = (eraser_state *) calloc (1, sizeof(*st));
+ XWindowAttributes xgwa;
+ XGCValues gcv;
+ unsigned long fg, bg;
+ double duration;
+ int which;
+ char *s;
+
+ st->dpy = dpy;
+ st->window = window;
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ st->width = xgwa.width;
+ st->height = xgwa.height;
+
+ bg = get_pixel_resource (dpy, xgwa.colormap, "background", "Background");
+ fg = get_pixel_resource (dpy, xgwa.colormap, "foreground", "Foreground");
+
+ gcv.foreground = fg;
+ gcv.background = bg;
+ st->fg_gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
+ gcv.foreground = bg;
+ gcv.background = fg;
+ st->bg_gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
+
+# ifdef HAVE_COCOA
+ /* Pretty much all of these leave turds if AA is on. */
+ jwxyz_XSetAntiAliasing (st->dpy, st->fg_gc, False);
+ jwxyz_XSetAntiAliasing (st->dpy, st->bg_gc, False);
+# endif
+
+ s = get_string_resource (dpy, "eraseMode", "Integer");
+ if (!s || !*s)
+ which = -1;
+ else
+ which = get_integer_resource(dpy, "eraseMode", "Integer");
+ if (s) free (s);
+
+ if (which < 0 || which >= countof(erasers))
+ which = random() % countof(erasers);
+ st->fn = erasers[which];
+
+ duration = get_float_resource (dpy, "eraseSeconds", "Float");
+ if (duration < 0.1 || duration > 10)
+ duration = 1;
+
+ st->start_time = double_time();
+ st->stop_time = st->start_time + duration;
+
+ XSync (st->dpy, False);
+
+ return st;
+}
+
+
+void
+eraser_free (eraser_state *st)
+{
+ XClearWindow (st->dpy, st->window); /* Final draw is black-on-black. */
+ st->ratio = 1.0;
+ st->fn (st); /* Free any memory. May also draw, but that doesn't matter. */
+ XFreeGC (st->dpy, st->fg_gc);
+ XFreeGC (st->dpy, st->bg_gc);
+ free (st);
+}
+
+eraser_state *
+erase_window (Display *dpy, Window window, eraser_state *st)
+{
+ double now, duration;
+ Bool first_p = False;
+ if (! st)
+ {
+ first_p = True;
+ st = eraser_init (dpy, window);
+ }
+
+ now = (first_p ? st->start_time : double_time());
+ duration = st->stop_time - st->start_time;
+
+ st->prev_ratio = st->ratio;
+ st->ratio = (now - st->start_time) / duration;
+
+ if (st->ratio < 1.0)
+ {
+ st->fn (st);
+ XSync (st->dpy, False);
+ }
+ else
+ {
+ eraser_free (st);
+ st = 0;
+ }
+ return st;
+}
--- /dev/null
+/* erase.c: Erase the screen in various more or less interesting ways.
+ * Copyright (c) 1997-2001, 2006 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __XSCREENSAVER_ERASE_H__
+#define __XSCREENSAVER_ERASE_H__
+
+typedef struct eraser_state eraser_state;
+
+extern void eraser_free (eraser_state *st);
+extern eraser_state *erase_window (Display *, Window, eraser_state *);
+
+#endif /* __XSCREENSAVER_ERASE_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-2011 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "utils.h"
+
+#include <sys/time.h> /* for gettimeofday() */
+
+#ifdef VMS
+# include "vms-gtod.h"
+#endif /* VMS */
+
+#include "visual.h"
+#include "usleep.h"
+#include "fade.h"
+
+Colormap
+copy_colormap (Screen *screen, Visual *visual,
+ Colormap cmap, Colormap into_cmap)
+{
+ int i;
+ Display *dpy = DisplayOfScreen (screen);
+ Window window = RootWindowOfScreen (screen);
+ int ncolors = CellsOfScreen (screen);
+ XColor *colors = 0;
+
+ /* If this is a colormap on a mono visual, or one with insanely many
+ color cells, bug out. */
+ if (ncolors <= 2 || ncolors > 4096)
+ return 0;
+ /* If this is a non-writable visual, bug out. */
+ if (!has_writable_cells (screen, visual))
+ return 0;
+
+ if (! into_cmap)
+ into_cmap = XCreateColormap (dpy, window, visual, AllocAll);
+ if (! cmap)
+ cmap = DefaultColormapOfScreen (screen);
+
+ colors = (XColor *) calloc(sizeof(XColor), ncolors);
+ for (i = 0; i < ncolors; i++)
+ colors [i].pixel = i;
+ XQueryColors (dpy, cmap, colors, ncolors);
+ XStoreColors (dpy, into_cmap, colors, ncolors);
+ free (colors);
+ return into_cmap;
+}
+
+
+void
+blacken_colormap (Screen *screen, Colormap cmap)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int ncolors = CellsOfScreen (screen);
+ XColor *colors;
+ int i;
+ if (ncolors > 4096)
+ return;
+ colors = (XColor *) calloc(sizeof(XColor), ncolors);
+ for (i = 0; i < ncolors; i++)
+ colors[i].pixel = i;
+ XStoreColors (dpy, cmap, colors, ncolors);
+ free (colors);
+}
+
+
+
+static void fade_screens_1 (Display *dpy, Colormap *cmaps,
+ Window *black_windows, int nwindows,
+ int seconds, int ticks,
+ Bool out_p, Bool clear_windows);
+
+#ifdef HAVE_SGI_VC_EXTENSION
+static int sgi_gamma_fade (Display *dpy,
+ Window *black_windows, int nwindows,
+ int seconds, int ticks,
+ Bool out_p, Bool clear_windows);
+#endif /* HAVE_SGI_VC_EXTENSION */
+
+#ifdef HAVE_XF86VMODE_GAMMA
+static int xf86_gamma_fade (Display *dpy,
+ Window *black_windows, int nwindows,
+ int seconds, int ticks,
+ Bool out_p, Bool clear_windows);
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+
+void
+fade_screens (Display *dpy, Colormap *cmaps,
+ Window *black_windows, int nwindows,
+ int seconds, int ticks,
+ Bool out_p, Bool clear_windows)
+{
+ int oseconds = seconds;
+ Bool was_in_p = !out_p;
+
+ /* When we're asked to fade in, first fade out, then fade in.
+ That way all the transitions are smooth -- from what's on the
+ screen, to black, to the desktop.
+ */
+ if (was_in_p)
+ {
+ clear_windows = True;
+ out_p = True;
+ seconds /= 3;
+ if (seconds == 0)
+ seconds = 1;
+ }
+
+ AGAIN:
+
+/* #### printf("\n\nfade_screens %d %d %d\n", seconds, ticks, out_p); */
+
+#ifdef HAVE_SGI_VC_EXTENSION
+ /* First try to do it by fading the gamma in an SGI-specific way... */
+ if (0 == sgi_gamma_fade(dpy, black_windows, nwindows,
+ seconds, ticks, out_p,
+ clear_windows))
+ ;
+ else
+#endif /* HAVE_SGI_VC_EXTENSION */
+
+#ifdef HAVE_XF86VMODE_GAMMA
+ /* Then try to do it by fading the gamma in an XFree86-specific way... */
+ if (0 == xf86_gamma_fade(dpy, black_windows, nwindows,
+ seconds, ticks, out_p,
+ clear_windows))
+ ;
+ else
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+ /* Else, do it the old-fashioned way, which (somewhat) loses if
+ there are TrueColor windows visible. */
+ fade_screens_1 (dpy, cmaps, black_windows, nwindows,
+ seconds, ticks,
+ out_p, clear_windows);
+
+ /* If we were supposed to be fading in, do so now (we just faded out,
+ so now fade back in.)
+ */
+ if (was_in_p)
+ {
+ was_in_p = False;
+ out_p = False;
+ seconds = oseconds * 2 / 3;
+ if (seconds == 0)
+ seconds = 1;
+ goto AGAIN;
+ }
+}
+
+
+static void
+sleep_from (struct timeval *now, struct timeval *then, long usecs_per_step)
+{
+ /* If several seconds have passed, the machine must have been asleep
+ or thrashing or something. Don't sleep in that case, to avoid
+ overflowing and sleeping for an unconscionably long time. This
+ function should only be sleeping for very short periods.
+ */
+ if (now->tv_sec - then->tv_sec < 5)
+ {
+ long diff = (((now->tv_sec - then->tv_sec) * 1000000) +
+ now->tv_usec - then->tv_usec);
+ if (usecs_per_step > diff)
+ usleep (usecs_per_step - diff);
+ }
+
+ then->tv_sec = now->tv_sec;
+ then->tv_usec = now->tv_usec;
+}
+
+
+
+/* The business with `cmaps_per_screen' is to fake out the SGI 8-bit video
+ hardware, which is capable of installing multiple (4) colormaps
+ simultaneously. We have to install multiple copies of the same set of
+ colors in order to fill up all the available slots in the hardware color
+ lookup table, so we install an extra N colormaps per screen to make sure
+ that all screens really go black.
+
+ I'm told that this trick also works with XInside's AcceleratedX when using
+ the Matrox Millennium card (which also allows multiple PseudoColor and
+ TrueColor visuals to co-exist and display properly at the same time.)
+
+ This trick works ok on the 24-bit Indy video hardware, but doesn't work at
+ all on the O2 24-bit hardware. I guess the higher-end hardware is too
+ "good" for this to work (dammit.) So... I figured out the "right" way to
+ do this on SGIs, which is to ramp the monitor's gamma down to 0. That's
+ what is implemented in sgi_gamma_fade(), so we use that if we can.
+ */
+static void
+fade_screens_1 (Display *dpy, Colormap *cmaps,
+ Window *black_windows, int nwindows,
+ int seconds, int ticks,
+ Bool out_p, Bool clear_windows)
+{
+ int i, j, k;
+ int steps = seconds * ticks;
+ long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
+ XEvent dummy_event;
+ int cmaps_per_screen = 5;
+ int nscreens = ScreenCount(dpy);
+ int ncmaps = nscreens * cmaps_per_screen;
+ Colormap *fade_cmaps = 0;
+ Bool installed = False;
+ int total_ncolors;
+ XColor *orig_colors, *current_colors, *screen_colors, *orig_screen_colors;
+ struct timeval then, now;
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+#endif
+
+ total_ncolors = 0;
+ for (i = 0; i < nscreens; i++)
+ total_ncolors += CellsOfScreen (ScreenOfDisplay(dpy, i));
+
+ orig_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
+ current_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
+
+ /* Get the contents of the colormap we are fading from or to. */
+ screen_colors = orig_colors;
+ for (i = 0; i < nscreens; i++)
+ {
+ int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, i));
+ Colormap cmap = (cmaps ? cmaps[i] : 0);
+ if (!cmap) cmap = DefaultColormap(dpy, i);
+
+ for (j = 0; j < ncolors; j++)
+ screen_colors[j].pixel = j;
+ XQueryColors (dpy, cmap, screen_colors, ncolors);
+
+ screen_colors += ncolors;
+ }
+
+ memcpy (current_colors, orig_colors, total_ncolors * sizeof (XColor));
+
+
+ /* Make the writable colormaps (we keep these around and reuse them.) */
+ if (!fade_cmaps)
+ {
+ fade_cmaps = (Colormap *) calloc(sizeof(Colormap), ncmaps);
+ for (i = 0; i < nscreens; i++)
+ {
+ Visual *v = DefaultVisual(dpy, i);
+ Screen *s = ScreenOfDisplay(dpy, i);
+ if (has_writable_cells (s, v))
+ for (j = 0; j < cmaps_per_screen; j++)
+ fade_cmaps[(i * cmaps_per_screen) + j] =
+ XCreateColormap (dpy, RootWindowOfScreen (s), v, AllocAll);
+ }
+ }
+
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday(&then, &tzp);
+#else
+ gettimeofday(&then);
+#endif
+
+ /* Iterate by steps of the animation... */
+ for (i = (out_p ? steps : 0);
+ (out_p ? i > 0 : i < steps);
+ (out_p ? i-- : i++))
+ {
+
+ /* For each screen, compute the current value of each color...
+ */
+ orig_screen_colors = orig_colors;
+ screen_colors = current_colors;
+ for (j = 0; j < nscreens; j++)
+ {
+ int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
+ for (k = 0; k < ncolors; k++)
+ {
+ /* This doesn't take into account the relative luminance of the
+ RGB components (0.299, 0.587, and 0.114 at gamma 2.2) but
+ the difference is imperceptible for this application... */
+ screen_colors[k].red = orig_screen_colors[k].red * i / steps;
+ screen_colors[k].green = orig_screen_colors[k].green * i / steps;
+ screen_colors[k].blue = orig_screen_colors[k].blue * i / steps;
+ }
+ screen_colors += ncolors;
+ orig_screen_colors += ncolors;
+ }
+
+ /* Put the colors into the maps...
+ */
+ screen_colors = current_colors;
+ for (j = 0; j < nscreens; j++)
+ {
+ int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
+ for (k = 0; k < cmaps_per_screen; k++)
+ {
+ Colormap c = fade_cmaps[j * cmaps_per_screen + k];
+ if (c)
+ XStoreColors (dpy, c, screen_colors, ncolors);
+ }
+ screen_colors += ncolors;
+ }
+
+ /* Put the maps on the screens, and then take the windows off the screen.
+ (only need to do this the first time through the loop.)
+ */
+ if (!installed)
+ {
+ for (j = 0; j < ncmaps; j++)
+ if (fade_cmaps[j])
+ XInstallColormap (dpy, fade_cmaps[j]);
+ installed = True;
+
+ if (black_windows && !out_p)
+ for (j = 0; j < nwindows; j++)
+ if (black_windows[j])
+ {
+ XUnmapWindow (dpy, black_windows[j]);
+ XClearWindow (dpy, black_windows[j]);
+ }
+ }
+
+ XSync (dpy, False);
+
+ /* If there is user activity, bug out. (Bug out on keypresses or
+ mouse presses, but not motion, and not release events. Bugging
+ out on motion made the unfade hack be totally useless, I think.)
+
+ We put the event back so that the calling code can notice it too.
+ It would be better to not remove it at all, but that's harder
+ because Xlib has such a non-design for this kind of crap, and
+ in this application it doesn't matter if the events end up out
+ of order, so in the grand unix tradition we say "fuck it" and
+ do something that mostly works for the time being.
+ */
+ if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask), &dummy_event))
+ {
+ XPutBackEvent (dpy, &dummy_event);
+ goto DONE;
+ }
+
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday(&now, &tzp);
+#else
+ gettimeofday(&now);
+#endif
+
+ /* If we haven't already used up our alotted time, sleep to avoid
+ changing the colormap too fast. */
+ sleep_from (&now, &then, usecs_per_step);
+ }
+
+ DONE:
+
+ if (orig_colors) free (orig_colors);
+ if (current_colors) free (current_colors);
+
+ /* If we've been given windows to raise after blackout, raise them before
+ releasing the colormaps.
+ */
+ if (out_p && black_windows)
+ {
+ for (i = 0; i < nwindows; i++)
+ {
+ if (clear_windows)
+ XClearWindow (dpy, black_windows[i]);
+ XMapRaised (dpy, black_windows[i]);
+ }
+ XSync(dpy, False);
+ }
+
+ /* Now put the target maps back.
+ If we're fading out, use the given cmap (or the default cmap, if none.)
+ If we're fading in, always use the default cmap.
+ */
+ for (i = 0; i < nscreens; i++)
+ {
+ Colormap cmap = (cmaps ? cmaps[i] : 0);
+ if (!cmap || !out_p)
+ cmap = DefaultColormap(dpy, i);
+ XInstallColormap (dpy, cmap);
+ }
+
+ /* The fade (in or out) is complete, so we don't need the black maps on
+ stage any more.
+ */
+ for (i = 0; i < ncmaps; i++)
+ if (fade_cmaps[i])
+ {
+ XUninstallColormap(dpy, fade_cmaps[i]);
+ XFreeColormap(dpy, fade_cmaps[i]);
+ fade_cmaps[i] = 0;
+ }
+ free(fade_cmaps);
+ fade_cmaps = 0;
+}
+
+
+\f
+/* SGI Gamma fading */
+
+#ifdef HAVE_SGI_VC_EXTENSION
+
+# include <X11/extensions/XSGIvc.h>
+
+struct screen_sgi_gamma_info {
+ int gamma_map; /* ??? always using 0 */
+ int nred, ngreen, nblue;
+ unsigned short *red1, *green1, *blue1;
+ unsigned short *red2, *green2, *blue2;
+ int gamma_size;
+ int gamma_precision;
+ Bool alpha_p;
+};
+
+
+static void sgi_whack_gamma(Display *dpy, int screen,
+ struct screen_sgi_gamma_info *info, float ratio);
+
+static int
+sgi_gamma_fade (Display *dpy,
+ Window *black_windows, int nwindows,
+ int seconds, int ticks,
+ Bool out_p, Bool clear_windows)
+{
+ int steps = seconds * ticks;
+ long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
+ XEvent dummy_event;
+ int nscreens = ScreenCount(dpy);
+ struct timeval then, now;
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+#endif
+ int i, screen;
+ int status = -1;
+ struct screen_sgi_gamma_info *info = (struct screen_sgi_gamma_info *)
+ calloc(nscreens, sizeof(*info));
+
+ /* Get the current gamma maps for all screens.
+ Bug out and return -1 if we can't get them for some screen.
+ */
+ for (screen = 0; screen < nscreens; screen++)
+ {
+ if (!XSGIvcQueryGammaMap(dpy, screen, info[screen].gamma_map,
+ &info[screen].gamma_size,
+ &info[screen].gamma_precision,
+ &info[screen].alpha_p))
+ goto FAIL;
+
+ if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
+ XSGIVC_COMPONENT_RED,
+ &info[screen].nred, &info[screen].red1))
+ goto FAIL;
+ if (! XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
+ XSGIVC_COMPONENT_GREEN,
+ &info[screen].ngreen, &info[screen].green1))
+ goto FAIL;
+ if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
+ XSGIVC_COMPONENT_BLUE,
+ &info[screen].nblue, &info[screen].blue1))
+ goto FAIL;
+
+ if (info[screen].gamma_precision == 8) /* Scale it up to 16 bits. */
+ {
+ int j;
+ for(j = 0; j < info[screen].nred; j++)
+ info[screen].red1[j] =
+ ((info[screen].red1[j] << 8) | info[screen].red1[j]);
+ for(j = 0; j < info[screen].ngreen; j++)
+ info[screen].green1[j] =
+ ((info[screen].green1[j] << 8) | info[screen].green1[j]);
+ for(j = 0; j < info[screen].nblue; j++)
+ info[screen].blue1[j] =
+ ((info[screen].blue1[j] << 8) | info[screen].blue1[j]);
+ }
+
+ info[screen].red2 = (unsigned short *)
+ malloc(sizeof(*info[screen].red2) * (info[screen].nred+1));
+ info[screen].green2 = (unsigned short *)
+ malloc(sizeof(*info[screen].green2) * (info[screen].ngreen+1));
+ info[screen].blue2 = (unsigned short *)
+ malloc(sizeof(*info[screen].blue2) * (info[screen].nblue+1));
+ }
+
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday(&then, &tzp);
+#else
+ gettimeofday(&then);
+#endif
+
+ /* If we're fading in (from black), then first crank the gamma all the
+ way down to 0, then take the windows off the screen.
+ */
+ if (!out_p)
+ {
+ for (screen = 0; screen < nscreens; screen++)
+ sgi_whack_gamma(dpy, screen, &info[screen], 0.0);
+
+ for (screen = 0; screen < nwindows; screen++)
+ if (black_windows && black_windows[screen])
+ {
+ XUnmapWindow (dpy, black_windows[screen]);
+ XClearWindow (dpy, black_windows[screen]);
+ XSync(dpy, False);
+ }
+ }
+
+ /* Iterate by steps of the animation... */
+ for (i = (out_p ? steps : 0);
+ (out_p ? i > 0 : i < steps);
+ (out_p ? i-- : i++))
+ {
+ for (screen = 0; screen < nscreens; screen++)
+ {
+ sgi_whack_gamma(dpy, screen, &info[screen],
+ (((float)i) / ((float)steps)));
+
+ /* If there is user activity, bug out. (Bug out on keypresses or
+ mouse presses, but not motion, and not release events. Bugging
+ out on motion made the unfade hack be totally useless, I think.)
+
+ We put the event back so that the calling code can notice it too.
+ It would be better to not remove it at all, but that's harder
+ because Xlib has such a non-design for this kind of crap, and
+ in this application it doesn't matter if the events end up out
+ of order, so in the grand unix tradition we say "fuck it" and
+ do something that mostly works for the time being.
+ */
+ if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask),
+ &dummy_event))
+ {
+ XPutBackEvent (dpy, &dummy_event);
+ goto DONE;
+ }
+
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday(&now, &tzp);
+#else
+ gettimeofday(&now);
+#endif
+
+ /* If we haven't already used up our alotted time, sleep to avoid
+ changing the colormap too fast. */
+ sleep_from (&now, &then, usecs_per_step);
+ }
+ }
+
+
+ DONE:
+
+ if (out_p && black_windows)
+ {
+ for (screen = 0; screen < nwindows; screen++)
+ {
+ if (clear_windows)
+ XClearWindow (dpy, black_windows[screen]);
+ XMapRaised (dpy, black_windows[screen]);
+ }
+ XSync(dpy, False);
+ }
+
+ /* I can't explain this; without this delay, we get a flicker.
+ I suppose there's some lossage with stale bits being in the
+ hardware frame buffer or something, and this delay gives it
+ time to flush out. This sucks! */
+ usleep(100000); /* 1/10th second */
+
+ for (screen = 0; screen < nscreens; screen++)
+ sgi_whack_gamma(dpy, screen, &info[screen], 1.0);
+ XSync(dpy, False);
+
+ status = 0;
+
+ FAIL:
+ for (screen = 0; screen < nscreens; screen++)
+ {
+ if (info[screen].red1) free (info[screen].red1);
+ if (info[screen].green1) free (info[screen].green1);
+ if (info[screen].blue1) free (info[screen].blue1);
+ if (info[screen].red2) free (info[screen].red2);
+ if (info[screen].green2) free (info[screen].green2);
+ if (info[screen].blue2) free (info[screen].blue2);
+ }
+ free(info);
+
+ return status;
+}
+
+static void
+sgi_whack_gamma(Display *dpy, int screen, struct screen_sgi_gamma_info *info,
+ float ratio)
+{
+ int k;
+
+ if (ratio < 0) ratio = 0;
+ if (ratio > 1) ratio = 1;
+ for (k = 0; k < info->gamma_size; k++)
+ {
+ info->red2[k] = info->red1[k] * ratio;
+ info->green2[k] = info->green1[k] * ratio;
+ info->blue2[k] = info->blue1[k] * ratio;
+ }
+
+ XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nred,
+ XSGIVC_MComponentRed, info->red2);
+ XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->ngreen,
+ XSGIVC_MComponentGreen, info->green2);
+ XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nblue,
+ XSGIVC_MComponentBlue, info->blue2);
+ XSync(dpy, False);
+}
+
+#endif /* HAVE_SGI_VC_EXTENSION */
+
+
+\f
+/* XFree86 4.x+ Gamma fading */
+
+#ifdef HAVE_XF86VMODE_GAMMA
+
+#include <X11/extensions/xf86vmode.h>
+
+typedef struct {
+ XF86VidModeGamma vmg;
+ int size;
+ unsigned short *r, *g, *b;
+} xf86_gamma_info;
+
+static int xf86_check_gamma_extension (Display *dpy);
+static Bool xf86_whack_gamma (Display *dpy, int screen,
+ xf86_gamma_info *ginfo, float ratio);
+
+static int
+xf86_gamma_fade (Display *dpy,
+ Window *black_windows, int nwindows,
+ int seconds, int ticks,
+ Bool out_p, Bool clear_windows)
+{
+ int steps = seconds * ticks;
+ long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
+ XEvent dummy_event;
+ int nscreens = ScreenCount(dpy);
+ struct timeval then, now;
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+#endif
+ int i, screen;
+ int status = -1;
+ xf86_gamma_info *info = 0;
+
+ static int ext_ok = -1;
+
+ /* Only probe the extension once: the answer isn't going to change. */
+ if (ext_ok == -1)
+ ext_ok = xf86_check_gamma_extension (dpy);
+
+ /* If this server doesn't have the gamma extension, bug out. */
+ if (ext_ok == 0)
+ goto FAIL;
+
+# ifndef HAVE_XF86VMODE_GAMMA_RAMP
+ if (ext_ok == 2) ext_ok = 1; /* server is newer than client! */
+# endif
+
+ info = (xf86_gamma_info *) calloc(nscreens, sizeof(*info));
+
+ /* Get the current gamma maps for all screens.
+ Bug out and return -1 if we can't get them for some screen.
+ */
+ for (screen = 0; screen < nscreens; screen++)
+ {
+ if (ext_ok == 1) /* only have gamma parameter, not ramps. */
+ {
+ if (!XF86VidModeGetGamma(dpy, screen, &info[screen].vmg))
+ goto FAIL;
+ }
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+ else if (ext_ok == 2) /* have ramps */
+ {
+ if (!XF86VidModeGetGammaRampSize(dpy, screen, &info[screen].size))
+ goto FAIL;
+ if (info[screen].size <= 0)
+ goto FAIL;
+
+ info[screen].r = (unsigned short *)
+ calloc(info[screen].size, sizeof(unsigned short));
+ info[screen].g = (unsigned short *)
+ calloc(info[screen].size, sizeof(unsigned short));
+ info[screen].b = (unsigned short *)
+ calloc(info[screen].size, sizeof(unsigned short));
+
+ if (!(info[screen].r && info[screen].g && info[screen].b))
+ goto FAIL;
+
+ if (!XF86VidModeGetGammaRamp(dpy, screen, info[screen].size,
+ info[screen].r,
+ info[screen].g,
+ info[screen].b))
+ goto FAIL;
+ }
+# endif /* HAVE_XF86VMODE_GAMMA_RAMP */
+ else
+ abort();
+ }
+
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday(&then, &tzp);
+#else
+ gettimeofday(&then);
+#endif
+
+ /* If we're fading in (from black), then first crank the gamma all the
+ way down to 0, then take the windows off the screen.
+ */
+ if (!out_p)
+ {
+ for (screen = 0; screen < nscreens; screen++)
+ xf86_whack_gamma(dpy, screen, &info[screen], 0.0);
+ for (screen = 0; screen < nwindows; screen++)
+ if (black_windows && black_windows[screen])
+ {
+ XUnmapWindow (dpy, black_windows[screen]);
+ XClearWindow (dpy, black_windows[screen]);
+ XSync(dpy, False);
+ }
+ }
+
+ /* Iterate by steps of the animation... */
+ for (i = (out_p ? steps : 0);
+ (out_p ? i > 0 : i < steps);
+ (out_p ? i-- : i++))
+ {
+ for (screen = 0; screen < nscreens; screen++)
+ {
+ xf86_whack_gamma(dpy, screen, &info[screen],
+ (((float)i) / ((float)steps)));
+
+ /* If there is user activity, bug out. (Bug out on keypresses or
+ mouse presses, but not motion, and not release events. Bugging
+ out on motion made the unfade hack be totally useless, I think.)
+
+ We put the event back so that the calling code can notice it too.
+ It would be better to not remove it at all, but that's harder
+ because Xlib has such a non-design for this kind of crap, and
+ in this application it doesn't matter if the events end up out
+ of order, so in the grand unix tradition we say "fuck it" and
+ do something that mostly works for the time being.
+ */
+ if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask),
+ &dummy_event))
+ {
+ XPutBackEvent (dpy, &dummy_event);
+ goto DONE;
+ }
+
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday(&now, &tzp);
+#else
+ gettimeofday(&now);
+#endif
+
+ /* If we haven't already used up our alotted time, sleep to avoid
+ changing the colormap too fast. */
+ sleep_from (&now, &then, usecs_per_step);
+ }
+ }
+
+
+ DONE:
+
+ if (out_p && black_windows)
+ {
+ for (screen = 0; screen < nwindows; screen++)
+ {
+ if (clear_windows)
+ XClearWindow (dpy, black_windows[screen]);
+ XMapRaised (dpy, black_windows[screen]);
+ }
+ XSync(dpy, False);
+ }
+
+ /* I can't explain this; without this delay, we get a flicker.
+ I suppose there's some lossage with stale bits being in the
+ hardware frame buffer or something, and this delay gives it
+ time to flush out. This sucks! */
+ usleep(100000); /* 1/10th second */
+
+ for (screen = 0; screen < nscreens; screen++)
+ xf86_whack_gamma(dpy, screen, &info[screen], 1.0);
+ XSync(dpy, False);
+
+ status = 0;
+
+ FAIL:
+ if (info)
+ {
+ for (screen = 0; screen < nscreens; screen++)
+ {
+ if (info[screen].r) free(info[screen].r);
+ if (info[screen].g) free(info[screen].g);
+ if (info[screen].b) free(info[screen].b);
+ }
+ free(info);
+ }
+
+ return status;
+}
+
+
+/* This bullshit is needed because the VidMode extension doesn't work
+ on remote displays -- but if the remote display has the extension
+ at all, XF86VidModeQueryExtension returns true, and then
+ XF86VidModeQueryVersion dies with an X error. Thank you XFree,
+ may I have another.
+ */
+
+static Bool error_handler_hit_p = False;
+
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+ error_handler_hit_p = True;
+ return 0;
+}
+
+static Bool
+safe_XF86VidModeQueryVersion (Display *dpy, int *majP, int *minP)
+{
+ Bool result;
+ XErrorHandler old_handler;
+ XSync (dpy, False);
+ error_handler_hit_p = False;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ result = XF86VidModeQueryVersion (dpy, majP, minP);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XSync (dpy, False);
+
+ return (error_handler_hit_p
+ ? False
+ : result);
+}
+
+
+
+/* VidModeExtension version 2.0 or better is needed to do gamma.
+ 2.0 added gamma values; 2.1 added gamma ramps.
+ */
+# define XF86_VIDMODE_GAMMA_MIN_MAJOR 2
+# define XF86_VIDMODE_GAMMA_MIN_MINOR 0
+# define XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR 2
+# define XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR 1
+
+
+
+/* Returns 0 if gamma fading not available; 1 if only gamma value setting
+ is available; 2 if gamma ramps are available.
+ */
+static int
+xf86_check_gamma_extension (Display *dpy)
+{
+ int event, error, major, minor;
+
+ if (!XF86VidModeQueryExtension (dpy, &event, &error))
+ return 0; /* display doesn't have the extension. */
+
+ if (!safe_XF86VidModeQueryVersion (dpy, &major, &minor))
+ return 0; /* unable to get version number? */
+
+ if (major < XF86_VIDMODE_GAMMA_MIN_MAJOR ||
+ (major == XF86_VIDMODE_GAMMA_MIN_MAJOR &&
+ minor < XF86_VIDMODE_GAMMA_MIN_MINOR))
+ return 0; /* extension is too old for gamma. */
+
+ if (major < XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR ||
+ (major == XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR &&
+ minor < XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR))
+ return 1; /* extension is too old for gamma ramps. */
+
+ /* Copacetic */
+ return 2;
+}
+
+
+/* XFree doesn't let you set gamma to a value smaller than this.
+ Apparently they didn't anticipate the trick I'm doing here...
+ */
+#define XF86_MIN_GAMMA 0.1
+
+
+static Bool
+xf86_whack_gamma(Display *dpy, int screen, xf86_gamma_info *info,
+ float ratio)
+{
+ Bool status;
+
+ XErrorHandler old_handler;
+ XSync (dpy, False);
+ error_handler_hit_p = False;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ if (ratio < 0) ratio = 0;
+ if (ratio > 1) ratio = 1;
+
+ if (info->size == 0) /* we only have a gamma number, not a ramp. */
+ {
+ XF86VidModeGamma g2;
+
+ g2.red = info->vmg.red * ratio;
+ g2.green = info->vmg.green * ratio;
+ g2.blue = info->vmg.blue * ratio;
+
+# ifdef XF86_MIN_GAMMA
+ if (g2.red < XF86_MIN_GAMMA) g2.red = XF86_MIN_GAMMA;
+ if (g2.green < XF86_MIN_GAMMA) g2.green = XF86_MIN_GAMMA;
+ if (g2.blue < XF86_MIN_GAMMA) g2.blue = XF86_MIN_GAMMA;
+# endif
+
+ status = XF86VidModeSetGamma (dpy, screen, &g2);
+ }
+ else
+ {
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+
+ unsigned short *r, *g, *b;
+ int i;
+ r = (unsigned short *) malloc(info->size * sizeof(unsigned short));
+ g = (unsigned short *) malloc(info->size * sizeof(unsigned short));
+ b = (unsigned short *) malloc(info->size * sizeof(unsigned short));
+
+ for (i = 0; i < info->size; i++)
+ {
+ r[i] = info->r[i] * ratio;
+ g[i] = info->g[i] * ratio;
+ b[i] = info->b[i] * ratio;
+ }
+
+ status = XF86VidModeSetGammaRamp(dpy, screen, info->size, r, g, b);
+
+ free (r);
+ free (g);
+ free (b);
+
+# else /* !HAVE_XF86VMODE_GAMMA_RAMP */
+ abort();
+# endif /* !HAVE_XF86VMODE_GAMMA_RAMP */
+ }
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XSync (dpy, False);
+
+ return status;
+}
+
+#endif /* HAVE_XF86VMODE_GAMMA */
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-1997, 2003 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __FADE_H__
+#define __FADE_H__
+
+extern Colormap copy_colormap (Screen *, Visual *, Colormap from, Colormap to);
+extern void blacken_colormap (Screen *, Colormap cmap);
+extern void fade_screens (Display *dpy,
+ Colormap *cmaps, Window *black_windows, int nwindows,
+ int seconds, int ticks,
+ Bool out_p, Bool clear_windows);
+#endif /* __FADE_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 2018-2020 by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* Like XLoadQueryFont, but if it fails, it tries some heuristics to
+ load something close.
+ */
+
+#define _GNU_SOURCE
+
+#include "utils.h"
+#include "visual.h"
+#include "xft.h"
+#include "font-retry.h"
+
+extern const char *progname;
+
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#undef DEBUG
+
+static void *
+load_font_retry_1 (Display *dpy, int screen, const char *xlfd, Bool xft_p)
+{
+
+# ifdef USE_XFT
+# define LOADFONT(F) (xft_p \
+ ? (void *) XftFontOpenXlfd (dpy, screen, (F)) \
+ : (void *) XLoadQueryFont (dpy, (F)))
+# else
+# define LOADFONT(F) ((void *) XLoadQueryFont (dpy, (F)))
+# endif
+
+ void *f = xlfd ? LOADFONT(xlfd) : 0;
+
+# ifndef USE_XFT
+ if (xft_p) abort();
+# endif
+
+# ifdef HAVE_JWXYZ
+ return f;
+# else /* !HAVE_JWXYZ */
+ if (! xlfd) xlfd = "<null>";
+ if (f)
+ {
+# ifdef DEBUG
+ fprintf (stderr, "%s: loaded %s\n", progname, xlfd);
+# endif
+ return f;
+ }
+ else
+ {
+ Bool bold_p = (!!strcasestr (xlfd, "-bold-") ||
+ !!strcasestr (xlfd, "-ocr"));
+ Bool italic_p = (!!strcasestr (xlfd, "-i-") ||
+ !!strcasestr (xlfd, "-o-"));
+ Bool fixed_p = (!!strcasestr (xlfd, "courier") ||
+ !!strcasestr (xlfd, "-ocr") ||
+ !!strcasestr (xlfd, "-m-") ||
+ !!strcasestr (xlfd, "-c-"));
+ int size = 0;
+
+# ifdef DEBUG
+ fprintf (stderr, "%s: failed %s\n", progname, xlfd);
+# endif
+
+ if (!strcmp (xlfd, "vga")) /* BSOD uses this: it has no XLFD name. */
+ fixed_p = True, size = 120;
+
+ /* Look for the first number in the string like "-180-" */
+ if (! size)
+ {
+ const char *s;
+ for (s = xlfd; *s; s++)
+ if (s[0] == '-' && s[1] >= '0' && s[1] <= '9')
+ {
+ int i = s[1] - '0';
+ const char *s2 = s+2;
+ while (*s2 >= '0' && *s2 <= '9')
+ {
+ i = i * 10 + *s2 - '0';
+ s2++;
+ }
+ if (*s2 != '-') continue; /* Number ends with dash */
+ if (i < 60 || i >= 2000) continue; /* In range 6pt - 200pt */
+ if (i % 10) continue; /* Multiple of 10 */
+
+ size = i;
+ break;
+ }
+ }
+
+ if (! size)
+ {
+ fprintf (stderr, "%s: unloadable, unparsable font: \"%s\"\n",
+ progname, xlfd);
+ xlfd = "fixed";
+ return LOADFONT(xlfd);
+ }
+ else
+ {
+ const char *variable[] = {
+ "helvetica",
+ "arial",
+ "bitstream vera sans", /* sometimes foundry is in family */
+ "vera sans", /* sometimes not? */
+ "gill sans",
+ "times",
+ "times new roman",
+ "new century schoolbook",
+ "utopia",
+ "palatino",
+ "lucida",
+ "clearlyu",
+ "bitstream charter", /* sometimes foundry is in family */
+ "charter", /* sometimes not? */
+ "utopia",
+ "luxi sans",
+ "latin modern roman",
+
+ /* Don't use a wildcard family. If none of the above worked, then
+ then almost none of the X11 fonts are installed, and it's not
+ unlikely that "-*-*-medium-r-*-*-*-140-*-*-*-10646-1" will
+ match an Arabic or or Japanese font that contains no Latin
+ glyphs at all, even in a Latin locale. So in that case, just
+ let "helvetica" fall back to "fixed".
+ */
+ /* "*" */
+ };
+ const char *fixed[] = {
+ "courier",
+ "courier new",
+ "courier 10 pitch",
+ "lucidatypewriter",
+ "american typewriter",
+ "luxi mono",
+ "fixed",
+ "ocr a std",
+ /* As above, but "can't happen" because we already tried fixed? */
+ /* "*" */
+ };
+ const char *charsets[] = { "iso10646-1", "iso8859-1", "*-*" };
+ const char *weights[] = { "bold", "medium" };
+ const char *slants[] = { "o", "i", "r" };
+ const char *spacings[] = { "m", "c", "p" };
+ int a, b, c, d, e, g;
+ char buf[1024];
+
+ for (a = 0; a < countof(charsets); a++)
+ for (b = (bold_p ? 0 : 1); b < countof(weights); b++)
+ for (c = (italic_p ? 0 : 2); c < countof(slants); c++)
+ for (d = 0;
+ d < (fixed_p ? countof(fixed) : countof(variable));
+ d++)
+ for (g = size; g >= 60; g -= 10)
+ for (e = (fixed_p ? 0 : 2); e < countof(spacings); e++)
+ {
+ sprintf (buf,
+ "-%s-%s-%s-%s-%s-%s-%s-%d-%s-%s-%s-%s-%s",
+ "*", /* foundry */
+ (fixed_p ? fixed[d] : variable[d]),
+ weights[b],
+ slants[c],
+ "*", /* set width */
+ "*", /* add style */
+ "*", /* pixel size */
+ g, /* point size */
+ "*", /* x resolution */
+ "*", /* y resolution */
+ spacings[e],
+ "*", /* average width */
+ charsets[a]);
+# ifdef DEBUG
+ fprintf(stderr, "%s: trying %s\n", progname, buf);
+# endif
+ f = LOADFONT(buf);
+ if (f)
+ {
+# ifdef DEBUG
+ fprintf (stderr,
+ "%s: substituted \"%s\" for \"%s\"\n",
+ progname, buf, xlfd);
+# endif
+ return f;
+ }
+ }
+
+ fprintf (stderr, "%s: unable to find any alternatives to \"%s\"\n",
+ progname, xlfd);
+ xlfd = "fixed";
+ return LOADFONT(xlfd);
+ }
+ }
+# endif /* !HAVE_JWXYZ */
+}
+
+XFontStruct *
+load_font_retry (Display *dpy, const char *xlfd)
+{
+ return (XFontStruct *) load_font_retry_1 (dpy, 0, xlfd, 0);
+}
+
+#ifdef USE_XFT
+XftFont *
+load_xft_font_retry (Display *dpy, int screen, const char *xlfd)
+{
+ return (XftFont *) load_font_retry_1 (dpy, screen, xlfd, 1);
+}
+
+#elif defined(HAVE_JWXYZ)
+
+XftFont *
+load_xft_font_retry (Display *dpy, int screen, const char *xlfd)
+{
+ return XftFontOpenXlfd (dpy, screen, xlfd);
+}
+
+#endif /* !HAVE_JWXYZ */
--- /dev/null
+/* xscreensaver, Copyright (c) 2018 by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __FONT_RETRY_H__
+#define __FONT_RETRY_H__
+
+/* Like XLoadQueryFont, but if it fails, it tries some heuristics to
+ load something close.
+ */
+extern XFontStruct *load_font_retry (Display *, const char *xlfd);
+
+# ifdef __XSCREENSAVER_XFT_H__ /* if xft.h has been included */
+extern XftFont *load_xft_font_retry (Display *, int screen, const char *xlfd);
+# endif
+
+#endif /* __FONT_RETRY_H__ */
--- /dev/null
+/* fps, Copyright (c) 2001-2019 Jamie Zawinski <jwz@jwz.org>
+ * Draw a frames-per-second display (Xlib and OpenGL).
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <time.h>
+#include "screenhackI.h"
+#include "fpsI.h"
+
+fps_state *
+fps_init (Display *dpy, Window window)
+{
+ fps_state *st;
+ const char *font;
+ XFontStruct *f;
+ Bool top_p;
+ XWindowAttributes xgwa;
+
+ if (! get_boolean_resource (dpy, "doFPS", "DoFPS"))
+ return 0;
+
+ if (!strcasecmp (progname, "BSOD")) return 0; /* Never worked right */
+
+ top_p = get_boolean_resource (dpy, "fpsTop", "FPSTop");
+
+ st = (fps_state *) calloc (1, sizeof(*st));
+
+ st->dpy = dpy;
+ st->window = window;
+ st->clear_p = get_boolean_resource (dpy, "fpsSolid", "FPSSolid");
+
+ font = get_string_resource (dpy, "fpsFont", "Font");
+
+ if (!font)
+ font = "-*-courier-bold-r-normal-*-*-180-*-*-*-*-*-*"; /* also texfont.c */
+ f = load_font_retry (dpy, font);
+ if (!f) abort();
+
+ {
+ XGCValues gcv;
+ XGetWindowAttributes (dpy, window, &xgwa);
+ gcv.font = f->fid;
+ gcv.foreground =
+ get_pixel_resource (st->dpy, xgwa.colormap, "foreground", "Foreground");
+ st->draw_gc = XCreateGC (dpy, window, GCFont|GCForeground, &gcv);
+ gcv.foreground =
+ get_pixel_resource (st->dpy, xgwa.colormap, "background", "Background");
+ st->erase_gc = XCreateGC (dpy, window, GCFont|GCForeground, &gcv);
+ }
+
+ st->font = f;
+ st->x = 10;
+ st->y = 10;
+ if (top_p)
+ st->y = - (st->font->ascent + st->font->descent + 10);
+
+# ifdef HAVE_IPHONE
+ /* Don't hide the FPS display under the iPhone X bezel.
+ #### This is the worst of all possible ways to do this! But how else?
+ */
+ if (xgwa.width == 2436 || xgwa.height == 2436)
+ {
+ st->x += 48;
+ st->y += 48 * (top_p ? -1 : 1);
+ }
+# endif
+
+ strcpy (st->string, "FPS: ... ");
+
+ return st;
+}
+
+void
+fps_free (fps_state *st)
+{
+ if (st->draw_gc) XFreeGC (st->dpy, st->draw_gc);
+ if (st->erase_gc) XFreeGC (st->dpy, st->erase_gc);
+ if (st->font) XFreeFont (st->dpy, st->font);
+ free (st);
+}
+
+
+void
+fps_slept (fps_state *st, unsigned long usecs)
+{
+ st->slept += usecs;
+}
+
+
+double
+fps_compute (fps_state *st, unsigned long polys, double depth)
+{
+ if (! st) return 0; /* too early? */
+
+ /* Every N frames (where N is approximately one second's worth of frames)
+ check the wall clock. We do this because checking the wall clock is
+ a slow operation.
+ */
+ if (st->frame_count++ >= st->last_ifps)
+ {
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(&st->this_frame_end, &tzp);
+# else
+ gettimeofday(&st->this_frame_end);
+# endif
+
+ if (st->prev_frame_end.tv_sec == 0)
+ st->prev_frame_end = st->this_frame_end;
+ }
+
+ /* If we've probed the wall-clock time, regenerate the string.
+ */
+ if (st->this_frame_end.tv_sec != st->prev_frame_end.tv_sec)
+ {
+ double uprev_frame_end = (st->prev_frame_end.tv_sec +
+ ((double) st->prev_frame_end.tv_usec
+ * 0.000001));
+ double uthis_frame_end = (st->this_frame_end.tv_sec +
+ ((double) st->this_frame_end.tv_usec
+ * 0.000001));
+ double fps = st->frame_count / (uthis_frame_end - uprev_frame_end);
+ double idle = (((double) st->slept * 0.000001) /
+ (uthis_frame_end - uprev_frame_end));
+ double load = 100 * (1 - idle);
+
+ if (load < 0) load = 0; /* well that's obviously nonsense... */
+
+ st->prev_frame_end = st->this_frame_end;
+ st->frame_count = 0;
+ st->slept = 0;
+ st->last_ifps = fps;
+ st->last_fps = fps;
+
+ sprintf (st->string, (polys
+ ? "FPS: %.1f \nLoad: %.1f%% "
+ : "FPS: %.1f \nLoad: %.1f%% "),
+ fps, load);
+
+ if (polys > 0)
+ {
+ const char *s = "";
+# if 0
+ if (polys >= (1024 * 1024)) polys >>= 20, s = "M";
+ else if (polys >= 2048) polys >>= 10, s = "K";
+# endif
+
+ strcat (st->string, "\nPolys: ");
+ if (polys >= 1000000)
+ sprintf (st->string + strlen(st->string), "%lu,%03lu,%03lu%s ",
+ (polys / 1000000), ((polys / 1000) % 1000),
+ (polys % 1000), s);
+ else if (polys >= 1000)
+ sprintf (st->string + strlen(st->string), "%lu,%03lu%s ",
+ (polys / 1000), (polys % 1000), s);
+ else
+ sprintf (st->string + strlen(st->string), "%lu%s ", polys, s);
+ }
+
+ if (depth >= 0.0)
+ {
+ const char *s = "";
+ unsigned long ldepth = depth;
+# if 0
+ if (depth >= (1024 * 1024 * 1024))
+ ldepth = depth / (1024 * 1024 * 1024), s = "G";
+ else if (depth >= (1024 * 1024)) ldepth >>= 20, s = "M";
+ else if (depth >= 2048) ldepth >>= 10, s = "K";
+# endif
+ strcat (st->string, "\nDepth: ");
+ if (ldepth >= 1000000000)
+ sprintf (st->string + strlen(st->string),
+ "%lu,%03lu,%03lu,%03lu%s ",
+ (ldepth / 1000000000),
+ ((ldepth / 1000000) % 1000),
+ ((ldepth / 1000) % 1000),
+ (ldepth % 1000), s);
+ else if (ldepth >= 1000000)
+ sprintf (st->string + strlen(st->string), "%lu,%03lu,%03lu%s ",
+ (ldepth / 1000000), ((ldepth / 1000) % 1000),
+ (ldepth % 1000), s);
+ else if (ldepth >= 1000)
+ sprintf (st->string + strlen(st->string), "%lu,%03lu%s ",
+ (ldepth / 1000), (ldepth % 1000), s);
+ else if (*s)
+ sprintf (st->string + strlen(st->string), "%lu%s ",
+ ldepth, s);
+ else
+ {
+ int L;
+ sprintf (st->string + strlen(st->string), "%.1f", depth);
+ L = strlen (st->string);
+ /* Remove trailing ".0" in case depth is not a fraction. */
+ if (st->string[L-2] == '.' && st->string[L-1] == '0')
+ st->string[L-2] = 0;
+ }
+ }
+ }
+
+ return st->last_fps;
+}
+
+
+
+/* Width (and optionally height) of the string in pixels.
+ */
+static int
+string_width (XFontStruct *f, const char *c, int *height_ret)
+{
+ int x = 0;
+ int max_w = 0;
+ int h = f->ascent + f->descent;
+ while (*c)
+ {
+ int cc = *((unsigned char *) c);
+ if (*c == '\n')
+ {
+ if (x > max_w) max_w = x;
+ x = 0;
+ h += f->ascent + f->descent;
+ }
+ else
+ x += (f->per_char
+ ? f->per_char[cc-f->min_char_or_byte2].width
+ : f->min_bounds.rbearing);
+ c++;
+ }
+ if (x > max_w) max_w = x;
+ if (height_ret) *height_ret = h;
+
+ return max_w;
+}
+
+
+/* This function is used only in Xlib mode. For GL mode, see glx/fps-gl.c.
+ */
+void
+fps_draw (fps_state *st)
+{
+ XWindowAttributes xgwa;
+ const char *string = st->string;
+ const char *s;
+ int x = st->x;
+ int y = st->y;
+ int lines = 1;
+ int lh = st->font->ascent + st->font->descent;
+
+ XGetWindowAttributes (st->dpy, st->window, &xgwa);
+
+ for (s = string; *s; s++)
+ if (*s == '\n') lines++;
+
+ if (y < 0)
+ y = -y + (lines-1) * lh;
+ else
+ y = xgwa.height - y;
+
+ y -= lh * (lines-1) + st->font->descent;
+
+ /* clear the background */
+ if (st->clear_p)
+ {
+ int w, h;
+ w = string_width (st->font, string, &h);
+ XFillRectangle (st->dpy, st->window, st->erase_gc,
+ x - st->font->descent,
+ y - lh,
+ w + 2*st->font->descent,
+ h + 2*st->font->descent);
+ }
+
+ /* draw the text */
+ while (lines)
+ {
+ s = strchr (string, '\n');
+ if (! s) s = string + strlen(string);
+ XDrawString (st->dpy, st->window, st->draw_gc,
+ x, y, string, (int) (s - string));
+ string = s;
+ string++;
+ lines--;
+ y += lh;
+ }
+}
--- /dev/null
+/* fps, Copyright (c) 2001-2011 Jamie Zawinski <jwz@jwz.org>
+ * Draw a frames-per-second display (Xlib and OpenGL).
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __XSCREENSAVER_FPS_H__
+# define __XSCREENSAVER_FPS_H__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+typedef struct fps_state fps_state;
+
+extern fps_state *fps_init (Display *, Window);
+extern void fps_free (fps_state *);
+extern void fps_slept (fps_state *, unsigned long usecs);
+extern double fps_compute (fps_state *, unsigned long polys, double depth);
+extern void fps_draw (fps_state *);
+
+/* Doesn't really belong here, but close enough. */
+#ifdef HAVE_MOBILE
+ extern double current_device_rotation (void);
+#else
+# define current_device_rotation() (0)
+#endif
+
+#endif /* __XSCREENSAVER_FPS_H__ */
--- /dev/null
+/* fps, Copyright (c) 2001-2014 Jamie Zawinski <jwz@jwz.org>
+ * Draw a frames-per-second display (Xlib and OpenGL).
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __XSCREENSAVER_FPSI_H__
+# define __XSCREENSAVER_FPSI_H__
+
+#include "fps.h"
+#undef HAVE_GLBITMAP
+
+
+struct fps_state {
+ Display *dpy;
+ Window window;
+ int x, y;
+ XFontStruct *font;
+ Bool clear_p;
+ char string[1024];
+
+ /* for glx/fps-gl.c */
+ void *gl_fps_data;
+
+ GC draw_gc, erase_gc;
+
+ int last_ifps;
+ double last_fps;
+ int frame_count;
+ unsigned long slept;
+ struct timeval prev_frame_end, this_frame_end;
+};
+
+#endif /* __XSCREENSAVER_FPSI_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-2016 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* This file contains code for grabbing an image of the screen to hack its
+ bits. This is a little tricky, since doing this involves the need to tell
+ the difference between drawing on the actual root window, and on the fake
+ root window used by the screensaver, since at this level the illusion
+ breaks down...
+
+ The hacks themselves use utils/grabclient.c to invoke the
+ "xscreensaver-getimage" program as a sub-process.
+
+ On "real" X11 systems:
+
+ "driver/xscreensaver-getimage" runs the code in this file to grab
+ the X11 root window image as a Pixmap.
+
+ On MacOS systems running X11, which nobody does any more:
+
+ "driver/xscreensaver-getimage" runs the Perl script
+ "driver/xscreensaver-getimage-desktop", which in turn runs the MacOS
+ program "/usr/sbin/screencapture" to get the Mac desktop image as a
+ PNG file.
+
+ On MacOS systems running the native Cocoa build, or on iOS or Android
+ systems:
+
+ "driver/xscreensaver-getimage" is not used. Instead, each saver's
+ "utils/grabclient.c" links against "OSX/grabclient-osx.m",
+ "OSX/grabclient-ios.m" or "jwxyz/jwxyz-android.c" to grab
+ screenshots directly without invoking a sub-process to do it.
+
+ See the comment at the top of utils/grabclient.c for a more detailed
+ explanation.
+ */
+
+#include "utils.h"
+#include "yarandom.h"
+
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+
+#ifdef HAVE_XMU
+# ifndef VMS
+# include <X11/Xmu/WinUtil.h>
+# else /* VMS */
+# include <Xmu/WinUtil.h>
+# endif /* VMS */
+#endif
+
+#include "usleep.h"
+#include "colors.h"
+#include "grabscreen.h"
+#include "visual.h"
+#include "resources.h"
+
+#include "vroot.h"
+#undef RootWindowOfScreen
+#undef RootWindow
+#undef DefaultRootWindow
+
+
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+# include <X11/extensions/readdisplay.h>
+ static Bool read_display (Screen *, Window, Pixmap, Bool);
+#endif /* HAVE_READ_DISPLAY_EXTENSION */
+
+
+static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
+
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+static void allocate_cubic_colormap (Screen *, Window, Visual *);
+void remap_image (Screen *, Window, Colormap, XImage *);
+#endif
+
+
+static Bool
+MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
+{
+ return (event->xany.type == MapNotify &&
+ event->xvisibility.window == (Window) window);
+}
+
+extern char *progname;
+Bool grab_verbose_p = False;
+
+void
+grabscreen_verbose(void)
+{
+ grab_verbose_p = True;
+}
+
+
+static void
+raise_window(Display *dpy, Window window, Bool dont_wait)
+{
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
+ progname, (unsigned long) window,
+ (dont_wait ? "not waiting" : "waiting"));
+
+ if (! dont_wait)
+ {
+ XWindowAttributes xgwa;
+ XSizeHints hints;
+ long supplied = 0;
+ memset(&hints, 0, sizeof(hints));
+ XGetWMNormalHints(dpy, window, &hints, &supplied);
+ XGetWindowAttributes (dpy, window, &xgwa);
+ hints.x = xgwa.x;
+ hints.y = xgwa.y;
+ hints.width = xgwa.width;
+ hints.height = xgwa.height;
+ hints.flags |= (PPosition|USPosition|PSize|USSize);
+ XSetWMNormalHints(dpy, window, &hints);
+
+ XSelectInput (dpy, window, (xgwa.your_event_mask | StructureNotifyMask));
+ }
+
+ XMapRaised(dpy, window);
+
+ if (! dont_wait)
+ {
+ XEvent event;
+ XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
+ XSync (dpy, True);
+ }
+}
+
+
+static Bool
+xscreensaver_window_p (Display *dpy, Window window)
+{
+ Atom type;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *version;
+ if (XGetWindowProperty (dpy, window,
+ XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
+ 0, 1, False, XA_STRING,
+ &type, &format, &nitems, &bytesafter,
+ &version)
+ == Success
+ && type != None)
+ return True;
+ return False;
+}
+
+
+
+/* Whether the given window is:
+ - the real root window;
+ - a direct child of the root window;
+ - a direct child of the window manager's decorations.
+ */
+Bool
+top_level_window_p (Screen *screen, Window window)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ Window root, parent, *kids;
+ unsigned int nkids;
+
+ if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
+ return False;
+
+ if (window == root)
+ return True;
+
+ /* If our direct parent is the real root window, then yes. */
+ if (parent == root)
+ return True;
+ else
+ {
+ Atom type = None;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *data;
+
+ /* If our direct parent has the WM_STATE property, then it is a
+ window manager decoration -- yes.
+ */
+ if (XGetWindowProperty (dpy, window,
+ XInternAtom (dpy, "WM_STATE", True),
+ 0, 0, False, AnyPropertyType,
+ &type, &format, &nitems, &bytesafter,
+ (unsigned char **) &data)
+ == Success
+ && type != None)
+ return True;
+ }
+
+ /* Else, no. We're deep in a tree somewhere.
+ */
+ return False;
+}
+
+
+static Bool error_handler_hit_p = False;
+static XErrorHandler old_ehandler = 0;
+static int
+BadWindow_ehandler (Display *dpy, XErrorEvent *error)
+{
+ error_handler_hit_p = True;
+ if (error->error_code == BadWindow || error->error_code == BadDrawable)
+ return 0;
+ else if (!old_ehandler)
+ {
+ abort();
+ return 0;
+ }
+ else
+ return (*old_ehandler) (dpy, error);
+}
+
+
+/* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
+ on a window whose depth is not the maximal depth of the screen? Or
+ something. Anyway, things don't work unless we: use SubwindowMode for
+ the real root window (or a legitimate virtual root window); but do not
+ use SubwindowMode for the xscreensaver window. I make no attempt to
+ explain.
+ */
+Bool
+use_subwindow_mode_p(Screen *screen, Window window)
+{
+ if (window != VirtualRootWindowOfScreen(screen))
+ return False;
+ else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
+ return False;
+ else
+ return True;
+}
+
+
+/* Install the colormaps of all visible windows, deepest first.
+ This should leave the colormaps of the topmost windows installed
+ (if only N colormaps can be installed at a time, then only the
+ topmost N windows will be shown in the right colors.)
+ */
+static void
+install_screen_colormaps (Screen *screen)
+{
+ unsigned int i;
+ Display *dpy = DisplayOfScreen (screen);
+ Window real_root;
+ Window parent, *kids = 0;
+ unsigned int nkids = 0;
+
+ XSync (dpy, False);
+ old_ehandler = XSetErrorHandler (BadWindow_ehandler);
+ error_handler_hit_p = False;
+
+ real_root = XRootWindowOfScreen (screen); /* not vroot */
+ if (XQueryTree (dpy, real_root, &real_root, &parent, &kids, &nkids))
+ for (i = 0; i < nkids; i++)
+ {
+ XWindowAttributes xgwa;
+ Window client;
+#ifdef HAVE_XMU
+ /* #### need to put XmuClientWindow() in xmu.c, sigh... */
+ if (! (client = XmuClientWindow (dpy, kids[i])))
+#endif
+ client = kids[i];
+ xgwa.colormap = 0;
+ XGetWindowAttributes (dpy, client, &xgwa);
+ if (xgwa.colormap && xgwa.map_state == IsViewable)
+ XInstallColormap (dpy, xgwa.colormap);
+ }
+ XInstallColormap (dpy, DefaultColormapOfScreen (screen));
+ XSync (dpy, False);
+ XSetErrorHandler (old_ehandler);
+ XSync (dpy, False);
+
+ if (kids)
+ XFree ((char *) kids);
+}
+
+
+void
+grab_screen_image_internal (Screen *screen, Window window)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XWindowAttributes xgwa;
+ Window real_root;
+ Bool root_p;
+ Bool saver_p;
+ Bool grab_mouse_p = False;
+ int unmap_time = 0;
+
+ real_root = XRootWindowOfScreen (screen); /* not vroot */
+ root_p = (window == real_root);
+ saver_p = xscreensaver_window_p (dpy, window);
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ screen = xgwa.screen;
+
+ if (saver_p)
+ /* I think this is redundant, but just to be safe... */
+ root_p = False;
+
+ if (saver_p)
+ /* The only time grabbing the mouse is important is if this program
+ is being run while the saver is locking the screen. */
+ grab_mouse_p = True;
+
+ if (!root_p)
+ {
+ double unmap = 0;
+ if (saver_p)
+ {
+ unmap = get_float_resource(dpy, "grabRootDelay", "Seconds");
+ if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
+ }
+ else
+ {
+ unmap = get_float_resource(dpy, "grabWindowDelay", "Seconds");
+ if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
+ }
+ unmap_time = unmap * 100000;
+ }
+
+ if (grab_verbose_p)
+ {
+ fprintf(stderr,
+ "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
+ progname, (unsigned long) window,
+ root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
+
+ fprintf(stderr, "%s: ", progname);
+ describe_visual(stderr, screen, xgwa.visual, False);
+ fprintf (stderr, "\n");
+ }
+
+
+ if (!root_p && !top_level_window_p (screen, window))
+ {
+ if (grab_verbose_p)
+ fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n",
+ progname, (unsigned long) window);
+ return;
+ }
+
+
+ if (!root_p)
+ XSetWindowBackgroundPixmap (dpy, window, None);
+
+ if (grab_mouse_p)
+ {
+ /* prevent random viewer of the screen saver (locker) from messing
+ with windows. We don't check whether it succeeded, because what
+ are our options, really... */
+ XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
+ XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
+ CurrentTime);
+ }
+
+ if (unmap_time > 0)
+ {
+ XUnmapWindow (dpy, window);
+ install_screen_colormaps (screen);
+ XSync (dpy, True);
+ usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
+ }
+
+ if (!root_p)
+ {
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+ if (! read_display(screen, window, 0, saver_p))
+#endif /* HAVE_READ_DISPLAY_EXTENSION */
+ {
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: read_display() failed\n", progname);
+#endif /* HAVE_READ_DISPLAY_EXTENSION */
+
+ copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
+ raise_window(dpy, window, saver_p);
+
+ /* Generally it's bad news to call XInstallColormap() explicitly,
+ but this file does a lot of sleazy stuff already... This is to
+ make sure that the window's colormap is installed, even in the
+ case where the window is OverrideRedirect. */
+ if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
+ XSync (dpy, False);
+ }
+ }
+ else /* root_p */
+ {
+ Pixmap pixmap;
+ pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
+
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+ if (! read_display(screen, window, pixmap, True))
+#endif
+ {
+ Window real_root = XRootWindowOfScreen (screen); /* not vroot */
+ XGCValues gcv;
+ GC gc;
+
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: read_display() failed\n", progname);
+#endif /* HAVE_READ_DISPLAY_EXTENSION */
+
+ copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
+
+ gcv.function = GXcopy;
+ gcv.subwindow_mode = IncludeInferiors;
+ gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
+ XCopyArea (dpy, real_root, pixmap, gc,
+ xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
+ XFreeGC (dpy, gc);
+ }
+ XSetWindowBackgroundPixmap (dpy, window, pixmap);
+ XFreePixmap (dpy, pixmap);
+ }
+
+ if (grab_verbose_p)
+ fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n",
+ progname, xgwa.depth,
+ (root_p ? "real root " : ""));
+
+ if (grab_mouse_p)
+ {
+ XUngrabPointer (dpy, CurrentTime);
+ XUngrabKeyboard (dpy, CurrentTime);
+ }
+
+ XSync (dpy, True);
+}
+
+
+/* When we are grabbing and manipulating a screen image, it's important that
+ we use the same colormap it originally had. So, if the screensaver was
+ started with -install, we need to copy the contents of the default colormap
+ into the screensaver's colormap.
+ */
+static void
+copy_default_colormap_contents (Screen *screen,
+ Colormap to_cmap,
+ Visual *to_visual)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ Visual *from_visual = DefaultVisualOfScreen (screen);
+ Colormap from_cmap = XDefaultColormapOfScreen (screen);
+
+ XColor *old_colors, *new_colors;
+ unsigned long *pixels;
+ XVisualInfo vi_in, *vi_out;
+ int out_count;
+ int from_cells, to_cells, max_cells, got_cells;
+ int i;
+
+ if (from_cmap == to_cmap)
+ return;
+
+ vi_in.screen = XScreenNumberOfScreen (screen);
+ vi_in.visualid = XVisualIDFromVisual (from_visual);
+ vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
+ &vi_in, &out_count);
+ if (! vi_out) abort ();
+ from_cells = vi_out [0].colormap_size;
+ XFree ((char *) vi_out);
+
+ vi_in.screen = XScreenNumberOfScreen (screen);
+ vi_in.visualid = XVisualIDFromVisual (to_visual);
+ vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
+ &vi_in, &out_count);
+ if (! vi_out) abort ();
+ to_cells = vi_out [0].colormap_size;
+ XFree ((char *) vi_out);
+
+ max_cells = (from_cells > to_cells ? to_cells : from_cells);
+
+ old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
+ new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
+ pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
+ for (i = 0; i < max_cells; i++)
+ old_colors[i].pixel = i;
+ XQueryColors (dpy, from_cmap, old_colors, max_cells);
+
+ got_cells = max_cells;
+ allocate_writable_colors (screen, to_cmap, pixels, &got_cells);
+
+ if (grab_verbose_p && got_cells != max_cells)
+ fprintf(stderr, "%s: got only %d of %d cells\n", progname,
+ got_cells, max_cells);
+
+ if (got_cells <= 0) /* we're screwed */
+ ;
+ else if (got_cells == max_cells && /* we're golden */
+ from_cells == to_cells)
+ XStoreColors (dpy, to_cmap, old_colors, got_cells);
+ else /* try to cope... */
+ {
+ for (i = 0; i < got_cells; i++)
+ {
+ XColor *c = old_colors + i;
+ int j;
+ for (j = 0; j < got_cells; j++)
+ if (pixels[j] == c->pixel)
+ {
+ /* only store this color value if this is one of the pixels
+ we were able to allocate. */
+ XStoreColors (dpy, to_cmap, c, 1);
+ break;
+ }
+ }
+ }
+
+
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: installing copy of default colormap\n", progname);
+
+ free (old_colors);
+ free (new_colors);
+ free (pixels);
+}
+
+
+\f
+/* The SGI ReadDisplay extension.
+ This extension lets you get back a 24-bit image of the screen, taking into
+ account the colors with which all windows are *currently* displayed, even
+ if those windows have different visuals. Without this extension, presence
+ of windows with different visuals or colormaps will result in technicolor
+ when one tries to grab the screen image.
+ */
+
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+
+static Bool
+read_display (Screen *screen, Window window, Pixmap into_pixmap,
+ Bool dont_wait)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XWindowAttributes xgwa;
+ int rd_event_base = 0;
+ int rd_error_base = 0;
+ unsigned long hints = 0;
+ XImage *image = 0;
+ XGCValues gcv;
+ int class;
+ GC gc;
+ Bool remap_p = False;
+
+ /* Check to see if the server supports the extension, and bug out if not.
+ */
+ if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
+ {
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: no XReadDisplay extension\n", progname);
+ return False;
+ }
+
+ /* If this isn't a visual we know how to handle, bug out. We handle:
+ = TrueColor in depths 8, 12, 15, 16, and 32;
+ = PseudoColor and DirectColor in depths 8 and 12.
+ */
+ XGetWindowAttributes(dpy, window, &xgwa);
+ class = visual_class (screen, xgwa.visual);
+ if (class == TrueColor)
+ {
+ if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 15 &&
+ xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32)
+ {
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: TrueColor depth %d unsupported\n",
+ progname, xgwa.depth);
+ return False;
+ }
+ }
+ else if (class == PseudoColor || class == DirectColor)
+ {
+ if (xgwa.depth != 8 && xgwa.depth != 12)
+ {
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
+ progname, xgwa.depth);
+ return False;
+ }
+ else
+ /* Allocate a TrueColor-like spread of colors for the image. */
+ remap_p = True;
+ }
+
+
+ /* Try and read the screen.
+ */
+ hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
+ image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
+ hints, &hints);
+ if (!image)
+ {
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: XReadDisplay() failed\n", progname);
+ return False;
+ }
+ if (!image->data)
+ {
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: XReadDisplay() returned no data\n", progname);
+ XDestroyImage(image);
+ return False;
+ }
+
+ /* XReadDisplay tends to LIE about the depth of the image it read.
+ It is returning an XImage which has `depth' and `bits_per_pixel'
+ confused!
+
+ That is, on a 24-bit display, where all visuals claim depth 24, and
+ where XGetImage would return an XImage with depth 24, and where
+ XPutImage will get a BadMatch with images that are not depth 24,
+ XReadDisplay is returning images with depth 32! Fuckwits!
+
+ So if the visual is of depth 24, but the image came back as depth 32,
+ hack it to be 24 lest we get a BadMatch from XPutImage.
+
+ I wonder what happens on an 8-bit SGI... Probably it still returns
+ an image claiming depth 32? Certainly it can't be 8. So, let's just
+ smash it to 32...
+ */
+ if (image->depth == 32 /* && xgwa.depth == 24 */ )
+ image->depth = 24;
+
+ /* If the visual of the window/pixmap into which we're going to draw is
+ less deep than the screen itself, then we need to convert the grabbed bits
+ to match the depth by clipping off the less significant bit-planes of each
+ color component.
+ */
+ if (image->depth > xgwa.depth)
+ {
+ int x, y;
+ /* We use the same image->data in both images -- that's ok, because
+ since we're reading from B and writing to A, and B uses more bytes
+ per pixel than A, the write pointer won't overrun the read pointer.
+ */
+ XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
+ ZPixmap, 0, image->data,
+ xgwa.width, xgwa.height,
+ 8, 0);
+ if (!image2)
+ {
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: out of memory?\n", progname);
+ return False;
+ }
+
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: converting from depth %d to depth %d\n",
+ progname, image->depth, xgwa.depth);
+
+ for (y = 0; y < image->height; y++)
+ for (x = 0; x < image->width; x++)
+ {
+ /* #### really these shift values should be determined from the
+ mask values -- but that's a pain in the ass, and anyway,
+ this is an SGI-specific extension so hardcoding assumptions
+ about the SGI server's behavior isn't *too* heinous... */
+ unsigned long pixel = XGetPixel(image, x, y);
+ unsigned int r = (pixel & image->red_mask);
+ unsigned int g = (pixel & image->green_mask) >> 8;
+ unsigned int b = (pixel & image->blue_mask) >> 16;
+
+ if (xgwa.depth == 8)
+ pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
+ else if (xgwa.depth == 12)
+ pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
+ else if (xgwa.depth == 16 || xgwa.depth == 15)
+ /* Gah! I don't understand why these are in the other order. */
+ pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
+ else
+ abort();
+
+ XPutPixel(image2, x, y, pixel);
+ }
+ image->data = 0;
+ XDestroyImage(image);
+ image = image2;
+ }
+
+ if (remap_p)
+ {
+ allocate_cubic_colormap (screen, window, xgwa.visual);
+ remap_image (screen, window, xgwa.colormap, image);
+ }
+
+ /* Now actually put the bits into the window or pixmap -- note the design
+ bogosity of this extension, where we've been forced to take 24 bit data
+ from the server to the client, and then push it back from the client to
+ the server, *without alteration*. We should have just been able to tell
+ the server, "put a screen image in this drawable", instead of having to
+ go through the intermediate step of converting it to an Image. Geez.
+ (Assuming that the window is of screen depth; we happen to handle less
+ deep windows, but that's beside the point.)
+ */
+ gcv.function = GXcopy;
+ gc = XCreateGC (dpy, window, GCFunction, &gcv);
+
+ if (into_pixmap)
+ {
+ gcv.function = GXcopy;
+ gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
+ XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
+ xgwa.width, xgwa.height);
+ }
+ else
+ {
+ gcv.function = GXcopy;
+ gc = XCreateGC (dpy, window, GCFunction, &gcv);
+
+ /* Ok, now we'll be needing that window on the screen... */
+ raise_window(dpy, window, dont_wait);
+
+ /* Plop down the bits... */
+ XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
+ }
+ XFreeGC (dpy, gc);
+
+ if (image->data)
+ {
+ free(image->data);
+ image->data = 0;
+ }
+ XDestroyImage(image);
+
+ return True;
+}
+#endif /* HAVE_READ_DISPLAY_EXTENSION */
+
+
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+
+/* Makes and installs a colormap that makes a PseudoColor or DirectColor
+ visual behave like a TrueColor visual of the same depth.
+
+ #### Duplicated in driver/xscreensaver-getimage.c
+ */
+static void
+allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XWindowAttributes xgwa;
+ Colormap cmap;
+ int nr, ng, nb, cells;
+ int r, g, b;
+ int depth;
+ XColor colors[4097];
+ int i;
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ cmap = xgwa.colormap;
+ depth = visual_depth (screen, visual);
+
+ switch (depth)
+ {
+ case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
+ case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
+ default: abort(); break;
+ }
+
+ memset(colors, 0, sizeof(colors));
+ for (r = 0; r < (1 << nr); r++)
+ for (g = 0; g < (1 << ng); g++)
+ for (b = 0; b < (1 << nb); b++)
+ {
+ i = (r | (g << nr) | (b << (nr + ng)));
+ colors[i].pixel = i;
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ if (depth == 8)
+ {
+ colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
+ (r << 4) | (r << 1));
+ colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
+ (g << 4) | (g << 1));
+ colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
+ (b << 8) | (b << 6) | (b << 4) |
+ (b << 2) | b);
+ }
+ else
+ {
+ colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
+ colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
+ colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
+ }
+ }
+
+ {
+ int j;
+ int allocated = 0;
+ int interleave = cells / 8; /* skip around, rather than allocating in
+ order, so that we get better coverage if
+ we can't allocated all of them. */
+ for (j = 0; j < interleave; j++)
+ for (i = 0; i < cells; i += interleave)
+ if (XAllocColor (dpy, cmap, &colors[i + j]))
+ allocated++;
+
+ if (grab_verbose_p)
+ fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
+ progname, allocated, cells);
+ }
+}
+
+/* Find the pixel index that is closest to the given color
+ (using linear distance in RGB space -- which is far from the best way.)
+
+ #### Duplicated in driver/xscreensaver-getimage.c
+ */
+static unsigned long
+find_closest_pixel (XColor *colors, int ncolors,
+ unsigned long r, unsigned long g, unsigned long b)
+{
+ unsigned long distance = ~0;
+ int i, found = 0;
+
+ if (ncolors == 0)
+ abort();
+ for (i = 0; i < ncolors; i++)
+ {
+ unsigned long d;
+ int rd, gd, bd;
+
+ rd = r - colors[i].red;
+ gd = g - colors[i].green;
+ bd = b - colors[i].blue;
+ if (rd < 0) rd = -rd;
+ if (gd < 0) gd = -gd;
+ if (bd < 0) bd = -bd;
+ d = (rd << 1) + (gd << 2) + bd;
+
+ if (d < distance)
+ {
+ distance = d;
+ found = i;
+ if (distance == 0)
+ break;
+ }
+ }
+
+ return found;
+}
+
+
+/* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
+ displayable with the given X colormap. The farther from a perfect
+ color cube the contents of the colormap are, the lossier the
+ transformation will be. No dithering is done.
+
+ #### Duplicated in driver/xscreensaver-getimage.c
+ */
+void
+remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ unsigned long map[4097];
+ int x, y, i;
+ int cells;
+ XColor colors[4097];
+
+ if (image->depth == 8)
+ cells = 256;
+ else if (image->depth == 12)
+ cells = 4096;
+ else
+ abort();
+
+ memset(map, -1, sizeof(*map));
+ memset(colors, -1, sizeof(*colors));
+
+ for (i = 0; i < cells; i++)
+ colors[i].pixel = i;
+ XQueryColors (dpy, cmap, colors, cells);
+
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: building table for %d bit image\n",
+ progname, image->depth);
+
+ for (i = 0; i < cells; i++)
+ {
+ unsigned short r, g, b;
+
+ if (cells == 256)
+ {
+ /* "RRR GGG BB" In an 8 bit map. Convert that to
+ "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
+ an even spread. */
+ r = (i & 0x07);
+ g = (i & 0x38) >> 3;
+ b = (i & 0xC0) >> 6;
+
+ r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
+ g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
+ b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
+ (b << 6) | (b << 4) | (b << 2) | b);
+ }
+ else
+ {
+ /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
+ "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
+ spread. */
+ r = (i & 0x00F);
+ g = (i & 0x0F0) >> 4;
+ b = (i & 0xF00) >> 8;
+
+ r = (r << 12) | (r << 8) | (r << 4) | r;
+ g = (g << 12) | (g << 8) | (g << 4) | g;
+ b = (b << 12) | (b << 8) | (b << 4) | b;
+ }
+
+ map[i] = find_closest_pixel (colors, cells, r, g, b);
+ }
+
+ if (grab_verbose_p)
+ fprintf(stderr, "%s: remapping colors in %d bit image\n",
+ progname, image->depth);
+
+ for (y = 0; y < image->height; y++)
+ for (x = 0; x < image->width; x++)
+ {
+ unsigned long pixel = XGetPixel(image, x, y);
+ if (pixel >= cells) abort();
+ XPutPixel(image, x, y, map[pixel]);
+ }
+}
+
+
+#endif /* HAVE_READ_DISPLAY_EXTENSION */
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-2014 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __GRABSCREEN_H__
+#define __GRABSCREEN_H__
+
+/* This will write an image onto the given Drawable.
+ The Drawable (arg 3) may be a Window or a Pixmap.
+
+ The Window must be the top-level window. The image *may or may not*
+ be written to the window, though it will definitely be written to
+ the drawable. It's fine for args 2 and 3 to be the same window, or
+ for arg 2 to be a Window, and arg 3 to be a Pixmap.
+
+ The loaded image might be from a file, or from a screen shot of the
+ desktop, or from the system's video input, depending on user
+ preferences.
+
+ When the callback is called, the image data will have been loaded
+ into the given drawable. Copy `name' if you want to keep it.
+
+ If it is from a file, then the `filename' argument will be the name
+ of the file. It may be NULL. If you want to keep this string, copy it.
+
+ The size and position of the image is in the `geometry' arg.
+ The image will generally have been scaled up to fit the window, but
+ if a loaded file had a different aspect ratio than the window, it
+ will have been centered, and the returned coords will describe that.
+
+ Many colors may be allocated from the window's colormap.
+ */
+extern void load_image_async (Screen *, Window, Drawable,
+ void (*callback) (Screen *, Window,
+ Drawable,
+ const char *name,
+ XRectangle *geometry,
+ void *closure),
+ void *closure);
+
+/* A utility wrapper around load_image_async() that is simpler if you
+ are only loading a single image at a time: just keep calling it
+ periodically until it returns NULL. When it does, the image has
+ been loaded.
+ */
+typedef struct async_load_state async_load_state;
+extern async_load_state *load_image_async_simple (async_load_state *,
+ Screen *,
+ Window top_level,
+ Drawable target,
+ char **filename_ret,
+ XRectangle *geometry_ret);
+
+
+/* Whether one should use GCSubwindowMode when drawing on this window
+ (assuming a screen image has been grabbed onto it.) Yes, this is a
+ total kludge. */
+extern Bool use_subwindow_mode_p(Screen *screen, Window window);
+
+/* Whether the given window is:
+ - the real root window;
+ - the virtual root window;
+ - a direct child of the root window;
+ - a direct child of the window manager's decorations.
+ */
+extern Bool top_level_window_p(Screen *screen, Window window);
+
+
+/* Don't call this: this is for the "xscreensaver-getimage" program only. */
+extern void grab_screen_image_internal (Screen *, Window);
+
+/* Don't use these: this is how "xscreensaver-getimage" and "grabclient.c"
+ pass the file name around. */
+#define XA_XSCREENSAVER_IMAGE_FILENAME "_SCREENSAVER_IMAGE_FILENAME"
+#define XA_XSCREENSAVER_IMAGE_GEOMETRY "_SCREENSAVER_IMAGE_GEOMETRY"
+
+/* For debugging: turn on verbosity. */
+extern void grabscreen_verbose (void);
+
+#ifdef HAVE_JWXYZ
+/* Don't use these: internal interface of grabclient.c. */
+extern Bool osx_grab_desktop_image (Screen *, Window, Drawable,
+ XRectangle *geom_ret);
+extern Bool osx_load_image_file (Screen *, Window, Drawable,
+ const char *filename, XRectangle *geom_ret);
+#endif /* HAVE_JWXYZ */
+
+#ifdef HAVE_IPHONE
+extern void ios_load_random_image (void (*callback) (void *uiimage,
+ const char *filename,
+ int w, int h,
+ void *closure),
+ void *closure,
+ int width, int height);
+#endif /* HAVE_IPHONE */
+
+#ifdef HAVE_ANDROID
+char *jwxyz_draw_random_image (Display *dpy, /* utils/grabclient.c */
+ Drawable drawable, GC gc);
+#endif
+
+#endif /* __GRABSCREEN_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1992, 1997 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* This file contains some utility routines for randomly picking the colors
+ to hack the screen with.
+ */
+
+#include "utils.h"
+#include "hsv.h"
+
+void
+hsv_to_rgb (int h, double s, double v,
+ unsigned short *r, unsigned short *g, unsigned short *b)
+{
+ double H, S, V, R, G, B;
+ double p1, p2, p3;
+ double f;
+ int i;
+
+ if (s < 0) s = 0;
+ if (v < 0) v = 0;
+ if (s > 1) s = 1;
+ if (v > 1) v = 1;
+
+ S = s; V = v;
+ H = (h % 360) / 60.0;
+ i = H;
+ f = H - i;
+ p1 = V * (1 - S);
+ p2 = V * (1 - (S * f));
+ p3 = V * (1 - (S * (1 - f)));
+ if (i == 0) { R = V; G = p3; B = p1; }
+ else if (i == 1) { R = p2; G = V; B = p1; }
+ else if (i == 2) { R = p1; G = V; B = p3; }
+ else if (i == 3) { R = p1; G = p2; B = V; }
+ else if (i == 4) { R = p3; G = p1; B = V; }
+ else { R = V; G = p1; B = p2; }
+ *r = R * 65535;
+ *g = G * 65535;
+ *b = B * 65535;
+}
+
+void
+rgb_to_hsv (unsigned short r, unsigned short g, unsigned short b,
+ int *h, double *s, double *v)
+{
+ double R, G, B, H, S, V;
+ double cmax, cmin;
+ double cmm;
+ int imax;
+ R = ((double) r) / 65535.0;
+ G = ((double) g) / 65535.0;
+ B = ((double) b) / 65535.0;
+ cmax = R; cmin = G; imax = 1;
+ if ( cmax < G ) { cmax = G; cmin = R; imax = 2; }
+ if ( cmax < B ) { cmax = B; imax = 3; }
+ if ( cmin > B ) { cmin = B; }
+ cmm = cmax - cmin;
+ V = cmax;
+ if (cmm == 0)
+ S = H = 0;
+ else
+ {
+ S = cmm / cmax;
+ if (imax == 1) H = (G - B) / cmm;
+ else if (imax == 2) H = 2.0 + (B - R) / cmm;
+ else /*if (imax == 3)*/ H = 4.0 + (R - G) / cmm;
+ if (H < 0) H += 6.0;
+ }
+ *h = (H * 60.0);
+ *s = S;
+ *v = V;
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 1992, 1997 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __HSV_H__
+#define __HSV_H__
+
+/* Converts between RGB and HSV color spaces.
+ R, G, and B are in the range 0 - 65535;
+ H is in the range 0 - 360;
+ S and V are in the range 0.0 - 1.0.
+ */
+extern void hsv_to_rgb (int h, double s, double v,
+ unsigned short *r,
+ unsigned short *g,
+ unsigned short *b);
+extern void rgb_to_hsv (unsigned short r, unsigned short g, unsigned short b,
+ int *h, double *s, double *v);
+
+#endif /* __HSV_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 2001-2014 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* I implemented this subset of libXPM here because I don't want the
+ xscreensaver daemon to depend on libXPM for two reasons: first,
+ because I want the logo to show up even if libXPM is not installed
+ on the system; and second, I don't want to have to security-audit
+ libXPM. The fewer libraries that are linked into the xscreensaver
+ daemon, the more likely to be secure it is.
+
+ Also, the Cocoa port uses this code since libXPM isn't available
+ by default on MacOS.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_JWXYZ
+# include "jwxyz.h"
+#else /* real Xlib */
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+#endif /* !HAVE_JWXYZ */
+
+#include "minixpm.h"
+
+extern const char *progname;
+
+static Bool
+bigendian (void)
+{
+ union { int i; char c[sizeof(int)]; } u;
+ u.i = 1;
+ return !u.c[0];
+}
+
+static const char hex[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
+ 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+XImage *
+minixpm_to_ximage (Display *dpy, Visual *visual, Colormap colormap, int depth,
+ unsigned long transparent_color,
+ const char * const * data,
+ int *width_ret, int *height_ret,
+ unsigned long **pixels_ret, int *npixels_ret,
+ unsigned char **mask_ret)
+{
+ int w, w8, h, ncolors, nbytes;
+ char c;
+ int x, y, i, pixel_count;
+ struct {
+ char byte;
+ int cr; int cg; int cb;
+ int mr; int mg; int mb;
+ } cmap[256];
+ unsigned char rmap[256];
+
+ unsigned long *pixels;
+ XImage *ximage = 0;
+
+ memset (cmap, 0, sizeof(cmap)); /* avoid warnings */
+
+ if (4 != sscanf ((const char *) *data,
+ "%d %d %d %d %c", &w, &h, &ncolors, &nbytes, &c)) {
+ fprintf (stderr, "%s: unparsable XPM header\n", progname);
+ abort();
+ }
+
+ if (ncolors < 1 || ncolors > 255) {
+ fprintf (stderr, "%s: XPM: ncolors is %d\n", progname, ncolors);
+ abort();
+ }
+ if (nbytes != 1) {
+ fprintf (stderr, "%s: %d-byte XPM files not supported\n",
+ progname, nbytes);
+ abort();
+ }
+ data++;
+
+ for (i = 0; i < ncolors; i++)
+ {
+ const char *line = *data;
+ cmap[i].byte = *line++;
+ while (*line)
+ {
+ int r, g, b;
+ char which;
+ while (*line == ' ' || *line == '\t')
+ line++;
+ which = *line;
+ if (!which) continue; /* whitespace at end of line */
+ line++;
+ if (which != 'c' && which != 'm') {
+ fprintf (stderr, "%s: unknown XPM pixel type '%c' in \"%s\"\n",
+ progname, which, *data);
+ abort();
+ }
+ while (*line == ' ' || *line == '\t')
+ line++;
+ if (!strncasecmp(line, "None", 4))
+ {
+ r = g = b = -1;
+ line += 4;
+ }
+ else if (!strncasecmp(line, "white", 5))
+ {
+ r = g = b = 255;
+ line += 5;
+ }
+ else if (!strncasecmp(line, "black", 5))
+ {
+ r = g = b = 0;
+ line += 5;
+ }
+ else
+ {
+ if (*line != '#') {
+ fprintf (stderr, "%s: unparsable XPM color spec: \"%s\"\n",
+ progname, line);
+ abort();
+ }
+ if (*line == '#')
+ line++;
+ r = (hex[(int) line[0]] << 4) | hex[(int) line[1]]; line += 2;
+ g = (hex[(int) line[0]] << 4) | hex[(int) line[1]]; line += 2;
+ b = (hex[(int) line[0]] << 4) | hex[(int) line[1]]; line += 2;
+ }
+
+ if (which == 'c')
+ {
+ cmap[i].cr = r;
+ cmap[i].cg = g;
+ cmap[i].cb = b;
+ }
+ else
+ {
+ cmap[i].mr = r;
+ cmap[i].mg = g;
+ cmap[i].mb = b;
+ }
+ }
+
+ data++;
+ }
+
+ if (depth == 1) transparent_color = 1;
+
+ pixels = (unsigned long *) calloc (ncolors+1, sizeof(*pixels));
+ pixel_count = 0;
+ for (i = 0; i < ncolors; i++)
+ {
+ if (cmap[i].cr == -1) /* transparent */
+ {
+ rmap[(int) cmap[i].byte] = 255;
+ }
+ else
+ {
+ XColor color;
+ color.flags = DoRed|DoGreen|DoBlue;
+ color.red = (cmap[i].cr << 8) | cmap[i].cr;
+ color.green = (cmap[i].cg << 8) | cmap[i].cg;
+ color.blue = (cmap[i].cb << 8) | cmap[i].cb;
+ if (depth == 1 ||
+ !XAllocColor (dpy, colormap, &color))
+ {
+ color.red = (cmap[i].mr << 8) | cmap[i].mr;
+ color.green = (cmap[i].mg << 8) | cmap[i].mg;
+ color.blue = (cmap[i].mb << 8) | cmap[i].mb;
+ if (!XAllocColor (dpy, colormap, &color)) {
+ fprintf (stderr, "%s: unable to allocate XPM color\n",
+ progname);
+ abort();
+ }
+ }
+ pixels[pixel_count] = color.pixel;
+ rmap[(int) cmap[i].byte] = pixel_count;
+ pixel_count++;
+ }
+ }
+
+ ximage = XCreateImage (dpy, visual, depth,
+ (depth == 1 ? XYBitmap : ZPixmap),
+ 0, 0, w, h, 8, 0);
+ if (! ximage)
+ {
+ if (pixels) free (pixels);
+ return 0;
+ }
+
+ ximage->bitmap_bit_order =
+ ximage->byte_order =
+ (bigendian() ? MSBFirst : LSBFirst);
+
+ ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
+ if (!ximage->data)
+ {
+ XDestroyImage (ximage);
+ if (pixels) free (pixels);
+ return 0;
+ }
+
+ w8 = (w + 7) / 8;
+ if (mask_ret)
+ {
+ int s = (w8 * h) + 1;
+ *mask_ret = (unsigned char *) malloc (s);
+ if (!*mask_ret)
+ mask_ret = 0;
+ else
+ memset (*mask_ret, 255, s);
+ }
+
+ for (y = 0; y < h; y++)
+ {
+ const char *line = *data++;
+ for (x = 0; x < w; x++)
+ {
+ int p = rmap[(int) *line];
+ line++;
+ XPutPixel (ximage, x, y, (p == 255 ? transparent_color : pixels[p]));
+
+ if (p == 255 && mask_ret)
+ (*mask_ret)[(y * w8) + (x >> 3)] &= (~(1 << (x & 7)));
+ }
+ }
+
+ *width_ret = w;
+ *height_ret = h;
+ *pixels_ret = pixels;
+ *npixels_ret = pixel_count;
+ return ximage;
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 2001-2006 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __MINIXPM_H__
+#define __MINIXPM_H__
+
+
+/* A dead simple XPM parser that knows how to make XImage structures.
+ Only handles single-byte color XPMs.
+ */
+
+extern XImage * minixpm_to_ximage (Display *, Visual *, Colormap, int depth,
+ unsigned long transparent_color,
+ const char * const * data,
+ int *width_ret, int *height_ret,
+ unsigned long **pixels_ret,
+ int *npixels_ret,
+ unsigned char **mask_ret);
+
+#endif /* __MINIXPM_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1997, 2001, 2004 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* If the server's root window contains a SERVER_OVERLAY_VISUALS property,
+ then that identifies the visuals which correspond to the video hardware's
+ overlay planes. Windows created in these kinds of visuals have the
+ property that one or more particular pixel values is transparent.
+
+ Vendor support:
+
+ SGI: - Supported on all systems since IRIX 4.0.
+ - Their version of the Motif toolkit renders menus into the overlay
+ visual, so that they don't cause exposure events on occluded
+ windows.
+ - 2 bit overlay plane with Indigo Entry graphics (e.g., Indy).
+ - 8 bit overlay on O2 with Indigo 24 graphics or better (e.g., O2).
+
+ HP: - Supported on workstations with CRX24 and CRX48Z graphics hardware.
+ - 8 bit or 24 bit overlay plane.
+ - The default visual is the overlay visual, with a transparent pixel.
+ That way, all Xlib programs draw into the overlay plane, and no
+ Xlib program causes exposures on occluded OpenGL windows.
+
+ IBM: - Supported on some graphics hardware (which?)
+
+ DEC: - Supported on some graphics hardware (which?)
+
+ SUN: - Some systems apparently implement it VERRRRRRY SLOWLY, so drawing
+ into the overlay plane is a performance killer (about as bad as
+ using the SHAPE extension.)
+
+
+ On my Indy, there are two transparent visuals, one of which is at layer 1,
+ and one of which is at layer 2. This is apparently the ordering in which
+ they are overlayed (1 being topmost.) The other difference between them
+ is that the topmost one only has 2 planes, while the next one has 8.
+
+ This code selects the topmost one, regardless of depth. Maybe that's not
+ the right thing. Well, in XScreenSaver, we only need to allocate two
+ colors from it (it's only used to display the stderr output, so that the
+ text can overlay the graphics without being obliterated by it.)
+
+ Documentation, such as it is, on SERVER_OVERLAY_VISUALS found on the web:
+
+ http://www.hp.com/xwindow/sharedInfo/Whitepapers/Visuals/server_overlay_visuals.html
+ http://www.xig.com/Pages/Ed-Overlays.html
+ */
+
+
+#include "utils.h"
+
+#include <X11/Xutil.h>
+#include <X11/Xproto.h>
+
+#include "visual.h"
+
+
+struct overlay_data
+{
+ CARD32 visual_id;
+ CARD32 transparency; /* 0: none; 1: pixel; 2: mask
+ ("mask" means "any pixel with these bits set
+ is a transparent pixel")
+ */
+ CARD32 value; /* the transparent pixel or mask */
+ CARD32 layer; /* -1: underlay; 0: normal; 1: popup; 2: overlay */
+};
+
+static int
+get_overlay_prop (Screen *screen, struct overlay_data **data_ret)
+{
+ int result;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data = 0;
+ Display *dpy = DisplayOfScreen(screen);
+ Window window = RootWindowOfScreen(screen);
+ Atom XA_SERVER_OVERLAY_VISUALS =
+ XInternAtom (dpy, "SERVER_OVERLAY_VISUALS", False);
+
+ *data_ret = 0;
+ result = XGetWindowProperty (dpy, window, XA_SERVER_OVERLAY_VISUALS,
+ 0, (65536 / sizeof (long)), False,
+ XA_SERVER_OVERLAY_VISUALS,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after,
+ &data);
+ if (result != Success ||
+ actual_type != XA_SERVER_OVERLAY_VISUALS ||
+ actual_format != 32 ||
+ nitems < 1)
+ {
+ if (data) XFree(data);
+ return 0;
+ }
+ else
+ {
+ struct overlay_data *d = (struct overlay_data *) data;
+ *data_ret = d;
+ return nitems / (sizeof(*d) / sizeof(CARD32));
+ }
+}
+
+
+Visual *
+get_overlay_visual (Screen *screen, unsigned long *transparent_pixel_ret)
+{
+ struct overlay_data *data = 0;
+ int n_visuals = get_overlay_prop (screen, &data);
+ Visual *visual = 0;
+ int depth = 0;
+ unsigned long pixel = 0;
+ unsigned int layer = 0;
+ int i;
+
+ if (data)
+ for (i = 0; i < n_visuals; i++)
+
+ /* Only accept ones that have a transparent pixel or mask. */
+ if (data[i].transparency == 1 ||
+ data[i].transparency == 2)
+ {
+ XVisualInfo vi_in, *vi_out;
+ int out_count;
+ vi_in.visualid = data[i].visual_id;
+ vi_out = XGetVisualInfo (DisplayOfScreen(screen), VisualIDMask,
+ &vi_in, &out_count);
+ if (vi_out)
+ {
+ /* Prefer the one at the topmost layer; after that, prefer
+ the one with the greatest depth (most colors.) */
+ if (layer < data[i].layer ||
+ (layer == data[i].layer &&
+ depth < vi_out[0].depth))
+ {
+ visual = vi_out[0].visual;
+ depth = vi_out[0].depth;
+ layer = data[i].layer;
+ pixel = data[i].value;
+ }
+ XFree(vi_out);
+ }
+ }
+
+ if (data) XFree(data);
+ if (visual && transparent_pixel_ret)
+ *transparent_pixel_ret = pixel;
+ return visual;
+}
--- /dev/null
+/* pow2, Copyright (c) 2016 Dave Odell <dmo2118@gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "pow2.h"
+
+#include <limits.h>
+
+int
+i_log2 (size_t x)
+{
+ /* -1 works best for to_pow2. */
+ if (!x)
+ return -1;
+
+ /* GCC 3.4 also has this. */
+# if defined __GNUC__ && __GNUC__ >= 4 || defined __clang__
+ return sizeof(long) * CHAR_BIT - __builtin_clzl(x) - 1;
+# else
+ {
+ unsigned bits = sizeof(x) * CHAR_BIT;
+ size_t mask = (size_t)-1;
+ unsigned result = bits - 1;
+
+ while (bits) {
+ if (!(x & mask)) {
+ result -= bits;
+ x <<= bits;
+ }
+
+ bits >>= 1;
+ mask <<= bits;
+ }
+
+ return result;
+ }
+# endif
+}
+
+size_t
+to_pow2 (size_t x)
+{
+ return !x ? 1 : 1 << (i_log2(x - 1) + 1);
+}
--- /dev/null
+/* pow2, Copyright (c) 2016 Dave Odell <dmo2118@gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef POW2_H
+#define POW2_H
+
+#include <stdlib.h>
+
+extern int i_log2(size_t x);
+extern size_t to_pow2 (size_t x); /* return the next larger power of 2. */
+
+#endif /* POW2_H */
--- /dev/null
+/* $NetBSD: queue.h,v 1.68 2014/11/19 08:10:01 uebayasi Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _QUEUE_H_
+#define _QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list. Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction. Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = (head)->slh_first; \
+ (var) != SLIST_END(head); \
+ (var) = (var)->field.sle_next)
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) != SLIST_END(head) && \
+ ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) do { \
+ (head)->slh_first = SLIST_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_AFTER(slistelm, field) do { \
+ (slistelm)->field.sle_next = \
+ SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while(curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods.
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = ((head)->lh_first); \
+ (var) != LIST_END(head); \
+ (var) = ((var)->field.le_next))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) != LIST_END(head) && \
+ ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_MOVE(head1, head2) do { \
+ LIST_INIT((head2)); \
+ if (!LIST_EMPTY((head1))) { \
+ (head2)->lh_first = (head1)->lh_first; \
+ LIST_INIT((head1)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+/*
+ * List functions.
+ */
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \
+ if ((head)->lh_first && \
+ (head)->lh_first->field.le_prev != &(head)->lh_first) \
+ QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_OP(elm, field) \
+ if ((elm)->field.le_next && \
+ (elm)->field.le_next->field.le_prev != \
+ &(elm)->field.le_next) \
+ QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \
+ __FILE__, __LINE__); \
+ if (*(elm)->field.le_prev != (elm)) \
+ QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \
+ (elm)->field.le_next = (void *)1L; \
+ (elm)->field.le_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_LIST_OP(elm, field)
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
+#endif
+
+#define LIST_INIT(head) do { \
+ (head)->lh_first = LIST_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != \
+ LIST_END(head)) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REMOVE(elm, field) do { \
+ QUEUEDEBUG_LIST_OP((elm), field) \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->sqh_first); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = ((var)->field.sqe_next))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->sqh_first); \
+ (var) != SIMPLEQ_END(head) && \
+ ((next = ((var)->field.sqe_next)), 1); \
+ (var) = (next))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+ == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->sqh_first == (elm)) { \
+ SIMPLEQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->sqh_first; \
+ while (curelm->field.sqe_next != (elm)) \
+ curelm = curelm->field.sqe_next; \
+ if ((curelm->field.sqe_next = \
+ curelm->field.sqe_next->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(curelm)->field.sqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_CONCAT(head1, head2) do { \
+ if (!SIMPLEQ_EMPTY((head2))) { \
+ *(head1)->sqh_last = (head2)->sqh_first; \
+ (head1)->sqh_last = (head2)->sqh_last; \
+ SIMPLEQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_LAST(head, type, field) \
+ (SIMPLEQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->sqh_last) - offsetof(struct type, field))))
+
+/*
+ * Tail queue definitions.
+ */
+#define _TAILQ_HEAD(name, type, qual) \
+struct name { \
+ qual type *tqh_first; /* first element */ \
+ qual type *qual *tqh_last; /* addr of last next element */ \
+}
+#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { TAILQ_END(head), &(head).tqh_first }
+
+#define _TAILQ_ENTRY(type, qual) \
+struct { \
+ qual type *tqe_next; /* next element */ \
+ qual type *qual *tqe_prev; /* address of previous next element */\
+}
+#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue access methods.
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) (NULL)
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head))
+
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var) != TAILQ_END(head); \
+ (var) = ((var)->field.tqe_next))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->tqh_first); \
+ (var) != TAILQ_END(head) && \
+ ((next) = TAILQ_NEXT(var, field), 1); (var) = (next))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));\
+ (var) != TAILQ_END(head); \
+ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) != TAILQ_END(head) && \
+ ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev))
+
+/*
+ * Tail queue functions.
+ */
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \
+ if ((head)->tqh_first && \
+ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
+ QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \
+ if (*(head)->tqh_last != NULL) \
+ QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_OP(elm, field) \
+ if ((elm)->field.tqe_next && \
+ (elm)->field.tqe_next->field.tqe_prev != \
+ &(elm)->field.tqe_next) \
+ QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \
+ __FILE__, __LINE__); \
+ if (*(elm)->field.tqe_prev != (elm)) \
+ QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \
+ if ((elm)->field.tqe_next == NULL && \
+ (head)->tqh_last != &(elm)->field.tqe_next) \
+ QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\
+ (head), (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \
+ (elm)->field.tqe_next = (void *)1L; \
+ (elm)->field.tqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
+#define QUEUEDEBUG_TAILQ_OP(elm, field)
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
+#endif
+
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = TAILQ_END(head); \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \
+ (elm)->field.tqe_next = TAILQ_END(head); \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \
+ TAILQ_END(head)) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \
+ QUEUEDEBUG_TAILQ_OP((elm), field) \
+ if (((elm)->field.tqe_next) != TAILQ_END(head)) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \
+ TAILQ_END(head)) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first; /* first element */ \
+ struct type **stqh_last; /* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+#define STAILQ_END(head) NULL
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head))
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_INIT(head) do { \
+ (head)->stqh_first = NULL; \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (head)->stqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.stqe_next = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (listelm)->field.stqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->stqh_first == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->stqh_first; \
+ while (curelm->field.stqe_next != (elm)) \
+ curelm = curelm->field.stqe_next; \
+ if ((curelm->field.stqe_next = \
+ curelm->field.stqe_next->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(curelm)->field.stqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->stqh_first); \
+ (var); \
+ (var) = ((var)->field.stqe_next))
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->stqh_last) - offsetof(struct type, field))))
+
+#endif /* !_QUEUE_H_ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-2014 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "utils.h"
+#include "resources.h"
+
+extern char *progname;
+
+
+#if !defined(HAVE_COCOA) && !defined(HAVE_ANDROID)
+
+#include <X11/Xresource.h>
+
+/* These are the Xlib/Xrm versions of these functions.
+ The Cocoa versions are on OSX/XScreenSaverView.m.
+ */
+
+extern char *progclass;
+extern XrmDatabase XtDatabase (Display *);
+
+static unsigned int get_time_resource (Display *dpy,
+ char *res_name, char *res_class,
+ Bool sec_p);
+
+#ifndef isupper
+# define isupper(c) ((c) >= 'A' && (c) <= 'Z')
+#endif
+#ifndef _tolower
+# define _tolower(c) ((c) - 'A' + 'a')
+#endif
+
+char *
+get_string_resource (Display *dpy, char *res_name, char *res_class)
+{
+ XrmValue value;
+ char *type;
+ char full_name [1024], full_class [1024];
+ strcpy (full_name, progname);
+ strcat (full_name, ".");
+ strcat (full_name, res_name);
+ strcpy (full_class, progclass);
+ strcat (full_class, ".");
+ strcat (full_class, res_class);
+ if (XrmGetResource (XtDatabase (dpy), full_name, full_class, &type, &value))
+ {
+ char *str = (char *) malloc (value.size + 1);
+ strncpy (str, (char *) value.addr, value.size);
+ str [value.size] = 0;
+ return str;
+ }
+ return 0;
+}
+
+Bool
+get_boolean_resource (Display *dpy, char *res_name, char *res_class)
+{
+ char *tmp, buf [100];
+ char *s = get_string_resource (dpy, res_name, res_class);
+ char *os = s;
+ if (! s) return 0;
+ for (tmp = buf; *s; s++)
+ *tmp++ = isupper (*s) ? _tolower (*s) : *s;
+ *tmp = 0;
+ free (os);
+
+ while (*buf &&
+ (buf[strlen(buf)-1] == ' ' ||
+ buf[strlen(buf)-1] == '\t'))
+ buf[strlen(buf)-1] = 0;
+
+ if (!strcmp (buf, "on") || !strcmp (buf, "true") || !strcmp (buf, "yes"))
+ return 1;
+ if (!strcmp (buf,"off") || !strcmp (buf, "false") || !strcmp (buf,"no"))
+ return 0;
+ fprintf (stderr, "%s: %s must be boolean, not %s.\n",
+ progname, res_name, buf);
+ return 0;
+}
+
+int
+get_integer_resource (Display *dpy, char *res_name, char *res_class)
+{
+ int val;
+ char c, *s = get_string_resource (dpy, res_name, res_class);
+ char *ss = s;
+ if (!s) return 0;
+
+ while (*ss && *ss <= ' ') ss++; /* skip whitespace */
+
+ if (ss[0] == '0' && (ss[1] == 'x' || ss[1] == 'X')) /* 0x: parse as hex */
+ {
+ if (1 == sscanf (ss+2, "%x %c", (unsigned int *) &val, &c))
+ {
+ free (s);
+ return val;
+ }
+ }
+ else /* else parse as dec */
+ {
+ if (1 == sscanf (ss, "%d %c", &val, &c))
+ {
+ free (s);
+ return val;
+ }
+ }
+
+ fprintf (stderr, "%s: %s must be an integer, not %s.\n",
+ progname, res_name, s);
+ free (s);
+ return 0;
+}
+
+double
+get_float_resource (Display *dpy, char *res_name, char *res_class)
+{
+ double val;
+ char c, *s = get_string_resource (dpy, res_name, res_class);
+ if (! s) return 0.0;
+ if (1 == sscanf (s, " %lf %c", &val, &c))
+ {
+ free (s);
+ return val;
+ }
+ fprintf (stderr, "%s: %s must be a float, not %s.\n",
+ progname, res_name, s);
+ free (s);
+ return 0.0;
+}
+
+#endif /* !HAVE_COCOA && !HAVE_ANDROID */
+
+
+/* These functions are the same with Xlib, Cocoa and Android:
+ */
+
+
+unsigned int
+get_pixel_resource (Display *dpy, Colormap cmap,
+ char *res_name, char *res_class)
+{
+ XColor color;
+ char *s = get_string_resource (dpy, res_name, res_class);
+ char *s2;
+ Bool ok = True;
+ if (!s) goto DEFAULT;
+
+ for (s2 = s + strlen(s) - 1; s2 > s; s2--)
+ if (*s2 == ' ' || *s2 == '\t')
+ *s2 = 0;
+ else
+ break;
+
+ if (! XParseColor (dpy, cmap, s, &color))
+ {
+ fprintf (stderr, "%s: can't parse color %s", progname, s);
+ ok = False;
+ goto DEFAULT;
+ }
+ if (! XAllocColor (dpy, cmap, &color))
+ {
+ fprintf (stderr, "%s: couldn't allocate color %s", progname, s);
+ ok = False;
+ goto DEFAULT;
+ }
+ free (s);
+ return (unsigned int) color.pixel;
+ DEFAULT:
+ if (s) free (s);
+
+ {
+ Bool black_p = (strlen(res_class) >= 10 &&
+ !strcasecmp ("Background",
+ res_class + strlen(res_class) - 10));
+ if (!ok)
+ fprintf (stderr, ": using %s.\n", (black_p ? "black" : "white"));
+ color.flags = DoRed|DoGreen|DoBlue;
+ color.red = color.green = color.blue = (black_p ? 0 : 0xFFFF);
+ if (XAllocColor (dpy, cmap, &color))
+ return (unsigned int) color.pixel;
+ else
+ {
+ fprintf (stderr, "%s: couldn't allocate %s either!\n", progname,
+ (black_p ? "black" : "white"));
+ /* We can't use BlackPixel/WhitePixel here, because we don't know
+ what screen we're allocating on (only an issue when running inside
+ the xscreensaver daemon: for hacks, DefaultScreen is fine.)
+ */
+ return 0;
+ }
+ }
+}
+
+
+int
+parse_time (const char *string, Bool seconds_default_p, Bool silent_p)
+{
+ unsigned int h, m, s;
+ char c;
+ if (3 == sscanf (string, " %u : %2u : %2u %c", &h, &m, &s, &c))
+ ;
+ else if (2 == sscanf (string, " : %2u : %2u %c", &m, &s, &c) ||
+ 2 == sscanf (string, " %u : %2u %c", &m, &s, &c))
+ h = 0;
+ else if (1 == sscanf (string, " : %2u %c", &s, &c))
+ h = m = 0;
+ else if (1 == sscanf (string, " %u %c",
+ (seconds_default_p ? &s : &m), &c))
+ {
+ h = 0;
+ if (seconds_default_p) m = 0;
+ else s = 0;
+ }
+ else
+ {
+ if (! silent_p)
+ fprintf (stderr, "%s: invalid time interval specification \"%s\".\n",
+ progname, string);
+ return -1;
+ }
+ if (s >= 60 && (h != 0 || m != 0))
+ {
+ if (! silent_p)
+ fprintf (stderr, "%s: seconds > 59 in \"%s\".\n", progname, string);
+ return -1;
+ }
+ if (m >= 60 && h > 0)
+ {
+ if (! silent_p)
+ fprintf (stderr, "%s: minutes > 59 in \"%s\".\n", progname, string);
+ return -1;
+ }
+ return ((h * 60 * 60) + (m * 60) + s);
+}
+
+static unsigned int
+get_time_resource (Display *dpy, char *res_name, char *res_class, Bool sec_p)
+{
+ int val;
+ char *s = get_string_resource (dpy, res_name, res_class);
+ if (!s) return 0;
+ val = parse_time (s, sec_p, False);
+ free (s);
+ return (val < 0 ? 0 : val);
+}
+
+unsigned int
+get_seconds_resource (Display *dpy, char *res_name, char *res_class)
+{
+ return get_time_resource (dpy, res_name, res_class, True);
+}
+
+unsigned int
+get_minutes_resource (Display *dpy, char *res_name, char *res_class)
+{
+ return get_time_resource (dpy, res_name, res_class, False);
+}
+
+
+/* A utility function for event-handler functions:
+ Returns True if the event is a simple click, Space, Tab, etc.
+ Returns False otherwise.
+ The idea here is that most hacks interpret to clicks or basic
+ keypresses as "change it up".
+
+ This isn't really the right file for this, but whatever.
+ */
+Bool
+screenhack_event_helper (Display *dpy, Window window, XEvent *event)
+{
+ if (event->xany.type == KeyPress)
+ {
+ KeySym keysym;
+ char c = 0;
+ XLookupString (&event->xkey, &c, 1, &keysym, 0);
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n' ||
+ keysym == XK_Left || keysym == XK_Right ||
+ keysym == XK_Up || keysym == XK_Down ||
+ keysym == XK_Prior || keysym == XK_Next)
+ return True;
+ }
+ else if (event->xany.type == ButtonPress)
+ {
+ if (event->xbutton.button == 1)
+ return True;
+ }
+
+ return False;
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-2014 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __XSCREENSAVER_RESOURCES_H__
+#define __XSCREENSAVER_RESOURCES_H__
+
+extern char *get_string_resource (Display*,char*,char*);
+extern Bool get_boolean_resource (Display*,char*,char*);
+extern int get_integer_resource (Display*,char*,char*);
+extern double get_float_resource (Display*,char*,char*);
+extern unsigned int get_pixel_resource (Display*,Colormap,char*,char*);
+extern unsigned int get_minutes_resource (Display*,char*,char*);
+extern unsigned int get_seconds_resource (Display*,char*,char*);
+extern int parse_time (const char *string, Bool seconds_default_p,
+ Bool silent_p);
+extern Pixmap
+xscreensaver_logo (Screen *screen, Visual *visual,
+ Drawable drawable, Colormap cmap,
+ unsigned long background_color,
+ unsigned long **pixels_ret, int *npixels_ret,
+ Pixmap *mask_ret,
+ Bool big_p);
+
+
+/* A utility function for event-handler functions:
+ Returns True if the event is a simple click, Space, Tab, etc.
+ Returns False otherwise.
+ The idea here is that most hacks interpret to clicks or basic
+ keypresses as "change it up".
+ */
+extern Bool screenhack_event_helper (Display *, Window, XEvent *);
+
+#endif /* __XSCREENSAVER_RESOURCES_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-2020 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * And remember: X Windows is to graphics hacking as roman numerals are to
+ * the square root of pi.
+ */
+
+/* This file contains simple code to open a window or draw on the root.
+ The idea being that, when writing a graphics hack, you can just link
+ with this .o to get all of the uninteresting junk out of the way.
+
+ Create a few static global procedures and variables:
+
+ static void *YOURNAME_init (Display *, Window);
+
+ Return an opaque structure representing your drawing state.
+
+ static unsigned long YOURNAME_draw (Display *, Window, void *closure);
+
+ Draw one frame.
+ The `closure' arg is your drawing state, that you created in `init'.
+ Return the number of microseconds to wait until the next frame.
+
+ This should return in some small fraction of a second.
+ Do not call `usleep' or loop excessively. For long loops, use a
+ finite state machine.
+
+ static void YOURNAME_reshape (Display *, Window, void *closure,
+ unsigned int width, unsigned int height);
+
+ Called when the size of the window changes with the new size.
+
+ static Bool YOURNAME_event (Display *, Window, void *closure,
+ XEvent *event);
+
+ Called when a keyboard or mouse event arrives.
+ Return True if you handle it in some way, False otherwise.
+
+ static void YOURNAME_free (Display *, Window, void *closure);
+
+ Called when you are done: free everything you've allocated,
+ including your private `state' structure.
+
+ NOTE: this is called in windowed-mode when the user typed
+ 'q' or clicks on the window's close box; but when
+ xscreensaver terminates this screenhack, it does so by
+ killing the process with SIGSTOP. So this callback is
+ mostly useless.
+
+ static char YOURNAME_defaults [] = { "...", "...", ... , 0 };
+
+ This variable is an array of strings, your default resources.
+ Null-terminate the list.
+
+ static XrmOptionDescRec YOURNAME_options[] = { { ... }, ... { 0,0,0,0 } }
+
+ This variable describes your command-line options.
+ Null-terminate the list.
+
+ Finally , invoke the XSCREENSAVER_MODULE() macro to tie it all together.
+
+ Additional caveats:
+
+ - Make sure that all functions in your module are static (check this
+ by running "nm -g" on the .o file).
+
+ - Do not use global variables: all such info must be stored in the
+ private `state' structure.
+
+ - Do not use static function-local variables, either. Put it in `state'.
+
+ Assume that there are N independent runs of this code going in the
+ same address space at the same time: they must not affect each other.
+
+ - Don't forget to write an XML file to describe the user interface
+ of your screen saver module. See .../hacks/config/README for details.
+ */
+
+#define DEBUG_PAIR
+
+#include <stdio.h>
+#include <X11/Intrinsic.h>
+#include <X11/IntrinsicP.h>
+#include <X11/CoreP.h>
+#include <X11/Shell.h>
+#include <X11/StringDefs.h>
+#include <X11/keysym.h>
+
+#ifdef __sgi
+# include <X11/SGIScheme.h> /* for SgiUseSchemes() */
+#endif /* __sgi */
+
+#ifdef HAVE_XMU
+# ifndef VMS
+# include <X11/Xmu/Error.h>
+# else /* VMS */
+# include <Xmu/Error.h>
+# endif
+#else
+# include "xmu.h"
+#endif
+
+#include "screenhackI.h"
+#include "version.h"
+#include "vroot.h"
+#include "fps.h"
+
+#ifdef HAVE_RECORD_ANIM
+# include "recanim.h"
+#endif
+
+#ifndef _XSCREENSAVER_VROOT_H_
+# error Error! You have an old version of vroot.h! Check -I args.
+#endif /* _XSCREENSAVER_VROOT_H_ */
+
+#ifndef isupper
+# define isupper(c) ((c) >= 'A' && (c) <= 'Z')
+#endif
+#ifndef _tolower
+# define _tolower(c) ((c) - 'A' + 'a')
+#endif
+
+
+/* This is defined by the SCREENHACK_MAIN() macro via screenhack.h.
+ */
+extern struct xscreensaver_function_table *xscreensaver_function_table;
+
+
+const char *progname; /* used by hacks in error messages */
+const char *progclass; /* used by ../utils/resources.c */
+Bool mono_p; /* used by hacks */
+
+#ifdef EXIT_AFTER
+static time_t exit_after; /* Exit gracefully after N seconds */
+#endif
+
+static XrmOptionDescRec default_options [] = {
+ { "-root", ".root", XrmoptionNoArg, "True" },
+ { "-window", ".root", XrmoptionNoArg, "False" },
+ { "-mono", ".mono", XrmoptionNoArg, "True" },
+ { "-install", ".installColormap", XrmoptionNoArg, "True" },
+ { "-noinstall",".installColormap", XrmoptionNoArg, "False" },
+ { "-visual", ".visualID", XrmoptionSepArg, 0 },
+ { "-window-id", ".windowID", XrmoptionSepArg, 0 },
+ { "-fps", ".doFPS", XrmoptionNoArg, "True" },
+ { "-no-fps", ".doFPS", XrmoptionNoArg, "False" },
+
+# ifdef DEBUG_PAIR
+ { "-pair", ".pair", XrmoptionNoArg, "True" },
+# endif
+# ifdef HAVE_RECORD_ANIM
+ { "-record-animation", ".recordAnim", XrmoptionSepArg, 0 },
+# endif
+# ifdef EXIT_AFTER
+ { "-exit-after", ".exitAfter", XrmoptionSepArg, 0 },
+# endif
+
+ { 0, 0, 0, 0 }
+};
+
+static char *default_defaults[] = {
+ ".root: false",
+ "*geometry: 1280x720", /* this should be .geometry, but noooo... */
+ "*mono: false",
+ "*installColormap: false",
+ "*doFPS: false",
+ "*multiSample: false",
+ "*visualID: default",
+ "*windowID: ",
+ "*desktopGrabber: xscreensaver-getimage %s",
+ 0
+};
+
+static XrmOptionDescRec *merged_options;
+static int merged_options_size;
+static char **merged_defaults;
+
+
+static void
+merge_options (void)
+{
+ struct xscreensaver_function_table *ft = xscreensaver_function_table;
+
+ const XrmOptionDescRec *options = ft->options;
+ const char * const *defaults = ft->defaults;
+ const char *progclass = ft->progclass;
+
+ int def_opts_size, opts_size;
+ int def_defaults_size, defaults_size;
+
+ for (def_opts_size = 0; default_options[def_opts_size].option;
+ def_opts_size++)
+ ;
+ for (opts_size = 0; options[opts_size].option; opts_size++)
+ ;
+
+ merged_options_size = def_opts_size + opts_size;
+ merged_options = (XrmOptionDescRec *)
+ malloc ((merged_options_size + 1) * sizeof(*default_options));
+ memcpy (merged_options, default_options,
+ (def_opts_size * sizeof(*default_options)));
+ memcpy (merged_options + def_opts_size, options,
+ ((opts_size + 1) * sizeof(*default_options)));
+
+ for (def_defaults_size = 0; default_defaults[def_defaults_size];
+ def_defaults_size++)
+ ;
+ for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
+ ;
+ merged_defaults = (char **)
+ malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
+ memcpy (merged_defaults, default_defaults,
+ def_defaults_size * sizeof(*defaults));
+ memcpy (merged_defaults + def_defaults_size, defaults,
+ (defaults_size + 1) * sizeof(*defaults));
+
+ /* This totally sucks. Xt should behave like this by default.
+ If the string in `defaults' looks like ".foo", change that
+ to "Progclass.foo".
+ */
+ {
+ char **s;
+ for (s = merged_defaults; *s; s++)
+ if (**s == '.')
+ {
+ const char *oldr = *s;
+ char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
+ strcpy (newr, progclass);
+ strcat (newr, oldr);
+ *s = newr;
+ }
+ else
+ *s = strdup (*s);
+ }
+}
+
+\f
+/* Make the X errors print out the name of this program, so we have some
+ clue which one has a bug when they die under the screensaver.
+ */
+
+static int
+screenhack_ehandler (Display *dpy, XErrorEvent *error)
+{
+ fprintf (stderr, "\nX error in %s:\n", progname);
+ if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
+ exit (-1);
+ else
+ fprintf (stderr, " (nonfatal.)\n");
+ return 0;
+}
+
+static Bool
+MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
+{
+ return (event->xany.type == MapNotify &&
+ event->xvisibility.window == (Window) window);
+}
+
+
+static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW, XA_NET_WM_PID;
+
+/* Dead-trivial event handling: exits if "q" or "ESC" are typed.
+ Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
+ Returns False if the screen saver should now terminate.
+ */
+static Bool
+screenhack_handle_event_1 (Display *dpy, XEvent *event)
+{
+ switch (event->xany.type)
+ {
+ case KeyPress:
+ {
+ KeySym keysym;
+ char c = 0;
+ XLookupString (&event->xkey, &c, 1, &keysym, 0);
+ if (c == 'q' ||
+ c == 'Q' ||
+ c == 3 || /* ^C */
+ c == 27) /* ESC */
+ return False; /* exit */
+ else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
+ XBell (dpy, 0); /* beep for non-chord keys */
+ }
+ break;
+ case ButtonPress:
+ XBell (dpy, 0);
+ break;
+ case ClientMessage:
+ {
+ if (event->xclient.message_type != XA_WM_PROTOCOLS)
+ {
+ char *s = XGetAtomName(dpy, event->xclient.message_type);
+ if (!s) s = "(null)";
+ fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
+ progname, s);
+ }
+ else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
+ {
+ char *s1 = XGetAtomName(dpy, event->xclient.message_type);
+ char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
+ if (!s1) s1 = "(null)";
+ if (!s2) s2 = "(null)";
+ fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
+ progname, s1, s2);
+ }
+ else
+ {
+ return False; /* exit */
+ }
+ }
+ break;
+ }
+ return True;
+}
+
+
+static Visual *
+pick_visual (Screen *screen)
+{
+ struct xscreensaver_function_table *ft = xscreensaver_function_table;
+
+ if (ft->pick_visual_hook)
+ {
+ Visual *v = ft->pick_visual_hook (screen);
+ if (v) return v;
+ }
+
+ return get_visual_resource (screen, "visualID", "VisualID", False);
+}
+
+
+/* Notice when the user has requested a different visual or colormap
+ on a pre-existing window (e.g., "-root -visual truecolor" or
+ "-window-id 0x2c00001 -install") and complain, since when drawing
+ on an existing window, we have no choice about these things.
+ */
+static void
+visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
+ Bool window_p)
+{
+ struct xscreensaver_function_table *ft = xscreensaver_function_table;
+
+ char *visual_string = get_string_resource (DisplayOfScreen (screen),
+ "visualID", "VisualID");
+ Visual *desired_visual = pick_visual (screen);
+ char win[100];
+ char why[100];
+
+ if (window == RootWindowOfScreen (screen))
+ strcpy (win, "root window");
+ else
+ sprintf (win, "window 0x%lx", (unsigned long) window);
+
+ if (window_p)
+ sprintf (why, "-window-id 0x%lx", (unsigned long) window);
+ else
+ strcpy (why, "-root");
+
+ if (visual_string && *visual_string)
+ {
+ char *s;
+ for (s = visual_string; *s; s++)
+ if (isupper (*s)) *s = _tolower (*s);
+
+ if (!strcmp (visual_string, "default") ||
+ !strcmp (visual_string, "default") ||
+ !strcmp (visual_string, "best"))
+ /* don't warn about these, just silently DWIM. */
+ ;
+ else if (visual != desired_visual)
+ {
+ fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
+ progname, visual_string, why);
+ fprintf (stderr, "%s: using %s's visual 0x%lx.\n",
+ progname, win, XVisualIDFromVisual (visual));
+ }
+ free (visual_string);
+ }
+
+ if (visual == DefaultVisualOfScreen (screen) &&
+ has_writable_cells (screen, visual) &&
+ get_boolean_resource (DisplayOfScreen (screen),
+ "installColormap", "InstallColormap"))
+ {
+ fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
+ progname, why);
+ fprintf (stderr, "%s: using %s's colormap 0x%lx.\n",
+ progname, win, (unsigned long) cmap);
+ }
+
+ if (ft->validate_visual_hook)
+ {
+ if (! ft->validate_visual_hook (screen, win, visual))
+ exit (1);
+ }
+}
+
+
+static void
+fix_fds (void)
+{
+ /* Bad Things Happen if stdin, stdout, and stderr have been closed
+ (as by the `sh incantation "attraction >&- 2>&-"). When you do
+ that, the X connection gets allocated to one of these fds, and
+ then some random library writes to stderr, and random bits get
+ stuffed down the X pipe, causing "Xlib: sequence lost" errors.
+ So, we cause the first three file descriptors to be open to
+ /dev/null if they aren't open to something else already. This
+ must be done before any other files are opened (or the closing
+ of that other file will again free up one of the "magic" first
+ three FDs.)
+
+ We do this by opening /dev/null three times, and then closing
+ those fds, *unless* any of them got allocated as #0, #1, or #2,
+ in which case we leave them open. Gag.
+
+ Really, this crap is technically required of *every* X program,
+ if you want it to be robust in the face of "2>&-".
+ */
+ int fd0 = open ("/dev/null", O_RDWR);
+ int fd1 = open ("/dev/null", O_RDWR);
+ int fd2 = open ("/dev/null", O_RDWR);
+ if (fd0 > 2) close (fd0);
+ if (fd1 > 2) close (fd1);
+ if (fd2 > 2) close (fd2);
+}
+
+
+static Boolean
+screenhack_table_handle_events (Display *dpy,
+ const struct xscreensaver_function_table *ft,
+ Window window, void *closure
+#ifdef DEBUG_PAIR
+ , Window window2, void *closure2
+#endif
+ )
+{
+ XtAppContext app = XtDisplayToApplicationContext (dpy);
+
+ if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
+ XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+
+ while (XPending (dpy))
+ {
+ XEvent event;
+ XNextEvent (dpy, &event);
+
+ if (event.xany.type == ConfigureNotify)
+ {
+ if (event.xany.window == window)
+ ft->reshape_cb (dpy, window, closure,
+ event.xconfigure.width, event.xconfigure.height);
+#ifdef DEBUG_PAIR
+ if (window2 && event.xany.window == window2)
+ ft->reshape_cb (dpy, window2, closure2,
+ event.xconfigure.width, event.xconfigure.height);
+#endif
+ }
+ else if (event.xany.type == ClientMessage ||
+ (! (event.xany.window == window
+ ? ft->event_cb (dpy, window, closure, &event)
+#ifdef DEBUG_PAIR
+ : (window2 && event.xany.window == window2)
+ ? ft->event_cb (dpy, window2, closure2, &event)
+#endif
+ : 0)))
+ if (! screenhack_handle_event_1 (dpy, &event))
+ return False;
+
+ if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
+ XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+ }
+
+# ifdef EXIT_AFTER
+ if (exit_after != 0 && time ((time_t *) 0) >= exit_after)
+ return False;
+# endif
+
+ return True;
+}
+
+
+static Boolean
+usleep_and_process_events (Display *dpy,
+ const struct xscreensaver_function_table *ft,
+ Window window, fps_state *fpst, void *closure,
+ unsigned long delay
+#ifdef DEBUG_PAIR
+ , Window window2, fps_state *fpst2, void *closure2,
+ unsigned long delay2
+#endif
+# ifdef HAVE_RECORD_ANIM
+ , record_anim_state *anim_state
+# endif
+ )
+{
+ do {
+ unsigned long quantum = 33333; /* 30 fps */
+ if (quantum > delay)
+ quantum = delay;
+ delay -= quantum;
+
+ XSync (dpy, False);
+
+#ifdef HAVE_RECORD_ANIM
+ if (anim_state) screenhack_record_anim (anim_state);
+#endif
+
+ if (quantum > 0)
+ {
+ usleep (quantum);
+ if (fpst) fps_slept (fpst, quantum);
+#ifdef DEBUG_PAIR
+ if (fpst2) fps_slept (fpst2, quantum);
+#endif
+ }
+
+ if (! screenhack_table_handle_events (dpy, ft, window, closure
+#ifdef DEBUG_PAIR
+ , window2, closure2
+#endif
+ ))
+ return False;
+ } while (delay > 0);
+
+ return True;
+}
+
+
+static void
+screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
+{
+ fps_compute (fpst, 0, -1);
+ fps_draw (fpst);
+}
+
+
+static void
+run_screenhack_table (Display *dpy,
+ Window window,
+# ifdef DEBUG_PAIR
+ Window window2,
+# endif
+# ifdef HAVE_RECORD_ANIM
+ record_anim_state *anim_state,
+# endif
+ const struct xscreensaver_function_table *ft)
+{
+
+ /* Kludge: even though the init_cb functions are declared to take 2 args,
+ actually call them with 3, for the benefit of xlockmore_init() and
+ xlockmore_setup().
+ */
+ void *(*init_cb) (Display *, Window, void *) =
+ (void *(*) (Display *, Window, void *)) ft->init_cb;
+
+ void (*fps_cb) (Display *, Window, fps_state *, void *) = ft->fps_cb;
+
+ void *closure = init_cb (dpy, window, ft->setup_arg);
+ fps_state *fpst = fps_init (dpy, window);
+ unsigned long delay = 0;
+
+#ifdef DEBUG_PAIR
+ void *closure2 = 0;
+ fps_state *fpst2 = 0;
+ unsigned long delay2 = 0;
+ if (window2) closure2 = init_cb (dpy, window2, ft->setup_arg);
+ if (window2) fpst2 = fps_init (dpy, window2);
+#endif
+
+ if (! closure) /* if it returns nothing, it can't possibly be re-entrant. */
+ abort();
+
+ if (! fps_cb) fps_cb = screenhack_do_fps;
+
+ while (1)
+ {
+ if (! usleep_and_process_events (dpy, ft,
+ window, fpst, closure, delay
+#ifdef DEBUG_PAIR
+ , window2, fpst2, closure2, delay2
+#endif
+#ifdef HAVE_RECORD_ANIM
+ , anim_state
+#endif
+ ))
+ break;
+
+ delay = ft->draw_cb (dpy, window, closure);
+#ifdef DEBUG_PAIR
+ delay2 = 0;
+ if (window2) delay2 = ft->draw_cb (dpy, window2, closure2);
+#endif
+
+ if (fpst) fps_cb (dpy, window, fpst, closure);
+#ifdef DEBUG_PAIR
+ if (fpst2) fps_cb (dpy, window2, fpst2, closure2);
+#endif
+ }
+
+#ifdef HAVE_RECORD_ANIM
+ /* Exiting before target frames hit: write the video anyway. */
+ if (anim_state) screenhack_record_anim_free (anim_state);
+#endif
+
+ ft->free_cb (dpy, window, closure);
+ if (fpst) ft->fps_free (fpst);
+
+#ifdef DEBUG_PAIR
+ if (window2) ft->free_cb (dpy, window2, closure2);
+ if (fpst2) ft->fps_free (fpst2);
+#endif
+}
+
+
+static Widget
+make_shell (Screen *screen, Widget toplevel, int width, int height)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ Visual *visual = pick_visual (screen);
+ Boolean def_visual_p = (toplevel &&
+ visual == DefaultVisualOfScreen (screen));
+
+ if (width <= 0) width = 600;
+ if (height <= 0) height = 480;
+
+ if (def_visual_p)
+ {
+ Window window;
+ XtVaSetValues (toplevel,
+ XtNmappedWhenManaged, False,
+ XtNwidth, width,
+ XtNheight, height,
+ XtNinput, True, /* for WM_HINTS */
+ NULL);
+ XtRealizeWidget (toplevel);
+ window = XtWindow (toplevel);
+
+ if (get_boolean_resource (dpy, "installColormap", "InstallColormap"))
+ {
+ Colormap cmap =
+ XCreateColormap (dpy, window, DefaultVisualOfScreen (screen),
+ AllocNone);
+ XSetWindowColormap (dpy, window, cmap);
+ }
+ }
+ else
+ {
+ unsigned int bg, bd;
+ Widget new;
+ Colormap cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen),
+ visual, AllocNone);
+ bg = get_pixel_resource (dpy, cmap, "background", "Background");
+ bd = get_pixel_resource (dpy, cmap, "borderColor", "Foreground");
+
+ new = XtVaAppCreateShell (progname, progclass,
+ topLevelShellWidgetClass, dpy,
+ XtNmappedWhenManaged, False,
+ XtNvisual, visual,
+ XtNdepth, visual_depth (screen, visual),
+ XtNwidth, width,
+ XtNheight, height,
+ XtNcolormap, cmap,
+ XtNbackground, (Pixel) bg,
+ XtNborderColor, (Pixel) bd,
+ XtNinput, True, /* for WM_HINTS */
+ NULL);
+
+ if (!toplevel) /* kludge for the second window in -pair mode... */
+ XtVaSetValues (new, XtNx, 0, XtNy, 550, NULL);
+
+ XtRealizeWidget (new);
+ toplevel = new;
+ }
+
+ return toplevel;
+}
+
+static void
+init_window (Display *dpy, Widget toplevel, const char *title)
+{
+ Window window;
+ XWindowAttributes xgwa;
+ long pid = getpid();
+ XtPopup (toplevel, XtGrabNone);
+ XtVaSetValues (toplevel, XtNtitle, title, NULL);
+
+ /* Select KeyPress, and announce that we accept WM_DELETE_WINDOW.
+ */
+ window = XtWindow (toplevel);
+ XGetWindowAttributes (dpy, window, &xgwa);
+ XSelectInput (dpy, window,
+ (xgwa.your_event_mask | KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask));
+ XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
+ PropModeReplace,
+ (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
+ XChangeProperty (dpy, window, XA_NET_WM_PID, XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *)&pid, 1);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ struct xscreensaver_function_table *ft = xscreensaver_function_table;
+
+ XWindowAttributes xgwa;
+ Widget toplevel;
+ Display *dpy;
+ Window window;
+# ifdef DEBUG_PAIR
+ Window window2 = 0;
+ Widget toplevel2 = 0;
+# endif
+# ifdef HAVE_RECORD_ANIM
+ record_anim_state *anim_state = 0;
+# endif
+ XtAppContext app;
+ Bool root_p;
+ Window on_window = 0;
+ XEvent event;
+ Boolean dont_clear;
+ char version[255];
+
+ fix_fds();
+
+ progname = argv[0]; /* reset later */
+ progclass = ft->progclass;
+
+ if (ft->setup_cb)
+ ft->setup_cb (ft, ft->setup_arg);
+
+ merge_options ();
+
+#ifdef __sgi
+ /* We have to do this on SGI to prevent the background color from being
+ overridden by the current desktop color scheme (we'd like our backgrounds
+ to be black, thanks.) This should be the same as setting the
+ "*useSchemes: none" resource, but it's not -- if that resource is
+ present in the `default_defaults' above, it doesn't work, though it
+ does work when passed as an -xrm arg on the command line. So screw it,
+ turn them off from C instead.
+ */
+ SgiUseSchemes ("none");
+#endif /* __sgi */
+
+ toplevel = XtAppInitialize (&app, progclass, merged_options,
+ merged_options_size, &argc, argv,
+ merged_defaults, 0, 0);
+
+ dpy = XtDisplay (toplevel);
+
+ XtGetApplicationNameAndClass (dpy,
+ (char **) &progname,
+ (char **) &progclass);
+
+ /* half-assed way of avoiding buffer-overrun attacks. */
+ if (strlen (progname) >= 100) ((char *) progname)[100] = 0;
+
+ XSetErrorHandler (screenhack_ehandler);
+
+ XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
+ XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
+ XA_NET_WM_PID = XInternAtom (dpy, "_NET_WM_PID", False);
+
+ {
+ char *v = (char *) strdup(strchr(screensaver_id, ' '));
+ char *s1, *s2, *s3, *s4;
+ const char *ot = get_string_resource (dpy, "title", "Title");
+ s1 = (char *) strchr(v, ' '); s1++;
+ s2 = (char *) strchr(s1, ' ');
+ s3 = (char *) strchr(v, '('); s3++;
+ s4 = (char *) strchr(s3, ')');
+ *s2 = 0;
+ *s4 = 0;
+ if (ot && !*ot) ot = 0;
+ sprintf (version, "%.50s%s%s: from the XScreenSaver %s distribution (%s)",
+ (ot ? ot : ""),
+ (ot ? ": " : ""),
+ progclass, s1, s3);
+ free(v);
+ }
+
+ if (argc > 1)
+ {
+ const char *s;
+ int i;
+ int x = 18;
+ int end = 78;
+ Bool help_p = (!strcmp(argv[1], "-help") ||
+ !strcmp(argv[1], "--help"));
+ fprintf (stderr, "%s\n", version);
+ for (s = progclass; *s; s++) fprintf(stderr, " ");
+ fprintf (stderr, " https://www.jwz.org/xscreensaver/\n\n");
+
+ if (!help_p)
+ fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
+ fprintf (stderr, "Options include: ");
+ for (i = 0; i < merged_options_size; i++)
+ {
+ char *sw = merged_options [i].option;
+ Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
+ int size = strlen (sw) + (argp ? 6 : 0) + 2;
+ if (x + size >= end)
+ {
+ fprintf (stderr, "\n\t\t ");
+ x = 18;
+ }
+ x += size;
+ fprintf (stderr, "%s", sw);
+ if (argp) fprintf (stderr, " <arg>");
+ if (i != merged_options_size - 1) fprintf (stderr, ", ");
+ }
+
+ fprintf (stderr, ".\n");
+
+#if 0
+ if (help_p)
+ {
+ fprintf (stderr, "\nResources:\n\n");
+ for (i = 0; i < merged_options_size; i++)
+ {
+ const char *opt = merged_options [i].option;
+ const char *res = merged_options [i].specifier + 1;
+ const char *val = merged_options [i].value;
+ char *s = get_string_resource (dpy, (char *) res, (char *) res);
+
+ if (s)
+ {
+ int L = strlen(s);
+ while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
+ s[--L] = 0;
+ }
+
+ fprintf (stderr, " %-16s %-18s ", opt, res);
+ if (merged_options [i].argKind == XrmoptionSepArg)
+ {
+ fprintf (stderr, "[%s]", (s ? s : "?"));
+ }
+ else
+ {
+ fprintf (stderr, "%s", (val ? val : "(null)"));
+ if (val && s && !strcasecmp (val, s))
+ fprintf (stderr, " [default]");
+ }
+ fprintf (stderr, "\n");
+ }
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ exit (help_p ? 0 : 1);
+ }
+
+ {
+ char **s;
+ for (s = merged_defaults; *s; s++)
+ free(*s);
+ }
+
+ free (merged_options);
+ free (merged_defaults);
+ merged_options = 0;
+ merged_defaults = 0;
+
+ dont_clear = get_boolean_resource (dpy, "dontClearRoot", "Boolean");
+ mono_p = get_boolean_resource (dpy, "mono", "Boolean");
+ if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
+ mono_p = True;
+
+ root_p = get_boolean_resource (dpy, "root", "Boolean");
+
+# ifdef EXIT_AFTER
+ {
+ int secs = get_integer_resource (dpy, "exitAfter", "Integer");
+ exit_after = (secs > 0
+ ? time((time_t *) 0) + secs
+ : 0);
+ }
+# endif
+
+ {
+ char *s = get_string_resource (dpy, "windowID", "WindowID");
+ if (s && *s)
+ on_window = get_integer_resource (dpy, "windowID", "WindowID");
+ if (s) free (s);
+ }
+
+ if (on_window)
+ {
+ window = (Window) on_window;
+ XtDestroyWidget (toplevel);
+ XGetWindowAttributes (dpy, window, &xgwa);
+ visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, True);
+
+ /* Select KeyPress and resize events on the external window.
+ */
+ xgwa.your_event_mask |= KeyPressMask | StructureNotifyMask;
+ XSelectInput (dpy, window, xgwa.your_event_mask);
+
+ /* Select ButtonPress and ButtonRelease events on the external window,
+ if no other app has already selected them (only one app can select
+ ButtonPress at a time: BadAccess results.)
+ */
+ if (! (xgwa.all_event_masks & (ButtonPressMask | ButtonReleaseMask)))
+ XSelectInput (dpy, window,
+ (xgwa.your_event_mask |
+ ButtonPressMask | ButtonReleaseMask));
+ }
+ else if (root_p)
+ {
+ window = VirtualRootWindowOfScreen (XtScreen (toplevel));
+ XtDestroyWidget (toplevel);
+ XGetWindowAttributes (dpy, window, &xgwa);
+ /* With RANDR, the root window can resize! */
+ XSelectInput (dpy, window, xgwa.your_event_mask | StructureNotifyMask);
+ visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, False);
+ }
+ else
+ {
+ Widget new = make_shell (XtScreen (toplevel), toplevel,
+ toplevel->core.width,
+ toplevel->core.height);
+ if (new != toplevel)
+ {
+ XtDestroyWidget (toplevel);
+ toplevel = new;
+ }
+
+ init_window (dpy, toplevel, version);
+ window = XtWindow (toplevel);
+ XGetWindowAttributes (dpy, window, &xgwa);
+
+# ifdef DEBUG_PAIR
+ if (get_boolean_resource (dpy, "pair", "Boolean"))
+ {
+ toplevel2 = make_shell (xgwa.screen, 0,
+ toplevel->core.width,
+ toplevel->core.height);
+ init_window (dpy, toplevel2, version);
+ window2 = XtWindow (toplevel2);
+ }
+# endif /* DEBUG_PAIR */
+ }
+
+ if (!dont_clear)
+ {
+ unsigned int bg = get_pixel_resource (dpy, xgwa.colormap,
+ "background", "Background");
+ XSetWindowBackground (dpy, window, bg);
+ XClearWindow (dpy, window);
+# ifdef DEBUG_PAIR
+ if (window2)
+ {
+ XSetWindowBackground (dpy, window2, bg);
+ XClearWindow (dpy, window2);
+ }
+# endif
+ }
+
+ if (!root_p && !on_window)
+ /* wait for it to be mapped */
+ XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
+
+ XSync (dpy, False);
+
+ /* This is the one and only place that the random-number generator is
+ seeded in any screenhack. You do not need to seed the RNG again,
+ it is done for you before your code is invoked. */
+# undef ya_rand_init
+ ya_rand_init (0);
+
+
+#ifdef HAVE_RECORD_ANIM
+ {
+ int frames = get_integer_resource (dpy, "recordAnim", "Integer");
+ if (frames > 0)
+ anim_state = screenhack_record_anim_init (xgwa.screen, window, frames);
+ }
+#endif
+
+ run_screenhack_table (dpy, window,
+# ifdef DEBUG_PAIR
+ window2,
+# endif
+# ifdef HAVE_RECORD_ANIM
+ anim_state,
+# endif
+ ft);
+
+#ifdef HAVE_RECORD_ANIM
+ if (anim_state) screenhack_record_anim_free (anim_state);
+#endif
+
+ XtDestroyWidget (toplevel);
+ XtDestroyApplicationContext (app);
+
+ return 0;
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-2018 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __SCREENHACK_H__
+#define __SCREENHACK_H__
+
+#include "screenhackI.h"
+
+/* In an Xlib world, we define two global symbols here:
+ a struct in `MODULENAME_xscreensaver_function_table',
+ and a pointer to that in `xscreensaver_function_table'.
+
+ In a Cocoa/Android world, we only define the prefixed symbol;
+ the un-prefixed symbol does not exist.
+ */
+#ifdef HAVE_JWXYZ
+# define XSCREENSAVER_LINK(NAME)
+#else
+# define XSCREENSAVER_LINK(NAME) \
+ struct xscreensaver_function_table *xscreensaver_function_table = &NAME;
+#endif
+
+
+#if defined(HAVE_JWXYZ) && !defined(__XLOCKMORE_INTERNAL_H__)
+ /* this is one enormous kludge... */
+# undef ya_rand_init
+ static void
+ xscreensaver_common_setup(struct xscreensaver_function_table *xsft, void *a)
+ { ya_rand_init(0); }
+#else
+# define xscreensaver_common_setup 0
+#endif
+
+
+#ifdef HAVE_JWXYZ
+# define SCREENHACK_VISUAL DEFAULT_VISUAL
+#else /* !HAVE_JWXYZ */
+# define SCREENHACK_VISUAL 0, 0
+#endif /* !HAVE_JWXYZ */
+
+#define XSCREENSAVER_MODULE_2(CLASS,NAME,PREFIX) \
+ struct xscreensaver_function_table \
+ NAME ## _xscreensaver_function_table = { \
+ CLASS, \
+ PREFIX ## _defaults, \
+ PREFIX ## _options, \
+ xscreensaver_common_setup, 0, \
+ PREFIX ## _init, \
+ PREFIX ## _draw, \
+ PREFIX ## _reshape, \
+ PREFIX ## _event, \
+ PREFIX ## _free, \
+ 0, fps_free, \
+ SCREENHACK_VISUAL }; \
+ XSCREENSAVER_LINK (NAME ## _xscreensaver_function_table)
+
+#define XSCREENSAVER_MODULE(CLASS,PREFIX) \
+ XSCREENSAVER_MODULE_2(CLASS,PREFIX,PREFIX)
+
+#endif /* __SCREENHACK_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1992-2018 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* Found in Don Hopkins' .plan file:
+ *
+ * The color situation is a total flying circus. The X approach to
+ * device independence is to treat everything like a MicroVax framebuffer
+ * on acid. A truely portable X application is required to act like the
+ * persistent customer in the Monty Python ``Cheese Shop'' sketch. Even
+ * the simplest applications must answer many difficult questions, like:
+ *
+ * WHAT IS YOUR DISPLAY?
+ * display = XOpenDisplay("unix:0");
+ * WHAT IS YOUR ROOT?
+ * root = RootWindow(display, DefaultScreen(display));
+ * AND WHAT IS YOUR WINDOW?
+ * win = XCreateSimpleWindow(display, root, 0, 0, 256, 256, 1,
+ * BlackPixel(display, DefaultScreen(display)),
+ * WhitePixel(display, DefaultScreen(display)))
+ * OH ALL RIGHT, YOU CAN GO ON.
+ *
+ * WHAT IS YOUR DISPLAY?
+ * display = XOpenDisplay("unix:0");
+ * WHAT IS YOUR COLORMAP?
+ * cmap = DefaultColormap(display, DefaultScreen(display));
+ * AND WHAT IS YOUR FAVORITE COLOR?
+ * favorite_color = 0; / * Black. * /
+ * / * Whoops! No, I mean: * /
+ * favorite_color = BlackPixel(display, DefaultScreen(display));
+ * / * AAAYYYYEEEEE!! (client dumps core & falls into the chasm) * /
+ *
+ * WHAT IS YOUR DISPLAY?
+ * display = XOpenDisplay("unix:0");
+ * WHAT IS YOUR VISUAL?
+ * struct XVisualInfo vinfo;
+ * if (XMatchVisualInfo(display, DefaultScreen(display),
+ * 8, PseudoColor, &vinfo) != 0)
+ * visual = vinfo.visual;
+ * AND WHAT IS THE NET SPEED VELOCITY OF AN XConfigureWindow REQUEST?
+ * / * Is that a SubStructureRedirectMask or a ResizeRedirectMask? * /
+ * WHAT?! HOW AM I SUPPOSED TO KNOW THAT?
+ * AAAAUUUGGGHHH!!!! (server dumps core & falls into the chasm)
+ */
+
+#ifndef __SCREENHACK_I_H__
+#define __SCREENHACK_I_H__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#ifdef __hpux
+ /* Which of the ten billion standards does values.h belong to?
+ What systems always have it? */
+# include <values.h>
+#endif
+
+#ifdef HAVE_JWXYZ
+# include "jwxyz.h"
+# include <string.h> /* X11/Xos.h brings this in. */
+/* From utils/visual.c. */
+# define DEFAULT_VISUAL -1
+# define GL_VISUAL -6
+#else /* real X11 */
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include <X11/Xresource.h>
+# include <X11/Xos.h>
+#endif /* !HAVE_JWXYZ */
+
+#if defined(HAVE_IPHONE) || defined(HAVE_ANDROID)
+# define HAVE_MOBILE
+#endif
+
+#ifdef HAVE_ANDROID
+ /* So that hacks' debug output shows up in logcat... */
+# undef fprintf
+# define fprintf(S, ...) Log(__VA_ARGS__)
+#endif
+
+/* M_PI ought to have been defined in math.h, but... */
+#ifndef M_PI
+# define M_PI 3.1415926535
+#endif
+
+#ifndef M_PI_2
+# define M_PI_2 1.5707963267
+#endif
+
+#ifndef Button6
+# define Button6 6
+# define Button7 7
+#endif
+
+#include "yarandom.h"
+#include "usleep.h"
+#include "resources.h"
+#include "hsv.h"
+#include "colors.h"
+#include "grabscreen.h"
+#include "visual.h"
+#include "fps.h"
+#include "font-retry.h"
+
+#ifdef HAVE_RECORD_ANIM
+# include "recanim.h"
+#endif
+
+/* Be Posixly correct */
+#undef bzero
+#define bzero __ERROR_use_memset_not_bzero_in_xscreensaver__
+#undef bcopy
+#define bcopy __ERROR_use_memcpy_not_bcopy_in_xscreensaver__
+#undef ftime
+#define ftime __ERROR_use_gettimeofday_not_ftime_in_xscreensaver__
+#undef sleep
+#define sleep __ERROR_do_not_sleep_in_xscreensaver__
+
+extern Bool mono_p;
+
+struct xscreensaver_function_table {
+
+ const char *progclass;
+ const char * const *defaults;
+ const XrmOptionDescRec *options;
+
+ void (*setup_cb) (struct xscreensaver_function_table *, void *);
+ void * setup_arg;
+
+ void * (*init_cb) (Display *, Window);
+ unsigned long (*draw_cb) (Display *, Window, void *);
+ void (*reshape_cb) (Display *, Window, void *,
+ unsigned int w, unsigned int h);
+ Bool (*event_cb) (Display *, Window, void *, XEvent *);
+ void (*free_cb) (Display *, Window, void *);
+ void (*fps_cb) (Display *, Window, fps_state *, void *);
+ void (*fps_free) (fps_state *);
+
+# ifndef HAVE_JWXYZ
+ Visual * (*pick_visual_hook) (Screen *);
+ Bool (*validate_visual_hook) (Screen *, const char *, Visual *);
+# else
+ int visual;
+# endif
+
+};
+
+extern const char *progname;
+
+#endif /* __SCREENHACK_I_H__ */
--- /dev/null
+/*
+ * Copyright (c) 1987, 1988, 1989 Stanford University
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Stanford not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Stanford makes no representations about
+ * the suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+ * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* This code came with the InterViews distribution, and was translated
+ from C++ to C by Matthieu Devin <devin@lucid.com> some time in 1992.
+ */
+
+#include "utils.h"
+#include "spline.h"
+
+#define SMOOTHNESS 1.0
+
+static void grow_spline_points (spline* s);
+static void mid_point (double x0, double y0, double x1, double y1,
+ double *mx, double *my);
+static int can_approx_with_line (double x0, double y0, double x2,
+ double y2, double x3, double y3);
+static void add_line (spline* s, double x0, double y0, double x1, double y1);
+static void add_bezier_arc (spline* s,
+ double x0, double y0, double x1, double y1,
+ double x2, double y2, double x3, double y3);
+static void third_point (double x0, double y0, double x1, double y1,
+ double *tx, double *ty);
+static void calc_section (spline* s, double cminus1x, double cminus1y,
+ double cx, double cy, double cplus1x, double cplus1y,
+ double cplus2x, double cplus2y);
+
+spline*
+make_spline (unsigned int size)
+{
+ spline* s = (spline*)calloc (1, sizeof (spline));
+ if (!s) abort();
+ s->n_controls = size;
+ s->control_x = (double*)calloc (s->n_controls, sizeof (double));
+ s->control_y = (double*)calloc (s->n_controls, sizeof (double));
+
+ s->n_points = 0;
+ s->allocated_points = s->n_controls;
+ s->points = (XPoint*)calloc (s->allocated_points, sizeof (XPoint));
+
+ if (!s->control_x || !s->control_y || !s->points)
+ abort();
+
+ return s;
+}
+
+static void
+grow_spline_points (spline *s)
+{
+ s->allocated_points = 10 + (s->allocated_points * 1.3);
+ s->points =
+ (XPoint*)realloc (s->points, s->allocated_points * sizeof (XPoint));
+ if (!s->points) abort();
+}
+
+static void
+mid_point (double x0, double y0,
+ double x1, double y1,
+ double *mx, double *my)
+{
+ *mx = (x0 + x1) / 2.0;
+ *my = (y0 + y1) / 2.0;
+}
+
+static void
+third_point (double x0, double y0,
+ double x1, double y1,
+ double *tx, double *ty)
+{
+ *tx = (2 * x0 + x1) / 3.0;
+ *ty = (2 * y0 + y1) / 3.0;
+}
+
+static int
+can_approx_with_line (double x0, double y0,
+ double x2, double y2,
+ double x3, double y3)
+{
+ double triangle_area, side_squared, dx, dy;
+
+ triangle_area = x0 * y2 - x2 * y0 + x2 * y3 - x3 * y2 + x3 * y0 - x0 * y3;
+ /* actually 4 times the area. */
+ triangle_area *= triangle_area;
+ dx = x3 - x0;
+ dy = y3 - y0;
+ side_squared = dx * dx + dy * dy;
+ return triangle_area <= SMOOTHNESS * side_squared;
+}
+
+static void
+add_line (spline *s,
+ double x0, double y0,
+ double x1, double y1)
+{
+ if (s->n_points >= s->allocated_points)
+ grow_spline_points (s);
+
+ if (s->n_points == 0)
+ {
+ s->points [s->n_points].x = x0;
+ s->points [s->n_points].y = y0;
+ s->n_points += 1;
+ }
+ s->points [s->n_points].x = x1;
+ s->points [s->n_points].y = y1;
+ s->n_points += 1;
+}
+
+static void
+add_bezier_arc (spline *s,
+ double x0, double y0,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+{
+ double midx01, midx12, midx23, midlsegx, midrsegx, cx,
+ midy01, midy12, midy23, midlsegy, midrsegy, cy;
+
+ mid_point (x0, y0, x1, y1, &midx01, &midy01);
+ mid_point (x1, y1, x2, y2, &midx12, &midy12);
+ mid_point (x2, y2, x3, y3, &midx23, &midy23);
+ mid_point (midx01, midy01, midx12, midy12, &midlsegx, &midlsegy);
+ mid_point (midx12, midy12, midx23, midy23, &midrsegx, &midrsegy);
+ mid_point (midlsegx, midlsegy, midrsegx, midrsegy, &cx, &cy);
+
+ if (can_approx_with_line (x0, y0, midlsegx, midlsegy, cx, cy))
+ add_line (s, x0, y0, cx, cy);
+ else if ((midx01 != x1) || (midy01 != y1) || (midlsegx != x2)
+ || (midlsegy != y2) || (cx != x3) || (cy != y3))
+ add_bezier_arc (s, x0, y0, midx01, midy01, midlsegx, midlsegy, cx, cy);
+
+ if (can_approx_with_line (cx, cy, midx23, midy23, x3, y3))
+ add_line (s, cx, cy, x3, y3);
+ else if ((cx != x0) || (cy != y0) || (midrsegx != x1) || (midrsegy != y1)
+ || (midx23 != x2) || (midy23 != y2))
+ add_bezier_arc (s, cx, cy, midrsegx, midrsegy, midx23, midy23, x3, y3);
+}
+
+static void
+calc_section (spline *s,
+ double cminus1x, double cminus1y,
+ double cx, double cy,
+ double cplus1x, double cplus1y,
+ double cplus2x, double cplus2y)
+{
+ double p0x, p1x, p2x, p3x, tempx,
+ p0y, p1y, p2y, p3y, tempy;
+
+ third_point (cx, cy, cplus1x, cplus1y, &p1x, &p1y);
+ third_point (cplus1x, cplus1y, cx, cy, &p2x, &p2y);
+ third_point (cx, cy, cminus1x, cminus1y, &tempx, &tempy);
+ mid_point (tempx, tempy, p1x, p1y, &p0x, &p0y);
+ third_point (cplus1x, cplus1y, cplus2x, cplus2y, &tempx, &tempy);
+ mid_point (tempx, tempy, p2x, p2y, &p3x, &p3y);
+ add_bezier_arc (s, p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y);
+}
+
+void
+compute_spline (spline *s)
+{
+ int i;
+ s->n_points = 0;
+
+ if (s->n_controls < 3)
+ return;
+
+ calc_section (s, s->control_x [0], s->control_y [0], s->control_x [0],
+ s->control_y [0], s->control_x [0], s->control_y [0],
+ s->control_x [1], s->control_y [1]);
+ calc_section (s, s->control_x [0], s->control_y [0], s->control_x [0],
+ s->control_y [0], s->control_x [1], s->control_y [1],
+ s->control_x [2], s->control_y [2]);
+
+ for (i = 1; i < s->n_controls - 2; i++)
+ calc_section (s, s->control_x [i - 1], s->control_y [i - 1],
+ s->control_x [i], s->control_y [i],
+ s->control_x [i + 1], s->control_y [i + 1],
+ s->control_x [i + 2], s->control_y [i + 2]);
+
+ calc_section (s, s->control_x [i - 1], s->control_y [i - 1],
+ s->control_x [i], s->control_y [i],
+ s->control_x [i + 1], s->control_y [i + 1],
+ s->control_x [i + 1], s->control_y [i + 1]);
+ calc_section (s, s->control_x [i], s->control_y [i],
+ s->control_x [i + 1], s->control_y [i + 1],
+ s->control_x [i + 1], s->control_y [i + 1],
+ s->control_x [i + 1], s->control_y [i + 1]);
+}
+
+void
+compute_closed_spline (spline *s)
+{
+ int i;
+ s->n_points = 0;
+
+ if (s->n_controls < 3)
+ return;
+
+ calc_section (s,
+ s->control_x [s->n_controls - 1],
+ s->control_y [s->n_controls - 1],
+ s->control_x [0], s->control_y [0],
+ s->control_x [1], s->control_y [1],
+ s->control_x [2], s->control_y [2]);
+
+ for (i = 1; i < s->n_controls - 2; i++)
+ calc_section (s, s->control_x [i - 1], s->control_y [i - 1],
+ s->control_x [i], s->control_y [i],
+ s->control_x [i + 1], s->control_y [i + 1],
+ s->control_x [i + 2], s->control_y [i + 2]);
+
+ calc_section (s, s->control_x [i - 1], s->control_y [i - 1],
+ s->control_x [i], s->control_y [i],
+ s->control_x [i + 1], s->control_y [i + 1],
+ s->control_x [0], s->control_y [0]);
+ calc_section (s, s->control_x [i], s->control_y [i],
+ s->control_x [i + 1], s->control_y [i + 1],
+ s->control_x [0], s->control_y [0],
+ s->control_x [1], s->control_y [1]);
+}
+
+void
+just_fill_spline (spline *s)
+{
+ int i;
+
+ while (s->allocated_points < s->n_controls + 1)
+ grow_spline_points (s);
+
+ for (i = 0; i < s->n_controls; i++)
+ {
+ s->points [i].x = s->control_x [i];
+ s->points [i].y = s->control_y [i];
+ }
+ s->points [s->n_controls].x = s->control_x [0];
+ s->points [s->n_controls].y = s->control_y [0];
+ s->n_points = s->n_controls + 1;
+}
+
+void
+append_spline_points (spline *s1, spline *s2)
+{
+ int i;
+ while (s1->allocated_points < s1->n_points + s2->n_points)
+ grow_spline_points (s1);
+ for (i = s1->n_points; i < s1->n_points + s2->n_points; i++)
+ {
+ s1->points [i].x = s2->points [i - s1->n_points].x;
+ s1->points [i].y = s2->points [i - s1->n_points].y;
+ }
+ s1->n_points = s1->n_points + s2->n_points;
+}
+
+void
+spline_bounding_box (spline *s, XRectangle *rectangle_out)
+{
+ int min_x;
+ int max_x;
+ int min_y;
+ int max_y;
+ int i;
+
+ if (s->n_points == 0)
+ {
+ rectangle_out->x = 0;
+ rectangle_out->y = 0;
+ rectangle_out->width = 0;
+ rectangle_out->height = 0;
+ }
+
+ min_x = s->points [0].x;
+ max_x = min_x;
+ min_y = s->points [0].y;
+ max_y = min_y;
+
+ for (i = 1; i < s->n_points; i++)
+ {
+ if (s->points [i].x < min_x)
+ min_x = s->points [i].x;
+ if (s->points [i].x > max_x)
+ max_x = s->points [i].x;
+ if (s->points [i].y < min_y)
+ min_y = s->points [i].y;
+ if (s->points [i].y > max_y)
+ max_y = s->points [i].y;
+ }
+ rectangle_out->x = min_x;
+ rectangle_out->y = min_y;
+ rectangle_out->width = max_x - min_x;
+ rectangle_out->height = max_y - min_y;
+}
+
+void
+free_spline(spline * s)
+{
+ free ((void *) s->control_x);
+ free ((void *) s->control_y);
+ free ((void *) s->points);
+ free ((void *) s);
+}
--- /dev/null
+/*
+ * Copyright (c) 1987, 1988, 1989 Stanford University
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Stanford not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Stanford makes no representations about
+ * the suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+ * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* This code came with the InterViews distribution, and was translated
+ from C++ to C by Matthieu Devin <devin@lucid.com> some time in 1992.
+ */
+
+#ifndef _SPLINE_H_
+#define _SPLINE_H_
+
+typedef struct _spline
+{
+ /* input */
+ unsigned int n_controls;
+ double* control_x;
+ double* control_y;
+
+ /* output */
+ unsigned int n_points;
+ XPoint* points;
+ unsigned int allocated_points;
+} spline;
+
+spline* make_spline (unsigned int size);
+void compute_spline (spline* s);
+void compute_closed_spline (spline* s);
+void just_fill_spline (spline* s);
+void append_spline_points (spline* s1, spline* s2);
+void spline_bounding_box (spline* s, XRectangle* rectangle_out);
+void free_spline(spline *s);
+
+#endif /* _SPLINE_H_ */
--- /dev/null
+/* xscreensaver, Copyright (c) 2012-2020 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Running programs under a pipe or pty and returning bytes from them.
+ * Uses these X resources:
+ *
+ * program: What to run. Usually "xscreensaver-text".
+ * relaunchDelay: secs How long after the command dies before restarting.
+ * usePty: bool Whether to run the command interactively.
+ * metaSendsESC: bool Whether to send Alt-x as ESC x in pty-mode.
+ * swapBSDEL: bool Swap Backspace and Delete in pty-mode.
+ *
+ * On iOS and Android, textclient-mobile.c is used instead.
+ */
+
+#include "utils.h"
+
+#if !defined(HAVE_IPHONE) && !defined(HAVE_ANDROID) /* whole file */
+
+#include "textclient.h"
+#include "resources.h"
+
+#ifndef HAVE_COCOA
+# define XK_MISCELLANY
+# include <X11/keysymdef.h>
+# include <X11/Xatom.h>
+# include <X11/Intrinsic.h>
+#endif
+
+#include <stdio.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+# include <fcntl.h> /* for O_RDWR */
+#endif
+
+#ifdef HAVE_FORKPTY
+# include <sys/ioctl.h>
+# ifdef HAVE_PTY_H
+# include <pty.h>
+# endif
+# ifdef HAVE_UTIL_H
+# include <util.h>
+# endif
+# ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+# endif
+#endif /* HAVE_FORKPTY */
+
+#undef DEBUG
+
+extern const char *progname;
+
+struct text_data {
+ Display *dpy;
+ char *program;
+ int pix_w, pix_h, char_w, char_h;
+ int max_lines;
+
+ Bool pty_p;
+ XtIntervalId pipe_timer;
+ FILE *pipe;
+ pid_t pid;
+ XtInputId pipe_id;
+ Bool input_available_p;
+ Time subproc_relaunch_delay;
+ XComposeStatus compose;
+
+ Bool meta_sends_esc_p;
+ Bool swap_bs_del_p;
+ Bool meta_done_once;
+ unsigned int meta_mask;
+
+ const char *out_buffer;
+ int out_column;
+};
+
+
+static void
+subproc_cb (XtPointer closure, int *source, XtInputId *id)
+{
+ text_data *d = (text_data *) closure;
+# ifdef DEBUG
+ if (! d->input_available_p)
+ fprintf (stderr, "%s: textclient: input available\n", progname);
+# endif
+ d->input_available_p = True;
+}
+
+
+# define BACKSLASH(c) \
+ (! ((c >= 'a' && c <= 'z') || \
+ (c >= 'A' && c <= 'Z') || \
+ (c >= '0' && c <= '9') || \
+ c == '.' || c == '_' || c == '-' || c == '+' || c == '/'))
+
+#ifdef HAVE_COCOA
+static char *
+escape_str (char *s, const char *src)
+{
+ while (*src) {
+ char c = *src++;
+ if (BACKSLASH(c)) *s++ = '\\';
+ *s++ = c;
+ }
+ return s;
+}
+#endif
+
+
+/* Let's see if we're able to fork and exec at all. Thanks, macOS.
+ */
+static Bool
+selftest (void)
+{
+ static Bool done = False;
+ pid_t pid;
+ char buf [255];
+ if (done) return True;
+
+ pid = fork ();
+ switch ((int) pid)
+ {
+ case -1:
+ sprintf (buf, "%s: textclient: selftest: couldn't fork", progname);
+ perror (buf);
+ return False;
+
+ case 0: /* child */
+ {
+ char * const av[] = { "/bin/sh", "-c", "true", 0 };
+ execvp (av[0], av);
+ exit (1); /* exits child fork */
+ break;
+ }
+
+ default: /* parent */
+ {
+ int status = -1;
+ int i;
+ /* Busy-loops are bad mmmmkayyyy */
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000L; /* 0.1 sec */
+ for (i = 0; i < 50; i++) { /* 5 sec max */
+ pid_t pid2 = waitpid (pid, &status, 0);
+ if (pid == pid2) break;
+ (void) select (0, 0, 0, 0, &tv);
+ }
+
+ if (status != 0)
+ {
+# ifdef DEBUG
+ fprintf (stderr, "%s: selftest: textclient: status %d\n",
+ progname, status);
+# endif
+ return False;
+ }
+ else
+ {
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: selftest ok\n", progname);
+# endif
+ done = True;
+ }
+ break;
+ }
+ }
+
+ return True;
+}
+
+
+static void start_timer (text_data *d);
+
+static void
+launch_text_generator (text_data *d)
+{
+ XtAppContext app = XtDisplayToApplicationContext (d->dpy);
+ char buf[255];
+ const char *oprogram = d->program;
+ char *s;
+
+ size_t oprogram_size = strlen(oprogram);
+ size_t len;
+
+# ifdef HAVE_COCOA
+ /* /bin/sh on OS X 10.10 wipes out the PATH. */
+ const char *path = getenv("PATH");
+ size_t cmd_capacity = (oprogram_size + strlen(path)) * 2 + 100;
+ char *cmd = s = malloc (cmd_capacity);
+ strcpy (s, "export PATH=");
+ s += strlen (s);
+ s = escape_str (s, path);
+ strcpy (s, "; ");
+ s += strlen (s);
+# else
+ char *cmd = s = malloc ((strlen(oprogram)) * 2 + 100);
+# endif
+
+ if (!selftest())
+ {
+ if (!d->out_buffer || !*d->out_buffer)
+ d->out_buffer = "Can't exec; Gatekeeper problem?\r\n\r\n";
+ start_timer (d);
+ return;
+ }
+
+ strcpy (s, "( ");
+ strcat (s, oprogram);
+ s += strlen (s);
+
+ /* Kludge! Special-case "xscreensaver-text" to tell it how wide
+ the screen is. We used to do this by just always feeding
+ `program' through sprintf() and setting the default value to
+ "xscreensaver-text --cols %d", but that makes things blow up
+ if someone ever uses a --program that includes a % anywhere.
+ */
+ len = 17; /* strlen("xscreensaver-text") */
+ if (oprogram_size >= len &&
+ !memcmp (oprogram, "xscreensaver-text", len) &&
+ (oprogram[len] == ' ' || !oprogram[len]))
+ {
+ /* strstr is sloppy here. Technically, we should be parsing the command
+ line to identify flags and their arguments. This will blow up if one
+ of those pesky end users could set .textLiteral to "--cols".
+ */
+ if (d->char_w && !strstr (oprogram, "--cols "))
+ sprintf (s, " --cols %d", d->char_w);
+ if (d->max_lines && !strstr (oprogram, "--lines "))
+ sprintf (s, " --lines %d", d->max_lines);
+ s += strlen(s);
+
+# ifdef HAVE_COCOA
+ /* Also special-case "xscreensaver-text" to specify the text content on
+ the command line. defaults(1) on macOS doesn't know about the default
+ screenhack resources that don't make it into the
+ ~/Library/Preferences/ByHost/org.jwz.xscreensaver.*.plist.
+ */
+
+ char *text_mode_flag = " --date";
+ char *value_res = NULL;
+ char *text_mode = get_string_resource (d->dpy, "textMode", "String");
+
+ if (text_mode)
+ {
+ if (!strcmp (text_mode, "1") || !strcmp (text_mode, "literal"))
+ {
+ text_mode_flag = " --text";
+ value_res = "textLiteral";
+ }
+ else if (!strcmp (text_mode, "2") || !strcmp (text_mode, "file"))
+ {
+ text_mode_flag = " --file";
+ value_res = "textFile";
+ }
+ else if (!strcmp (text_mode, "3") || !strcmp (text_mode, "url"))
+ {
+ text_mode_flag = " --url";
+ value_res = "textURL";
+ }
+ else if (!strcmp (text_mode, "4") || !strcmp (text_mode, "program"))
+ {
+ text_mode_flag = " --program";
+ value_res = "textProgram";
+ }
+
+ free (text_mode);
+ }
+
+ strcpy (s, text_mode_flag);
+ s += strlen (s);
+
+ if (value_res)
+ {
+ size_t old_s = s - cmd;
+ char *value = get_string_resource (d->dpy, value_res, "");
+ if (!value)
+ value = strdup("");
+ cmd = realloc(cmd, cmd_capacity + strlen(value) * 2);
+ s = cmd + old_s;
+ *s = ' ';
+ ++s;
+ s = escape_str(s, value);
+ free(value);
+ }
+# endif /* HAVE_COCOA */
+ }
+
+ strcpy (s, " ) 2>&1");
+
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: launch %s: %s\n", progname,
+ (d->pty_p ? "pty" : "pipe"), cmd);
+# endif
+
+#ifdef HAVE_FORKPTY
+ if (d->pty_p)
+ {
+ int fd;
+ struct winsize ws;
+
+ ws.ws_col = d->char_w;
+ ws.ws_row = d->char_h;
+ ws.ws_xpixel = d->pix_w;
+ ws.ws_ypixel = d->pix_h;
+
+ d->pipe = 0;
+
+# ifdef HAVE_COCOA
+ if (getenv ("MallocScribble"))
+ /* This is here to stop me from wasting my time trying to answer
+ this question the next time I forget about it. */
+ fprintf (stderr, "%s: WARNING: forkpty hates 'Enable Guard Malloc'\n",
+ progname);
+# endif
+
+ if ((d->pid = forkpty(&fd, NULL, NULL, &ws)) < 0)
+ {
+ /* Unable to fork */
+ sprintf (buf, "%.100s: forkpty", progname);
+ perror (buf);
+ }
+ else if (!d->pid)
+ {
+ /* This is the child fork. */
+ char *av[10];
+ int i = 0;
+ if (putenv ("TERM=vt100"))
+ abort();
+ av[i++] = "/bin/sh";
+ av[i++] = "-c";
+ av[i++] = cmd;
+ av[i] = 0;
+# ifdef DEBUG
+ {
+ int j;
+ fprintf (stderr, "%s: textclient: execvp:", progname);
+ for (j = 0; j < i; j++)
+ fprintf (stderr, " %s", av[j]);
+ fprintf (stderr, "\n");
+ }
+# endif
+ execvp (av[0], av);
+ sprintf (buf, "%.100s: %.100s", progname, oprogram);
+ perror (buf);
+ exit (1);
+ }
+ else
+ {
+ /* This is the parent fork. */
+ if (d->pipe) abort();
+ d->pipe = fdopen (fd, "r+");
+ if (d->pipe_id) abort();
+ d->pipe_id =
+ XtAppAddInput (app, fileno (d->pipe),
+ (XtPointer) (XtInputReadMask | XtInputExceptMask),
+ subproc_cb, (XtPointer) d);
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: pid = %d\n", progname, d->pid);
+# endif
+ }
+ }
+ else
+#endif /* HAVE_FORKPTY */
+ {
+ /* don't mess up controlling terminal on "-pipe -program tcsh". */
+ static int protected_stdin_p = 0;
+ if (! protected_stdin_p) {
+ fclose (stdin);
+ open ("/dev/null", O_RDWR); /* re-allocate fd 0 */
+ protected_stdin_p = 1;
+ }
+
+ if (d->pipe) abort();
+ if ((d->pipe = popen (cmd, "r")))
+ {
+ if (d->pipe_id) abort();
+ d->pipe_id =
+ XtAppAddInput (app, fileno (d->pipe),
+ (XtPointer) (XtInputReadMask | XtInputExceptMask),
+ subproc_cb, (XtPointer) d);
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: popen\n", progname);
+# endif
+ }
+ else
+ {
+ sprintf (buf, "%.100s: %.100s", progname, cmd);
+ perror (buf);
+ }
+ }
+
+ free (cmd);
+}
+
+
+static void
+relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
+{
+ text_data *d = (text_data *) closure;
+ /* if (!d->pipe_timer) abort(); */
+ d->pipe_timer = 0;
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: launch timer fired\n", progname);
+# endif
+ launch_text_generator (d);
+}
+
+
+static void
+start_timer (text_data *d)
+{
+ XtAppContext app = XtDisplayToApplicationContext (d->dpy);
+
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: relaunching in %d\n", progname,
+ (int) d->subproc_relaunch_delay);
+# endif
+ if (d->pipe_timer)
+ XtRemoveTimeOut (d->pipe_timer);
+ d->pipe_timer =
+ XtAppAddTimeOut (app, d->subproc_relaunch_delay,
+ relaunch_generator_timer,
+ (XtPointer) d);
+}
+
+
+static void
+close_pipe (text_data *d)
+{
+ if (d->pid)
+ {
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: kill %d\n", progname, d->pid);
+# endif
+ kill (d->pid, SIGTERM);
+ }
+ d->pid = 0;
+
+ if (d->pipe_id)
+ XtRemoveInput (d->pipe_id);
+ d->pipe_id = 0;
+
+ if (d->pipe)
+ {
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: pclose\n", progname);
+# endif
+ pclose (d->pipe);
+ }
+ d->pipe = 0;
+
+
+}
+
+
+void
+textclient_reshape (text_data *d,
+ int pix_w, int pix_h,
+ int char_w, int char_h,
+ int max_lines)
+{
+# if defined(HAVE_FORKPTY) && defined(TIOCSWINSZ)
+
+ d->pix_w = pix_w;
+ d->pix_h = pix_h;
+ d->char_w = char_w;
+ d->char_h = char_h;
+ d->max_lines = max_lines;
+
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: reshape: %dx%d, %dx%d\n", progname,
+ pix_w, pix_h, char_w, char_h);
+# endif
+
+ if (d->pid && d->pipe)
+ {
+ /* Tell the sub-process that the screen size has changed. */
+ struct winsize ws;
+ ws.ws_col = char_w;
+ ws.ws_row = char_h;
+ ws.ws_xpixel = pix_w;
+ ws.ws_ypixel = pix_h;
+ ioctl (fileno (d->pipe), TIOCSWINSZ, &ws);
+ kill (d->pid, SIGWINCH);
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: SIGWINCH\n", progname);
+# endif
+ }
+# endif /* HAVE_FORKPTY && TIOCSWINSZ */
+
+
+ /* If we're running xscreensaver-text, then kill and restart it any
+ time the window is resized so that it gets an updated --cols arg
+ right away. But if we're running something else, leave it alone.
+ */
+ if (!strcmp (d->program, "xscreensaver-text"))
+ {
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: reshape relaunch\n", progname);
+# endif
+ close_pipe (d);
+ d->input_available_p = False;
+ start_timer (d);
+ }
+}
+
+
+text_data *
+textclient_open (Display *dpy)
+{
+ text_data *d = (text_data *) calloc (1, sizeof (*d));
+
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: init\n", progname);
+# endif
+
+ d->dpy = dpy;
+
+ if (get_boolean_resource (dpy, "usePty", "UsePty"))
+ {
+# ifdef HAVE_FORKPTY
+ d->pty_p = True;
+# else
+ fprintf (stderr,
+ "%s: no pty support on this system; using a pipe instead.\n",
+ progname);
+# endif
+ }
+
+ d->subproc_relaunch_delay =
+ get_integer_resource (dpy, "relaunchDelay", "Time");
+ if (d->subproc_relaunch_delay < 1)
+ d->subproc_relaunch_delay = 1;
+ d->subproc_relaunch_delay *= 1000;
+
+
+ d->meta_sends_esc_p = get_boolean_resource (dpy, "metaSendsESC", "Boolean");
+ d->swap_bs_del_p = get_boolean_resource (dpy, "swapBSDEL", "Boolean");
+
+ d->program = get_string_resource (dpy, "program", "Program");
+
+
+# ifdef HAVE_FORKPTY
+ /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */
+ {
+ const char *s = getenv ("XSCREENSAVER_STANDALONE");
+ if (s && *s && strcmp(s, "0"))
+ {
+ d->pty_p = 1;
+ d->program = strdup (getenv ("SHELL"));
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: standalone: %s\n",
+ progname, d->program);
+# endif
+ }
+ }
+# endif
+
+ start_timer (d);
+
+ return d;
+}
+
+
+void
+textclient_close (text_data *d)
+{
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: free\n", progname);
+# endif
+
+ close_pipe (d);
+ if (d->program)
+ free (d->program);
+ if (d->pipe_timer)
+ XtRemoveTimeOut (d->pipe_timer);
+ d->pipe_timer = 0;
+ memset (d, 0, sizeof (*d));
+ free (d);
+}
+
+int
+textclient_getc (text_data *d)
+{
+ XtAppContext app = XtDisplayToApplicationContext (d->dpy);
+ int ret = -1;
+
+ if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
+ XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+
+ if (d->out_buffer && *d->out_buffer)
+ {
+ ret = *d->out_buffer;
+ d->out_buffer++;
+ }
+ else if (d->input_available_p && d->pipe)
+ {
+ unsigned char s[2];
+ int n = read (fileno (d->pipe), (void *) s, 1);
+ if (n > 0)
+ ret = s[0];
+ else /* EOF */
+ {
+ if (d->pid)
+ {
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: waitpid %d\n",
+ progname, d->pid);
+# endif
+ waitpid (d->pid, NULL, 0);
+ d->pid = 0;
+ }
+
+ close_pipe (d);
+
+ if (d->out_column > 0)
+ {
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: adding blank line at EOF\n",
+ progname);
+# endif
+ d->out_buffer = "\r\n\r\n";
+ }
+
+ start_timer (d);
+ }
+ d->input_available_p = False;
+ }
+
+ if (ret == '\r' || ret == '\n')
+ d->out_column = 0;
+ else if (ret > 0)
+ d->out_column++;
+
+# ifdef DEBUG
+ if (ret <= 0)
+ fprintf (stderr, "%s: textclient: getc: %d\n", progname, ret);
+ else if (ret < ' ')
+ fprintf (stderr, "%s: textclient: getc: %03o\n", progname, ret);
+ else
+ fprintf (stderr, "%s: textclient: getc: '%c'\n", progname, (char) ret);
+# endif
+
+ return ret;
+}
+
+
+/* The interpretation of the ModN modifiers is dependent on what keys
+ are bound to them: Mod1 does not necessarily mean "meta". It only
+ means "meta" if Meta_L or Meta_R are bound to it. If Meta_L is on
+ Mod5, then Mod5 is the one that means Meta. Oh, and Meta and Alt
+ aren't necessarily the same thing. Icepicks in my forehead!
+ */
+static unsigned int
+do_icccm_meta_key_stupidity (Display *dpy)
+{
+ unsigned int modbits = 0;
+# ifndef HAVE_COCOA
+ int i, j, k;
+ XModifierKeymap *modmap = XGetModifierMapping (dpy);
+ for (i = 3; i < 8; i++)
+ for (j = 0; j < modmap->max_keypermod; j++)
+ {
+ int code = modmap->modifiermap[i * modmap->max_keypermod + j];
+ KeySym *syms;
+ int nsyms = 0;
+ if (code == 0) continue;
+ syms = XGetKeyboardMapping (dpy, code, 1, &nsyms);
+ for (k = 0; k < nsyms; k++)
+ if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R ||
+ syms[k] == XK_Alt_L || syms[k] == XK_Alt_R)
+ modbits |= (1 << i);
+ XFree (syms);
+ }
+ XFreeModifiermap (modmap);
+# endif /* HAVE_COCOA */
+ return modbits;
+}
+
+
+/* Returns a mask of the bit or bits of a KeyPress event that mean "meta".
+ */
+static unsigned int
+meta_modifier (text_data *d)
+{
+ if (!d->meta_done_once)
+ {
+ /* Really, we are supposed to recompute this if a KeymapNotify
+ event comes in, but fuck it. */
+ d->meta_done_once = True;
+ d->meta_mask = do_icccm_meta_key_stupidity (d->dpy);
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: ICCCM Meta is 0x%08X\n",
+ progname, d->meta_mask);
+# endif
+ }
+ return d->meta_mask;
+}
+
+
+Bool
+textclient_putc (text_data *d, XKeyEvent *k)
+{
+ KeySym keysym;
+ unsigned char c = 0;
+ XLookupString (k, (char *) &c, 1, &keysym, &d->compose);
+ if (c != 0 && d->pipe)
+ {
+ if (!d->swap_bs_del_p) ;
+ else if (c == 127) c = 8;
+ else if (c == 8) c = 127;
+
+ /* If meta was held down, send ESC, or turn on the high bit. */
+ if (k->state & meta_modifier (d))
+ {
+ if (d->meta_sends_esc_p)
+ fputc ('\033', d->pipe);
+ else
+ c |= 0x80;
+ }
+
+ fputc (c, d->pipe);
+ fflush (d->pipe);
+ k->type = 0; /* don't interpret this event defaultly. */
+
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: putc '%c'\n", progname, (char) c);
+# endif
+
+ return True;
+ }
+ return False;
+}
+
+#endif /* !HAVE_IPHONE -- whole file */
--- /dev/null
+/* xscreensaver, Copyright (c) 2012-2016 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Running "xscreensaver-text" and returning bytes from it.
+ */
+
+#ifndef __TEXTCLIENT_H__
+#define __TEXTCLIENT_H__
+
+# ifdef HAVE_IPHONE
+# undef HAVE_FORKPTY
+# endif
+
+typedef struct text_data text_data;
+
+extern text_data *textclient_open (Display *);
+extern void textclient_close (text_data *);
+extern void textclient_reshape (text_data *,
+ int pix_w, int pix_h,
+ int char_w, int char_h,
+ int max_lines);
+extern int textclient_getc (text_data *);
+extern Bool textclient_putc (text_data *, XKeyEvent *);
+
+# if defined(HAVE_IPHONE) || defined(HAVE_ANDROID)
+extern char *textclient_mobile_date_string (void);
+extern char *textclient_mobile_url_string (Display *, const char *url);
+# endif
+
+#endif /* __TEXTCLIENT_H__ */
--- /dev/null
+/* -*- mode: c; tab-width: 4; fill-column: 78 -*- */
+/* vi: set ts=4 tw=78: */
+
+/*
+thread_util.c, Copyright (c) 2014 Dave Odell <dmo2118@gmail.com>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation. No representations are made about the suitability of this
+software for any purpose. It is provided "as is" without express or
+implied warranty.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h> /* Only used by thread_memory_alignment(). */
+#include <string.h>
+
+#if HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if defined __MACH__ && defined __APPLE__ /* OS X, iOS */
+# include <sys/sysctl.h>
+# include <inttypes.h>
+#endif
+
+#include "thread_util.h"
+
+#include "aligned_malloc.h"
+#include "resources.h"
+
+#define IS_POWER_OF_2(x) ((x) > 0 && !((x) & ((x) - 1)))
+
+/*
+ arraysize(a). Also known as countof(x), XtNumber(x), NELEMS(x), LEN(x),
+ NUMOF(x), ARRAY_SIZE(x), etc., since the fine folks behind C never got
+ around to including this incredibly useful macro in the standard library,
+ which is where it belongs.
+
+ Much of the code here assumes that multiple processors in a system all use
+ the same cache line size...which might be wrong on occasion.
+*/
+
+#define arraysize(a) (sizeof(a) / sizeof(*(a)))
+#define arrayend(a) ((a) + arraysize(a))
+
+/*
+These numbers are from:
+- Linux: arch/(arch name)/include/asm/cache.h, note
+ L1_CACHE_BYTES/L1_CACHE_SHIFT/SMP_CACHE_BYTES.
+- FreeBSD: sys/(sys name)/include/param.h, note
+ CACHE_LINE_SHIFT/CACHE_LINE_SIZE.
+
+Preprocessor symbols come from:
+- TARGET_CPU_CPP_BUILTINS() in the GNU C preprocessor
+ <http://code.ohloh.net/?s=%22TARGET_CPU_CPP_BUILTINS%22&fp=304413>
+- http://predef.sourceforge.net/
+*/
+
+/*
+Several architectures need preprocessor symbols.
+
+Qualcomm Hexagon: 1 << 5
+Imagination Technologies META: 1 << 6
+OpenRISC: 16 (Linux has the cache line size as a todo.)
+Unicore: 1 << 5
+*/
+
+#if HAVE_PTHREAD
+
+# if !HAVE_UNISTD_H
+# error unistd.h must be present whenever pthread.h is.
+# endif
+
+# if defined __MACH__ && defined __APPLE__ /* OS X, iOS */
+# include <TargetConditionals.h> /* For TARGET_OS_IPHONE. */
+# ifdef TARGET_OS_IPHONE
+# define _CACHE_LINE_SIZE 64
+# endif
+# endif
+
+# if defined __FreeBSD__ && !defined _CACHE_LINE_SIZE
+# include <machine/param.h>
+# ifdef CACHE_LINE_SIZE
+# define _CACHE_LINE_SIZE CACHE_LINE_SIZE
+# endif
+# endif
+
+# if !defined _CACHE_LINE_SIZE
+# if defined __alpha || defined __alpha__
+/* DEC Alpha */
+# define _CACHE_LINE_SIZE 64 /* EV6 and above. EV4 and EV5 use 32 bytes. */
+# elif defined __arm__
+/* ARM architecture */
+# define _CACHE_LINE_SIZE (1 << 6)
+# elif defined __AVR || defined __AVR__
+/* Atmel AVR32 */
+# define _CACHE_LINE_SIZE (1 << 5)
+# elif defined __bfin || defined __BFIN__
+/* Analog Devices Blackfin */
+# define _CACHE_LINE_SIZE (1 << 5)
+# elif defined _TMS320C6X || defined __TMS320C6X__
+/* Texas Instruments TMS320C6x */
+# define _CACHE_LINE_SIZE (1 << 7) /* From L2. L1 data cache line is 1 << 6. */
+# elif defined __cris
+/* Axis Communications ETRAX CRIS */
+# define _CACHE_LINE_SIZE 32
+# elif defined __ia64__ || defined _IA64
+/* Intel Itanium */
+# define _CACHE_LINE_SIZE (1 << 7)
+# elif defined __M32R__ || defined __m32r__
+/* Mitsubishi/Renesas M32R */
+# define _CACHE_LINE_SIZE (1 << 4)
+# elif defined __m68k__ || defined M68000 || defined __MC68K__
+/* Motorola 68000 */
+# define _CACHE_LINE_SIZE (1 << 4)
+# elif defined __MICROBLAZE__ || defined __microblaze__
+/* Xilinx MicroBlaze */
+# define _CACHE_LINE_SIZE (1 << 5)
+# elif defined __mips__ || defined __mips || defined __MIPS__
+/* MIPS */
+# define _CACHE_LINE_SIZE (1 << 6)
+# elif defined __mn10300__ || defined __MN10300__
+/* Matsushita/Panasonic MN103 */
+# define _CACHE_LINE_SIZE 32 /* MN103E010 has 16 bytes. */
+# elif defined __hppa || defined __hppa__
+/* Hewlett-Packard PA-RISC */
+# define _CACHE_LINE_SIZE 64 /* PA-RISC 2.0 uses 64 bytes, PA-RISC 1.1 uses 32. */
+# elif defined __powerpc || defined _ARCH_PPC
+/* Power Architecture (a.k.a. PowerPC) */
+# define _CACHE_LINE_SIZE (1 << 7) /* Linux has a list of PPC models with associated L1_CACHE_SHIFT values. */
+# elif defined __s390__ || defined __370__ || defined __zarch__ || defined __SYSC_ZARCH__
+/* IBM System/390 */
+# define _CACHE_LINE_SIZE 256
+# elif defined SUNPLUS || defined __SCORE__ || defined __score__
+/* Sunplus S+core */
+# define _CACHE_LINE_SIZE (1 << 4)
+# elif defined __sh__
+/* Hitachi SuperH */
+# define _CACHE_LINE_SIZE (1 << 5) /* SH3 and earlier used 1 << 4. */
+# elif defined __sparc__ || defined __sparc
+/* SPARC */
+# define _CACHE_LINE_SIZE (1 << 7) /* Linux and FreeBSD disagree as to what this should be. */
+# elif defined __tile__
+/* Tilera TILE series */
+# define _CACHE_LINE_SIZE (1 << 6) /* TILEPro uses different sizes for L1 and L2. */
+# elif defined __i386 || defined __x86_64
+/* x86(-64) */
+# define _CACHE_LINE_SIZE (1 << 7)
+# elif defined __xtensa__ || defined __XTENSA__
+/* Cadence Design Systems/Tensilica Xtensa */
+# define _CACHE_LINE_SIZE (1 << 5) /* 1 << 4 on some models. */
+# endif
+# endif /* !defined _CACHE_LINE_SIZE */
+
+# if defined __NetBSD__ && !defined _CACHE_LINE_SIZE
+/*
+NetBSD defines COHERENCY_UNIT to be 32 on MIPS, and 64 for all other platforms -- which is wrong. Still, this is what the kernel
+uses; if this value didn't work, the system wouldn't run.
+*/
+# include <sys/param.h>
+# ifdef COHERENCY_UNIT
+# define _CACHE_LINE_SIZE COHERENCY_UNIT
+# endif
+# endif
+
+# ifndef _CACHE_LINE_SIZE
+# define _CACHE_LINE_SIZE 256 /* Fallback cache line size. */
+# endif
+
+static unsigned _get_cache_line_size(void)
+{
+ /*
+ The general idea:
+ - Try to get the actual cache line size from the operating system.
+ - In the interest of keeping things simple, this only checks with
+ glibc and OS X.
+ - A few other methods that could be added:
+ - Query x86 CPUs directly with the CPUID instruction.
+ - Query various ELF systems through the auxillary vector.
+ (Power, Alpha, SuperH)
+ - Query Linux through
+ /sys/devices/system/cpu/cpu?/cache/index?/coherency_line_size
+ (x86 only, AFAIK)
+ - Query Linux through cache_alignment in /proc/cpuinfo
+ - Query Solaris through PICL.
+ - If that fails, return a value appropriate for the current CPU
+ architecture.
+ - Otherwise, return a sufficiently large number.
+ */
+
+ /*
+ sysconf(3) is not a syscall, it's a glibc call that, for cache line sizes,
+ uses CPUID on x86 and returns 0 on other platforms. If it were to work on
+ most other platforms, it would have to get cache information from the
+ kernel, since that information is usually made available by the processor
+ only in privileged mode.
+ https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/i386/sysconf.c;hb=HEAD
+ */
+
+ /* uClibc, newlib, dietlibc, musl, Bionic do not have this. */
+
+# if HAVE_UNISTD_H && ( \
+ defined _SC_LEVEL1_DCACHE_LINESIZE || \
+ defined _SC_LEVEL2_CACHE_LINESIZE || \
+ defined _SC_LEVEL3_CACHE_LINESIZE || \
+ defined _SC_LEVEL4_CACHE_LINESIZE)
+ {
+ static const int names[] =
+ {
+# ifdef _SC_LEVEL1_DCACHE_LINESIZE
+ _SC_LEVEL1_DCACHE_LINESIZE,
+# endif
+# ifdef _SC_LEVEL2_CACHE_LINESIZE
+ _SC_LEVEL2_CACHE_LINESIZE,
+# endif
+# ifdef _SC_LEVEL3_CACHE_LINESIZE
+ _SC_LEVEL3_CACHE_LINESIZE,
+# endif
+# ifdef _SC_LEVEL4_CACHE_LINESIZE
+ _SC_LEVEL4_CACHE_LINESIZE
+# endif
+ };
+
+ const int *name;
+ long result = 0;
+
+ for(name = names; name != arrayend(names); ++name)
+ {
+ long sysconf_result = sysconf(*name); /* Can return -1 or 0 on
+ failure. */
+
+ if(sysconf_result > result)
+ result = sysconf_result;
+ }
+
+ if(result)
+ return result;
+
+ /* Currently, this fails for every platform that isn't x86. Perhaps
+ future versions will support other processors? */
+ }
+# endif
+
+# if defined __MACH__ && defined __APPLE__
+ {
+ uint32_t result; /* sysctl.h says that hw.cachelinesize is a
+ CTLTYPE_INT. */
+ size_t size = sizeof(result);
+ static const int name[] = {CTL_HW, HW_CACHELINE};
+
+ if(!sysctl((int *)name, 2, &result, &size, NULL, 0)) /* (int *) is for OS X. */
+ {
+ assert(size == sizeof(result));
+ return result;
+ };
+ }
+# endif
+
+ /* Guess based on the CPU type. */
+ return _CACHE_LINE_SIZE;
+}
+
+const pthread_mutex_t mutex_initializer =
+# if defined _GNU_SOURCE && !defined NDEBUG
+ PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+# else
+ PTHREAD_MUTEX_INITIALIZER
+# endif
+ ;
+
+const pthread_cond_t cond_initializer = PTHREAD_COND_INITIALIZER;
+
+static int _has_pthread = 0; /* Initialize when needed. */
+static int _cache_line_size = sizeof(void *);
+
+/* This is actually the init function for various things in here. */
+int threads_available(Display *dpy)
+{
+/* This is maybe not thread-safe, but: this should -- and generally will --
+ be called before the program launches its second thread. */
+
+ if(!_has_pthread)
+ {
+# if _POSIX_THREADS
+ _has_pthread = _POSIX_THREADS;
+# else
+ _has_pthread = sysconf(_SC_THREADS);
+# endif
+
+ if(_has_pthread >= 0)
+ {
+ if(get_boolean_resource(dpy, "useThreads", "Boolean"))
+ {
+ _cache_line_size = _get_cache_line_size();
+ assert(_cache_line_size >= sizeof(void *));
+ assert(IS_POWER_OF_2(_cache_line_size));
+ }
+ else
+ {
+ _has_pthread = -1;
+ }
+ }
+ }
+
+ return _has_pthread;
+}
+
+#endif /* HAVE_PTHREAD */
+
+/*
+ hardware_concurrency() -
+
+ Various platforms offer various statistics that look like they should be
+ useful: sysconf(_SC_NPROCESSORS_ONLN) (i.e. the number of 'online'
+ processors) in particular is available on many Unixes, and is frequently
+ used for functions like hardware_concurrency(). But 'online' is somewhat
+ ambiguous; it can mean:
+
+ 1. The number of CPU cores that are not (temporarily) asleep. (e.g. Android
+ can sometimes put cores to sleep if they aren't being used, and this is
+ reflected in _SC_NPROCESSORS_ONLN.)
+
+ 2. The maximum number of CPU cores that can be provided to this application,
+ as currently set by the system administrator. (2) is the one that
+ hardware_concurrency() ultimately needs.
+*/
+
+/*
+ Shamelessly plagarized from Boost.Thread and Stack Overflow
+ <http://stackoverflow.com/q/150355>. GNU libstdc++ has some of this too,
+ see thread::hardware_concurrency() in thread.cc.
+ http://gcc.gnu.org/viewcvs/gcc/trunk/libstdc%2B%2B-v3/src/c%2B%2B11/thread.cc?view=markup
+
+ This might not work right on less common systems for various reasons.
+*/
+
+#if HAVE_PTHREAD
+# if defined __APPLE__ && defined __MACH__ || \
+ defined __FreeBSD__ || \
+ defined __OpenBSD__ || \
+ defined __NetBSD__ || \
+ defined __DragonFly__ || \
+ defined __minix
+
+/*
+ BSD Unixes use sysctl(3) for this.
+ Some BSDs also support sysconf(3) for this, but in each case this was added
+ after sysctl(3).
+ Linux: sysctl is present, but strongly deprecated.
+ Minix uses the NetBSD userspace, so it has both this and sysconf(3).
+ QNX: sysctl is present for kern.* and net.*, but it doesn't say anything
+ about hw.*
+*/
+
+/* __APPLE__ without __MACH__ is OS 9 or earlier. __APPLE__ with __MACH__ is OS X. */
+
+/*
+The usual thing to do here is for sysctl(3) to call __sysctl(2).
+ http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/gen/sysctl.c?only_with_tag=HEAD
+ http://svnweb.freebsd.org/base/head/lib/libc/gen/sysctl.c?view=markup
+*/
+
+/*
+ OS X: Xcode Instruments (as of Xcode 4; Apple likes to move things like
+ this around) can disable CPUs as a debugging tool.
+ Instruments -> Preferences... (Command-,) -> General -> Active Processor Cores
+ FreeBSD, OpenBSD: It doesn't look like CPUs can be disabled.
+ NetBSD: CPUs can be disabled manually through cpuctl(8).
+*/
+
+# include <stddef.h>
+
+/* FreeBSD: sys/sysctl.h needs sys/types.h, but the one doesn't bring the
+ other in automatically. */
+# include <sys/types.h>
+# include <sys/sysctl.h>
+
+static unsigned _hardware_concurrency(void)
+{
+ int count;
+ size_t size = sizeof(count);
+
+# if defined __APPLE__ && defined __MACH__
+ /* Apple sez: sysctl("hw.logicalcpu") is affected by the "current power
+ management mode", so use hw.logicalcpu_max. */
+ /* https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/sysctl.3.html */
+ if(!sysctlbyname("hw.logicalcpu_max", &count, &size, NULL, 0)) /* Preferred on more recent Darwin. */
+ {
+ assert(size == sizeof(count));
+ return count;
+ }
+# endif
+
+# if defined HW_NCPUONLINE
+ /* NetBSD has this. */
+ {
+ static const int name[] = {CTL_HW, HW_NCPUONLINE};
+ if(!sysctl(name, 2, &count, &size, NULL, 0))
+ {
+ assert(size == sizeof(count));
+ return count;
+ }
+ }
+# endif
+
+ {
+ static const int name[] = {CTL_HW, HW_NCPU};
+ if(!sysctl((int *)name, 2, &count, &size, NULL, 0)) /* (int *) is for OS X. */
+ {
+ assert(size == sizeof(count));
+ return count;
+ }
+ }
+
+ return 1;
+}
+
+# elif HAVE_UNISTD_H && defined _SC_NPROCESSORS_ONLN
+
+/*
+Supported by:
+Linux 2.0 was the first version to provide SMP support via clone(2).
+ (e)glibc on Linux provides this, which in turn uses get_nprocs().
+ get_nprocs in turn uses /sys/devices/system/cpu/online, /proc/stat, or /proc/cpuinfo, whichever's available.
+ https://sourceware.org/git/?p=glibc.git;a=blob;f=posix/sysconf.c;hb=HEAD
+ https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD
+ Linux usually isn't configured to auto-enable/disable cores.
+SunOS (Solaris), sometime between 4.1.3 and 5.5.1.
+ This includes all open source derivatives of 5.10. (Illumos, OpenIndiana)
+ sysconf(_SC_NPROCESSORS_ONLN) call _sysconfig(2).
+ Not sure if CPU power management (enabled by default, see cpupm and
+ cpu_deep_idle in power.conf(4)) affects this.
+ psradm(1M) can bring up/down CPU cores, which affects
+ sysconf(_SC_NPROCESSORS_ONLN).
+ http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/sysconf.c
+ Minix 3.2, at the latest. (This is the first version to support SMP.)
+ AIX 7.1, probably earlier.
+
+Also:
+Mac OS X apparently has this on 10.5+.
+FreeBSD 5.0, NetBSD 5.0 also have this. They both call sysctl(3).
+ http://svnweb.freebsd.org/base/head/lib/libc/gen/sysconf.c?view=markup
+ http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/gen/sysconf.c?only_with_tag=HEAD
+
+QNX has sysconf(3), but it doesn't have _SC_NPROCESSORS_*.
+*/
+
+static unsigned _hardware_concurrency(void)
+{
+ long count = sysconf(_SC_NPROCESSORS_ONLN);
+ return count > 0 ? count : 1;
+}
+
+# else
+
+static unsigned _hardware_concurrency(void)
+{
+ return 1; /* Fallback for unknown systems. */
+}
+
+# endif
+#endif
+
+unsigned hardware_concurrency(Display *dpy)
+{
+#if HAVE_PTHREAD
+ if(threads_available(dpy) >= 0)
+ return _hardware_concurrency();
+#endif
+ return 1;
+}
+
+/* thread_memory_alignment() - */
+
+unsigned thread_memory_alignment(Display *dpy)
+{
+ (void)threads_available(dpy);
+#if HAVE_PTHREAD
+ return _cache_line_size;
+#else
+ return sizeof(void *);
+#endif
+}
+
+/* Thread pool - */
+
+static unsigned _threadpool_count_serial(struct threadpool *self)
+{
+#if HAVE_PTHREAD
+ assert(_has_pthread);
+ if(_has_pthread >= 0)
+ return self->count ? 1 : 0;
+#endif
+ return self->count;
+}
+
+static void _serial_destroy(struct threadpool *self)
+{
+ void *thread = self->serial_threads;
+ unsigned i, count = _threadpool_count_serial(self);
+
+ for(i = 0; i != count; ++i)
+ {
+ self->thread_destroy(thread);
+ thread = (char *)thread + self->thread_size;
+ }
+
+ free(self->serial_threads);
+}
+
+#if HAVE_PTHREAD
+
+static void _parallel_abort(struct threadpool *self)
+{
+ assert(self->count > 1);
+ self->count = self->parallel_unfinished + 1 /* The '+ 1' should technically be _threadpool_count_serial(self). */;
+ PTHREAD_VERIFY(pthread_cond_broadcast(&self->cond));
+}
+
+struct _parallel_startup_type
+{
+ struct threadpool *parent;
+ int (*thread_create)(void *self, struct threadpool *pool, unsigned id);
+ int last_errno;
+};
+
+static unsigned _threadpool_count_parallel(struct threadpool *self)
+{
+ assert(_has_pthread);
+ assert(self->count >= 1);
+ return self->count - 1 /* The '- 1' should technically be _threadpool_count_serial(self). */;
+}
+
+static void *_start_routine(void *startup_raw);
+
+/* Tricky lock sequence: _add_next_thread unlocks on error. */
+static void _add_next_thread(struct _parallel_startup_type *self)
+{
+ assert(!self->last_errno);
+
+ if(self->parent->parallel_unfinished == _threadpool_count_parallel(self->parent))
+ {
+ PTHREAD_VERIFY(pthread_cond_broadcast(&self->parent->cond));
+ }
+ else
+ {
+ pthread_t *thread = self->parent->parallel_threads + self->parent->parallel_unfinished;
+ self->last_errno = pthread_create(thread, NULL, _start_routine, self);
+ if(self->last_errno)
+ _parallel_abort(self->parent);
+ }
+}
+
+static void *_thread_free_and_unlock(struct threadpool *self, void *thread)
+{
+ PTHREAD_VERIFY(pthread_mutex_unlock(&self->mutex));
+# if !HAVE_ALLOCA
+ thread_free(thread);
+# endif
+ return NULL;
+}
+
+static void *_thread_destroy_and_unlock(struct threadpool *self, void *thread)
+{
+ self->thread_destroy(thread);
+ return _thread_free_and_unlock(self, thread);
+}
+
+/* At one point, one of the threads refused to destroy itself at the end. Why?! And why won't it happen again? */
+
+static void *_start_routine(void *startup_raw)
+{
+ struct _parallel_startup_type *startup = (struct _parallel_startup_type *)startup_raw;
+
+ struct threadpool *parent = startup->parent;
+
+ void *thread;
+
+ PTHREAD_VERIFY(pthread_mutex_lock(&parent->mutex));
+ ++parent->parallel_unfinished;
+
+# if HAVE_ALLOCA
+/* Ideally, the thread object goes on the thread's stack. This guarantees no false sharing with other threads, and in a NUMA
+ configuration, ensures that the thread object is using memory from the right node. */
+ thread = alloca(parent->thread_size);
+# else
+ startup->last_errno = thread_malloc(&thread, NULL, parent->thread_size);
+ if(startup->last_errno)
+ {
+ _parallel_abort(parent);
+ PTHREAD_VERIFY(pthread_mutex_unlock(&parent->mutex));
+ return NULL;
+ }
+# endif
+
+/* Setting thread affinity for threads running in lock-step can cause delays
+ and jumpiness. Ideally, there would be some way to recommend (but not
+ require) that a thread run on a certain core/set of cores. */
+
+/* Neither Linux nor libnuma seem to support the concept of a preferred/ideal
+ CPU for a thread/process. */
+
+/* Untested. */
+/* {
+ cpu_set_t cpu_set;
+ CPU_ZERO(&cpu_set);
+ CPU_SET(&cpu_set, &parent._threads_unfinished);
+ pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpu_set);
+ } */
+
+ startup->last_errno = startup->thread_create(thread, parent, parent->parallel_unfinished);
+ if(startup->last_errno)
+ {
+ _parallel_abort(parent);
+ return _thread_free_and_unlock(parent, thread); /* Tail calls make everything better. */
+ }
+
+ assert(!startup->last_errno);
+ _add_next_thread(startup); /* Calls _parallel_abort() on failure. */
+ if(startup->last_errno)
+ return _thread_destroy_and_unlock(parent, thread);
+
+ for(;;)
+ {
+ for(;;)
+ {
+ /*
+ This must come before the '.threads' check, otherwise if
+ threadpool_destroy is called immediately after a run starts, then
+ it's possible that not all threads would be launched for the final
+ run. This can cause deadlock in conjunction with things like
+ barriers.
+ */
+ if(parent->parallel_pending)
+ break; /* Start a run. */
+
+ if(!parent->parallel_threads)
+ return _thread_destroy_and_unlock(parent, thread); /* Threads are shutting down. */
+
+ PTHREAD_VERIFY(pthread_cond_wait(&parent->cond, &parent->mutex));
+ }
+
+ --parent->parallel_pending;
+ if(!parent->parallel_pending)
+ PTHREAD_VERIFY(pthread_cond_broadcast(&parent->cond));
+ /* All threads have started processing, other threads can finish. */
+
+ PTHREAD_VERIFY(pthread_mutex_unlock(&parent->mutex));
+
+ parent->thread_run(thread);
+
+ PTHREAD_VERIFY(pthread_mutex_lock(&parent->mutex));
+# if 0
+ if(!parent->parallel_threads) /* I don't think this is necessary anymore. */
+ break;
+# endif
+ /* Don't loop around until all other threads have begun processing. */
+
+ /* I suspect it doesn't matter whether this comes before or after the threads_unfinished check. */
+ while(parent->parallel_pending)
+ PTHREAD_VERIFY(pthread_cond_wait(&parent->cond, &parent->mutex));
+
+ --parent->parallel_unfinished;
+ if(!parent->parallel_unfinished)
+ PTHREAD_VERIFY(pthread_cond_broadcast(&parent->cond)); /* All threads done for now. */
+ }
+
+ /* return _thread_destroy_and_unlock(parent, thread); */
+}
+
+static void _unlock_and_destroy(struct threadpool *self)
+{
+ pthread_t *threads;
+
+ threads = self->parallel_threads;
+ self->parallel_threads = NULL;
+
+ if(threads)
+ PTHREAD_VERIFY(pthread_cond_broadcast(&self->cond));
+
+ PTHREAD_VERIFY(pthread_mutex_unlock(&self->mutex));
+
+ if(threads)
+ {
+ unsigned i, count = _threadpool_count_parallel(self);
+ for(i = 0; i != count; ++i)
+ PTHREAD_VERIFY(pthread_join(threads[i], NULL));
+
+ free(threads);
+ PTHREAD_VERIFY(pthread_cond_destroy(&self->cond));
+ PTHREAD_VERIFY(pthread_mutex_destroy(&self->mutex));
+ }
+
+ _serial_destroy(self);
+}
+
+#endif /* HAVE_PTHREAD */
+
+int threadpool_create(struct threadpool *self, const struct threadpool_class *cls, Display *dpy, unsigned count)
+{
+ (void)threads_available(dpy);
+
+ self->count = count;
+
+/* If threads are not present, run each "thread" in sequence on the calling
+ thread. Otherwise, only run the first thread on the main thread. */
+
+ assert(cls);
+
+ self->thread_size = cls->size;
+ self->thread_destroy = cls->destroy;
+
+ {
+ void *thread;
+ unsigned i, count_serial = _threadpool_count_serial(self);
+
+ if(count_serial)
+ {
+ thread = malloc(cls->size * count_serial);
+ if(!thread)
+ return ENOMEM;
+ }
+ else
+ {
+ /* Might as well skip the malloc. */
+ thread = NULL;
+ }
+
+ self->serial_threads = thread;
+
+ for(i = 0; i != count_serial; ++i)
+ {
+ int error = cls->create(thread, self, i);
+ if(error)
+ {
+ self->count = i;
+ _serial_destroy(self);
+ return error;
+ }
+
+ thread = (char *)thread + self->thread_size;
+ }
+ }
+
+#if HAVE_PTHREAD
+ assert(_has_pthread); /* _has_pthread should be either -1 or >0. */
+ if(_has_pthread >= 0)
+ {
+ unsigned count_parallel = _threadpool_count_parallel(self);
+ self->mutex = mutex_initializer;
+ self->cond = cond_initializer;
+ self->parallel_pending = 0;
+ self->parallel_unfinished = 0;
+ if(!count_parallel)
+ {
+ self->parallel_threads = NULL;
+ return 0;
+ }
+
+ self->parallel_threads = malloc(sizeof(pthread_t) * count_parallel);
+ if(!self->parallel_threads)
+ return ENOMEM;
+
+ {
+ struct _parallel_startup_type startup;
+ startup.parent = self;
+ startup.thread_create = cls->create;
+ startup.last_errno = 0;
+
+ PTHREAD_VERIFY(pthread_mutex_lock(&self->mutex));
+ _add_next_thread(&startup);
+
+ if(!startup.last_errno)
+ {
+ while(self->parallel_unfinished != count_parallel && self->parallel_threads)
+ PTHREAD_VERIFY(pthread_cond_wait(&self->cond, &self->mutex));
+ }
+
+ /* This must come after the if(!startup.last_errno). */
+ if(startup.last_errno)
+ {
+ _unlock_and_destroy(self);
+ }
+ else
+ {
+ self->parallel_unfinished = 0;
+ PTHREAD_VERIFY(pthread_mutex_unlock(&self->mutex));
+ }
+
+ return startup.last_errno;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+void threadpool_destroy(struct threadpool *self)
+{
+#if HAVE_PTHREAD
+ if(_has_pthread >= 0)
+ {
+ PTHREAD_VERIFY(pthread_mutex_lock(&self->mutex));
+ _unlock_and_destroy(self);
+ return;
+ }
+#endif
+
+ _serial_destroy(self);
+}
+
+void threadpool_run(struct threadpool *self, void (*func)(void *))
+{
+#if HAVE_PTHREAD
+ if(_has_pthread >= 0)
+ {
+ unsigned count = _threadpool_count_parallel(self);
+ PTHREAD_VERIFY(pthread_mutex_lock(&self->mutex));
+
+ /* Do not call threadpool_run() twice without a threadpool_wait() in the middle. */
+ assert(!self->parallel_pending);
+ assert(!self->parallel_unfinished);
+
+ self->parallel_pending = count;
+ self->parallel_unfinished = count;
+ self->thread_run = func;
+ PTHREAD_VERIFY(pthread_cond_broadcast(&self->cond));
+ PTHREAD_VERIFY(pthread_mutex_unlock(&self->mutex));
+ }
+#endif
+
+ /* It's perfectly valid to move this to the beginning of threadpool_wait(). */
+ {
+ void *thread = self->serial_threads;
+ unsigned i, count = _threadpool_count_serial(self);
+ for(i = 0; i != count; ++i)
+ {
+ func(thread);
+ thread = (char *)thread + self->thread_size;
+ }
+ }
+}
+
+void threadpool_wait(struct threadpool *self)
+{
+#if HAVE_PTHREAD
+ if(_has_pthread >= 0)
+ {
+ PTHREAD_VERIFY(pthread_mutex_lock(&self->mutex));
+ while(self->parallel_unfinished)
+ PTHREAD_VERIFY(pthread_cond_wait(&self->cond, &self->mutex));
+ PTHREAD_VERIFY(pthread_mutex_unlock(&self->mutex));
+ }
+#endif
+}
+
+/* io_thread - */
+
+#if HAVE_PTHREAD
+/* Without threads at compile time, there's only stubs in thread_util.h. */
+
+# define VERSION_CHECK(cc_major, cc_minor, req_major, req_minor) \
+ ((cc_major) > (req_major) || \
+ (cc_major) == (req_major) && (cc_minor) >= (req_minor))
+
+# if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 7) || \
+ defined(__clang__) && \
+ (!defined(__apple_build_version__) && VERSION_CHECK(__clang_major__, __clang_minor__, 3, 1) || \
+ defined(__apple_build_version__) && VERSION_CHECK(__clang_major__, __clang_minor__, 3, 1)) || \
+ defined(__ICC) && __ICC >= 1400
+
+/*
+ Clang 3.0 has a partial implementation of GNU atomics; 3.1 rounds it out.
+ http://llvm.org/viewvc/llvm-project/cfe/tags/RELEASE_30/final/include/clang/Basic/Builtins.def?view=markup
+ http://llvm.org/viewvc/llvm-project/cfe/tags/RELEASE_31/final/include/clang/Basic/Builtins.def?view=markup
+
+ Apple changes the Clang version to track Xcode versions; use
+ __apple_build_version__ to distinguish between the two.
+
+ Xcode 4.3 uses Apple LLVM 3.1, which corresponds to Clang 3.1.
+ https://en.wikipedia.org/wiki/Xcode
+
+ Earlier versions of Intel C++ may also support these intrinsics.
+ */
+
+#define _status_load(status) (__atomic_load_n((status), __ATOMIC_SEQ_CST))
+#define _status_exchange(obj, desired) (__atomic_exchange_n((obj), (desired), __ATOMIC_SEQ_CST))
+
+/* C11 atomics are around the corner, but they're not here yet for many
+ systems. (Including mine.) */
+/*
+#elif __STDC_VERSION__ >= 201112l && !defined __STDC_NO_ATOMICS__
+
+#include <stdatomic.h>
+
+#define _status_load(status) (atomic_load((status)))
+#define _status_exchange(obj, desired) (atomic_exchange((obj), (desired)))
+*/
+
+/* Solaris profiles atomic ops on at least Solaris 10. See atomic_swap(3C) and
+ membar_ops(3C). This would probably also need a snippet in configure.in.
+ http://graegert.com/programming/using-atomic-operations-in-c-on-solaris-10
+*/
+
+# else
+
+/* No atomic variables, so here's some ugly mutex-based code instead. */
+
+/* Nothing ever destroys this mutex. */
+pthread_mutex_t _global_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#define _lock() PTHREAD_VERIFY(pthread_mutex_lock(&_global_mutex))
+#define _unlock() PTHREAD_VERIFY(pthread_mutex_unlock(&_global_mutex))
+
+static enum _io_thread_status _status_load(enum _io_thread_status *status)
+{
+ enum _io_thread_status result;
+ _lock();
+ result = *status;
+ _unlock();
+ return result;
+}
+
+static enum _io_thread_status _status_exchange(enum _io_thread_status *obj, enum _io_thread_status desired)
+{
+ enum _io_thread_status result;
+ _lock();
+ result = *obj;
+ *obj = desired;
+ _unlock();
+ return result;
+}
+
+# endif
+
+void *io_thread_create(struct io_thread *self, void *parent, void *(*start_routine)(void *), Display *dpy, unsigned stacksize)
+{
+ if(threads_available(dpy) >= 0)
+ {
+ int error;
+ pthread_attr_t attr;
+ pthread_attr_t *attr_ptr = NULL;
+
+ if(stacksize)
+ {
+ attr_ptr = &attr;
+ if(pthread_attr_init(&attr))
+ return NULL;
+# if (defined _POSIX_SOURCE || defined _POSIX_C_SOURCE || defined _XOPEN_SOURCE) && !defined __GNU__
+ /* PTHREAD_STACK_MIN needs the above test. */
+ assert(stacksize >= PTHREAD_STACK_MIN);
+# endif
+ PTHREAD_VERIFY(pthread_attr_setstacksize(&attr, stacksize));
+ }
+
+ /* This doesn't need to be an atomic store, since pthread_create(3)
+ "synchronizes memory with respect to other threads".
+ http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_11 */
+ self->status = _io_thread_working;
+
+ error = pthread_create(&self->thread, attr_ptr, start_routine, parent);
+ assert(!error || error == EAGAIN);
+ if(error)
+ parent = NULL;
+
+ if(attr_ptr)
+ PTHREAD_VERIFY(pthread_attr_destroy(attr_ptr));
+
+ return parent;
+ }
+
+ return NULL;
+}
+
+int io_thread_return(struct io_thread *self)
+{
+ if(_has_pthread >= 0)
+ {
+ enum _io_thread_status old_status = _status_exchange(&self->status, _io_thread_done);
+ assert(old_status == _io_thread_working ||
+ old_status == _io_thread_cancelled);
+ return old_status != _io_thread_working;
+ }
+
+ return 0;
+}
+
+int io_thread_is_done(struct io_thread *self)
+{
+ if(_has_pthread >= 0)
+ {
+ int result = _status_load(&self->status);
+ assert(result != _io_thread_cancelled);
+ return result;
+ }
+ return 1;
+}
+
+int io_thread_cancel(struct io_thread *self)
+{
+ if(_has_pthread >= 0)
+ {
+ enum _io_thread_status old_status =
+ _status_exchange(&self->status, _io_thread_cancelled);
+ assert(old_status == _io_thread_working ||
+ old_status == _io_thread_done);
+
+ PTHREAD_VERIFY(pthread_detach(self->thread));
+ return old_status != _io_thread_working;
+ }
+
+ return 0;
+}
+
+void io_thread_finish(struct io_thread *self)
+{
+ if(_has_pthread >= 0)
+ {
+# ifndef NDEBUG
+ enum _io_thread_status status = _status_load(&self->status);
+ assert(status == _io_thread_working ||
+ status == _io_thread_done);
+# endif
+ PTHREAD_VERIFY(pthread_join(self->thread, NULL));
+ assert(_status_load(&self->status) == _io_thread_done);
+ }
+}
+
+#endif /* HAVE_PTHREAD */
--- /dev/null
+/* -*- mode: c; tab-width: 4; fill-column: 78 -*- */
+/* vi: set ts=4 tw=78: */
+
+/*
+thread_util.h, Copyright (c) 2014 Dave Odell <dmo2118@gmail.com>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation. No representations are made about the suitability of this
+software for any purpose. It is provided "as is" without express or
+implied warranty.
+*/
+
+#ifndef THREAD_UTIL_H
+#define THREAD_UTIL_H
+
+/* thread_util.h because C11 took threads.h. */
+
+/* And POSIX threads because there aren't too many systems that support C11
+ threads that don't already support POSIX threads.
+ ...Not that it would be too hard to convert from the one to the other.
+ Or to have both.
+ */
+
+/* Beware!
+ Multithreading is a great way to add insidious and catastrophic bugs to
+ a program. Make sure you understand the risks.
+
+ You may wish to become familiar with race conditions, deadlocks, mutexes,
+ condition variables, and, in lock-free code, memory ordering, cache
+ hierarchies, etc., before working with threads.
+
+ On the other hand, if a screenhack locks up or crashes, it's not the
+ end of the world: XScreenSaver won't unlock the screen if that happens.
+*/
+
+/*
+ The basic stragegy for applying threads to a CPU-hungry screenhack:
+
+ 1. Find the CPU-hungry part of the hack.
+
+ 2. Change that part so the workload can be divided into N equal-sized
+ loads, where N is the number of CPU cores in the machine.
+ (For example: with two cores, one core could render even scan lines,
+ and the other odd scan lines.)
+
+ 2a. Keeping in mind that two threads should not write to the same memory
+ at the same time. Specifically, they should not be writing to the
+ same cache line at the same time -- so align memory allocation and
+ memory accesses to the system cache line size as necessary.
+
+ 3. On screenhack_init, create a threadpool object. This creates N worker
+ threads, and each thread creates and owns a user-defined struct.
+ After creation, the threads are idle.
+
+ 4. On screenhack_frame, call threadpool_run(). Each thread simultaneously
+ wakes up, calls a function that does one of the equal-sized loads,
+ then goes back to sleep. The main thread then calls threadpool_wait(),
+ which returns once all the worker threads have finished.
+
+ Using this to implement SMP won't necessarily increase performance by
+ a factor of N (again, N is CPU cores.). Both X11 and Cocoa on OS X can
+ impose a not-insignificant amount of overhead even when simply blitting
+ full-screen XImages @ 30 FPS.
+
+ On systems with simultaneous multithreading (a.k.a. Hyper-threading),
+ performance gains may be slim to non-existant.
+ */
+
+#include "aligned_malloc.h"
+
+#if HAVE_CONFIG_H
+/* For HAVE_PTHREAD. */
+# include "config.h"
+#endif
+
+#include <stddef.h>
+
+#if HAVE_UNISTD_H
+/* For _POSIX_THREADS. */
+# include <unistd.h>
+#endif
+
+#if defined HAVE_JWXYZ
+# include "jwxyz.h"
+#else
+# include <X11/Xlib.h>
+#endif
+
+#if HAVE_PTHREAD
+int threads_available(Display *dpy);
+#else
+# define threads_available(dpy) (-1)
+#endif
+/* > 0: Threads are available. This is normally _POSIX_VERSION.
+ -1: Threads are not available.
+*/
+
+unsigned hardware_concurrency(Display *dpy);
+/* This is supposed to return the number of available CPU cores. This number
+ isn't necessarily constant: a system administrator can hotplug or
+ enable/disable CPUs on certain systems, or the system can deactivate a
+ malfunctioning core -- but these are rare.
+
+ If threads are unavailable, this function will return 1.
+
+ This function isn't fast; the result should be cached.
+*/
+
+unsigned thread_memory_alignment(Display *dpy);
+
+/* Returns the proper alignment for memory allocated by a thread that is
+ shared with other threads.
+
+ A typical CPU accesses the system RAM through a cache, and this cache is
+ divided up into cache lines - aligned chunks of memory typically 32 or 64
+ bytes in size. Cache faults cause cache lines to be populated from
+ memory. And, in a multiprocessing environment, two CPU cores can access the
+ same cache line. The consequences of this depend on the CPU model:
+
+ - x86 implements the MESI protocol [1] to maintain cache coherency between
+ CPU cores, with a serious performance penalty on both Intel [1] and AMD
+ [2]. Intel uses the term "false sharing" to describe two CPU cores
+ accessing different memory in the same cache line.
+
+ - ARM allows CPU caches to become inconsistent in this case [3]. Memory
+ fences are needed to prevent horrible non-deterministic bugs from
+ occurring. Other CPU architectures have similar behavior to one of the
+ above, depending on whether they are "strongly-orderered" (like x86), or
+ "weakly-ordered" (like ARM).
+
+ Aligning multithreaded memory accesses according to the cache line size
+ neatly sidesteps both issues.
+
+ One complication is that CPU caches are divided up into separate levels,
+ and occasionally different levels can have different cache line sizes, so
+ to be safe this function returns the largest cache line size among all
+ levels.
+
+ If multithreading is not in effect, this returns sizeof(void *), because
+ posix_memalign(3) will error out if the alignment is set to be smaller than
+ that.
+
+ [1] Intel(R) 64 and IA-32 Architectures Optimization Reference Manual
+ (Order Number: 248966-026): 2.1.5 Cache Hierarchy
+ [2] Software Optimization Guide for AMD Family 10h Processors (Publication
+ #40546): 11.3.4 Data Sharing between Caches
+ [3] http://wanderingcoder.net/2011/04/01/arm-memory-ordering/
+*/
+
+/*
+ Note: aligned_malloc uses posix_memalign(3) when available, or malloc(3)
+ otherwise. As of SUSv2 (1997), and *probably* earlier, these are guaranteed
+ to be thread-safe. C89 does not discuss threads, or thread safety;
+ non-POSIX systems, watch out!
+ http://pubs.opengroup.org/onlinepubs/7908799/xsh/threads.html
+ http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html
+*/
+
+/* int thread_malloc(void **ptr, Display *dpy, unsigned size); */
+#define thread_malloc(ptr, dpy, size) \
+ (aligned_malloc((ptr), thread_memory_alignment(dpy), (size)))
+
+/*
+ This simply does a malloc aligned to thread_memory_alignment(). See
+ above. On failure, an errno is returned, usually ENOMEM.
+
+ It's possible for two malloc()'d blocks to at least partially share the
+ same cache line. When a different thread is writing to each block, then bad
+ things can happen (see thread_memory_alignment). Better malloc()
+ implementations will divide memory into pools belonging to one thread or
+ another, causing memory blocks belonging to different threads to typically
+ be located on different memory pages (see getpagesize(2)), mitigating the
+ problem in question...but there's nothing stopping threads from passing
+ memory to each other. And it's not practical for the system to align each
+ block to 64 or 128 byte boundaries -- it's not uncommon to need lots and
+ lots of 8-32 byte allocations, and the waste could become a bit excessive.
+
+ Some rules of thumb to take away from this:
+
+ 1. Use thread_alloc for memory that might be written to by a thread that
+ didn't originally allocate the object.
+
+ 2. Use thread_alloc for memory that will be handed from one thread to
+ another.
+
+ 3. Use malloc if a single thread allocates, reads from, writes to, and
+ frees the block of memory.
+
+ Oddly, I (Dave) have not seen this problem described anywhere else.
+*/
+
+#define thread_free(ptr) aligned_free(ptr)
+
+#if HAVE_PTHREAD
+# if defined _POSIX_THREADS && _POSIX_THREADS >= 0
+/*
+ See The Open Group Base Specifications Issue 7, <unistd.h>, Constants for
+ Options and Option Groups
+ http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html#tag_13_77_03_02
+*/
+
+# include <pthread.h>
+
+/* Most PThread synchronization functions only fail when they are misused. */
+# if defined NDEBUG
+# define PTHREAD_VERIFY(expr) (void)(expr)
+# else
+# include <assert.h>
+# define PTHREAD_VERIFY(expr) assert(!(expr))
+# endif
+
+extern const pthread_mutex_t mutex_initializer;
+extern const pthread_cond_t cond_initializer;
+
+# else
+ /* Whatever caused HAVE_PTHREAD to be defined (configure script,
+ usually) made a mistake if this is reached. */
+ /* Maybe this should be a warning. */
+# error HAVE_PTHREAD is defined, but _POSIX_THREADS is not.
+ /* #undef HAVE_PTHREAD */
+# endif
+#endif
+
+struct threadpool
+{
+/* This is always the same as the count parameter fed to threadpool_create().
+ Here's a neat trick: if the threadpool is zeroed out with a memset, and
+ threadpool_create() is never called to create 0 threads, then
+ threadpool::count can be used to determine if the threadpool object was
+ ever initialized. */
+ unsigned count;
+
+ /* Copied from threadpool_class. No need for thread_create here, though. */
+ size_t thread_size;
+ void (*thread_run)(void *self);
+ void (*thread_destroy)(void *self);
+
+ void *serial_threads;
+
+#if HAVE_PTHREAD
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+
+ /* Number of threads waiting for the startup signal. */
+ unsigned parallel_pending;
+
+ /* Number of threads still running. During startup, this is the index of the thread currently being initialized. */
+ unsigned parallel_unfinished;
+
+ pthread_t *parallel_threads;
+#endif
+};
+
+/*
+ The threadpool_* functions manage a group of threads (naturally). Each
+ thread owns an object described by a threadpool_class. When
+ threadpool_run() is called, the specified func parameter is called on each
+ thread in parallel. Sometime after calling threadpool_run(), call
+ threadpool_wait(), which waits for each thread to return from
+ threadpool_class::run().
+
+ Note that thread 0 runs on the thread from which threadpool_run is called
+ from, so if each thread has an equal workload, then when threadpool_run
+ returns, the other threads will be finished or almost finished. Adding code
+ between threadpool_run and threadpool_wait increases the odds that
+ threadpool_wait won't actually have to wait at all -- which is nice.
+
+ If the system does not provide threads, then these functions will fake it:
+ everything will appear to work normally from the perspective of the caller,
+ but when threadpool_run() is called, the "threads" are run synchronously;
+ threadpool_wait() does nothing.
+*/
+
+struct threadpool_class
+{
+ /* Size of the thread private object. */
+ size_t size;
+
+/* Create the thread private object. Called in sequence for each thread
+ (effectively) from threadpool_create.
+ self: A pointer to size bytes of memory, allocated to hold the thread
+ object.
+ pool: The threadpool object that owns all the threads. If the threadpool
+ is nested in another struct, try GET_PARENT_OBJ.
+ id: The ID for the thread; numbering starts at zero and goes up by one
+ for each thread.
+ Return 0 on success. On failure, return a value from errno.h; this will
+ be returned from threadpool_create. */
+ int (*create)(void *self, struct threadpool *pool, unsigned id);
+
+/* Destroys the thread private object. Called in sequence (though not always
+ the same sequence as create). Warning: During shutdown, it is possible
+ for destroy() to be called while other threads are still in
+ threadpool_run(). */
+ void (*destroy)(void *self);
+};
+
+/* Returns 0 on success, on failure can return ENOMEM, or any error code from
+ threadpool_class.create. */
+int threadpool_create(struct threadpool *self, const struct threadpool_class *cls, Display *dpy, unsigned count);
+void threadpool_destroy(struct threadpool *self);
+
+void threadpool_run(struct threadpool *self, void (*func)(void *));
+void threadpool_wait(struct threadpool *self);
+
+/*
+ io_thread is meant to wrap blocking I/O operations in a one-shot worker
+ thread, with cancel semantics.
+
+ Unlike threadpool_*, io_thread will not 'fake it'; it is up to the caller
+ to figure out what to do if the system doesn't have threads. In
+ particular, the start_routine passed to io_thread_create will never be
+ called.
+
+ Clients of io_thread implement four functions:
+ - state *process_start(...);
+ Starts the worker thread.
+ - bool process_is_done(state *);
+ Returns true if the I/O operation is complete.
+ - void process_cancel(state *);
+ "Cancels" the I/O operation. The thread will continue to run, but it
+ will detach, and clean itself up upon completion.
+ - int process_finish(state *, ...)
+ Waits for the I/O operation to complete, returns results, and cleans up.
+
+ Or: /---\
+ \/ | /--> cancel
+ start -> is_done --+
+ \--> finish
+
+ These functions follow a basic pattern:
+ - start:
+ 1. Allocate a thread state object with thread_alloc. This state object
+ contains an io_thread member.
+ 2. Save parameters from the start parameters to the state object.
+ 3. Start the thread with _io_thread_create. The thread receives the state
+ object as its parameter.
+ - On the worker thread:
+ 1. Do the I/O.
+ 2. Call io_thread_return.
+ 2a. If the result != 0, free the state object.
+ - is_done:
+ 1. Just call _io_thread_is_done.
+ - cancel:
+ 1. Call io_thread_cancel.
+ 1a. If the result != 0, free the state object.
+ - finish:
+ 1. Call io_thread_finish.
+ 2. Copy results out of the state object as needed.
+ 3. Free the state object...or return it to the caller.
+
+ Incidentally, there may sometimes be asynchronous versions of blocking I/O
+ functions (struct aiocb and friends, for example); these should be
+ preferred over io_thread when performance is a concern.
+ */
+
+enum _io_thread_status
+{
+ _io_thread_working, _io_thread_done, _io_thread_cancelled
+};
+
+struct io_thread
+{
+#if HAVE_PTHREAD
+ /* Common misconception: "volatile" should be applied to atomic variables,
+ such as 'status', below. This is false, see
+ <http://stackoverflow.com/q/2484980>. */
+ enum _io_thread_status status;
+ pthread_t thread;
+#else
+ char gcc_emits_a_warning_when_the_struct_has_no_members;
+#endif
+};
+
+#if HAVE_PTHREAD
+
+void *io_thread_create(struct io_thread *self, void *parent, void *(*start_routine)(void *), Display *dpy, unsigned stacksize);
+/*
+ Create the thread, returns NULL on failure. Failure is usually due to
+ ENOMEM, or the system doesn't support threads.
+ self: The io_thread object to be initialized.
+ parent: The parameter to start_routine. The io_thread should be
+ contained within or be reachable from this.
+ start_routine: The start routine for the worker thread.
+ dpy: The X11 Display, so that '*useThreads' is honored.
+ stacksize: The stack size for the thread. Set to 0 for the system
+ default.
+ A note about stacksize: Linux, for example, uses a default of 2 MB of
+ stack per thread. Now, this memory is usually committed on the first
+ write, so lots of threads won't waste RAM, but it does mean that on a
+ 32-bit system, there's a limit of just under 1024 threads with the 2 MB
+ default due to typical address space limitations of 2 GB for userspace
+ processes. And 1024 threads might not always be enough...
+ */
+
+int io_thread_return(struct io_thread *self);
+/* Called at the end of start_routine, from above. Returns non-zero if the
+ thread has been cancelled, and cleanup needs to take place. */
+
+int io_thread_is_done(struct io_thread *self);
+/* Call from the main thread. Returns non-zero if the thread finished. */
+
+int io_thread_cancel(struct io_thread *self);
+/* Call from the main thread if the results from the worker thread are not
+ needed. This cleans up the io_thread. Returns non-zero if cleanup needs
+ to take place. */
+
+void io_thread_finish(struct io_thread *self);
+/* Call from the main thread to wait for the worker thread to finish. This
+ cleans up the io_thread. */
+
+#else
+
+#define IO_THREAD_STACK_MIN 0
+
+#define io_thread_create(self, parent, start_routine, dpy, stacksize) NULL
+#define io_thread_return(self) 0
+#define io_thread_is_done(self) 1
+#define io_thread_cancel(self) 0
+#define io_thread_finish(self)
+
+#endif
+
+#if HAVE_PTHREAD
+# define THREAD_DEFAULTS "*useThreads: True",
+# define THREAD_DEFAULTS_XLOCK "*useThreads: True\n"
+# define THREAD_OPTIONS \
+ {"-threads", ".useThreads", XrmoptionNoArg, "True"}, \
+ {"-no-threads", ".useThreads", XrmoptionNoArg, "False"},
+#else
+# define THREAD_DEFAULTS
+# define THREAD_DEFAULTS_XLOCK
+# define THREAD_OPTIONS
+#endif
+
+/*
+ If a variable 'member' is known to be a member (named 'member_name') of a
+ struct (named 'struct_name'), then this can find a pointer to the struct
+ that contains it.
+*/
+#define GET_PARENT_OBJ(struct_name, member_name, member) (struct_name *)((char *)member - offsetof(struct_name, member_name));
+
+#endif
--- /dev/null
+/* xscreensaver, Copyright (c) 1992, 1996, 1997, 2003
+ * Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#else /* !HAVE_CONFIG_H */
+# ifndef NO_SELECT
+# define HAVE_SELECT
+# endif
+#endif /* !HAVE_CONFIG_H */
+
+#ifdef __STDC__
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if defined(VMS)
+# include <descrip.h>
+# include <stdio.h>
+# include <lib$routines.h>
+#elif defined(HAVE_SELECT)
+# include <sys/time.h> /* for struct timeval */
+#endif
+
+
+#ifdef __SCREENHACK_USLEEP_H__
+ERROR, do not include that here
+#endif
+
+extern void screenhack_usleep (unsigned long usecs); /* suppress warning */
+
+void
+screenhack_usleep (unsigned long usecs)
+{
+# if defined(VMS)
+ float seconds = ((float) usecs)/1000000.0;
+ unsigned long int statvms = lib$wait(&seconds);
+
+#elif defined(HAVE_SELECT)
+ /* usleep() doesn't exist everywhere, and select() is faster anyway. */
+ struct timeval tv;
+ tv.tv_sec = usecs / 1000000L;
+ tv.tv_usec = usecs % 1000000L;
+ (void) select (0, 0, 0, 0, &tv);
+
+#else /* !VMS && !HAVE_SELECT */
+ /* If you don't have select() or usleep(), I guess you lose...
+ Maybe you have napms() instead? Let me know. */
+ usleep (usecs);
+
+#endif /* !VMS && !HAVE_SELECT */
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 1992, 1996 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __SCREENHACK_USLEEP_H__
+#define __SCREENHACK_USLEEP_H__
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+extern void screenhack_usleep (unsigned long usecs);
+
+#undef usleep
+#define usleep(usecs) screenhack_usleep(usecs)
+
+#endif /* __SCREENHACK_USLEEP_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 2014-2016 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_JWXYZ
+# include "jwxyz.h"
+#else /* !HAVE_JWXYZ */
+# include <X11/Xlib.h>
+#endif
+
+#include "utf8wc.h"
+
+
+/* "Unicode Replacement Character", displayed in lieu of invalid characters. */
+# define INVALID 0xFFFD
+
+
+/* Mask the number to be within the valid range of unicode characters.
+ */
+static unsigned long
+uc_truncate (unsigned long uc)
+{
+ uc &= 0x7FFFFFFFL; /* Unicode is 31 bits */
+ if (uc > 0x10FFFF) uc = INVALID; /* But UTF-8 is 4 bytes */
+ if (uc == 0) uc = INVALID; /* no nulls */
+
+ if (uc >= 0xD800 && uc <= 0xDFFF)
+ /* Reserved for use with UTF-16: not a real character. */
+ uc = INVALID;
+
+ return uc;
+}
+
+
+/* Parse the first UTF8 character at the front of the string.
+ Return the Unicode character, and the number of bytes read.
+ */
+long
+utf8_decode (const unsigned char *in, long length, unsigned long *unicode_ret)
+{
+ const unsigned char *start = in;
+ const unsigned char *end = in + length;
+ unsigned long uc = INVALID;
+ unsigned long min = 0;
+ unsigned char c;
+
+ if (length <= 0) goto DONE;
+
+ c = *in++;
+
+# define PREMATURE_EOF { in = end; goto DONE; }
+
+ if ((c & 0xC0) == 0x80) { /* 10xxxxxx - lonely continuation byte */
+ uc = INVALID;
+
+ } else if ((c & 0x80) == 0) { /* 0xxxxxxx - 7 bits in 1 byte */
+ uc = (c & 0x7F); /* 01111111 */
+
+ } else if ((c & 0xE0) == 0xC0) { /* 110xxxxx - 11 bits in 2 bytes */
+ if (in+1 > end) PREMATURE_EOF;
+ min = 1 << 7;
+ uc = (((c & 0x1F) << 6) | /* 00011111------ */
+ (in[0] & 0x3F)); /* 00111111 */
+ in += 1;
+
+ } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx - 16 bits in 3 bytes */
+ if (in+2 > end) PREMATURE_EOF;
+ min = 1 << 11;
+ uc = (((c & 0x0F) << 12) | /* 00001111----+------- */
+ ((in[0] & 0x3F) << 6) | /* 00111111------ */
+ ((in[1] & 0x3F))); /* 00111111 */
+ in += 2;
+
+ } else if ((c & 0xF8) == 0xF0) { /* 11110xxx - 21 bits in 4 bytes */
+ if (in+3 > end) PREMATURE_EOF;
+ min = 1 << 16;
+ uc = (((c & 0x07) << 18) | /* 00000111--+-------+------- */
+ ((in[0] & 0x3F) << 12) | /* 01111111----+------- */
+ ((in[1] & 0x3F) << 6) | /* 00111111------ */
+ ((in[2] & 0x3F))); /* 00111111 */
+ in += 3;
+
+ } else if ((c & 0xFC) == 0xF8) { /* 111110xx - 26 bits in 5 bytes */
+ if (in+4 > end) PREMATURE_EOF;
+ min = 1 << 21;
+ uc = (((c & 0x03) << 24) | /* 00000011--------+-------+------- */
+ ((in[0] & 0x3F) << 18) | /* 00111111--+-------+------- */
+ ((in[1] & 0x3F) << 12) | /* 00111111----+------- */
+ ((in[2] & 0x3F) << 6) | /* 00111111------ */
+ ((in[3] & 0x3F))); /* 00111111 */
+ in += 4;
+
+ } else if ((c & 0xFE) == 0xFC) { /* 1111110x - 31 bits in 6 bytes */
+ if (in+5 > end) PREMATURE_EOF;
+ min = 1 << 26;
+ uc = (((c & 0x01) << 30) | /* 00000001------+-------+-------+------- */
+ ((in[0] & 0x3F) << 24) | /* 00111111+-------+-------+------- */
+ ((in[1] & 0x3F) << 18) | /* 00111111--+-------+------- */
+ ((in[2] & 0x3F) << 12) | /* 00111111----+------- */
+ ((in[3] & 0x3F) << 6) | /* 00111111------ */
+ ((in[4] & 0x3F))); /* 00111111 */
+ in += 5;
+ } else {
+ uc = INVALID; /* Unparsable sequence. */
+ }
+
+ DONE:
+
+ length = in - start;
+
+ /* If any of the continuation bytes didn't begin with the continuation tag,
+ the sequence is invalid; stop at the bad byte, not consuming later ones.
+ (It's easier to check this after the fact than up above.) */
+ {
+ int i;
+ for (i = 1; i < length; i++)
+ if ((start[i] & 0xC0) != 0x80) {
+ uc = INVALID;
+ length = i+1;
+ break;
+ }
+ }
+
+ if (uc < min)
+ /* A multi-byte sequence encoded a character that could have been
+ encoded with a shorter sequence, e.g., hiding ASCII inside a
+ multi-byte sequence. Something hinky's going on. Reject it. */
+ uc = INVALID;
+
+ uc = uc_truncate (uc);
+
+ if (unicode_ret)
+ *unicode_ret = uc;
+
+ return length;
+}
+
+
+/* Converts a Unicode character to a multi-byte UTF8 sequence.
+ Returns the number of bytes written.
+ */
+int
+utf8_encode (unsigned long uc, char *out, long length)
+{
+ const char *old = out;
+
+ uc = uc_truncate (uc);
+
+ if (uc < 0x80 && length >= 1) /* 7 bits in 1 byte */
+ {
+ *out++ = uc; /* 0xxxxxxx */
+ }
+ else if (uc < 0x800 && length >= 2) /* 11 bits in 2 bytes */
+ {
+ *out++ = (0xC0 | ((uc >> 6) & 0x1F)); /* 110xxxxx */
+ *out++ = (0x80 | (uc & 0x3F)); /* 10xxxxxx */
+ }
+ else if (uc < 0x10000L && length >= 3) /* 16 bits in 3 bytes */
+ {
+ *out++ = (0xE0 | ((uc >> 12) & 0x0F)); /* 1110xxxx */
+ *out++ = (0x80 | ((uc >> 6) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | (uc & 0x3F)); /* 10xxxxxx */
+ }
+ else if (uc < 0x200000L && length >= 4) /* 21 bits in 4 bytes */
+ {
+ *out++ = (0xF0 | ((uc >> 18) & 0x07)); /* 11110xxx */
+ *out++ = (0x80 | ((uc >> 12) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | ((uc >> 6) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | (uc & 0x3F)); /* 10xxxxxx */
+ }
+ else if (uc < 0x4000000L && length >= 5) /* 26 bits in 5 bytes */
+ {
+ *out++ = (0xF8 | ((uc >> 24) & 0x03)); /* 111110xx */
+ *out++ = (0x80 | ((uc >> 18) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | ((uc >> 12) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | ((uc >> 6) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | (uc & 0x3F)); /* 10xxxxxx */
+ }
+ else if (length >= 6) /* 31 bits in 6 bytes */
+ {
+ *out++ = (0xFC | ((uc >> 30) & 0x01)); /* 1111110x */
+ *out++ = (0x80 | ((uc >> 24) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | ((uc >> 18) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | ((uc >> 12) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | ((uc >> 6) & 0x3F)); /* 10xxxxxx */
+ *out++ = (0x80 | (uc & 0x3F)); /* 10xxxxxx */
+ }
+
+ return (int) (out - old);
+}
+
+
+/* Converts a null-terminated UTF8 string to a null-terminated XChar2b array.
+ This only handles characters that can be represented in 16 bits, the
+ Basic Multilingual Plane. (No hieroglyphics, Elvish, Klingon or Emoji.)
+ */
+XChar2b *
+utf8_to_XChar2b (const char *string, int *length_ret)
+{
+ long in_len = strlen(string);
+ const unsigned char *in = (const unsigned char *) string;
+ const unsigned char *in_end = in + in_len;
+ XChar2b *c2b = (XChar2b *) malloc ((in_len + 1) * sizeof(*c2b));
+ XChar2b *out = c2b;
+ if (! out) return 0;
+
+ while (in < in_end)
+ {
+ unsigned long uc = 0;
+ long L = utf8_decode (in, in_end - in, &uc);
+ in += L;
+
+ /* If it can't be represented in a 16-bit XChar2b,
+ use "Unicode Replacement Character". */
+ if (uc > 0xFFFF) uc = INVALID;
+
+ out->byte1 = (uc >> 8) & 0xFF;
+ out->byte2 = uc & 0xFF;
+ out++;
+ }
+
+ out->byte1 = 0;
+ out->byte2 = 0;
+
+ if (length_ret)
+ *length_ret = (int) (out - c2b);
+
+ /* shrink */
+ c2b = (XChar2b *) realloc (c2b, (out - c2b + 1) * sizeof(*c2b));
+
+ return c2b;
+}
+
+
+/* Split a UTF8 string into an array of strings, one per character.
+ The sub-strings will be null terminated and may be multiple bytes.
+ */
+char **
+utf8_split (const char *string, int *length_ret)
+{
+ const unsigned char *in = (const unsigned char *) string;
+ long len = strlen (string);
+ const unsigned char *end = in + len;
+ char **ret = (char **) malloc ((len+1) * sizeof(*ret));
+ int i = 0;
+ int zwjp = 0;
+ if (!ret) return 0;
+
+ while (in < end)
+ {
+ unsigned long uc;
+ long len2 = utf8_decode (in, len, &uc);
+ char tmp[10];
+ memcpy (tmp, (char *) in, len2);
+ tmp[len2] = 0;
+ ret[i++] = strdup (tmp);
+ in += len2;
+
+ /* If this is a Combining Diacritical, append it to the previous
+ character. E.g., "y\314\206\314\206" is one string, not three.
+
+ If this is ZWJ, Zero Width Joiner, then we append both this character
+ and the following character, e.g. "X ZWJ Y" is one string not three.
+
+ #### Hmmm, should this also include every character in the
+ "Symbol, Modifier" category, or does ZWJ get used for those?
+ https://www.fileformat.info/info/unicode/category/Sk/list.htm
+
+ Is it intended that "Latin small letter C, 0063" + "Cedilla, 00B8"
+ should be a single glyph? Or is that what "Combining Cedilla, 0327"
+ is for? I'm confused by the fact that the skin tones (1F3FB-1F3FF)
+ do not seem to be in a readily-identifiable block the way the various
+ combining diacriticals are.
+ */
+ if (i > 1 &&
+ ((uc >= 0x300 && uc <= 0x36F) || /* Combining Diacritical */
+ (uc >= 0x1AB0 && uc <= 0x1AFF) || /* Combining Diacritical Ext. */
+ (uc >= 0x1DC0 && uc <= 0x1DFF) || /* Combining Diacritical Supp. */
+ (uc >= 0x20D0 && uc <= 0x20FF) || /* Combining Diacritical Sym. */
+ (uc >= 0xFE20 && uc <= 0xFE2F) || /* Combining Half Marks */
+ (uc >= 0x1F3FB && uc <= 0x1F3FF) || /* Emoji skin tone modifiers */
+ zwjp || uc == 0x200D)) /* Zero Width Joiner */
+ {
+ long L1 = strlen(ret[i-2]);
+ long L2 = strlen(ret[i-1]);
+ char *s2 = (char *) malloc (L1 + L2 + 1);
+ memcpy (s2, ret[i-2], L1);
+ memcpy (s2 + L1, ret[i-1], L2);
+ s2[L1 + L2] = 0;
+ free (ret[i-2]);
+ ret[i-2] = s2;
+ i--;
+ zwjp = (uc == 0x200D); /* Swallow the next character as well */
+ }
+ }
+ ret[i] = 0;
+
+ if (length_ret)
+ *length_ret = i;
+
+ /* shrink */
+ ret = (char **) realloc (ret, (i+1) * sizeof(*ret));
+
+ return ret;
+}
+
+
+/* Converts a null-terminated XChar2b array to a null-terminated UTF8 string.
+ */
+char *
+XChar2b_to_utf8 (const XChar2b *in, int *length_ret)
+{
+ int in_len = 0;
+ const XChar2b *in_end;
+ int out_len;
+ char *utf8, *out;
+ const char *out_end;
+
+ /* Find the null termination on the XChar2b. */
+ for (in_end = in; in_end->byte1 || in_end->byte2; in_end++, in_len++)
+ ;
+
+ out_len = (in_len + 1) * 3; /* 16 bit chars = 3 bytes max */
+ utf8 = out = (char *) malloc (out_len + 1);
+ if (! out) return 0;
+ out_end = out + out_len;
+
+ while (in < in_end)
+ {
+ unsigned long uc = (in->byte1 << 8) | in->byte2;
+ int wrote = utf8_encode (uc, out, out_end - out);
+ if (wrote > 3) abort(); /* Can't happen with 16 bit input */
+ out += wrote;
+ in++;
+ }
+ *out = 0;
+
+ out_len = (int) (out - utf8 + 1);
+
+ if (length_ret)
+ *length_ret = out_len;
+
+ /* shrink */
+ utf8 = (char *) realloc (utf8, out_len);
+
+ return utf8;
+}
+
+
+/* Converts a UTF8 string to the closest Latin1 or ASCII equivalent.
+ */
+char *
+utf8_to_latin1 (const char *string, Bool ascii_p)
+{
+ long in_len = strlen(string);
+ const unsigned char *in = (const unsigned char *) string;
+ const unsigned char *in_end = in + in_len;
+ unsigned char *ret = (unsigned char *) malloc (in_len + 1);
+ unsigned char *out = ret;
+
+ if (! ret) return 0;
+
+ while (in < in_end)
+ {
+ unsigned long uc = 0;
+ long len2 = utf8_decode (in, in_end - in, &uc);
+ in += len2;
+
+ if (uc == '\240') /* */
+ uc = ' ';
+ else if (uc >= 0x300 && uc <= 0x36F)
+ uc = 0; /* Discard "Combining Diacritical Marks" */
+ else if (uc >= 0x1AB0 && uc <= 0x1AFF)
+ uc = 0; /* Discard "Combining Diacritical Marks Extended" */
+ else if (uc >= 0x1DC0 && uc <= 0x1DFF)
+ uc = 0; /* Discard "Combining Diacritical Marks Supplement" */
+ else if (uc >= 0x20D0 && uc <= 0x20FF)
+ uc = 0; /* Discard "Combining Diacritical Marks for Symbols" */
+ else if (uc >= 0xFE20 && uc <= 0xFE2F)
+ uc = 0; /* Discard "Combining Half Marks" */
+
+ else if (uc > 0xFF)
+ switch (uc) {
+
+ /* Map "Unicode General Punctuation Block" to Latin1 equivalents. */
+
+ case 0x2000: /* EN QUAD */
+ case 0x2001: /* EM QUAD */
+ case 0x2002: /* EN SPACE */
+ case 0x2003: /* EM SPACE */
+ case 0x2004: /* THREE-PER-EM SPACE */
+ case 0x2005: /* FOUR-PER-EM SPACE */
+ case 0x2006: /* SIX-PER-EM SPACE */
+ case 0x2007: /* FIGURE SPACE */
+ case 0x2008: /* PUNCTUATION SPACE */
+ case 0x2009: /* THIN SPACE */
+ case 0x200A: /* HAIR SPACE */
+ uc = ' ';
+ break;
+
+ case 0x2010: /* HYPHEN */
+ case 0x2011: /* NON-BREAKING HYPHEN */
+ case 0x2012: /* FIGURE DASH */
+ case 0x2013: /* EN DASH */
+ case 0x2014: /* EM DASH */
+ case 0x2015: /* HORIZONTAL BAR */
+ uc = '-';
+ break;
+
+ case 0x2018: /* LEFT SINGLE QUOTATION MARK */
+ case 0x2019: /* SINGLE LOW-9 QUOTATION MARK */
+ case 0x201A: /* SINGLE LOW-9 QUOTATION MARK */
+ case 0x201B: /* SINGLE HIGH-REVERSED-9 QUOTATION MARK */
+ uc = '\'';
+ break;
+
+ case 0x201C: /* LEFT DOUBLE QUOTATION MARK */
+ case 0x201D: /* RIGHT DOUBLE QUOTATION MARK */
+ case 0x201E: /* DOUBLE LOW-9 QUOTATION MARK */
+ case 0x201F: /* DOUBLE HIGH-REVERSED-9 QUOTATION MARK */
+ uc = '"';
+ break;
+
+ case 0x2022: uc = '\267'; break; /* BULLET */
+ case 0x2023: uc = '\273'; break; /* TRIANGULAR BULLET */
+ case 0x2027: uc = '\267'; break; /* HYPHENATION POINT */
+ case 0x202F: uc = ' '; break; /* NARROW NO-BREAK SPACE */
+ case 0x2038: uc = '^'; break; /* CARET */
+ case 0x2039: uc = '\253'; break; /* SINGLE LEFT ANGLE QUOTATION MARK */
+ case 0x203A: uc = '\273'; break; /* SINGLE RIGHT ANGLE QUOTATION MARK*/
+ case 0x2041: uc = '^'; break; /* CARET INSERTION POINT */
+ case 0x2042: uc = '*'; break; /* ASTERISM */
+ case 0x2043: uc = '='; break; /* HYPHEN BULLET */
+ case 0x2044: uc = '/'; break; /* FRACTION SLASH */
+ case 0x204B: uc = '\266'; break; /* REVERSED PILCROW SIGN */
+ case 0x204C: uc = '\267'; break; /* BLACK LEFTWARDS BULLET */
+ case 0x204D: uc = '\267'; break; /* BLACK RIGHTWARDS BULLET */
+ case 0x204E: uc = '*'; break; /* LOW ASTERISK */
+ case 0x204F: uc = ';'; break; /* REVERSED SEMICOLON */
+ default:
+ break;
+ }
+
+ if (uc > 0xFF)
+ /* "Inverted question mark" looks enough like 0xFFFD,
+ the "Unicode Replacement Character". */
+ uc = (ascii_p ? '#' : '\277');
+
+ if (ascii_p) /* Map Latin1 to the closest ASCII versions. */
+ {
+ const unsigned char latin1_to_ascii[96] =
+ " !C##Y|S_C#<=-R_##23'uP.,1o>###?"
+ "AAAAAAECEEEEIIIIDNOOOOOx0UUUUYpS"
+ "aaaaaaeceeeeiiiionooooo/ouuuuypy";
+ if (uc >= 0xA0)
+ uc = latin1_to_ascii[uc - 0xA0];
+ }
+
+ if (uc > 0)
+ *out++ = (unsigned char) uc;
+ }
+ *out = 0;
+
+ /* shrink */
+ ret = (unsigned char *) realloc (ret, (out - ret + 1) * sizeof(*ret));
+
+ return (char *) ret;
+}
+
+
+/*************************************************************************
+
+ cd ../hacks ; make test-utf8wc
+
+ *************************************************************************/
+
+#ifdef SELFTEST
+
+/* Convert a UTF8 string to Unicode and back again.
+ */
+static char *
+split_and_join (const char *string)
+{
+ const unsigned char *in = (const unsigned char *) string;
+ int len = strlen (string);
+ const unsigned char *end = in + len;
+ unsigned long *unicode = (unsigned long *)
+ malloc((len + 1) * sizeof(*unicode));
+ int i = 0;
+ char *ret, *out, *out_end;
+
+ while (in < end)
+ {
+ long len2 = utf8_decode (in, len, &unicode[i]);
+ i++;
+ in += len2;
+ }
+ unicode[i] = 0;
+
+ i = i*6 + 1;
+ out = ret = (char *) malloc(i);
+ out_end = out + i;
+ i = 0;
+ while (unicode[i])
+ {
+ int len2 = utf8_encode (unicode[i], out, out_end - out);
+ out += len2;
+ i++;
+ }
+ *out = 0;
+ free (unicode);
+
+ return ret;
+}
+
+
+static void
+LOG (FILE *out, const char *prefix, const char *s)
+{
+ fprintf (out, "%6s: \"", prefix);
+ while (*s)
+ {
+ unsigned char c = *s;
+ if (c == '"' || c == '\\') fprintf(out, "\\%c", c);
+ else if (c < 32 || c >= 127) fprintf(out, "\\%03o", c);
+ else fprintf (out, "%c", c);
+ s++;
+ }
+ fprintf (out, "\"\n");
+}
+
+
+int
+main (int argc, char **argv)
+{
+ /* Adapted from http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+ */
+
+# define URC "\357\277\275" /* 0xFFFD, "Unicode Replacement Character" */
+
+ static const struct { const char *name, *in, *target, *target2; } tests[] = {
+ /* 1 Some correct UTF-8 text */
+
+ /* The Greek word 'kosme': */
+ { "1", "\316\272\341\275\271\317\203\316\274\316\265" },
+
+
+ /* 2 Boundary condition test cases */
+
+ /* 2.1 First possible sequence of a certain length */
+
+ { "2.1.1", /* 1 byte (U-00000000): */ "\000" },
+ { "2.1.2", /* 2 bytes (U-00000080): */ "\302\200" },
+ { "2.1.3", /* 3 bytes (U-00000800): */ "\340\240\200" },
+ { "2.1.4", /* 4 bytes (U-00010000): */ "\360\220\200\200", 0, URC },
+ { "2.1.5", /* 5 bytes (U-00200000): */ "\370\210\200\200\200", URC },
+ { "2.1.6", /* 6 bytes (U-04000000): */ "\374\204\200\200\200\200", URC },
+
+ /* 2.2 Last possible sequence of a certain length */
+
+ { "2.2.1", /* 1 byte (U-0000007F): */ "\177" },
+ { "2.2.2", /* 2 bytes (U-000007FF): */ "\337\277" },
+ { "2.2.3", /* 3 bytes (U-0000FFFF): */ "\357\277\277" },
+ { "2.2.4", /* 4 bytes (U-001FFFFF): */ "\367\277\277\277", URC },
+ { "2.2.5", /* 5 bytes (U-03FFFFFF): */ "\373\277\277\277\277", URC },
+ { "2.2.6", /* 6 bytes (U-7FFFFFFF): */ "\375\277\277\277\277\277", URC },
+
+ /* 2.3 Other boundary conditions */
+
+ { "2.3.1", /* U-0000D7FF = ed 9f bf = */ "\355\237\277" },
+ { "2.3.2", /* U-0000E000 = ee 80 80 = */ "\356\200\200" },
+ { "2.3.3", /* U-0000FFFD = ef bf bd = */ URC },
+ { "2.3.4", /* U-0010FFFF = f4 8f bf bf = */ "\364\217\277\277", 0, URC },
+ { "2.3.5", /* U-00110000 = f4 90 80 80 = */ "\364\220\200\200", URC },
+
+
+ /* 3 Malformed sequences */
+
+ /* 3.1 Unexpected continuation bytes */
+
+ /* Each unexpected continuation byte should be separately signalled as a
+ malformed sequence of its own. */
+
+ { "3.1.1", /* First continuation byte 0x80: */ "\200", URC },
+ { "3.1.2", /* Last continuation byte 0xbf: */ "\277", URC },
+ { "3.1.3", /* 2 continuation bytes: */ "\200\277", URC URC },
+ { "3.1.4", /* 3 continuation bytes: */ "\200\277\200", URC URC URC },
+ { "3.1.5", /* 4 continuation bytes: */ "\200\277\200\277",
+ URC URC URC URC },
+ { "3.1.6", /* 5 continuation bytes: */ "\200\277\200\277\200",
+ URC URC URC URC URC },
+ { "3.1.7", /* 6 continuation bytes: */ "\200\277\200\277\200\277",
+ URC URC URC URC URC URC },
+ { "3.1.8", /* 7 continuation bytes: */ "\200\277\200\277\200\277\200",
+ URC URC URC URC URC URC URC },
+
+ { "3.1.9", /* Sequence of all 64 possible continuation bytes (0x80-0xbf):*/
+
+ "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
+ "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
+ "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
+ "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277",
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC },
+
+ /* 3.2 Lonely start characters */
+
+ { "3.2.1", /* All 32 first bytes of 2-byte sequences (0xc0-0xdf),
+ each followed by a space character: */
+
+ "\300 \301 \302 \303 \304 \305 \306 \307 \310 \311 \312 \313 \314 "
+ "\315 \316 \317 \320 \321 \322 \323 \324 \325 \326 \327 \330 \331 "
+ "\332 \333 \334 \335 \336 \337 ",
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC },
+
+ { "3.2.2", /* All 16 first bytes of 3-byte sequences (0xe0-0xef),
+ each followed by a space character: */
+ "\340 \341 \342 \343 \344 \345 \346 \347 "
+ "\350 \351 \352 \353 \354 \355 \356 \357 ",
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC },
+
+ { "3.2.3", /* All 8 first bytes of 4-byte sequences (0xf0-0xf7),
+ each followed by a space character: */
+ URC URC URC URC URC URC URC URC },
+
+ { "3.2.4", /* All 4 first bytes of 5-byte sequences (0xf8-0xfb),
+ each followed by a space character: */
+ "\370 \371 \372 \373 ",
+ URC URC URC URC },
+
+ { "3.2.5", /* All 2 first bytes of 6-byte sequences (0xfc-0xfd),
+ each followed by a space character: */
+ "\374 \375 ", URC URC },
+
+ /* 3.3 Sequences with last continuation byte missing */
+
+ /* All bytes of an incomplete sequence should be signalled as a single
+ malformed sequence, i.e., you should see only a single replacement
+ character in each of the next 10 tests. (Characters as in section 2) */
+
+ { "3.3.1", /* 2-byte sequence with last byte missing (U+0000): */
+ "\300", URC },
+ { "3.3.2", /* 3-byte sequence with last byte missing (U+0000): */
+ "\340\200", URC },
+ { "3.3.3", /* 4-byte sequence with last byte missing (U+0000): */
+ "\360\200\200", URC },
+ { "3.3.4", /* 5-byte sequence with last byte missing (U+0000): */
+ "\370\200\200\200", URC },
+ { "3.3.5", /* 6-byte sequence with last byte missing (U+0000): */
+ "\374\200\200\200\200", URC },
+ { "3.3.6", /* 2-byte sequence with last byte missing (U-000007FF): */
+ "\337", URC },
+ { "3.3.7", /* 3-byte sequence with last byte missing (U-0000FFFF): */
+ "\357\277", URC },
+ { "3.3.8", /* 4-byte sequence with last byte missing (U-001FFFFF): */
+ "\367\277\277", URC },
+ { "3.3.9", /* 5-byte sequence with last byte missing (U-03FFFFFF): */
+ "\373\277\277\277", URC },
+ { "3.3.10", /* 6-byte sequence with last byte missing (U-7FFFFFFF): */
+ "\375\277\277\277\277", URC },
+
+ /* 3.4 Concatenation of incomplete sequences */
+
+ /* All the 10 sequences of 3.3 concatenated, you should see 10 malformed
+ sequences being signalled: */
+
+ { "3.4", "\300\340\200\360\200\200\370\200\200\200\374\200\200\200\200"
+ "\337\357\277\367\277\277\373\277\277\277\375\277\277\277\277",
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC },
+
+ /* 3.5 Impossible bytes */
+
+ /* The following two bytes cannot appear in a correct UTF-8 string */
+
+ { "3.5.1", /* fe = */ "\376", URC },
+ { "3.5.2", /* ff = */ "\377", URC },
+ { "3.5.3", /* fe fe ff ff = */ "\376\376\377\377", URC URC URC URC },
+
+
+ /* 4 Overlong sequences */
+
+ /* 4.1 Examples of an overlong ASCII character */
+
+ { "4.1.1", /* U+002F = c0 af = */ "\300\257", URC },
+ { "4.1.2", /* U+002F = e0 80 af = */ "\340\200\257", URC },
+ { "4.1.3", /* U+002F = f0 80 80 af = */ "\360\200\200\257", URC },
+ { "4.1.4", /* U+002F = f8 80 80 80 af = */ "\370\200\200\200\257",
+ URC },
+ { "4.1.5", /* U+002F = fc 80 80 80 80 af = */ "\374\200\200\200\200\257",
+ URC },
+
+ /* 4.2 Maximum overlong sequences */
+
+ { "4.2.1", /* U-0000007F = c1 bf = */ "\301\277", URC },
+ { "4.2.2", /* U-000007FF = e0 9f bf = */ "\340\237\277", URC },
+ { "4.2.3", /* U-0000FFFF = f0 8f bf bf = */ "\360\217\277\277",
+ URC },
+ { "4.2.4", /* U-001FFFFF = f8 87 bf bf bf = */ "\370\207\277\277\277",
+ URC },
+ { "4.2.5", /* U-03FFFFFF = fc 83 bf bf bf bf = */ URC },
+
+ /* 4.3 Overlong representation of the NUL character */
+
+ { "4.3.1", /* U+0000 = c0 80 = */ "\300\200", URC },
+ { "4.3.2", /* U+0000 = e0 80 80 = */ "\340\200\200", URC },
+ { "4.3.3", /* U+0000 = f0 80 80 80 = */ "\360\200\200\200", URC },
+ { "4.3.4", /* U+0000 = f8 80 80 80 80 = */ "\370\200\200\200\200",
+ URC },
+ { "4.3.5", /* U+0000 = fc 80 80 80 80 80 = */ "\374\200\200\200\200\200",
+ URC },
+
+
+ /* 5 Illegal code positions */
+
+ /* 5.1 Single UTF-16 surrogates */
+
+ { "5.1.1", /* U+D800 = ed a0 80 = */ "\355\240\200", URC },
+ { "5.1.2", /* U+DB7F = ed ad bf = */ "\355\255\277", URC },
+ { "5.1.3", /* U+DB80 = ed ae 80 = */ "\355\256\200", URC },
+ { "5.1.4", /* U+DBFF = ed af bf = */ "\355\257\277", URC },
+ { "5.1.5", /* U+DC00 = ed b0 80 = */ "\355\260\200", URC },
+ { "5.1.6", /* U+DF80 = ed be 80 = */ "\355\276\200", URC },
+ { "5.1.7", /* U+DFFF = ed bf bf = */ "\355\277\277", URC },
+
+ /* 5.2 Paired UTF-16 surrogates */
+
+ { "5.2.1", /* U+D800 U+DC00 = ed a0 80 ed b0 80 = */ URC URC },
+ { "5.2.2", /* U+D800 U+DFFF = ed a0 80 ed bf bf = */ URC URC },
+ { "5.2.3", /* U+DB7F U+DC00 = ed ad bf ed b0 80 = */ URC URC },
+ { "5.2.4", /* U+DB7F U+DFFF = ed ad bf ed bf bf = */ URC URC },
+ { "5.2.5", /* U+DB80 U+DC00 = ed ae 80 ed b0 80 = */ URC URC },
+ { "5.2.6", /* U+DB80 U+DFFF = ed ae 80 ed bf bf = */ URC URC },
+ { "5.2.7", /* U+DBFF U+DC00 = ed af bf ed b0 80 = */ URC URC },
+ { "5.2.8", /* U+DBFF U+DFFF = ed af bf ed bf bf = */ URC URC },
+
+ /* 5.3 Other illegal code positions */
+
+ { "5.3.1", /* U+FFFE = ef bf be = */ "\357\277\276" },
+ { "5.3.2", /* U+FFFF = ef bf bf = */ "\357\277\277" },
+
+
+ /* 6 Some other junk */
+
+ { "6.0", "" },
+ { "6.1", "\001\002\003\004\005 ABC" },
+ { "6.2", /* every non-ASCII Latin1 character */
+ "\302\241\302\242\302\243\302\244\302\245\302\246\302\247\302\250"
+ "\302\251\302\252\302\253\302\254\302\255\302\256\302\257\302\260"
+ "\302\261\302\262\302\263\302\264\302\265\302\266\302\267\302\270"
+ "\302\271\302\272\302\273\302\274\302\275\302\276\302\277\303\200"
+ "\303\201\303\202\303\203\303\204\303\205\303\206\303\207\303\210"
+ "\303\211\303\212\303\213\303\214\303\215\303\216\303\217\303\220"
+ "\303\221\303\222\303\223\303\224\303\225\303\226\303\227\303\230"
+ "\303\231\303\232\303\233\303\234\303\235\303\236\303\237\303\240"
+ "\303\241\303\242\303\243\303\244\303\245\303\246\303\247\303\250"
+ "\303\251\303\252\303\253\303\254\303\255\303\256\303\257\303\260"
+ "\303\261\303\262\303\263\303\264\303\265\303\266\303\267\303\270"
+ "\303\271\303\272\303\273\303\274\303\275\303\276\303\277" },
+
+ { "6.3", /* Christmas tree */
+ "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020"
+ "\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\040"
+ "\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057\060"
+ "\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077\100"
+ "\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117\120"
+ "\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137\140"
+ "\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157\160"
+ "\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177\200"
+ "\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220"
+ "\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240"
+ "\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260"
+ "\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300"
+ "\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320"
+ "\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340"
+ "\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360"
+ "\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377",
+
+ "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020"
+ "\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+ " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\177"
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC
+ URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC URC
+ URC URC URC URC URC URC URC URC URC URC URC URC },
+ };
+
+ int i;
+ int ok = 1;
+ for (i = 0; i < sizeof(tests)/sizeof(*tests); i++)
+ {
+ const char *name = tests[i].name;
+ const char *in = tests[i].in;
+ const char *target = (tests[i].target ? tests[i].target : in);
+ const char *target2 = (tests[i].target2 ? tests[i].target2 : target);
+ char *out = split_and_join (in);
+ XChar2b *out16 = utf8_to_XChar2b (in, 0);
+ char *out2 = XChar2b_to_utf8 (out16, 0);
+ if (strcmp (out, target))
+ {
+ LOG (stderr, name, target);
+ LOG (stderr, "FAIL", out);
+ fprintf (stderr, "\n");
+ ok = 0;
+ }
+ if (strcmp (out2, target2))
+ {
+ LOG (stderr, name, target2);
+ LOG (stderr, "FAIL2", out2);
+ fprintf (stderr, "\n");
+ ok = 0;
+ }
+ free (out);
+ free (out2);
+ free (out16);
+ }
+
+ /* Check conversion from UTF8 to Latin1 and ASCII. */
+ {
+ const char *utf8 = ("son \303\256le int\303\251rieure, \303\240 "
+ "c\303\264t\303\251 de l'alc\303\264ve "
+ "ovo\303\257de, o\303\271 les b\303\273ches "
+ "se consument dans l'\303\242tre");
+ const char *latin1 = ("son \356le int\351rieure, \340 "
+ "c\364t\351 de l'alc\364ve ovo\357de, "
+ "o\371 les b\373ches se consument dans "
+ "l'\342tre");
+ const char *ascii = ("son ile interieure, a cote de l'alcove "
+ "ovoide, ou les buches se consument dans "
+ "l'atre");
+ char *latin1b = utf8_to_latin1 (utf8, False);
+ char *ascii2 = utf8_to_latin1 (utf8, True);
+ if (strcmp (latin1, latin1b))
+ {
+ LOG (stderr, "LATIN1", utf8);
+ LOG (stderr, "FAIL3", latin1b);
+ fprintf (stderr, "\n");
+ ok = 0;
+ }
+ if (strcmp (ascii, ascii2))
+ {
+ LOG (stderr, "ASCII", utf8);
+ LOG (stderr, "FAIL4", ascii2);
+ fprintf (stderr, "\n");
+ ok = 0;
+ }
+ free (latin1b);
+ free (ascii2);
+ }
+
+ /* Check de-composition of emoji that should all be treated as a unit
+ for measurement and display purposes. */
+ {
+ static const char * const tests[] = {
+
+ /* 0: "Man" */
+ " \360\237\221\250 ",
+
+ /* 1: "Blackula" = "Vampire, dark skin tone" = 1F9DB 1F3FF */
+ " \360\237\247\233\360\237\217\277 ",
+
+ /* 2: "Black male teacher" = "Man, dark skin tone, ZWJ, school" =
+ 1F468 1F3FF 200D 1F3EB
+ */
+ " \360\237\221\250\360\237\217\277\342\200\215\360\237\217\253 ",
+
+ /* 3: "Female runner" = "Runner, ZWJ, female sign" = 1F3C3 200D 2640 */
+ " \360\237\217\203\342\200\215\342\231\200 ",
+
+ /* 4: "Woman astronaut" = "Woman, ZWJ, rocket ship" = 1F3C3 200D 1F680 */
+ " \360\237\217\203\342\200\215\360\237\232\200 ",
+
+ /* 5:
+ Group of people displayed as a single glyph:
+ Woman, dark skin tone, ZWJ, 1F469 1F3FF 200D
+ Man, light skin tone, ZWJ, 1F468 1F3FB 200D
+ Boy, medium skin tone, ZWJ, 1F466 1F3FD 200D
+ Girl, dark skin tone. 1F467 1F3FF
+ */
+ " \360\237\221\251\360\237\217\277\342\200\215"
+ "\360\237\221\250\360\237\217\273\342\200\215"
+ "\360\237\221\246\360\237\217\275\342\200\215"
+ "\360\237\221\247\360\237\217\277 ",
+ };
+ int i;
+ for (i = 0; i < sizeof(tests)/sizeof(*tests); i++)
+ {
+ int L = 0;
+ char **out = utf8_split (tests[i], &L);
+ char name[100];
+ int j;
+ sprintf (name, "SPLIT %d: %d glyphs", i, L-2);
+ if (L != 3)
+ {
+ LOG (stderr, name, tests[i]);
+ ok = 0;
+ }
+ for (j = 0; j < L; j++)
+ free (out[j]);
+ free (out);
+ }
+ }
+
+ if (ok) fprintf (stderr, "OK\n");
+ return (ok == 0);
+}
+
+#endif /* SELFTEST */
--- /dev/null
+/* xscreensaver, Copyright (c) 2014-2015 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __XSCREENSAVER_UTF8WC_H__
+#define __XSCREENSAVER_UTF8WC_H__
+
+/* Utilities for converting between UTF8 and XChar2b. */
+
+/* Converts a null-terminated UTF8 string to a null-terminated XChar2b array.
+ This only handles characters that can be represented in 16 bits, the
+ Basic Multilingual Plane. (No hieroglyphics, Elvish, Klingon or Emoji.)
+ */
+extern XChar2b * utf8_to_XChar2b (const char *, int *length_ret);
+
+/* Converts a null-terminated XChar2b array to a null-terminated UTF8 string.
+ */
+extern char * XChar2b_to_utf8 (const XChar2b *, int *length_ret);
+
+/* Split a UTF8 string into an array of strings, one per character.
+ The sub-strings will be null terminated and may be multiple bytes.
+ */
+extern char ** utf8_split (const char *string, int *length_ret);
+
+/* Converts a UTF8 string to the closest Latin1 or ASCII equivalent.
+ */
+extern char *utf8_to_latin1 (const char *string, Bool ascii_p);
+
+/* Converts a Unicode character to a multi-byte UTF8 sequence.
+ Returns the number of bytes written.
+ */
+extern int utf8_encode (unsigned long uc, char *out, long length);
+
+/* Parse the first UTF8 character at the front of the string.
+ Return the Unicode character, and the number of bytes read.
+ */
+extern long utf8_decode (const unsigned char *in, long length,
+ unsigned long *unicode_ret);
+
+#endif /* __XSCREENSAVER_UTF8WC_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1997-2014 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#ifdef HAVE_JWXYZ
+# include "jwxyz.h"
+#else /* real X11 */
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include <X11/Xos.h>
+#endif /* !HAVE_JWXYZ */
--- /dev/null
+static const char screensaver_id[] =
+ "@(#)xscreensaver 5.45 (08-Dec-2020), by Jamie Zawinski (jwz@jwz.org)";
--- /dev/null
+/* xscreensaver, Copyright (c) 1993-2017 by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* This file contains some code for intelligently picking the best visual
+ (where "best" is biased in the direction of either: high color counts;
+ or: having writable color cells...)
+ */
+
+#include "utils.h"
+#include "resources.h" /* for get_string_resource() */
+#include "visual.h"
+
+#include <string.h>
+#ifndef HAVE_ANDROID
+#include <X11/Xutil.h>
+#else
+#include "../android/android-visual.h"
+#endif
+
+extern char *progname;
+
+#ifndef isupper
+# define isupper(c) ((c) >= 'A' && (c) <= 'Z')
+#endif
+#ifndef _tolower
+# define _tolower(c) ((c) - 'A' + 'a')
+#endif
+
+
+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 MONO_VISUAL -3
+#define GRAY_VISUAL -4
+#define COLOR_VISUAL -5
+#define GL_VISUAL -6
+#define SPECIFIC_VISUAL -7
+
+Visual *
+get_visual (Screen *screen, const char *string, Bool prefer_writable_cells,
+ Bool verbose_p)
+{
+ 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 || !*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, " %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 (vclass == DEFAULT_VISUAL)
+ result = DefaultVisualOfScreen (screen);
+ else if (vclass == BEST_VISUAL)
+ 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)
+ {
+ 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)
+ result = visual;
+ else if (verbose_p)
+ fprintf (stderr, "%s: no visual of class %s.\n", progname, v);
+ }
+
+ if (v) free (v);
+ return result;
+}
+
+Visual *
+get_visual_resource (Screen *screen, char *name, char *class,
+ Bool prefer_writable_cells)
+{
+ 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);
+}
+
+
+static Visual *
+pick_best_visual (Screen *screen, Bool prefer_writable_cells, Bool color_only)
+{
+ Visual *visual;
+
+ 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;
+ 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 *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;
+ int out_count;
+
+ vi_in.class = visual_class;
+ vi_in.screen = screen_number (screen);
+ vi_out = XGetVisualInfo (dpy, (VisualClassMask | VisualScreenMask),
+ &vi_in, &out_count);
+ if (vi_out)
+ {
+ /* choose the 'best' one, if multiple */
+ int i, best;
+ Visual *visual;
+/* 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 = (best < out_count ? vi_out [best].visual : 0);
+ XFree ((char *) vi_out);
+ return visual;
+ }
+ else
+ return 0;
+}
+
+static Visual *
+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;
+}
+
+
+static XVisualInfo *
+visual_info_id (Screen *screen, int id)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XVisualInfo vi_in;
+ int out_count;
+ vi_in.screen = screen_number (screen);
+ vi_in.visualid = id;
+ return XGetVisualInfo (dpy, VisualScreenMask | VisualIDMask,
+ &vi_in, &out_count);
+}
+
+static XVisualInfo *
+visual_info (Screen *screen, Visual *visual)
+{
+ XVisualInfo *vi_out = visual_info_id (screen, XVisualIDFromVisual (visual));
+ if (! vi_out) abort ();
+ return vi_out;
+}
+
+Visual *
+id_to_visual (Screen *screen, int id)
+{
+ XVisualInfo *vi_out = visual_info_id (screen, id);
+ if (vi_out)
+ {
+ Visual *v = vi_out[0].visual;
+ XFree ((char *) vi_out);
+ return v;
+ }
+ return 0;
+}
+
+int
+visual_depth (Screen *screen, Visual *visual)
+{
+ XVisualInfo *vi_out = visual_info (screen, visual);
+ int d = vi_out [0].depth;
+ XFree ((char *) vi_out);
+ return d;
+}
+
+
+/* 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.)
+
+ Still, it can be useful to know bits_per_pixel before the XImage exists.
+
+ 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 <ftp://ftp.x.org/pub/>.
+ */
+int
+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;
+}
+
+
+int
+visual_class (Screen *screen, Visual *visual)
+{
+ XVisualInfo *vi_out = visual_info (screen, visual);
+ int c = vi_out [0].class;
+ XFree ((char *) vi_out);
+ 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 (FILE *f, Screen *screen, Visual *visual, Bool private_cmap_p)
+{
+ char n[10];
+ XVisualInfo *vi_out = visual_info (screen, visual);
+ 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," :
+ vi_out->class == TrueColor ? "TrueColor, " :
+ vi_out->class == GrayScale ? "GrayScale, " :
+ vi_out->class == PseudoColor ? "PseudoColor," :
+ vi_out->class == DirectColor ? "DirectColor," :
+ "UNKNOWN: "),
+ vi_out->depth, n);
+ XFree ((char *) vi_out);
+}
+
+int
+screen_number (Screen *screen)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int i;
+ for (i = 0; i < ScreenCount (dpy); i++)
+ if (ScreenOfDisplay (dpy, i) == screen)
+ return i;
+ abort ();
+ return 0;
+}
+
+int
+visual_cells (Screen *screen, Visual *visual)
+{
+ XVisualInfo *vi_out = visual_info (screen, visual);
+ int c = vi_out [0].colormap_size;
+ 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;
+}
+
+
+void
+visual_rgb_masks (Screen *screen, Visual *visual, unsigned long *red_mask,
+ unsigned long *green_mask, unsigned long *blue_mask)
+{
+ XVisualInfo *vi_out = visual_info (screen, visual);
+ *red_mask = vi_out->red_mask;
+ *green_mask = vi_out->green_mask;
+ *blue_mask = vi_out->blue_mask;
+ XFree ((char *) vi_out);
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 1993-2014 by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __VISUAL_H__
+#define __VISUAL_H__
+
+extern Visual *get_visual (Screen *, const char *name, Bool, Bool);
+extern Visual *get_visual_resource (Screen *, char *, char *, Bool);
+extern int visual_depth (Screen *, Visual *);
+extern int visual_pixmap_depth (Screen *, Visual *);
+extern int visual_class (Screen *, Visual *);
+extern int visual_cells (Screen *, Visual *);
+extern int screen_number (Screen *);
+extern Visual *find_similar_visual (Screen *, Visual *old);
+extern void describe_visual (FILE *f, Screen *, Visual *, Bool private_cmap_p);
+extern Visual *get_overlay_visual (Screen *, unsigned long *pixel_return);
+extern Bool has_writable_cells (Screen *, Visual *);
+extern Visual *id_to_visual (Screen *, int);
+extern void visual_rgb_masks (Screen *screen, Visual *visual,
+ unsigned long *red_mask,
+ unsigned long *green_mask,
+ unsigned long *blue_mask);
+
+extern Visual *get_gl_visual (Screen *);
+extern void describe_gl_visual (FILE *, Screen *, Visual *, Bool priv_cmap_p);
+extern Bool validate_gl_visual (FILE *, Screen *, const char *, Visual *);
+
+#endif /* __VISUAL_H__ */
--- /dev/null
+/* -*- Mode: C; tab-width: 2 -*- */
+/*****************************************************************************/
+/** Copyright 1991 by Andreas Stolcke **/
+/** Copyright 1990 by Solbourne Computer Inc. **/
+/** Longmont, Colorado **/
+/** **/
+/** All Rights Reserved **/
+/** **/
+/** Permission to use, copy, modify, and distribute this software and **/
+/** its documentation for any purpose and without fee is hereby **/
+/** granted, provided that the above copyright notice appear in all **/
+/** copies and that both that copyright notice and this permis- **/
+/** sion notice appear in supporting documentation, and that the **/
+/** name of Solbourne not be used in advertising **/
+/** in publicity pertaining to distribution of the software without **/
+/** specific, written prior permission. **/
+/** **/
+/** ANDREAS STOLCKE AND SOLBOURNE COMPUTER INC. DISCLAIMS ALL WARRANTIES **/
+/** WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF **/
+/** MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL ANDREAS STOLCKE **/
+/** OR SOLBOURNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL **/
+/** DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA **/
+/** OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER **/
+/** TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE **/
+/** OR PERFORMANCE OF THIS SOFTWARE. **/
+/*****************************************************************************/
+/*
+ * vroot.h -- Virtual Root Window handling header file
+ *
+ * This header file redefines the X11 macros RootWindow and DefaultRootWindow,
+ * making them look for a virtual root window as provided by certain `virtual'
+ * window managers like swm and tvtwm. If none is found, the ordinary root
+ * window is returned, thus retaining backward compatibility with standard
+ * window managers.
+ * The function implementing the virtual root lookup remembers the result of
+ * its last invocation to avoid overhead in the case of repeated calls
+ * on the same display and screen arguments.
+ * The lookup code itself is taken from Tom LaStrange's ssetroot program.
+ *
+ * Most simple root window changing X programs can be converted to using
+ * virtual roots by just including
+ *
+ * #include <X11/vroot.h>
+ *
+ * after all the X11 header files. It has been tested on such popular
+ * X clients as xphoon, xfroot, xloadimage, and xaqua.
+ * It also works with the core clients xprop, xwininfo, xwd, and editres
+ * (and is necessary to get those clients working under tvtwm).
+ * It does NOT work with xsetroot; get the xsetroot replacement included in
+ * the tvtwm distribution instead.
+ *
+ * Andreas Stolcke <stolcke@ICSI.Berkeley.EDU>, 9/7/90
+ * - replaced all NULL's with properly cast 0's, 5/6/91
+ * - free children list (suggested by Mark Martin <mmm@cetia.fr>), 5/16/91
+ * - include X11/Xlib.h and support RootWindowOfScreen, too 9/17/91
+ *
+ * Jamie Zawinski <jwz@jwz.org>, 28-Apr-1997
+ * - use ANSI C
+ *
+ * Jamie Zawinski <jwz@jwz.org>, 3-Sep-2003
+ * - if the environment variable "XSCREENSAVER_WINDOW" is set, use that
+ * as the root window instead of searching for __SWM_VROOT.
+ *
+ * Jamie Zawinski <jwz@jwz.org>, 14-Aug-2004
+ * - changes to get gcc to stop whining about "type punning".
+ *
+ * Jamie Zawinski <jwz@jwz.org>, 16-Dec-2004
+ * - fixed that last fix.
+ */
+
+#ifndef _VROOT_H_
+#define _VROOT_H_
+#define _XSCREENSAVER_VROOT_H_
+
+#if !defined(lint) && !defined(SABER)
+static const char vroot_rcsid[] =
+ "#Id: vroot.h,v 1.8 2004/12/16 05:33:54 jwz Exp #" "\n"
+ "#Id: vroot.h,v 1.4 1991/09/30 19:23:16 stolcke Exp stolcke #";
+#endif
+
+#include <X11/X.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+
+static Window
+#ifdef __STDC__ /* ANSIfication added by jwz, to avoid superfluous warnings. */
+VirtualRootWindowOfScreen(Screen *screen)
+#else /* !__STDC__ */
+VirtualRootWindowOfScreen(screen) Screen *screen;
+#endif /* !__STDC__ */
+{
+ static Screen *save_screen = (Screen *)0;
+ static Window root = (Window)0;
+
+ if (screen != save_screen) {
+ Display *dpy = DisplayOfScreen(screen);
+ Atom __SWM_VROOT = None;
+ int i;
+ Window rootReturn, parentReturn, *children;
+ unsigned int numChildren;
+
+ /* first check for a hex or decimal window ID in the environment */
+ const char *xss_id = getenv("XSCREENSAVER_WINDOW");
+ if (xss_id && *xss_id) {
+ unsigned long id = 0;
+ char c;
+ if (1 == sscanf (xss_id, " 0x%lx %c", &id, &c) ||
+ 1 == sscanf (xss_id, " %lu %c", &id, &c)) {
+ root = (Window) id;
+ save_screen = screen;
+ return root;
+ }
+ }
+
+ root = RootWindowOfScreen(screen);
+
+ /* go look for a virtual root */
+ __SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False);
+ if (XQueryTree(dpy, root, &rootReturn, &parentReturn,
+ &children, &numChildren)) {
+ for (i = 0; i < numChildren; i++) {
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytesafter;
+ unsigned char *newRoot = 0;
+
+ if (XGetWindowProperty(dpy, children[i],
+ __SWM_VROOT, 0, 1, False, XA_WINDOW,
+ &actual_type, &actual_format,
+ &nitems, &bytesafter,
+ &newRoot) == Success
+ && newRoot) {
+ root = *((Window *) newRoot);
+ break;
+ }
+ }
+ if (children)
+ XFree((char *)children);
+ }
+
+ save_screen = screen;
+ }
+
+ return root;
+}
+
+#undef RootWindowOfScreen
+#define RootWindowOfScreen(s) VirtualRootWindowOfScreen(s)
+
+#undef RootWindow
+#define RootWindow(dpy,screen) VirtualRootWindowOfScreen(ScreenOfDisplay(dpy,screen))
+
+#undef DefaultRootWindow
+#define DefaultRootWindow(dpy) VirtualRootWindowOfScreen(DefaultScreenOfDisplay(dpy))
+
+#endif /* _VROOT_H_ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1998, 1999, 2006
+ * by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* The XDBE (Double Buffering) extension is pretty tricky to use, since you
+ can get X errors at inconvenient times during initialization. This file
+ contains a utility routine to make it easier to deal with.
+ */
+
+#include "utils.h"
+#include "xdbe.h"
+#include "resources.h" /* for get_string_resource() */
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+# include <X11/Xmu/Error.h>
+#endif
+
+extern char *progname;
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION /* whole file */
+
+static Bool xdbe_got_x_error = False;
+static int
+xdbe_ehandler (Display *dpy, XErrorEvent *error)
+{
+ xdbe_got_x_error = True;
+
+#ifdef DEBUG
+ fprintf (stderr, "\n%s: ignoring X error from DOUBLE-BUFFER:\n", progname);
+ XmuPrintDefaultErrorMessage (dpy, error, stderr);
+ fprintf (stderr, "\n");
+#endif
+
+ return 0;
+}
+
+
+XdbeBackBuffer
+xdbe_get_backbuffer (Display *dpy, Window window,
+ XdbeSwapAction action)
+{
+ XdbeBackBuffer b;
+ XErrorHandler old_handler;
+ int maj, min;
+
+ if (!get_boolean_resource(dpy, "useDBE", "Boolean"))
+ return 0;
+
+ if (!XdbeQueryExtension (dpy, &maj, &min))
+ return 0;
+
+ XSync (dpy, False);
+ xdbe_got_x_error = False;
+ old_handler = XSetErrorHandler (xdbe_ehandler);
+ b = XdbeAllocateBackBufferName(dpy, window, XdbeUndefined);
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+
+ if (xdbe_got_x_error)
+ return 0;
+
+ return b;
+}
+
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
--- /dev/null
+/* xscreensaver, Copyright (c) 1998, 1999 by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* The XDBE (Double Buffering) extension is pretty tricky to use, since you
+ can get X errors at inconvenient times during initialization. This file
+ contains a utility routine to make it easier to deal with.
+ */
+
+#ifndef __XSCREENSAVER_XDBE_H__
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+
+# include <X11/extensions/Xdbe.h>
+
+extern XdbeBackBuffer xdbe_get_backbuffer (Display *, Window, XdbeSwapAction);
+
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+
+#endif /* __XSCREENSAVER_XDBE_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 2014-2018 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* Compatibility layer using XDrawString, XDrawString16() or Xutf8DrawString().
+ This layer is used by X11 systems without Xft, and by MacOS / iOS.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef HAVE_XFT
+
+# include "utils.h"
+# include "resources.h"
+# include "xft.h"
+# include "utf8wc.h"
+
+extern const char *progname;
+
+struct _XftDraw {
+ Display *dpy;
+ Drawable drawable;
+ GC gc;
+ unsigned long pixel;
+ Font fid;
+ Visual *visual;
+ Colormap colormap;
+};
+
+
+XftFont *
+XftFontOpenXlfd (Display *dpy, int screen, _Xconst char *xlfd)
+{
+ XftFont *ff = (XftFont *) calloc (1, sizeof(*ff));
+
+ if (!dpy || !xlfd) abort();
+ if (!ff) return 0;
+ ff->xfont = XLoadQueryFont (dpy, xlfd);
+ if (!ff->xfont)
+ {
+ free (ff);
+ return 0;
+ }
+
+ ff->name = strdup (xlfd);
+ ff->ascent = ff->xfont->ascent;
+ ff->descent = ff->xfont->descent;
+ ff->height = ff->ascent + ff->descent;
+
+# ifdef HAVE_XUTF8DRAWSTRING
+ {
+ unsigned i;
+
+ // In the event of -*-random-* (under JWXYZ), get the actual XLFD,
+ // otherwise we'll get another random font that doesn't match ff->xfont.
+ char *xlfd_resolved = NULL;
+
+ char **missing_charset_list_return;
+ int missing_charset_count_return;
+ char *def_string_return;
+
+ char *ss;
+
+ for (i = 0; i != ff->xfont->n_properties; ++i) {
+ if (ff->xfont->properties[i].name == XA_FONT) {
+ xlfd_resolved = XGetAtomName (dpy, ff->xfont->properties[i].card32);
+ if (xlfd_resolved)
+ xlfd = xlfd_resolved;
+ break;
+ }
+ }
+
+ ss = (char *) malloc (strlen(xlfd) + 10);
+ strcpy (ss, xlfd);
+ strcat (ss, ",*");
+ ff->fontset = XCreateFontSet (dpy, ss,
+ &missing_charset_list_return,
+ &missing_charset_count_return,
+ &def_string_return);
+
+# if 0
+ {
+ int i;
+ for (i = 0; i < missing_charset_count_return; i++)
+ fprintf (stderr, "%s: missing charset: %s\n",
+ ss, missing_charset_list_return[i]);
+ }
+# endif
+
+ /* Apparently this is not to be freed. */
+ /* if (def_string_return) XFree (def_string_return); */
+
+ if (missing_charset_list_return)
+ XFreeStringList (missing_charset_list_return);
+
+ free (ss);
+ free (xlfd_resolved);
+ }
+# endif
+
+ return ff;
+}
+
+
+void
+XftFontClose (Display *dpy, XftFont *font)
+{
+ if (!dpy || !font) abort();
+ free (font->name);
+ XFreeFont (dpy, font->xfont);
+# ifdef HAVE_XUTF8DRAWSTRING
+ XFreeFontSet (dpy, font->fontset);
+# endif
+ free (font);
+}
+
+
+Bool
+XftColorAllocName (Display *dpy,
+ _Xconst Visual *visual,
+ Colormap cmap,
+ _Xconst char *name,
+ XftColor *result)
+{
+ XColor color;
+ if (!dpy || !visual || !name || !result) abort();
+
+ if (! XParseColor (dpy, cmap, name, &color))
+ {
+ fprintf (stderr, "%s: can't parse color %s", progname, name);
+ return False;
+ }
+ else if (! XAllocColor (dpy, cmap, &color))
+ {
+ fprintf (stderr, "%s: couldn't allocate color %s", progname, name);
+ return False;
+ }
+ else
+ {
+ XRenderColor color2;
+ color2.red = color.red;
+ color2.green = color.green;
+ color2.blue = color.blue;
+ color2.alpha = 0xFFFF;
+ XftColorAllocValue (dpy, visual, cmap, &color2, result);
+ result->pixel = color.pixel;
+ return True;
+ }
+}
+
+
+static short
+maskbase (unsigned long m)
+{
+ short i;
+ if (!m)
+ return 0;
+ i = 0;
+ while (! (m&1))
+ {
+ m >>= 1;
+ i++;
+ }
+ return i;
+}
+
+
+static short
+masklen (unsigned long m)
+{
+ unsigned long y;
+ y = (m >> 1) & 033333333333;
+ y = m - y - ((y >>1) & 033333333333);
+ return (short) (((y + (y >> 3)) & 030707070707) % 077);
+}
+
+
+Bool
+XftColorAllocValue (Display *dpy,
+ _Xconst Visual *visual,
+ Colormap cmap,
+ _Xconst XRenderColor *color,
+ XftColor *result)
+{
+ if (!dpy || !visual || !color || !result) abort();
+ if (visual->class == TrueColor)
+ {
+ int red_shift = maskbase (visual->red_mask);
+ int red_len = masklen (visual->red_mask);
+ int green_shift = maskbase (visual->green_mask);
+ int green_len = masklen (visual->green_mask);
+ int blue_shift = maskbase (visual->blue_mask);
+ int blue_len = masklen (visual->blue_mask);
+ result->pixel = (((color->red >> (16 - red_len)) << red_shift) |
+ ((color->green >> (16 - green_len)) << green_shift) |
+ ((color->blue >> (16 - blue_len)) << blue_shift));
+# ifdef HAVE_JWXYZ
+ result->pixel |= BlackPixel(dpy, 0); /* alpha */
+# endif
+ }
+ else
+ {
+ XColor xcolor;
+ xcolor.red = color->red;
+ xcolor.green = color->green;
+ xcolor.blue = color->blue;
+ if (!XAllocColor (dpy, cmap, &xcolor))
+ return False;
+ result->pixel = xcolor.pixel;
+ }
+ result->color.red = color->red;
+ result->color.green = color->green;
+ result->color.blue = color->blue;
+ result->color.alpha = color->alpha;
+ return True;
+}
+
+
+void
+XftColorFree (Display *dpy,
+ Visual *visual,
+ Colormap cmap,
+ XftColor *color)
+{
+ if (!dpy || !visual || !color) abort();
+ if (visual->class != TrueColor)
+ XFreeColors (dpy, cmap, &color->pixel, 1, 0);
+}
+
+
+XftDraw *
+XftDrawCreate (Display *dpy,
+ Drawable drawable,
+ Visual *visual,
+ Colormap colormap)
+{
+ XftDraw *dd = (XftDraw *) calloc (1, sizeof(*dd));
+ if (!dpy || !drawable || !visual) abort();
+ if (!dd) return 0;
+
+ dd->dpy = dpy;
+ dd->drawable = drawable;
+ dd->visual = visual;
+ dd->colormap = colormap;
+ dd->gc = XCreateGC (dpy, drawable, 0, 0);
+ return dd;
+}
+
+
+void
+XftDrawDestroy (XftDraw *draw)
+{
+ if (!draw) abort();
+ XFreeGC (draw->dpy, draw->gc);
+ free (draw);
+}
+
+
+void
+XftTextExtentsUtf8 (Display *dpy,
+ XftFont *font,
+ _Xconst FcChar8 *string,
+ int len,
+ XGlyphInfo *extents)
+{
+ XCharStruct overall;
+
+ if (!dpy || !font || !string || !extents) abort();
+
+# ifdef HAVE_XUTF8DRAWSTRING
+ {
+ XRectangle ink;
+ int advancement =
+ Xutf8TextExtents (font->fontset, (const char *) string, len, &ink, 0);
+ XmbRectangle_to_XCharStruct (ink, overall, advancement);
+ }
+# else /* !HAVE_XUTF8DRAWSTRING */
+ {
+ char *s2 = (char *) malloc (len + 1);
+ int direction, ascent, descent;
+ XChar2b *s16;
+ int s16_len = 0;
+ strncpy (s2, (char *) string, len);
+ s2[len] = 0;
+ s16 = utf8_to_XChar2b (s2, &s16_len);
+ XTextExtents16 (font->xfont, s16, s16_len,
+ &direction, &ascent, &descent, &overall);
+ free (s2);
+ free (s16);
+ }
+# endif /* !HAVE_XUTF8DRAWSTRING */
+
+ XCharStruct_to_XGlyphInfo (overall, *extents);
+}
+
+
+void
+XftDrawStringUtf8 (XftDraw *draw,
+ _Xconst XftColor *color,
+ XftFont *font,
+ int x,
+ int y,
+ _Xconst FcChar8 *string,
+ int len)
+{
+ if (!draw || !color || !font || !string) abort();
+
+ if (color->pixel != draw->pixel)
+ {
+ XSetForeground (draw->dpy, draw->gc, color->pixel);
+ draw->pixel = color->pixel;
+ }
+ if (font->xfont->fid != draw->fid)
+ {
+ XSetFont (draw->dpy, draw->gc, font->xfont->fid);
+ draw->fid = font->xfont->fid;
+ }
+
+# ifdef HAVE_XUTF8DRAWSTRING
+ /* If we have Xutf8DrawString, use it instead of XDrawString16 because
+ there is some chance it will handle characters of more than 16 bits
+ (beyond the Basic Multilingual Plane).
+ */
+
+ /* #### I guess I don't really understand how FontSet works, because when
+ using the real X11 implementation of Xutf8DrawString, this seems
+ to just truncate the text at the first non-ASCII character.
+
+ The XDrawString16() path works, however, at the expense of losing
+ everything above Basic Multilingual. However, that path is only
+ taken on X11 systems that are old enough to not have libXft,
+ which means that the chance of Unicode working was already slim.
+ */
+ Xutf8DrawString (draw->dpy, draw->drawable, font->fontset, draw->gc, x, y,
+ (const char *) string, len);
+# else
+ {
+ int s16_len = 0;
+ char *s2 = (char *) malloc (len + 1);
+ XChar2b *s16;
+ strncpy (s2, (char *) string, len);
+ s2[len] = 0;
+ s16 = utf8_to_XChar2b (s2, &s16_len);
+ free (s2);
+ XDrawString16 (draw->dpy, draw->drawable, draw->gc, x, y, s16, s16_len);
+ free (s16);
+ }
+# endif
+}
+
+#else /* HAVE_XFT */
+
+const int Wempty_translation_unit_is_a_dumb_warning = 0;
+
+#endif /* HAVE_XFT */
--- /dev/null
+/* xscreensaver, Copyright (c) 2014-2015 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* Compatibility layer using XDrawString, XDrawString16() or Xutf8DrawString().
+ This layer is used by X11 systems without Xft, and by MacOS / iOS.
+ */
+
+#ifndef __XSCREENSAVER_XFT_H__
+#define __XSCREENSAVER_XFT_H__
+
+/* The XGlyphInfo field names and values are, of course, arbitrarily
+ different from XCharStruct for no sensible reason. These macros
+ translate between them.
+ */
+
+# define XGlyphInfo_to_XCharStruct(G,C) do { \
+ (C).lbearing = -(G).x; \
+ (C).rbearing = (G).width - (G).x; \
+ (C).ascent = (G).y; \
+ (C).descent = (G).height - (G).y; \
+ (C).width = (G).xOff; \
+} while (0)
+
+# define XCharStruct_to_XGlyphInfo(C,G) do { \
+ (G).x = -(C).lbearing; \
+ (G).y = (C).ascent; \
+ (G).xOff = (C).width; \
+ (G).yOff = 0; \
+ (G).width = (C).rbearing - (C).lbearing; \
+ (G).height = (C).ascent + (C).descent; \
+} while (0)
+
+/* Xutf8TextExtents returns a bounding box in an XRectangle, which
+ conveniently interprets everything in the opposite direction
+ from XGlyphInfo!
+ */
+# define XCharStruct_to_XmbRectangle(C,R) do { \
+ (R).x = (C).lbearing; \
+ (R).y = -(C).ascent; \
+ (R).width = (C).rbearing - (C).lbearing; \
+ (R).height = (C).ascent + (C).descent; \
+} while (0)
+
+# define XmbRectangle_to_XCharStruct(R,C,ADV) do { \
+ (C).lbearing = (R).x; \
+ (C).rbearing = (R).width + (R).x; \
+ (C).ascent = -(R).y; \
+ (C).descent = (R).height + (R).y; \
+ (C).width = (ADV); \
+} while (0)
+
+
+# ifdef HAVE_XFT
+
+# include <X11/Xft/Xft.h>
+
+# else /* !HAVE_XFT -- the rest of the file */
+
+# ifdef HAVE_COCOA
+# include "jwxyz.h"
+#elif defined(HAVE_ANDROID)
+# include "jwxyz.h"
+# else
+# include <X11/Xlib.h>
+# endif
+
+/* This doesn't seem to work right under X11. See comment in xft.c. */
+# ifndef HAVE_COCOA
+# undef HAVE_XUTF8DRAWSTRING
+# endif
+
+
+# ifndef _Xconst
+# define _Xconst const
+# endif
+
+typedef struct _XGlyphInfo {
+ unsigned short width, height; /* bounding box of the ink */
+ short x, y; /* distance from upper left of bbox to glyph origin. */
+ short xOff, yOff; /* distance from glyph origin to next origin. */
+} XGlyphInfo;
+
+
+typedef struct _XftFont {
+ XFontStruct *xfont;
+# ifdef HAVE_XUTF8DRAWSTRING
+ XFontSet fontset;
+# endif
+ char *name;
+ int ascent;
+ int descent;
+ int height;
+} XftFont;
+
+typedef struct {
+ unsigned short red;
+ unsigned short green;
+ unsigned short blue;
+ unsigned short alpha;
+} XRenderColor;
+
+typedef struct _XftColor {
+ unsigned long pixel;
+ XRenderColor color;
+} XftColor;
+
+typedef struct _XftDraw XftDraw;
+
+typedef unsigned char FcChar8;
+
+
+XftFont *XftFontOpenXlfd (Display *dpy, int screen, _Xconst char *xlfd);
+#define XftFontOpenName XftFontOpenXlfd
+
+void XftFontClose (Display *dpy, XftFont *font);
+
+Bool XftColorAllocName (Display *dpy,
+ _Xconst Visual *visual,
+ Colormap cmap,
+ _Xconst char *name,
+ XftColor *result);
+
+Bool XftColorAllocValue (Display *dpy,
+ _Xconst Visual *visual,
+ Colormap cmap,
+ _Xconst XRenderColor *color,
+ XftColor *result);
+
+void XftColorFree (Display *dpy,
+ Visual *visual,
+ Colormap cmap,
+ XftColor *color);
+
+XftDraw *XftDrawCreate (Display *dpy,
+ Drawable drawable,
+ Visual *visual,
+ Colormap colormap);
+
+void XftDrawDestroy (XftDraw *draw);
+
+void
+XftTextExtentsUtf8 (Display *dpy,
+ XftFont *pub,
+ _Xconst FcChar8 *string,
+ int len,
+ XGlyphInfo *extents);
+
+void
+XftDrawStringUtf8 (XftDraw *draw,
+ _Xconst XftColor *color,
+ XftFont *pub,
+ int x,
+ int y,
+ _Xconst FcChar8 *string,
+ int len);
+
+# endif /* !HAVE_XFT */
+
+#endif /* __XSCREENSAVER_XFT_H__ */
--- /dev/null
+/* ximage-loader.c --- converts image files or data to XImages or Pixmap.
+ * xscreensaver, Copyright (c) 1998-2020 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_JWXYZ
+# include "jwxyz.h"
+#else
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+#endif
+
+#include "ximage-loader.h"
+
+#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_COCOA) || defined(HAVE_ANDROID)
+# undef HAVE_LIBPNG
+#endif
+
+#ifdef HAVE_COCOA
+# include "grabscreen.h" /* for osx_load_image_file() */
+#endif
+
+#ifdef HAVE_GDK_PIXBUF
+
+# if (__GNUC__ >= 4) /* Ignore useless warnings generated by GTK headers */
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wlong-long"
+# pragma GCC diagnostic ignored "-Wvariadic-macros"
+# pragma GCC diagnostic ignored "-Wpedantic"
+# endif
+
+# include <gdk-pixbuf/gdk-pixbuf.h>
+# ifdef HAVE_GTK2
+# include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
+# else /* !HAVE_GTK2 */
+# include <gdk-pixbuf/gdk-pixbuf-xlib.h>
+# endif /* !HAVE_GTK2 */
+
+# if (__GNUC__ >= 4)
+# pragma GCC diagnostic pop
+# endif
+
+#endif /* HAVE_GDK_PIXBUF */
+
+#ifdef HAVE_LIBPNG
+# include <png.h>
+#endif
+
+#ifdef HAVE_ANDROID
+ /* So that debug output shows up in logcat... */
+extern void Log(const char *format, ...);
+# undef fprintf
+# define fprintf(S, ...) Log(__VA_ARGS__)
+#endif
+
+extern char *progname;
+
+static Bool
+bigendian (void)
+{
+ union { int i; char c[sizeof(int)]; } u;
+ u.i = 1;
+ return !u.c[0];
+}
+
+
+#ifdef HAVE_GDK_PIXBUF
+
+/* Loads the image to an XImage, RGBA -- GDK Pixbuf version.
+ */
+static XImage *
+make_ximage (Display *dpy, Visual *visual, const char *filename,
+ const unsigned char *image_data, unsigned long data_size)
+{
+ GdkPixbuf *pb;
+ static int initted = 0;
+# ifdef HAVE_GTK2
+ GError *gerr = NULL;
+# endif
+
+ if (!initted)
+ {
+# ifdef HAVE_GTK2
+# if !GLIB_CHECK_VERSION(2, 36 ,0)
+ g_type_init ();
+# endif
+# endif
+ if (dpy)
+ {
+ /* Turns out gdk-pixbuf works even if you don't have display
+ connection, which is good news for analogtv-cli. */
+ gdk_pixbuf_xlib_init (dpy, DefaultScreen (dpy));
+ xlib_rgb_init (dpy, DefaultScreenOfDisplay (dpy));
+ }
+ initted = 1;
+ }
+
+ if (filename)
+ {
+# ifdef HAVE_GTK2
+ pb = gdk_pixbuf_new_from_file (filename, &gerr);
+ if (!pb)
+ {
+ fprintf (stderr, "%s: %s\n", progname, gerr->message);
+ return 0;
+ }
+# else
+ pb = gdk_pixbuf_new_from_file (filename);
+ if (!pb)
+ {
+ fprintf (stderr, "%s: GDK unable to load %s: %s\n",
+ progname, filename, (gerr ? gerr->message : "?"));
+ return 0;
+ }
+# endif /* HAVE_GTK2 */
+ }
+ else
+ {
+# ifdef HAVE_GTK2
+ GInputStream *s =
+ g_memory_input_stream_new_from_data (image_data, data_size, 0);
+ pb = gdk_pixbuf_new_from_stream (s, 0, &gerr);
+
+ g_input_stream_close (s, NULL, NULL);
+ /* #### valgrind on xflame says there's a small leak in s? */
+ g_object_unref (s);
+
+ if (! pb)
+ {
+ /* fprintf (stderr, "%s: GDK unable to parse image data: %s\n",
+ progname, (gerr ? gerr->message : "?")); */
+ return 0;
+ }
+# else /* !HAVE_GTK2 */
+ fprintf (stderr, "%s: image loading not supported with GTK 1.x\n",
+ progname);
+ return 0;
+# endif /* !HAVE_GTK2 */
+ }
+
+ if (!pb) abort();
+
+ {
+ XImage *image;
+ int w = gdk_pixbuf_get_width (pb);
+ int h = gdk_pixbuf_get_height (pb);
+ guchar *row = gdk_pixbuf_get_pixels (pb);
+ int stride = gdk_pixbuf_get_rowstride (pb);
+ int chan = gdk_pixbuf_get_n_channels (pb);
+ int x, y;
+
+ image = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0, w, h, 32, 0);
+ image->data = (char *) malloc(h * image->bytes_per_line);
+
+ /* Set the bit order in the XImage structure to whatever the
+ local host's native bit order is.
+ */
+ image->bitmap_bit_order =
+ image->byte_order =
+ (bigendian() ? MSBFirst : LSBFirst);
+
+ if (!image->data)
+ {
+ fprintf (stderr, "%s: out of memory (%d x %d)\n", progname, w, h);
+ return 0;
+ }
+
+ for (y = 0; y < h; y++)
+ {
+ guchar *i = row;
+ for (x = 0; x < w; x++)
+ {
+ unsigned long rgba = 0;
+ switch (chan) {
+ case 1:
+ rgba = ((0xFF << 24) |
+ (*i << 16) |
+ (*i << 8) |
+ *i);
+ i++;
+ break;
+ case 3:
+ rgba = ((0xFF << 24) |
+ (i[2] << 16) |
+ (i[1] << 8) |
+ i[0]);
+ i += 3;
+ break;
+ case 4:
+ rgba = ((i[3] << 24) |
+ (i[2] << 16) |
+ (i[1] << 8) |
+ i[0]);
+ i += 4;
+ break;
+ default:
+ abort();
+ break;
+ }
+ XPutPixel (image, x, y, rgba);
+ }
+ row += stride;
+ }
+
+ /* #### valgrind on xflame says there's a small leak in pb? */
+ g_object_unref (pb);
+ return image;
+ }
+}
+
+#elif defined(HAVE_JWXYZ) /* MacOS, iOS or Android */
+
+/* Loads the image to an XImage, RGBA -- MacOS, iOS or Android version.
+ */
+static XImage *
+make_ximage (Display *dpy, Visual *visual, const char *filename,
+ const unsigned char *image_data, unsigned long data_size)
+{
+ XImage *ximage = 0;
+
+ if (filename)
+ {
+# ifdef HAVE_COCOA /* MacOS */
+ XRectangle geom;
+ Screen *screen = DefaultScreenOfDisplay (dpy);
+ Window window = RootWindowOfScreen (screen);
+ XWindowAttributes xgwa;
+ XGetWindowAttributes (dpy, window, &xgwa);
+ Pixmap pixmap =
+ XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
+ int x, y;
+
+ if (! osx_load_image_file (screen, window, pixmap, filename, &geom))
+ {
+ fprintf (stderr, "%s: %s failed\n", progname, filename);
+ return 0;
+ }
+
+ ximage = XGetImage (dpy, pixmap, geom.x, geom.y,
+ geom.width, geom.height,
+ ~0L, ZPixmap);
+ if (!ximage) abort();
+
+ /* Have to convert ABGR to RGBA */
+ for (y = 0; y < ximage->height; y++)
+ for (x = 0; x < ximage->width; x++)
+ {
+ unsigned long p = XGetPixel (ximage, x, y);
+ unsigned long a = (p >> 24) & 0xFF;
+ unsigned long b = (p >> 16) & 0xFF;
+ unsigned long g = (p >> 8) & 0xFF;
+ unsigned long r = (p >> 0) & 0xFF;
+ p = (r << 24) | (g << 16) | (b << 8) | (a << 0);
+ XPutPixel (ximage, x, y, p);
+ }
+
+ XFreePixmap (dpy, pixmap);
+
+# else /* !HAVE_COCOA -- iOS or Android. */
+ fprintf (stderr, "%s: image file loading not supported\n", progname);
+ return 0;
+# endif /* !HAVE_COCOA */
+ }
+ else
+ {
+ ximage = jwxyz_png_to_ximage (dpy, visual, image_data, data_size);
+ }
+
+ return ximage;
+}
+
+#elif defined(HAVE_LIBPNG)
+
+typedef struct {
+ const unsigned char *buf;
+ png_size_t siz, ptr;
+} png_read_closure;
+
+static void
+png_reader_fn (png_structp png_ptr, png_bytep buf, png_size_t siz)
+{
+ png_read_closure *r = png_get_io_ptr (png_ptr);
+ if (siz > r->siz - r->ptr)
+ png_error (png_ptr, "PNG internal read error");
+ memcpy (buf, r->buf + r->ptr, siz);
+ r->ptr += siz;
+}
+
+
+/* Loads the image to an XImage, RGBA -- libpng version.
+ */
+static XImage *
+make_ximage (Display *dpy, Visual *visual,
+ const char *filename, const unsigned char *image_data,
+ unsigned long data_size)
+{
+ XImage *image = 0;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_infop end_info;
+ png_uint_32 width, height, channels;
+ int bit_depth, color_type, interlace_type;
+ FILE *fp = 0;
+
+ png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ if (!png_ptr) return 0;
+
+ info_ptr = png_create_info_struct (png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_read_struct (&png_ptr, 0, 0);
+ return 0;
+ }
+
+ end_info = png_create_info_struct (png_ptr);
+ if (!end_info)
+ {
+ png_destroy_read_struct (&png_ptr, &info_ptr, 0);
+ return 0;
+ }
+
+ if (setjmp (png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
+ return 0;
+ }
+
+ if (filename)
+ {
+ fp = fopen (filename, "r");
+ if (! fp)
+ {
+ fprintf (stderr, "%s: unable to read %s\n", progname, filename);
+ return 0;
+ }
+ png_init_io (png_ptr, fp);
+ }
+ else
+ {
+ png_read_closure closure;
+ closure.buf = image_data;
+ closure.siz = data_size;
+ closure.ptr = 0;
+ png_set_read_fn (png_ptr, (void *) &closure, png_reader_fn);
+ }
+
+ png_read_info (png_ptr, info_ptr);
+ png_get_IHDR (png_ptr, info_ptr,
+ &width, &height, &bit_depth, &color_type,
+ &interlace_type, 0, 0);
+
+ png_set_strip_16 (png_ptr); /* Truncate 16 bits per component to 8 */
+ png_set_packing (png_ptr); /* Unpack to 1 pixel per byte */
+
+# if 0
+ if (color_type == PNG_COLOR_TYPE_PALETTE) /* Colormap to RGB */
+ png_set_palette_rgb (png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) /* Mono to 8bit */
+ png_set_gray_1_2_4_to_8 (png_ptr);
+# endif
+
+ if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) /* Fix weird alpha */
+ png_set_tRNS_to_alpha (png_ptr);
+
+ /* At least 8 bits deep */
+ if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8)
+ png_set_expand (png_ptr);
+
+ if (bit_depth == 8 && /* Convert RGB to RGBA */
+ (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_PALETTE))
+ png_set_filler (png_ptr, 0xFF, PNG_FILLER_AFTER);
+
+ /* Grayscale to color */
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ png_set_expand (png_ptr);
+
+
+ /* Convert graysale to color */
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb (png_ptr);
+
+# if 0
+ {
+ png_color_16 *bg;
+ if (png_get_bKGD (png_ptr, info_ptr, &bg))
+ png_set_background (png_ptr, bg, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
+ }
+# endif
+
+ /* Commit */
+ png_read_update_info (png_ptr, info_ptr);
+
+ channels = png_get_channels (png_ptr, info_ptr);
+
+ {
+ png_bytep *rows = png_malloc (png_ptr, height * sizeof(*rows));
+ int x, y;
+ for (y = 0; y < height; y++)
+ rows[y] = png_malloc (png_ptr, png_get_rowbytes (png_ptr, info_ptr));
+ png_read_image (png_ptr, rows);
+ png_read_end (png_ptr, info_ptr);
+
+ image = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
+ width, height, 32, 0);
+ image->data = (char *) malloc (height * image->bytes_per_line);
+
+ /* Set the bit order in the XImage structure to whatever the
+ local host's native bit order is.
+ */
+ image->bitmap_bit_order =
+ image->byte_order =
+ (bigendian() ? MSBFirst : LSBFirst);
+
+ if (!image->data)
+ {
+ fprintf (stderr, "%s: out of memory (%lu x %lu)\n",
+ progname, (unsigned long)width, (unsigned long)height);
+ return 0;
+ }
+
+ for (y = 0; y < height; y++)
+ {
+ png_bytep i = rows[y];
+ for (x = 0; x < width; x++)
+ {
+ unsigned long rgba;
+ switch (channels) {
+ case 4:
+ rgba = ((i[3] << 24) |
+ (i[2] << 16) |
+ (i[1] << 8) |
+ i[0]);
+ break;
+ case 3:
+ rgba = ((0xFF << 24) |
+ (i[2] << 16) |
+ (i[1] << 8) |
+ i[0]);
+ break;
+ case 2:
+ rgba = ((i[1] << 24) |
+ (i[0] << 16) |
+ (i[0] << 8) |
+ i[0]);
+ break;
+ case 1:
+ rgba = ((0xFF << 24) |
+ (i[0] << 16) |
+ (i[0] << 8) |
+ i[0]);
+ break;
+ default:
+ abort();
+ }
+ XPutPixel (image, x, y, rgba);
+ i += channels;
+ }
+ png_free (png_ptr, rows[y]);
+ }
+
+ png_free (png_ptr, rows);
+ }
+
+ png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
+ if (fp) fclose (fp);
+
+ return image;
+}
+
+
+#else /* No image loaders! */
+
+static XImage *
+make_ximage (Display *dpy, Visual *visual,
+ const char *filename, const unsigned char *image_data,
+ unsigned long data_size)
+{
+ fprintf (stderr, "%s: no image loading support!\n", progname);
+ return 0;
+}
+
+#endif /* no loaders */
+
+
+/* Given a bitmask, returns the position and width of the field.
+ */
+static void
+decode_mask (unsigned long mask, unsigned long *pos_ret,
+ unsigned long *size_ret)
+{
+ int i;
+ for (i = 0; i < 32; i++)
+ if (mask & (1L << i))
+ {
+ int j = 0;
+ *pos_ret = i;
+ for (; i < 32; i++, j++)
+ if (! (mask & (1L << i)))
+ break;
+ *size_ret = j;
+ return;
+ }
+}
+
+
+/* Loads the image to a Pixmap and optional 1-bit mask.
+ */
+static Pixmap
+make_pixmap (Display *dpy, Window window,
+ const char *filename,
+ const unsigned char *image_data, unsigned long data_size,
+ int *width_ret, int *height_ret, Pixmap *mask_ret)
+{
+ XWindowAttributes xgwa;
+ XImage *in, *out, *mask = 0;
+ Pixmap pixmap;
+ XGCValues gcv;
+ GC gc;
+ int x, y;
+
+ unsigned long crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
+ unsigned long srpos=0, sgpos=0, sbpos=0;
+ unsigned long srmsk=0, sgmsk=0, sbmsk=0;
+ unsigned long srsiz=0, sgsiz=0, sbsiz=0;
+
+# ifdef HAVE_JWXYZ
+ // BlackPixel has alpha: 0xFF000000.
+ unsigned long black = BlackPixelOfScreen (DefaultScreenOfDisplay (dpy));
+#else
+ unsigned long black = 0;
+# endif
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+
+ in = make_ximage (dpy, xgwa.visual, filename, image_data, data_size);
+ if (!in) return 0;
+
+ /* Create a new image in the depth and bit-order of the server. */
+ out = XCreateImage (dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 0,
+ in->width, in->height, 8, 0);
+
+ out->bitmap_bit_order = in->bitmap_bit_order;
+ out->byte_order = in->byte_order;
+
+ out->bitmap_bit_order = BitmapBitOrder (dpy);
+ out->byte_order = ImageByteOrder (dpy);
+
+ out->data = (char *) malloc (out->height * out->bytes_per_line);
+ if (!out->data) abort();
+
+ if (mask_ret)
+ {
+ mask = XCreateImage (dpy, xgwa.visual, 1, XYPixmap, 0, 0,
+ in->width, in->height, 8, 0);
+ mask->byte_order = in->byte_order;
+ mask->data = (char *) malloc (mask->height * mask->bytes_per_line);
+ }
+
+ /* Find the server's color masks.
+ */
+ srmsk = out->red_mask;
+ sgmsk = out->green_mask;
+ sbmsk = out->blue_mask;
+
+ if (!(srmsk && sgmsk && sbmsk)) abort(); /* No server color masks? */
+
+ decode_mask (srmsk, &srpos, &srsiz);
+ decode_mask (sgmsk, &sgpos, &sgsiz);
+ decode_mask (sbmsk, &sbpos, &sbsiz);
+
+ /* 'in' is RGBA in client endianness. Convert to what the server wants. */
+ if (bigendian())
+ crpos = 24, cgpos = 16, cbpos = 8, capos = 0;
+ else
+ crpos = 0, cgpos = 8, cbpos = 16, capos = 24;
+
+ for (y = 0; y < in->height; y++)
+ for (x = 0; x < in->width; x++)
+ {
+ unsigned long p = XGetPixel (in, x, y);
+ unsigned char a = (p >> capos) & 0xFF;
+ unsigned char b = (p >> cbpos) & 0xFF;
+ unsigned char g = (p >> cgpos) & 0xFF;
+ unsigned char r = (p >> crpos) & 0xFF;
+ XPutPixel (out, x, y, ((r << srpos) |
+ (g << sgpos) |
+ (b << sbpos) |
+ black));
+ if (mask)
+ XPutPixel (mask, x, y, (a ? 1 : 0));
+ }
+
+ XDestroyImage (in);
+ in = 0;
+
+ pixmap = XCreatePixmap (dpy, window, out->width, out->height, xgwa.depth);
+ gc = XCreateGC (dpy, pixmap, 0, &gcv);
+ XPutImage (dpy, pixmap, gc, out, 0, 0, 0, 0, out->width, out->height);
+ XFreeGC (dpy, gc);
+
+ if (mask)
+ {
+ Pixmap p2 = XCreatePixmap (dpy, window, mask->width, mask->height, 1);
+ gcv.foreground = 1;
+ gcv.background = 0;
+ gc = XCreateGC (dpy, p2, GCForeground|GCBackground, &gcv);
+ XPutImage (dpy, p2, gc, mask, 0, 0, 0, 0, mask->width, mask->height);
+ XFreeGC (dpy, gc);
+ XDestroyImage (mask);
+ mask = 0;
+ *mask_ret = p2;
+ }
+
+ if (width_ret) *width_ret = out->width;
+ if (height_ret) *height_ret = out->height;
+
+ XDestroyImage (out);
+
+ return pixmap;
+}
+
+
+/* Textures are upside down, so invert XImages before returning them.
+ */
+static void
+flip_ximage (XImage *ximage)
+{
+ char *data2, *in, *out;
+ int y;
+
+ if (!ximage) return;
+ data2 = malloc (ximage->bytes_per_line * ximage->height);
+ if (!data2) abort();
+ in = ximage->data;
+ out = data2 + ximage->bytes_per_line * (ximage->height - 1);
+ for (y = 0; y < ximage->height; y++)
+ {
+ memcpy (out, in, ximage->bytes_per_line);
+ in += ximage->bytes_per_line;
+ out -= ximage->bytes_per_line;
+ }
+ free (ximage->data);
+ ximage->data = data2;
+}
+
+
+Pixmap
+image_data_to_pixmap (Display *dpy, Window window,
+ const unsigned char *image_data, unsigned long data_size,
+ int *width_ret, int *height_ret,
+ Pixmap *mask_ret)
+{
+ return make_pixmap (dpy, window, 0, image_data, data_size,
+ width_ret, height_ret, mask_ret);
+}
+
+Pixmap
+file_to_pixmap (Display *dpy, Window window, const char *filename,
+ int *width_ret, int *height_ret,
+ Pixmap *mask_ret)
+{
+ return make_pixmap (dpy, window, filename, 0, 0,
+ width_ret, height_ret, mask_ret);
+}
+
+
+/* This XImage has RGBA data, which is what OpenGL code typically expects.
+ Also it is upside down: the origin is at the bottom left of the image.
+ X11 typically expects 0RGB as it has no notion of alpha, only 1-bit masks.
+ With X11 code, you should probably use the _pixmap routines instead.
+ */
+XImage *
+image_data_to_ximage (Display *dpy, Visual *visual,
+ const unsigned char *image_data,
+ unsigned long data_size)
+{
+ XImage *ximage = make_ximage (dpy, visual, 0, image_data, data_size);
+ flip_ximage (ximage);
+ return ximage;
+}
+
+XImage *
+file_to_ximage (Display *dpy, Visual *visual, const char *filename)
+{
+ XImage *ximage = make_ximage (dpy, visual, filename, 0, 0);
+ flip_ximage (ximage);
+ return ximage;
+}
--- /dev/null
+/* ximage-loader.h --- converts XPM data to Pixmaps.
+ * xscreensaver, Copyright (c) 1998-2018 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef _XIMAGE_LOADER_H_
+#define _XIMAGE_LOADER_H_
+
+extern Pixmap file_to_pixmap (Display *, Window, const char *filename,
+ int *width_ret, int *height_ret,
+ Pixmap *mask_ret);
+
+extern Pixmap image_data_to_pixmap (Display *, Window,
+ const unsigned char *image_data,
+ unsigned long data_size,
+ int *width_ret, int *height_ret,
+ Pixmap *mask_ret);
+
+/* This XImage has RGBA data, which is what OpenGL code typically expects.
+ Also it is upside down: the origin is at the bottom left of the image.
+ X11 typically expects 0RGB as it has no notion of alpha, only 1-bit masks.
+ With X11 code, you should probably use the _pixmap routines instead.
+ */
+extern XImage *image_data_to_ximage (Display *, Visual *,
+ const unsigned char *image_data,
+ unsigned long data_size);
+
+extern XImage *file_to_ximage (Display *, Visual *, const char *filename);
+
+#endif /* _XIMAGE_LOADER_H_ */
--- /dev/null
+/* This file contains compatibility routines for systems without Xmu.
+ * You would be better served by installing Xmu on your machine or
+ * yelling at your vendor to ship it.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef HAVE_XMU
+/*
+ * Copyright 1989 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission. M.I.T. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "xmu.h"
+
+#ifndef NEED_EVENTS
+# define NEED_EVENTS /* to make Xproto.h define xEvent */
+#endif
+#ifndef VMS
+# include <X11/Xproto.h> /* for xEvent (used by Xlibint.h) */
+# include <X11/Xlibint.h> /* for _XExtension */
+#else /* VMS */
+# include <X11/Xlib.h>
+#endif /* VMS */
+#include <X11/Intrinsic.h> /* for XtSpecificationRelease */
+
+/*
+ * XmuPrintDefaultErrorMessage - print a nice error that looks like the usual
+ * message. Returns 1 if the caller should consider exitting else 0.
+ */
+int XmuPrintDefaultErrorMessage (Display *dpy, XErrorEvent *event, FILE *fp)
+{
+ char buffer[BUFSIZ];
+ char mesg[BUFSIZ];
+ char number[32];
+ char *mtype = "XlibMessage";
+ _XExtension *ext = (_XExtension *)NULL;
+ XGetErrorText(dpy, event->error_code, buffer, BUFSIZ);
+ XGetErrorDatabaseText(dpy, mtype, "XError", "X Error", mesg, BUFSIZ);
+ (void) fprintf(fp, "%s: %s\n ", mesg, buffer);
+ XGetErrorDatabaseText(dpy, mtype, "MajorCode", "Request Major code %d",
+ mesg, BUFSIZ);
+ (void) fprintf(fp, mesg, event->request_code);
+ if (event->request_code < 128) {
+ sprintf(number, "%d", event->request_code);
+ XGetErrorDatabaseText(dpy, "XRequest", number, "", buffer, BUFSIZ);
+ } else {
+ /* XXX this is non-portable */
+ for (ext = dpy->ext_procs;
+ ext && (ext->codes.major_opcode != event->request_code);
+ ext = ext->next)
+ ;
+ if (ext)
+ strcpy(buffer, ext->name);
+ else
+ buffer[0] = '\0';
+ }
+ (void) fprintf(fp, " (%s)", buffer);
+ fputs("\n ", fp);
+#if (XtSpecificationRelease >= 5)
+ if (event->request_code >= 128) {
+ XGetErrorDatabaseText(dpy, mtype, "MinorCode", "Request Minor code %d",
+ mesg, BUFSIZ);
+ (void) fprintf(fp, mesg, event->minor_code);
+ if (ext) {
+ sprintf(mesg, "%s.%d", ext->name, event->minor_code);
+ XGetErrorDatabaseText(dpy, "XRequest", mesg, "", buffer, BUFSIZ);
+ (void) fprintf(fp, " (%s)", buffer);
+ }
+ fputs("\n ", fp);
+ }
+ if (event->error_code >= 128) {
+ /* let extensions try to print the values */
+ /* XXX this is non-portable code */
+ for (ext = dpy->ext_procs; ext; ext = ext->next) {
+ if (ext->error_values)
+ (*ext->error_values)(dpy, event, fp);
+ }
+ /* the rest is a fallback, providing a simple default */
+ /* kludge, try to find the extension that caused it */
+ buffer[0] = '\0';
+ for (ext = dpy->ext_procs; ext; ext = ext->next) {
+ if (ext->error_string)
+ (*ext->error_string)(dpy, event->error_code, &ext->codes,
+ buffer, BUFSIZ);
+ if (buffer[0])
+ break;
+ }
+ if (buffer[0])
+ sprintf(buffer, "%s.%d", ext->name,
+ event->error_code - ext->codes.first_error);
+ else
+ strcpy(buffer, "Value");
+ XGetErrorDatabaseText(dpy, mtype, buffer, "", mesg, BUFSIZ);
+ if (*mesg) {
+ (void) fprintf(fp, mesg, event->resourceid);
+ fputs("\n ", fp);
+ }
+ } else if ((event->error_code == BadWindow) ||
+ (event->error_code == BadPixmap) ||
+ (event->error_code == BadCursor) ||
+ (event->error_code == BadFont) ||
+ (event->error_code == BadDrawable) ||
+ (event->error_code == BadColor) ||
+ (event->error_code == BadGC) ||
+ (event->error_code == BadIDChoice) ||
+ (event->error_code == BadValue) ||
+ (event->error_code == BadAtom)) {
+ if (event->error_code == BadValue)
+ XGetErrorDatabaseText(dpy, mtype, "Value", "Value 0x%x",
+ mesg, BUFSIZ);
+ else if (event->error_code == BadAtom)
+ XGetErrorDatabaseText(dpy, mtype, "AtomID", "AtomID 0x%x",
+ mesg, BUFSIZ);
+ else
+ XGetErrorDatabaseText(dpy, mtype, "ResourceID", "ResourceID 0x%x",
+ mesg, BUFSIZ);
+ (void) fprintf(fp, mesg, event->resourceid);
+ fputs("\n ", fp);
+ }
+#elif (XtSpecificationRelease == 4)
+ XGetErrorDatabaseText(dpy, mtype, "MinorCode", "Request Minor code %d",
+ mesg, BUFSIZ);
+ (void) fprintf(fp, mesg, event->minor_code);
+ fputs("\n ", fp);
+ if (ext) {
+ sprintf(mesg, "%s.%d", ext->name, event->minor_code);
+ XGetErrorDatabaseText(dpy, "XRequest", mesg, "", buffer, BUFSIZ);
+ (void) fprintf(fp, " (%s)", buffer);
+ }
+ XGetErrorDatabaseText(dpy, mtype, "ResourceID", "ResourceID 0x%x",
+ mesg, BUFSIZ);
+ (void) fprintf(fp, mesg, event->resourceid);
+ fputs("\n ", fp);
+#else
+ERROR! Unsupported release of X11
+#endif
+ XGetErrorDatabaseText(dpy, mtype, "ErrorSerial", "Error Serial #%d",
+ mesg, BUFSIZ);
+ (void) fprintf(fp, mesg, event->serial);
+ fputs("\n ", fp);
+ XGetErrorDatabaseText(dpy, mtype, "CurrentSerial", "Current Serial #%d",
+ mesg, BUFSIZ);
+ (void) fprintf(fp, mesg, NextRequest(dpy)-1);
+ fputs("\n", fp);
+ if (event->error_code == BadImplementation) return 0;
+ return 1;
+}
+
+#else /* HAVE_XMU */
+
+/* Shut up the stupid "gcc -pedantic" warning */
+int _I_dont_care_that_ISO_C_forbids_an_empty_source_file_ = 1;
+
+#endif /* HAVE_XMU */
--- /dev/null
+/* This file contains compatibility routines for systems without Xmu.
+ * You would be better served by installing Xmu on your machine or
+ * yelling at your vendor to ship it.
+ */
+
+#ifndef __XMU_H__
+#define __XMU_H__
+
+#include <X11/Xlib.h>
+#include <stdio.h>
+
+int XmuPrintDefaultErrorMessage (Display *dpy, XErrorEvent *event, FILE *fp);
+
+#endif /* __XMU_H__ */
--- /dev/null
+/* xscreensaver, Copyright (c) 1993-2017 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* The MIT-SHM (Shared Memory) extension is pretty tricky to use.
+ This file contains the common boiler-plate for creating a shared
+ XImage structure, and for making sure that the shared memory segments
+ get allocated and shut down cleanly.
+
+ This code currently deals only with shared XImages, not with shared Pixmaps.
+ It also doesn't use "completion events", but so far that doesn't seem to
+ be a problem (and I'm not entirely clear on when they would actually be
+ needed, anyway.)
+
+ If you don't have man pages for this extension, see
+ https://www.x.org/releases/current/doc/xextproto/shm.html
+
+ (This document seems not to ever remain available on the web in one place
+ for very long; you can search for it by the title, "MIT-SHM -- The MIT
+ Shared Memory Extension".)
+
+ To monitor the system's shared memory segments, run "ipcs -m".
+ */
+
+#include "utils.h"
+
+/* #define DEBUG */
+
+#include <errno.h> /* for perror() */
+
+#ifdef HAVE_JWXYZ
+# include "jwxyz.h"
+#else
+# include <X11/Xutil.h> /* for XDestroyImage() */
+#endif
+
+#include "xshm.h"
+#include "resources.h" /* for get_string_resource() */
+#include "thread_util.h" /* for thread_malloc() */
+
+#ifdef DEBUG
+# include <X11/Xmu/Error.h>
+#endif
+
+extern char *progname;
+
+
+/* The documentation for the XSHM extension implies that if the server
+ supports XSHM but is not the local machine, the XShm calls will return
+ False; but this turns out not to be the case. Instead, the server
+ throws a BadAccess error. So, we need to catch X errors around all
+ of our XSHM calls, sigh.
+ */
+
+#ifdef HAVE_XSHM_EXTENSION
+
+static Bool shm_got_x_error = False;
+XErrorHandler old_handler = 0;
+static int
+shm_ehandler (Display *dpy, XErrorEvent *error)
+{
+ shm_got_x_error = True;
+
+#ifdef DEBUG
+ fprintf (stderr, "\n%s: ignoring X error from XSHM:\n", progname);
+ XmuPrintDefaultErrorMessage (dpy, error, stderr);
+ fprintf (stderr, "\n");
+#endif
+
+ return 0;
+}
+
+
+#define CATCH_X_ERROR(DPY) do { \
+ XSync((DPY), False); \
+ shm_got_x_error = False; \
+ if (old_handler != shm_ehandler) \
+ old_handler = XSetErrorHandler (shm_ehandler); \
+} while(0)
+
+#define UNCATCH_X_ERROR(DPY) do { \
+ XSync((DPY), False); \
+ if (old_handler) \
+ XSetErrorHandler (old_handler); \
+ old_handler = 0; \
+} while(0)
+
+#endif /* HAVE_XSHM_EXTENSION */
+
+
+static void
+print_error (int err)
+{
+ fprintf(stderr, "%s: %s\n", progname, strerror(err));
+}
+
+static XImage *
+create_fallback (Display *dpy, Visual *visual,
+ unsigned int depth,
+ int format, XShmSegmentInfo *shm_info,
+ unsigned int width, unsigned int height)
+{
+ XImage *image = XCreateImage (dpy, visual, depth, format, 0, NULL,
+ width, height, BitmapPad(dpy), 0);
+ shm_info->shmid = -1;
+
+ if (!image) {
+ print_error (ENOMEM);
+ } else {
+ /* Sometimes the XImage data needs to be aligned, such as for SIMD (SSE2
+ in Fireworkx), or multithreading (AnalogTV).
+ */
+ int error = thread_malloc ((void **)&image->data, dpy,
+ image->height * image->bytes_per_line);
+ if (error) {
+ print_error (error);
+ XDestroyImage (image);
+ image = NULL;
+ } else {
+ memset (image->data, 0, image->height * image->bytes_per_line);
+ }
+ }
+
+ return image;
+}
+
+
+XImage *
+create_xshm_image (Display *dpy, Visual *visual,
+ unsigned int depth,
+ int format, XShmSegmentInfo *shm_info,
+ unsigned int width, unsigned int height)
+{
+#ifndef HAVE_XSHM_EXTENSION
+
+ return create_fallback (dpy, visual, depth, format, shm_info, width, height);
+
+#else /* HAVE_XSHM_EXTENSION */
+
+ Status status;
+ XImage *image = 0;
+ if (!get_boolean_resource(dpy, "useSHM", "Boolean") ||
+ !XShmQueryExtension (dpy)) {
+ return create_fallback (dpy, visual, depth, format, shm_info,
+ width, height);
+ }
+
+ CATCH_X_ERROR(dpy);
+ image = XShmCreateImage(dpy, visual, depth,
+ format, NULL, shm_info, width, height);
+ UNCATCH_X_ERROR(dpy);
+ if (shm_got_x_error)
+ return create_fallback (dpy, visual, depth, format, shm_info,
+ width, height);
+
+#ifdef DEBUG
+ fprintf(stderr, "\n%s: XShmCreateImage(... %d, %d)\n", progname,
+ width, height);
+#endif
+
+ shm_info->shmid = shmget(IPC_PRIVATE,
+ image->bytes_per_line * image->height,
+ IPC_CREAT | 0777);
+#ifdef DEBUG
+ fprintf(stderr, "%s: shmget(IPC_PRIVATE, %d, IPC_CREAT | 0777) ==> %d\n",
+ progname, image->bytes_per_line * image->height, shm_info->shmid);
+#endif
+
+ if (shm_info->shmid == -1)
+ {
+ char buf[1024];
+ sprintf (buf, "%s: shmget failed", progname);
+ perror(buf);
+ XDestroyImage (image);
+ image = 0;
+ XSync(dpy, False);
+ }
+ else
+ {
+ shm_info->readOnly = False;
+ image->data = shm_info->shmaddr = shmat(shm_info->shmid, 0, 0);
+
+#ifdef DEBUG
+ fprintf(stderr, "%s: shmat(%d, 0, 0) ==> %d\n", progname,
+ shm_info->shmid, (int) image->data);
+#endif
+
+ CATCH_X_ERROR(dpy);
+ status = XShmAttach(dpy, shm_info);
+ UNCATCH_X_ERROR(dpy);
+ if (shm_got_x_error)
+ status = False;
+
+ if (!status)
+ {
+ fprintf (stderr, "%s: XShmAttach failed!\n", progname);
+ XDestroyImage (image);
+ XSync(dpy, False);
+ shmdt (shm_info->shmaddr);
+ image = 0;
+ }
+#ifdef DEBUG
+ else
+ fprintf(stderr, "%s: XShmAttach(dpy, shm_info) ==> True\n", progname);
+#endif
+
+ XSync(dpy, False);
+
+ /* Delete the shared segment right now; the segment won't actually
+ go away until both the client and server have deleted it. The
+ server will delete it as soon as the client disconnects, so we
+ should delete our side early in case of abnormal termination.
+ (And note that, in the context of xscreensaver, abnormal
+ termination is the rule rather than the exception, so this would
+ leak like a sieve if we didn't do this...)
+
+ #### Are we leaking anyway? Perhaps because of the window of
+ opportunity between here and the XShmAttach call above, during
+ which we might be killed? Do we need to establish a signal
+ handler for this case?
+ */
+ shmctl (shm_info->shmid, IPC_RMID, 0);
+
+#ifdef DEBUG
+ fprintf(stderr, "%s: shmctl(%d, IPC_RMID, 0)\n\n", progname,
+ shm_info->shmid);
+#endif
+ }
+
+ if (!image) {
+ return create_fallback (dpy, visual, depth, format, shm_info,
+ width, height);
+ }
+
+ return image;
+
+#endif /* HAVE_XSHM_EXTENSION */
+}
+
+
+Bool
+put_xshm_image (Display *dpy, Drawable d, GC gc, XImage *image,
+ int src_x, int src_y, int dest_x, int dest_y,
+ unsigned int width, unsigned int height,
+ XShmSegmentInfo *shm_info)
+{
+#ifdef HAVE_XSHM_EXTENSION
+ assert (shm_info); /* Don't just s/XShmPutImage/put_xshm_image/. */
+ if (shm_info->shmid != -1) {
+ /* XShmPutImage is asynchronous; the contents of the XImage must not be
+ modified until the server has placed the pixels on the screen and the
+ client has received an XShmCompletionEvent. Breaking this rule can cause
+ tearing. That said, put_xshm_image doesn't provide a send_event
+ parameter, so we're always breaking this rule. Not that it seems to
+ matter; everything (so far) looks fine without it.
+
+ ####: Add a send_event parameter. And fake it for XPutImage.
+ */
+ return XShmPutImage (dpy, d, gc, image, src_x, src_y, dest_x, dest_y,
+ width, height, False);
+ }
+#endif /* HAVE_XSHM_EXTENSION */
+
+ return XPutImage (dpy, d, gc, image, src_x, src_y, dest_x, dest_y,
+ width, height);
+}
+
+
+Bool
+get_xshm_image (Display *dpy, Drawable d, XImage *image, int x, int y,
+ unsigned long plane_mask, XShmSegmentInfo *shm_info)
+{
+#ifdef HAVE_XSHM_EXTENSION
+ if (shm_info->shmid != -1) {
+ return XShmGetImage (dpy, d, image, x, y, plane_mask);
+ }
+#endif /* HAVE_XSHM_EXTENSION */
+ return XGetSubImage (dpy, d, x, y, image->width, image->height, plane_mask,
+ image->format, image, 0, 0) != NULL;
+}
+
+
+void
+destroy_xshm_image (Display *dpy, XImage *image, XShmSegmentInfo *shm_info)
+{
+#ifdef HAVE_XSHM_EXTENSION
+ Status status;
+
+ if (shm_info->shmid == -1) {
+#endif /* HAVE_XSHM_EXTENSION */
+
+ /* Don't let XDestroyImage free image->data. */
+ thread_free (image->data);
+ image->data = NULL;
+ XDestroyImage (image);
+ return;
+
+#ifdef HAVE_XSHM_EXTENSION
+ }
+
+ CATCH_X_ERROR(dpy);
+ status = XShmDetach (dpy, shm_info);
+ UNCATCH_X_ERROR(dpy);
+ if (shm_got_x_error)
+ status = False;
+ if (!status)
+ fprintf (stderr, "%s: XShmDetach failed!\n", progname);
+# ifdef DEBUG
+ else
+ fprintf (stderr, "%s: XShmDetach(dpy, shm_info) ==> True\n", progname);
+# endif
+
+ XDestroyImage (image);
+ XSync(dpy, False);
+
+ status = shmdt (shm_info->shmaddr);
+
+ if (status != 0)
+ {
+ char buf[1024];
+ sprintf (buf, "%s: shmdt(0x%lx) failed", progname,
+ (unsigned long) shm_info->shmaddr);
+ perror(buf);
+ }
+# ifdef DEBUG
+ else
+ fprintf (stderr, "%s: shmdt(shm_info->shmaddr) ==> 0\n", progname);
+# endif
+
+ XSync(dpy, False);
+
+#endif /* HAVE_XSHM_EXTENSION */
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998, 2001
+ * by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* The MIT-SHM (Shared Memory) extension is pretty tricky to use.
+ This file contains the common boiler-plate for creating a shared
+ XImage structure, and for making sure that the shared memory segments
+ get allocated and shut down cleanly.
+ */
+
+#ifndef __XSCREENSAVER_XSHM_H__
+#define __XSCREENSAVER_XSHM_H__
+
+#ifdef HAVE_XSHM_EXTENSION
+
+# include <sys/ipc.h>
+# include <sys/shm.h>
+# include <X11/extensions/XShm.h>
+
+#else /* !HAVE_XSHM_EXTENSION */
+
+typedef struct {
+ int shmid; /* Always -1. */
+} dummy_segment_info;
+
+/* In case XShmSegmentInfo */
+#undef XShmSegmentInfo
+#define XShmSegmentInfo dummy_segment_info
+
+#endif
+
+extern XImage *create_xshm_image (Display *dpy, Visual *visual,
+ unsigned int depth,
+ int format, XShmSegmentInfo *shm_info,
+ unsigned int width, unsigned int height);
+extern Bool put_xshm_image (Display *dpy, Drawable d, GC gc, XImage *image,
+ int src_x, int src_y, int dest_x, int dest_y,
+ unsigned int width, unsigned int height,
+ XShmSegmentInfo *shm_info);
+extern Bool get_xshm_image (Display *dpy, Drawable d, XImage *image,
+ int x, int y, unsigned long plane_mask,
+ XShmSegmentInfo *shm_info);
+extern void destroy_xshm_image (Display *dpy, XImage *image,
+ XShmSegmentInfo *shm_info);
+
+#endif /* __XSCREENSAVER_XSHM_H__ */
--- /dev/null
+/* yarandom.c -- Yet Another Random Number Generator.
+ * Copyright (c) 1997-2014 by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* The unportable mess that is rand(), random(), drand48() and friends led me
+ to ask Phil Karlton <karlton@netscape.com> what the Right Thing to Do was.
+ He responded with this. It is non-cryptographically secure, reasonably
+ random (more so than anything that is in any C library), and very fast.
+
+ I don't understand how it works at all, but he says "look at Knuth,
+ Vol. 2 (original edition), page 26, Algorithm A. In this case n=55,
+ k=24 and m=2^32."
+
+ So there you have it.
+
+ ---------------------------
+ Note: xlockmore 4.03a10 uses this very simple RNG:
+
+ if ((seed = seed % 44488 * 48271 - seed / 44488 * 3399) < 0)
+ seed += 2147483647;
+ return seed-1;
+
+ of which it says
+
+ ``Dr. Park's algorithm published in the Oct. '88 ACM "Random Number
+ Generators: Good Ones Are Hard To Find" His version available at
+ ftp://cs.wm.edu/pub/rngs.tar Present form by many authors.''
+
+ Karlton says: ``the usual problem with that kind of RNG turns out to
+ be unexepected short cycles for some word lengths.''
+
+ Karlton's RNG is faster, since it does three adds and two stores, while the
+ xlockmore RNG does two multiplies, two divides, three adds, and one store.
+
+ Compiler optimizations make a big difference here:
+ gcc -O: difference is 1.2x.
+ gcc -O2: difference is 1.4x.
+ gcc -O3: difference is 1.5x.
+ SGI cc -O: difference is 2.4x.
+ SGI cc -O2: difference is 2.4x.
+ SGI cc -O3: difference is 5.1x.
+ Irix 6.2; Indy r5k; SGI cc version 6; gcc version 2.7.2.1.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> /* for getpid() */
+#endif
+#include <sys/time.h> /* for gettimeofday() */
+
+#include "yarandom.h"
+# undef ya_rand_init
+
+
+/* The following 'random' numbers are taken from CRC, 18th Edition, page 622.
+ Each array element was taken from the corresponding line in the table,
+ except that a[0] was from line 100. 8s and 9s in the table were simply
+ skipped. The high order digit was taken mod 4.
+ */
+#define VectorSize 55
+static unsigned int a[VectorSize] = {
+ 035340171546, 010401501101, 022364657325, 024130436022, 002167303062, /* 5 */
+ 037570375137, 037210607110, 016272055420, 023011770546, 017143426366, /* 10 */
+ 014753657433, 021657231332, 023553406142, 004236526362, 010365611275, /* 14 */
+ 007117336710, 011051276551, 002362132524, 001011540233, 012162531646, /* 20 */
+ 007056762337, 006631245521, 014164542224, 032633236305, 023342700176, /* 25 */
+ 002433062234, 015257225043, 026762051606, 000742573230, 005366042132, /* 30 */
+ 012126416411, 000520471171, 000725646277, 020116577576, 025765742604, /* 35 */
+ 007633473735, 015674255275, 017555634041, 006503154145, 021576344247, /* 40 */
+ 014577627653, 002707523333, 034146376720, 030060227734, 013765414060, /* 45 */
+ 036072251540, 007255221037, 024364674123, 006200353166, 010126373326, /* 50 */
+ 015664104320, 016401041535, 016215305520, 033115351014, 017411670323 /* 55 */
+};
+
+static int i1, i2;
+
+unsigned int
+ya_random (void)
+{
+ register int ret = a[i1] + a[i2];
+ a[i1] = ret;
+ if (++i1 >= VectorSize) i1 = 0;
+ if (++i2 >= VectorSize) i2 = 0;
+ return ret;
+}
+
+void
+ya_rand_init(unsigned int seed)
+{
+ int i;
+ if (seed == 0)
+ {
+ struct timeval tp;
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(&tp, &tzp);
+#else
+ gettimeofday(&tp);
+#endif
+ /* Since the multiplications will have a larger effect on the
+ upper bits than the lower bits, after every addition in the
+ seed, perform a bitwise rotate by an odd number, resulting
+ in a better distribution of randomness throughout the bits.
+ -- Brian Carlson, 2010.
+ */
+#define ROT(X,N) (((X)<<(N)) | ((X)>>((sizeof(unsigned int)*8)-(N))))
+ seed = (999U * (unsigned int) tp.tv_sec);
+ seed = ROT (seed, 11);
+ seed += (1001 * (unsigned int) tp.tv_usec);
+ seed = ROT (seed, 7);
+ seed += (1003 * (unsigned int) getpid());
+ seed = ROT (seed, 13);
+ }
+
+ a[0] += seed;
+ for (i = 1; i < VectorSize; i++)
+ {
+ seed = seed*999;
+ seed = ROT (seed, 9);
+ seed += a[i-1]*1001;
+ seed = ROT (seed, 15);
+ a[i] += seed;
+ }
+
+ i1 = a[0] % VectorSize;
+ i2 = (i1 + 24) % VectorSize;
+}
--- /dev/null
+/* xscreensaver, Copyright (c) 1997, 1998, 2003 by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __YARANDOM_H__
+#define __YARANDOM_H__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#undef random
+#undef rand
+#undef drand48
+#undef srandom
+#undef srand
+#undef frand
+#undef sranddev
+#undef srandomdev
+#undef arc4random
+#undef arc4random_addrandom
+#undef arc4random_buf
+#undef arc4random_stir
+#undef arc4random_uniform
+#undef erand48
+#undef jrand48
+#undef lcong48
+#undef lrand48
+#undef mrand48
+#undef nrand48
+#undef seed48
+#undef srand48
+#undef rand_r
+#undef RAND_MAX
+
+#ifdef VMS
+# include "vms-gtod.h"
+#endif
+
+extern unsigned int ya_random (void);
+extern void ya_rand_init (unsigned int);
+
+#define random() ya_random()
+#define RAND_MAX 0xFFFFFFFF
+
+/*#define srandom(i) ya_rand_init(0)*/
+
+/* Define these away to keep people from using the wrong APIs in xscreensaver.
+ */
+#define rand __ERROR_use_random_not_rand_in_xscreensaver__
+#define drand48 __ERROR_use_frand_not_drand48_in_xscreensaver__
+#define srandom __ERROR_do_not_call_srandom_in_xscreensaver__
+#define srand __ERROR_do_not_call_srand_in_xscreensaver__
+#define sranddev __ERROR_do_not_call_sranddev_in_xscreensaver__
+#define ya_rand_init __ERROR_do_not_call_ya_rand_init_in_xscreensaver__
+#define srandomdev __ERROR_do_not_call_srandomdev_in_xscreensaver__
+#define arc4random __ERROR_do_not_call_arc4random_in_xscreensaver__
+#define arc4random_addrandom __ERROR_do_not_call_arc4random_in_xscreensaver__
+#define arc4random_buf __ERROR_do_not_call_arc4random_in_xscreensaver__
+#define arc4random_stir __ERROR_do_not_call_arc4random_in_xscreensaver__
+#define arc4random_uniform __ERROR_do_not_call_arc4random_in_xscreensaver__
+#define erand48 __ERROR_do_not_call_erand48_in_xscreensaver__
+#define jrand48 __ERROR_do_not_call_jrand48_in_xscreensaver__
+#define lcong48 __ERROR_do_not_call_lcong48_in_xscreensaver__
+#define lrand48 __ERROR_do_not_call_lrand48_in_xscreensaver__
+#define mrand48 __ERROR_do_not_call_mrand48_in_xscreensaver__
+#define nrand48 __ERROR_do_not_call_nrand48_in_xscreensaver__
+#define seed48 __ERROR_do_not_call_seed48_in_xscreensaver__
+#define srand48 __ERROR_do_not_call_srand48_in_xscreensaver__
+#define rand_r __ERROR_do_not_call_rand_r_in_xscreensaver__
+
+
+#if defined (__GNUC__) && (__GNUC__ >= 2)
+ /* Implement frand using GCC's statement-expression extension. */
+
+# define frand(f) \
+ __extension__ \
+ ({ double tmp = ((((double) random()) * ((double) (f))) / \
+ ((double) ((unsigned int)~0))); \
+ tmp < 0 ? (-tmp) : tmp; })
+
+#else /* not GCC2 - implement frand using a global variable.*/
+
+static double _frand_tmp_;
+# define frand(f) \
+ (_frand_tmp_ = ((((double) random()) * ((double) (f))) / \
+ ((double) ((unsigned int)~0))), \
+ _frand_tmp_ < 0 ? (-_frand_tmp_) : _frand_tmp_)
+
+#endif /* not GCC2 */
+
+/* Compatibility with the xlockmore RNG API
+ (note that the xlockmore hacks never expect negative numbers.)
+ */
+#define LRAND() ((long) (random() & 0x7fffffff))
+
+/* The first NRAND(n) is much faster, when uint64_t is available to allow it.
+ * Especially on ARM and other processors without built-in division.
+ *
+ * n must be greater than zero.
+ *
+ * The division by RAND_MAX+1 should optimize down to a bit shift.
+ *
+ * Although the result here is never negative, this needs to return signed
+ * integers: A binary operator in C requires operands have the same type, and
+ * if one side is signed and the other is unsigned, but they're both the same
+ * size, then the signed operand is cast to unsigned, and the result is
+ * similarly unsigned. This can potentially cause problems when idioms like
+ * "NRAND(n) * RANDSIGN()" (where RANDSIGN() returns either -1 or 1) is used
+ * to get random numbers from the range (-n,n).
+ */
+#ifdef HAVE_INTTYPES_H
+# define NRAND(n) ((int32_t) ((uint64_t) random() * (uint32_t) (n) / \
+ ((uint64_t) RAND_MAX + 1)))
+#else
+# define NRAND(n) ((int) (LRAND() % (n)))
+#endif
+
+#define MAXRAND (2147483648.0) /* unsigned 1<<31 as a float */
+#define SRAND(n) /* already seeded by screenhack.c */
+
+#endif /* __YARANDOM_H__ */