--- /dev/null
+/* xflame, Copyright (c) 1996-1999 Carsten Haitzler <raster@redhat.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.
+ */
+
+/* Version history as near as I (jwz) can piece together:
+
+ * Carsten Haitzler <raster@redhat.com> wrote the first version in 1996.
+
+ * Rahul Jain <rahul@rice.edu> added support for TrueColor displays.
+
+ * Someone did a rough port of it to use the xscreensaver utility routines
+ instead of creating its own window by hand.
+
+ * Someone (probably Raster) came up with a subsequent version that had
+ a Red Hat logo hardcoded into it.
+
+ * Daniel Zahn <stumpy@religions.com> found that version in 1998, and
+ hacked it to be able to load a different logo from a PGM (P5) file,
+ with a single hardcoded pathname.
+
+ * Jamie Zawinski <jwz@jwz.org> found several versions of xflame in
+ March 1999, and pieced them together. Changes:
+
+ - Correct and fault-tolerant use of the Shared Memory extension;
+ previous versions of xflame did not work when $DISPLAY was remote.
+
+ - Replaced PGM-reading code with code that can read arbitrary XBM
+ and XPM files (color ones will be converted to grayscale.)
+
+ - Command-line options all around -- no hardcoded pathnames or
+ behavioral constants.
+
+ - General cleanup and portability tweaks.
+ */
+
+
+/* portions by Daniel Zahn <stumpy@religions.com> */
+
+
+#include "screenhack.h"
+#include <X11/Xutil.h>
+#include <limits.h>
+
+#ifdef HAVE_XSHM_EXTENSION
+# include "xshm.h"
+#endif /* HAVE_XSHM_EXTENSION */
+
+#ifdef HAVE_XPM
+# include <X11/xpm.h>
+# ifndef PIXEL_ALREADY_TYPEDEFED
+# define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
+# endif
+#endif
+
+#ifdef HAVE_XMU
+# ifndef VMS
+# include <X11/Xmu/Drawing.h>
+# else /* VMS */
+# include <Xmu/Drawing.h>
+# endif /* VMS */
+#endif /* HAVE_XMU */
+
+#define MAX 255
+
+static Display *display;
+static Window window;
+static int depth;
+static int width;
+static int height;
+static Colormap colormap;
+static Visual *visual;
+static Bool shared;
+static XImage *xim;
+#ifdef HAVE_XSHM_EXTENSION
+static XShmSegmentInfo shminfo;
+#endif /* HAVE_XSHM_EXTENSION */
+static GC gc;
+static int ctab[256];
+
+static unsigned char *flame;
+static unsigned char *theim;
+static int fwidth;
+static int fheight;
+static int top;
+static int hspread;
+static int vspread;
+static int residual;
+
+static int ihspread;
+static int ivspread;
+static int iresidual;
+static int variance;
+static int vartrend;
+
+static void
+GetXInfo(Display *disp, Window win)
+{
+ XWindowAttributes xwa;
+
+ XGetWindowAttributes(disp,win,&xwa);
+
+ window = win;
+ display = disp;
+ colormap = xwa.colormap;
+ depth = xwa.depth;
+ visual = xwa.visual;
+ width = xwa.width;
+ height = xwa.height;
+
+ if (width%2)
+ width++;
+ if (height%2)
+ height++;
+}
+
+static void
+MakeImage(void)
+{
+ XGCValues gcv;
+
+ shared = True;
+ xim = create_xshm_image (display, visual, depth, ZPixmap, NULL,
+ &shminfo, width, height);
+ if (!xim)
+ {
+ shared = False;
+ xim = XCreateImage (display, visual, depth, ZPixmap, 0, NULL,
+ width, height, 32, 0);
+ if (xim)
+ xim->data = (char *) calloc(xim->height, xim->bytes_per_line);
+ if (!xim || !xim->data)
+ {
+ fprintf(stderr,"%s: out of memory.\n", progname);
+ exit(1);
+ }
+ }
+
+ gc = XCreateGC(display,window,0,&gcv);
+ if (!gc) exit (1);
+}
+
+
+static void
+InitColors(void)
+{
+ int i = 0, j = 0;
+ for (i = 0; i < 256 * 2; i += 2)
+ {
+ XColor xcl;
+ int r = (i - 0) * 3;
+ int g = (i - 80) * 3;
+ int b = (i - 160) * 3;
+
+ if (r < 0) r = 0;
+ if (r > 255) r = 255;
+ if (g < 0) g = 0;
+ if (g > 255) g = 255;
+ if (b < 0) b = 0;
+ if (b > 255) b = 255;
+
+ xcl.red = (unsigned short)((r << 8) | r);
+ xcl.green = (unsigned short)((g << 8) | g);
+ xcl.blue = (unsigned short)((b << 8) | b);
+ xcl.flags = DoRed | DoGreen | DoBlue;
+
+ XAllocColor(display,colormap,&xcl);
+
+ ctab[j++] = (int)xcl.pixel;
+ }
+}
+
+
+static void
+DisplayImage(void)
+{
+ if (shared)
+ XShmPutImage(display, window, gc, xim, 0,(top - 1) << 1, 0,
+ (top - 1) << 1, width, height - ((top - 1) << 1), False);
+ else
+ XPutImage(display, window, gc, xim, 0, (top - 1) << 1, 0,
+ (top - 1) << 1, width, height - ((top - 1) << 1));
+}
+
+
+static void
+InitFlame(void)
+{
+ fwidth = width / 2;
+ fheight = height / 2;
+ flame = (unsigned char *) malloc((fwidth + 2) * (fheight + 2)
+ * sizeof(unsigned char));
+
+ if (!flame)
+ {
+ fprintf(stderr,"%s: out of memory\n", progname);
+ exit(1);
+ }
+
+ top = 1;
+ ihspread = get_integer_resource("hspread", "Integer");
+ ivspread = get_integer_resource("vspread", "Integer");
+ iresidual = get_integer_resource("residual", "Integer");
+ variance = get_integer_resource("variance", "Integer");
+ vartrend = get_integer_resource("vartrend", "Integer");
+
+# define THROTTLE(VAR,NAME) \
+ if (VAR < 0 || VAR > 255) { \
+ fprintf(stderr, "%s: %s must be in the range 0-255 (not %d).\n", \
+ progname, NAME, VAR); \
+ exit(1); }
+ THROTTLE (ihspread, "hspread");
+ THROTTLE (ivspread, "vspread");
+ THROTTLE (iresidual,"residual");
+ THROTTLE (variance, "variance");
+ THROTTLE (vartrend, "vartrend");
+# undef THROTTLE
+
+
+
+ hspread = ihspread;
+ vspread = ivspread;
+ residual = iresidual;
+}
+
+
+static void
+Flame2Image16(void)
+{
+ int x,y;
+ unsigned short *ptr;
+ unsigned char *ptr1;
+ int v1,v2,v3,v4;
+
+ ptr = (unsigned short *)xim->data;
+ ptr += (top << 1) * width;
+ ptr1 = flame + 1 + (top * (fwidth + 2));
+
+ for(y = top; y < fheight; y++)
+ {
+ for( x = 0; x < fwidth; x++)
+ {
+ v1 = (int)*ptr1;
+ v2 = (int)*(ptr1 + 1);
+ v3 = (int)*(ptr1 + fwidth + 2);
+ v4 = (int)*(ptr1 + fwidth + 2 + 1);
+ ptr1++;
+ *ptr++ = (unsigned short)ctab[v1];
+ *ptr = (unsigned short)ctab[(v1 + v2) >> 1];
+ ptr += width - 1;
+ *ptr++ = (unsigned short)ctab[(v1 + v3) >> 1];
+ *ptr = (unsigned short)ctab[(v1 + v4) >> 1];
+ ptr -= width - 1;
+ }
+ ptr += width;
+ ptr1 += 2;
+ }
+}
+
+static void
+Flame2Image32(void)
+{
+ int x,y;
+ unsigned int *ptr;
+ unsigned char *ptr1;
+ int v1,v2,v3,v4;
+
+ ptr = (unsigned int *)xim->data;
+ ptr += (top << 1) * width;
+ ptr1 = flame + 1 + (top * (fwidth + 2));
+
+ for( y = top; y < fheight; y++)
+ {
+ for( x = 0; x < fwidth; x++)
+ {
+ v1 = (int)*ptr1;
+ v2 = (int)*(ptr1 + 1);
+ v3 = (int)*(ptr1 + fwidth + 2);
+ v4 = (int)*(ptr1 + fwidth + 2 + 1);
+ ptr1++;
+ *ptr++ = (unsigned int)ctab[v1];
+ *ptr = (unsigned int)ctab[(v1 + v2) >> 1];
+ ptr += width - 1;
+ *ptr++ = (unsigned int)ctab[(v1 + v3) >> 1];
+ *ptr = (unsigned int)ctab[(v1 + v4) >> 1];
+ ptr -= width - 1;
+ }
+ ptr += width;
+ ptr1 += 2;
+ }
+}
+
+static void
+Flame2Image8(void)
+{
+ int x,y;
+ unsigned char *ptr;
+ unsigned char *ptr1;
+ int v1,v2,v3,v4;
+
+ ptr = (unsigned char *)xim->data;
+ ptr += (top << 1) * width;
+ ptr1 = flame + 1 + (top * (fwidth + 2));
+
+ for(y=top;y<fheight;y++)
+ {
+ for(x=0;x<fwidth;x++)
+ {
+ v1 = (int)*ptr1;
+ v2 = (int)*(ptr1 + 1);
+ v3 = (int)*(ptr1 + fwidth + 2);
+ v4 = (int)*(ptr1 + fwidth + 2 + 1);
+ ptr1++;
+ *ptr++ = (unsigned char)ctab[v1];
+ *ptr = (unsigned char)ctab[(v1 + v2) >> 1];
+ ptr += width - 1;
+ *ptr++ = (unsigned char)ctab[(v1 + v3) >> 1];
+ *ptr = (unsigned char)ctab[(v1 + v4) >> 1];
+ ptr -= width - 1;
+ }
+ ptr += width;
+ ptr1 += 2;
+ }
+}
+
+static void
+Flame2Image1234567(void)
+{
+ int x,y;
+ unsigned char *ptr1;
+ int v1,v2,v3,v4;
+
+ ptr1 = flame + 1 + (top * (fwidth + 2));
+
+ for( y = top; y < fheight; y++)
+ {
+ for( x = 0; x < fwidth; x++)
+ {
+ v1 = (int)*ptr1;
+ v2 = (int)*(ptr1 + 1);
+ v3 = (int)*(ptr1 + fwidth + 2);
+ v4 = (int)*(ptr1 + fwidth + 2 + 1);
+ ptr1++;
+ XPutPixel(xim,(x << 1), (y << 1), ctab[v1]);
+ XPutPixel(xim,(x << 1) + 1,(y << 1), ctab[(v1 + v2) >> 1]);
+ XPutPixel(xim,(x << 1), (y << 1) + 1,ctab[(v1 + v3) >> 1]);
+ XPutPixel(xim,(x << 1) + 1,(y << 1) + 1,ctab[(v1 + v4) >> 1]);
+ }
+ }
+}
+
+static void
+Flame2Image(void)
+{
+ if (depth >= 24) Flame2Image32();
+ else if (depth == 16) Flame2Image16();
+ else if (depth == 8) Flame2Image8();
+ else if (depth == 15) Flame2Image16();
+ else if (depth < 8) Flame2Image1234567();
+ else if (depth == 12) Flame2Image16();
+}
+
+static void
+FlameActive(void)
+{
+ int x,v1;
+ unsigned char *ptr1;
+
+ ptr1 = flame + ((fheight + 1) * (fwidth + 2));
+
+ for (x = 0; x < fwidth + 2; x++)
+ {
+ v1 = *ptr1;
+ v1 += ((random() % variance) - vartrend);
+ *ptr1++ = v1 % 255;
+ }
+
+ v1= (random() % 100);
+ if (v1 == 10)
+ residual += (random()%10);
+ else if (v1 == 20)
+ hspread += (random()%15);
+ else if (v1 == 30)
+ vspread += (random()%20);
+
+ residual = ((iresidual* 10) + (residual *90)) / 100;
+ hspread = ((ihspread * 10) + (hspread *90)) / 100;
+ vspread = ((ivspread * 10) + (vspread *90)) / 100;
+}
+
+
+static void
+FlameAdvance(void)
+{
+ int x,y;
+ unsigned char *ptr2;
+ int newtop = top;
+
+ for (y = fheight + 1; y >= top; y--)
+ {
+ int used = 0;
+ unsigned char *ptr1 = flame + 1 + (y * (fwidth + 2));
+ for (x = 0; x < fwidth; x++)
+ {
+ int v1 = (int)*ptr1;
+ int v2, v3;
+ if (v1 > 0)
+ {
+ used = 1;
+ ptr2 = ptr1 - fwidth - 2;
+ v3 = (v1 * vspread) >> 8;
+ v2 = (int)*(ptr2);
+ v2 += v3;
+ if (v2 > MAX)
+ v2 = MAX;
+
+ *(ptr2) = (unsigned char)v2;
+ v3 = (v1 * hspread) >> 8;
+ v2 = (int)*(ptr2 + 1);
+ v2 += v3;
+ if (v2 > MAX)
+ v2 = MAX;
+
+ *(ptr2 + 1) = (unsigned char)v2;
+ v2 = (int)*(ptr2 - 1);
+ v2 += v3;
+ if (v2 > MAX)
+ v2 = MAX;
+
+ *(ptr2 - 1) = (unsigned char)v2;
+
+ if (y < fheight + 1)
+ {
+ v1 = (v1 * residual) >> 8;
+ *ptr1 = (unsigned char)v1;
+ }
+ }
+ ptr1++;
+ if (used)
+ newtop = y - 1;
+ }
+ }
+
+ top = newtop - 1;
+
+ if (top < 1)
+ top = 1;
+}
+
+
+static void
+FlameFill(int val)
+{
+ int x, y;
+ for (y = 0; y < fheight + 1; y++)
+ {
+ unsigned char *ptr1 = flame + 1 + (y * (fwidth + 2));
+ for (x = 0; x < fwidth; x++)
+ {
+ *ptr1 = val;
+ ptr1++;
+ }
+ }
+}
+
+
+static void
+FlamePasteData(unsigned char *d, int xx, int yy, int w, int h)
+{
+ unsigned char *ptr1,*ptr2;
+ ptr2 = d;
+
+ if (xx < 0) xx = 0;
+ if (yy < 0) yy = 0;
+
+ if ((xx >= 0) &&
+ (yy >= 0) &&
+ (xx + w <= fwidth) &&
+ (yy + h <= fheight))
+ {
+ int x, y;
+ for (y = 0; y < h; y++)
+ {
+ ptr1 = flame + 1 + xx + ((yy + y) * (fwidth + 2));
+ for (x = 0; x < w; x++)
+ {
+ if (*ptr2 / 24)
+ *ptr1 += random() % (*ptr2 / 24);
+
+ ptr1++;
+ ptr2++;
+ }
+ }
+ }
+ else
+ {
+ static Bool warned = False;
+ if (!warned)
+ {
+ fprintf (stderr, "%s: window is %dx%d; image must be "
+ "smaller than %dx%d (not %dx%d).\n",
+ progname, width, height, fwidth, fheight, w, h);
+ warned = True;
+ }
+ }
+}
+
+
+static unsigned char *
+loadBitmap(int *w, int *h)
+{
+ char *bitmap_name = get_string_resource ("bitmap", "Bitmap");
+
+ if (bitmap_name &&
+ *bitmap_name &&
+ !!strcmp(bitmap_name, "none"))
+ {
+ XpmInfo xpm_info = { 0, };
+ XpmImage xpm_image = { 0, };
+
+ int result = XpmReadFileToXpmImage (bitmap_name, &xpm_image, &xpm_info);
+ if (result == XpmSuccess)
+ {
+ int x, y;
+ unsigned char *result, *o;
+ unsigned char *grays;
+ XWindowAttributes xgwa;
+
+ *w = xpm_image.width;
+ *h = xpm_image.height;
+ result = (unsigned char *) malloc ((*w) * (*h));
+ if (!result)
+ {
+ fprintf(stderr, "%s: out of memory loading %s\n",
+ progname, bitmap_name);
+ exit (1);
+ }
+
+ XGetWindowAttributes (display, window, &xgwa);
+
+ grays = (unsigned char *) calloc (xpm_image.ncolors+1, 1);
+ for (x = 0; x < xpm_image.ncolors; x++)
+ {
+ XColor xc;
+ XpmColor *xpmc = &xpm_image.colorTable[x];
+ char *cstring = 0;
+ if (xpmc->g_color && *xpmc->g_color)
+ cstring = xpmc->g_color;
+ else if (xpmc->g4_color && *xpmc->g4_color)
+ cstring = xpmc->g4_color;
+ else if (xpmc->c_color && *xpmc->c_color)
+ cstring = xpmc->c_color;
+ else
+ cstring = xpmc->m_color;
+
+ memset (&xc, 0, sizeof(xc));
+ if (!cstring ||
+ !*cstring ||
+ !XParseColor (display, xgwa.colormap, cstring, &xc))
+ grays[x] = 0;
+ else
+ grays[x] = (int) (((xc.red * 0.299) +
+ (xc.green * 0.587) +
+ (xc.blue * 0.114))
+ / 255);
+ }
+
+ o = result;
+ for (y = 0; y < *h; y++)
+ for (x = 0; x < *w; x++)
+ {
+ int color = xpm_image.data[(y * (*w)) + x];
+ if (color < 0 || color > xpm_image.ncolors) abort();
+ *o++ = grays[color];
+ }
+ return result;
+ }
+ else /* failed to read XPM -- fall through and try XBM */
+ {
+#ifdef HAVE_XMU
+ XImage *ximage = 0;
+ int width, height, xh, yh;
+ int x, y;
+ unsigned char *result, *o;
+ Pixmap bitmap =
+ XmuLocateBitmapFile (DefaultScreenOfDisplay (display),
+ bitmap_name, 0, 0, &width, &height, &xh, &yh);
+ if (!bitmap)
+ {
+ fprintf(stderr, "%s: unable to load bitmap file %s\n",
+ progname, bitmap_name);
+ exit (1);
+ }
+ ximage = XGetImage (display, bitmap, 0, 0, width, height,
+ 1L, XYPixmap);
+ XFreePixmap (display, bitmap);
+
+ if (ximage->depth != 1) abort();
+
+ *w = ximage->width;
+ *h = ximage->height;
+ result = (unsigned char *) malloc ((*w) * (*h));
+ if (!result)
+ {
+ fprintf(stderr, "%s: out of memory loading %s\n",
+ progname, bitmap_name);
+ exit (1);
+ }
+
+ o = result;
+ for (y = 0; y < *h; y++)
+ for (x = 0; x < *w; x++)
+ *o++ = (XGetPixel(ximage, x, y) ? 255 : 0);
+
+ return result;
+
+#else /* !XMU */
+ fprintf (stderr,
+ "%s: your vendor doesn't ship the standard Xmu library.\n",
+ progname);
+ fprintf (stderr, "\tWe can't load XBM files without it.\n");
+ exit (1);
+#endif /* !XMU */
+ }
+ }
+
+ *w = 0;
+ *h = 0;
+ return 0;
+
+}
+
+
+\f
+char *progclass = "XFlame";
+
+char *defaults [] = {
+ ".background: black",
+ ".foreground: red",
+ "*bitmap: none",
+ "*bitmapBaseline: 20",
+ "*delay: 10000",
+ "*hspread: 30",
+ "*vspread: 97",
+ "*residual: 99",
+ "*variance: 50",
+ "*vartrend: 20",
+
+#ifdef HAVE_XSHM_EXTENSION
+ "*useSHM: False", /* xshm turns out not to help. */
+#endif /* HAVE_XSHM_EXTENSION */
+ 0
+};
+
+XrmOptionDescRec options [] = {
+ { "-delay", ".delay", XrmoptionSepArg, 0 },
+ { "-bitmap", ".bitmap", XrmoptionSepArg, 0 },
+ { "-baseline", ".bitmapBaseline", XrmoptionSepArg, 0 },
+ { "-hspread", ".hspread", XrmoptionSepArg, 0 },
+ { "-vspread", ".vspread", XrmoptionSepArg, 0 },
+ { "-residual", ".residual", XrmoptionSepArg, 0 },
+ { "-variance", ".variance", XrmoptionSepArg, 0 },
+ { "-vartrend", ".vartrend", XrmoptionSepArg, 0 },
+#ifdef HAVE_XSHM_EXTENSION
+ { "-shm", ".useSHM", XrmoptionNoArg, "True" },
+ { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
+#endif /* HAVE_XSHM_EXTENSION */
+ { 0, 0, 0, 0 }
+};
+
+void
+screenhack (Display *disp, Window win)
+{
+ int theimx = 0, theimy = 0;
+ int baseline = get_integer_resource ("bitmapBaseline", "Integer");
+ int delay = get_integer_resource ("delay", "Integer");
+ xim = NULL;
+ top = 1;
+ flame = NULL;
+
+ GetXInfo(disp,win);
+ InitColors();
+ theim = loadBitmap(&theimx, &theimy);
+
+ /* utils/xshm.c doesn't provide a way to free the shared-memory image, which
+ makes it hard for us to react to window resizing. So, punt for now. The
+ size of the window at startup is the size it will stay.
+ */
+ GetXInfo(disp,win);
+
+ MakeImage();
+ InitFlame();
+ FlameFill(0);
+
+ while (1)
+ {
+ FlameActive();
+
+ if (theim)
+ FlamePasteData(theim, (fwidth - theimx) / 2,
+ fheight - theimy - baseline, theimx, theimy);
+
+ FlameAdvance();
+ Flame2Image();
+ DisplayImage();
+
+ XSync(display,False);
+ screenhack_handle_events(display);
+ if (delay)
+ usleep (delay);
+ }
+}