http://www.jwz.org/xscreensaver/xscreensaver-5.12.tar.gz
[xscreensaver] / hacks / flame.c
index 3f98e3e3036efbf670f287091a1776a9bbb611ef..01f16ababa6c235a7e66b03c2781d0992570d9f6 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1993, 1995 Jamie Zawinski <jwz@netscape.com>
+/* xscreensaver, Copyright (c) 1993-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
@@ -10,7 +10,7 @@
  */
 
 /* This file was ported from xlock for use in xscreensaver (and standalone)
- * by jwz on 18-Oct-93.  Original copyright reads:
+ * by jwz on 18-Oct-93.  (And again, 11-May-97.)  Original copyright reads:
  *
  *   static char sccsid[] = "@(#)flame.c 1.4 91/09/27 XLOCK";
  *
  *                    Mountain View, CA  94043
  *
  * Revision History:
+ * 01-Jun-95: This should look more like the original with some updates by
+ *            Scott Draves.
  * 27-Jun-91: vary number of functions used.
  * 24-Jun-91: fixed portability problem with integer mod (%).
- * 06-Jun-91: Written. (received from Scott Graves, spot@cs.cmu.edu).
+ * 06-Jun-91: Written. (received from Scott Draves, spot@cs.cmu.edu).
  */
 
+#include <math.h>
 #include "screenhack.h"
 
+#include <signal.h>            /* so we can ignore SIGFPE */
+
 #define POINT_BUFFER_SIZE 10
 #define MAXLEV 4
+#define MAXKINDS  10
+
+struct state {
+  Display *dpy;
+  Window window;
+
+  double f[2][3][MAXLEV];      /* three non-homogeneous transforms */
+  int max_total;
+  int max_levels;
+  int max_points;
+  int cur_level;
+  int variation;
+  int snum;
+  int anum;
+  int num_points;
+  int total_points;
+  int pixcol;
+  int ncolors;
+  XColor *colors;
+  XPoint points [POINT_BUFFER_SIZE];
+  GC gc;
+
+  int delay, delay2;
+  int width, height;
+
+  short lasthalf;
+
+  int flame_alt;
+  int do_reset;
+};
 
-static double f[2][3][MAXLEV]; /* three non-homogeneous transforms */
-static int max_total;
-static int max_levels;
-static int max_points;
-static int cur_level;
-static int snum;
-static int anum;
-static int num_points;
-static int total_points;
-static int pixcol;
-static int npixels;
-static unsigned long *pixels;
-static XPoint points [POINT_BUFFER_SIZE];
-static GC gc;
-
-static int delay, delay2;
-static int width, height;
 
 static short
-halfrandom (mv)
-     int mv;
+halfrandom (struct state *st, int mv)
 {
-  static short lasthalf = 0;
   unsigned long r;
 
-  if (lasthalf)
+  if (st->lasthalf)
     {
-      r = lasthalf;
-      lasthalf = 0;
+      r = st->lasthalf;
+      st->lasthalf = 0;
     }
   else
     {
       r = random ();
-      lasthalf = r >> 16;
+      st->lasthalf = r >> 16;
     }
   return (r % mv);
 }
 
-
-static void
-init_flame (dpy, window)
-     Display *dpy;
-     Window window;
+static void *
+flame_init (Display *dpy, Window window)
 {
+  struct state *st = (struct state *) calloc (1, sizeof(*st));
   XGCValues gcv;
   XWindowAttributes xgwa;
   Colormap cmap;
-  XGetWindowAttributes (dpy, window, &xgwa);
-  width = xgwa.width;
-  height = xgwa.height;
+
+  st->dpy = dpy;
+  st->window = window;
+
+#if defined(SIGFPE) && defined(SIG_IGN)
+  /* No doubt a better fix would be to track down where the NaN is coming
+     from, and code around that; but this should do.  Apparently most systems
+     (Linux, Solaris, Irix, ...) ignore FPE by default -- but FreeBSD dumps
+     core by default. */
+  signal (SIGFPE, SIG_IGN);
+#endif
+
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
+  st->width = xgwa.width;
+  st->height = xgwa.height;
   cmap = xgwa.colormap;
 
-  max_points = get_integer_resource ("iterations", "Integer");
-  if (max_points <= 0) max_points = 100;
+  st->max_points = get_integer_resource (st->dpy, "iterations", "Integer");
+  if (st->max_points <= 0) st->max_points = 100;
 
-  max_levels = max_points;
+  st->max_levels = st->max_points;
 
-  max_total = get_integer_resource ("points", "Integer");
-  if (max_total <= 0) max_total = 10000;
+  st->max_total = get_integer_resource (st->dpy, "points", "Integer");
+  if (st->max_total <= 0) st->max_total = 10000;
 
-  delay = get_integer_resource ("delay", "Integer");
-  if (delay < 0) delay = 0;
-  delay2 = get_integer_resource ("delay2", "Integer");
-  if (delay2 < 0) delay2 = 0;
+  st->delay = get_integer_resource (st->dpy, "delay", "Integer");
+  if (st->delay < 0) st->delay = 0;
+  st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
+  if (st->delay2 < 0) st->delay2 = 0;
+
+  st->variation = random() % MAXKINDS;
 
   if (mono_p)
-    npixels = 0;
+    st->ncolors = 0;
   else
     {
-      int i = get_integer_resource ("ncolors", "Integer");
-      double saturation = 1.0;
-      double value = 1.0;
-      XColor color;
-      if (i <= 0) i = 128;
-
-      pixels = (unsigned long *) malloc ((i+1) * sizeof (*pixels));
-      for (npixels = 0; npixels < i; npixels++)
-       {
-         hsv_to_rgb ((360*npixels)/i, saturation, value,
-                     &color.red, &color.green, &color.blue);
-         if (! XAllocColor (dpy, cmap, &color))
-           break;
-         pixels [npixels] = color.pixel;
-       }
+      st->ncolors = get_integer_resource (st->dpy, "colors", "Integer");
+      if (st->ncolors <= 0) st->ncolors = 128;
+      st->colors = (XColor *) malloc ((st->ncolors+1) * sizeof (*st->colors));
+      make_smooth_colormap (st->dpy, xgwa.visual, xgwa.colormap, st->colors, &st->ncolors,
+                           True, 0, True);
+      if (st->ncolors <= 2)
+       mono_p = True, st->ncolors = 0;
     }
 
-  gcv.foreground = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
-  gcv.background = get_pixel_resource ("background", "Background", dpy, cmap);
+  gcv.foreground = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
+  gcv.background = get_pixel_resource (st->dpy, cmap, "background", "Background");
 
   if (! mono_p)
     {
-      pixcol = halfrandom (npixels);
-      gcv.foreground = (pixels [pixcol]);
+      st->pixcol = halfrandom (st, st->ncolors);
+      gcv.foreground = (st->colors [st->pixcol].pixel);
     }
 
-  gc = XCreateGC (dpy, window, GCForeground | GCBackground, &gcv);
+  st->gc = XCreateGC (st->dpy, st->window, GCForeground | GCBackground, &gcv);
+  return st;
 }
 
 static int
-recurse (x, y, l, dpy, win)
-     register double x, y;
-     register int l;
-     Display *dpy;
-     Window win;
+recurse (struct state *st, double x, double y, int l, Display *dpy, Window win)
 {
-  int xp, yp, i;
+  int /*xp, yp,*/ i;
   double nx, ny;
 
-  if (l == max_levels)
+  if (l == st->max_levels)
     {
-      total_points++;
-      if (total_points > max_total) /* how long each fractal runs */
+      st->total_points++;
+      if (st->total_points > st->max_total) /* how long each fractal runs */
        return 0;
 
       if (x > -1.0 && x < 1.0 && y > -1.0 && y < 1.0)
        {
-         xp = points[num_points].x = (int) ((width / 2) * (x + 1.0));
-         yp = points[num_points].y = (int) ((height / 2) * (y + 1.0));
-         num_points++;
-         if (num_points >= POINT_BUFFER_SIZE)
+/*       xp = st->points[st->num_points].x = (int) ((st->width / 2) * (x + 1.0));
+         yp = st->points[st->num_points].y = (int) ((st->height / 2) * (y + 1.0));*/
+         st->num_points++;
+         if (st->num_points >= POINT_BUFFER_SIZE)
            {
-             XDrawPoints (dpy, win, gc, points, num_points, CoordModeOrigin);
-             num_points = 0;
-             /* if (delay) usleep (delay); */
-             /* XSync (dpy, True); */
+             XDrawPoints (st->dpy, win, st->gc, st->points, st->num_points, CoordModeOrigin);
+             st->num_points = 0;
            }
        }
     }
   else
     {
-      for (i = 0; i < snum; i++)
+      for (i = 0; i < st->snum; i++)
        {
-         nx = f[0][0][i] * x + f[0][1][i] * y + f[0][2][i];
-         ny = f[1][0][i] * x + f[1][1][i] * y + f[1][2][i];
-         if (i < anum)
+
+         /* Scale back when values get very large. Spot sez:
+            "I think this happens on HPUX.  I think it's non-IEEE
+            to generate an exception instead of a silent NaN."
+          */
+         if ((abs(x) > 1.0E5) || (abs(y) > 1.0E5))
+           x = x / y;
+
+         nx = st->f[0][0][i] * x + st->f[0][1][i] * y + st->f[0][2][i];
+         ny = st->f[1][0][i] * x + st->f[1][1][i] * y + st->f[1][2][i];
+         if (i < st->anum)
            {
-             nx = sin(nx);
-             ny = sin(ny);
+             switch (st->variation)
+               {
+               case 0: /* sinusoidal */
+                 nx = sin(nx);
+                 ny = sin(ny);
+                 break;
+               case 1: /* complex */
+                 {
+                   double r2 = nx * nx + ny * ny + 1e-6;
+                   nx = nx / r2;
+                   ny = ny / r2;
+                 }
+                 break;
+               case 2: /* bent */
+                 if (nx < 0.0)
+                   nx = nx * 2.0;
+                 if (ny < 0.0)
+                   ny = ny / 2.0;
+                 break;
+               case 3: /* swirl */
+                 {
+                   double r = (nx * nx + ny * ny);     /* times k here is fun */
+                   double c1 = sin(r);
+                   double c2 = cos(r);
+                   double t = nx;
+
+                   if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
+                     ny = 1e4;
+                   else
+                     ny = c2 * t + c1 * ny;
+                   nx = c1 * nx - c2 * ny;
+                 }
+                 break;
+               case 4: /* horseshoe */
+                 {
+                   double r, c1, c2, t;
+
+                   /* Avoid atan2: DOMAIN error message */
+                   if (nx == 0.0 && ny == 0.0)
+                     r = 0.0;
+                   else
+                     r = atan2(nx, ny);      /* times k here is fun */
+                   c1 = sin(r);
+                   c2 = cos(r);
+                   t = nx;
+
+                   nx = c1 * nx - c2 * ny;
+                   ny = c2 * t + c1 * ny;
+                 }
+                 break;
+               case 5: /* drape */
+                 {
+                   double t;
+
+                   /* Avoid atan2: DOMAIN error message */
+                   if (nx == 0.0 && ny == 0.0)
+                     t = 0.0;
+                   else
+                     t = atan2(nx, ny) / M_PI;
+
+                   if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
+                     ny = 1e4;
+                   else
+                     ny = sqrt(nx * nx + ny * ny) - 1.0;
+                   nx = t;
+                 }
+                 break;
+               case 6: /* broken */
+                 if (nx > 1.0)
+                   nx = nx - 1.0;
+                 if (nx < -1.0)
+                   nx = nx + 1.0;
+                 if (ny > 1.0)
+                   ny = ny - 1.0;
+                 if (ny < -1.0)
+                   ny = ny + 1.0;
+                 break;
+               case 7: /* spherical */
+                 {
+                   double r = 0.5 + sqrt(nx * nx + ny * ny + 1e-6);
+
+                   nx = nx / r;
+                   ny = ny / r;
+                 }
+                 break;
+               case 8: /*  */
+                 nx = atan(nx) / M_PI_2;
+                 ny = atan(ny) / M_PI_2;
+                 break;
+/* #if 0 */  /* core dumps on some machines, why not all? */
+               case 9: /* complex sine */
+                 {
+                   double u = nx;
+                   double v = ny;
+                   double ev = exp(v);
+                   double emv = exp(-v);
+
+                   nx = (ev + emv) * sin(u) / 2.0;
+                   ny = (ev - emv) * cos(u) / 2.0;
+                 }
+                 break;
+               case 10:        /* polynomial */
+                 if (nx < 0)
+                   nx = -nx * nx;
+                 else
+                   nx = nx * nx;
+                 if (ny < 0)
+                   ny = -ny * ny;
+                 else
+                   ny = ny * ny;
+                 break;
+/* #endif */
+               default:
+                 nx = sin(nx);
+                 ny = sin(ny);
+               }
            }
-         if (!recurse (nx, ny, l + 1, dpy, win))
+         if (!recurse (st, nx, ny, l + 1, st->dpy, win))
            return 0;
        }
     }
   return 1;
 }
 
-
-static void
-flame (dpy, window)
-     Display *dpy;
-     Window window;
+static unsigned long
+flame_draw (Display *dpy, Window window, void *closure)
 {
+  struct state *st = (struct state *) closure;
   int i, j, k;
-  static int alt = 0;
+  unsigned long this_delay = st->delay;
 
-  if (!(cur_level++ % max_levels))
+  if (st->do_reset)
     {
-      if (delay2) usleep (delay2);
-      XClearWindow (dpy, window);
-      alt = !alt;
+      st->do_reset = 0;
+      XClearWindow (st->dpy, st->window);
+    }
+
+  if (!(st->cur_level++ % st->max_levels))
+    {
+      st->do_reset = 1;
+      this_delay = st->delay2;
+      st->flame_alt = !st->flame_alt;
+      st->variation = random() % MAXKINDS;
     }
   else
     {
-      if (npixels > 2)
+      if (st->ncolors > 2)
        {
-         XSetForeground (dpy, gc, pixels [pixcol]);
-         if (--pixcol < 0)
-           pixcol = npixels - 1;
+         XSetForeground (st->dpy, st->gc, st->colors [st->pixcol].pixel);
+         if (--st->pixcol < 0)
+           st->pixcol = st->ncolors - 1;
        }
     }
 
   /* number of functions */
-  snum = 2 + (cur_level % (MAXLEV - 1));
+  st->snum = 2 + (st->cur_level % (MAXLEV - 1));
 
   /* how many of them are of alternate form */
-  if (alt)
-    anum = 0;
+  if (st->flame_alt)
+    st->anum = 0;
   else
-    anum = halfrandom (snum) + 2;
+    st->anum = halfrandom (st, st->snum) + 2;
 
   /* 6 coefs per function */
-  for (k = 0; k < snum; k++)
+  for (k = 0; k < st->snum; k++)
     {
       for (i = 0; i < 2; i++)
        for (j = 0; j < 3; j++)
-         f[i][j][k] = ((double) (random() & 1023) / 512.0 - 1.0);
+         st->f[i][j][k] = ((double) (random() & 1023) / 512.0 - 1.0);
     }
-  num_points = 0;
-  total_points = 0;
-  (void) recurse (0.0, 0.0, 0, dpy, window);
-  XDrawPoints (dpy, window, gc, points, num_points, CoordModeOrigin);
-  XSync (dpy, True);
-  if (delay) usleep (delay);
+  st->num_points = 0;
+  st->total_points = 0;
+  recurse (st, 0.0, 0.0, 0, st->dpy, st->window);
+  XDrawPoints (st->dpy, st->window, st->gc, st->points, st->num_points, CoordModeOrigin);
+
+  return this_delay;
 }
 
 
-#ifdef __hpux
+#if defined(__hpux) && defined(PLOSS)
 /* I don't understand why this is necessary, but I'm told that this program
    does nothing at all on HP-sUX without it.
+
+   I'm further told that HPUX 11.0 doesn't define PLOSS, and works ok without
+   this section.  Go figure.
  */
 #undef random
 #undef srandom
@@ -264,12 +409,11 @@ int matherr(x)
 
 
 \f
-char *progclass = "Flame";
-
-char *defaults [] = {
-  "Flame.background:   black",         /* to placate SGI */
-  "Flame.foreground:   white",
-  "*colors:    128",
+static const char *flame_defaults [] = {
+  ".background:        black",
+  ".foreground:        white",
+  "*fpsSolid:  true",
+  "*colors:    64",
   "*iterations:        25",
   "*delay:     50000",
   "*delay2:    2000000",
@@ -277,21 +421,33 @@ char *defaults [] = {
   0
 };
 
-XrmOptionDescRec options [] = {
-  { "-ncolors",                ".colors",      XrmoptionSepArg, 0 },
+static XrmOptionDescRec flame_options [] = {
+  { "-colors",         ".colors",      XrmoptionSepArg, 0 },
   { "-iterations",     ".iterations",  XrmoptionSepArg, 0 },
   { "-delay",          ".delay",       XrmoptionSepArg, 0 },
   { "-delay2",         ".delay2",      XrmoptionSepArg, 0 },
-  { "-points",         ".points",      XrmoptionSepArg, 0 }
+  { "-points",         ".points",      XrmoptionSepArg, 0 },
+  { 0, 0, 0, 0 }
 };
-int options_size = (sizeof (options) / sizeof (options[0]));
 
-void
-screenhack (dpy, window)
-     Display *dpy;
-     Window window;
+static void
+flame_reshape (Display *dpy, Window window, void *closure, 
+                 unsigned int w, unsigned int h)
 {
-  init_flame (dpy, window);
-  while (1)
-    flame (dpy, window);
 }
+
+static Bool
+flame_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+  return False;
+}
+
+static void
+flame_free (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+  free (st);
+}
+
+XSCREENSAVER_MODULE ("Flame", flame)
+