http://slackware.bholcomb.com/slackware/slackware-11.0/source/xap/xscreensaver/xscree...
[xscreensaver] / hacks / pyro.c
index 996f7833d72dc7723662283aef7ec8c542350aaa..ec2e02b3cfa14b75d28384d84873badccf3f9846 100644 (file)
@@ -1,4 +1,5 @@
-/* xscreensaver, Copyright (c) 1992, 1994 Jamie Zawinski <jwz@netscape.com>
+/* xscreensaver, Copyright (c) 1992, 1994, 1996, 1998, 2001, 2005, 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
@@ -13,6 +14,7 @@
    John S. Pezaris <pz@hx.lcs.mit.edu>
  */
 
+#include <math.h>
 #include "screenhack.h"
 
 struct projectile {
@@ -27,16 +29,61 @@ struct projectile {
   struct projectile *next_free;
 };
 
-static struct projectile *projectiles, *free_projectiles;
+#define PI_2000 6284
+
+
+struct state {
+  Display *dpy;
+  Window window;
+
+   struct projectile *projectiles, *free_projectiles;
+   struct projectile **sorted_projectiles;
+
+   GC draw_gc, erase_gc;
+   unsigned int default_fg_pixel;
+   Colormap cmap;
+
+   int how_many, frequency, scatter, delay;
+
+   int sin_cache[PI_2000];
+   int cos_cache[PI_2000];
+
+   int draw_xlim, draw_ylim, real_draw_xlim, real_draw_ylim;
+
+   unsigned long last_pixel;
+};
+
+
+
+/* Slightly whacked, for better explosions
+ */
+
+static void
+cache(struct state *st)
+{               /*needs to be run once. Could easily be */
+  int i;        /*reimplemented to run and cache at compile-time,*/
+  double dA;    /*saving on init_pyro time */
+  for (i=0; i<PI_2000; i++)
+    {
+      dA=sin(((double) (random() % (PI_2000/2)))/1000.0);
+      /*Emulation of spherical distribution*/
+      dA+=asin(frand(1.0))/M_PI_2*0.1;
+      /*Approximating the integration of the binominal, for
+        well-distributed randomness*/
+      st->cos_cache[i]=(int) (cos(((double)i)/1000.0)*dA*2500.0);
+      st->sin_cache[i]=(int) (sin(((double)i)/1000.0)*dA*2500.0);
+    }
+}
+
 
 static struct projectile *
-get_projectile ()
+get_projectile (struct state *st)
 {
   struct projectile *p;
-  if (free_projectiles)
+  if (st->free_projectiles)
     {
-      p = free_projectiles;
-      free_projectiles = p->next_free;
+      p = st->free_projectiles;
+      st->free_projectiles = p->next_free;
       p->next_free = 0;
       p->dead = False;
       return p;
@@ -46,21 +93,18 @@ get_projectile ()
 }
 
 static void
-free_projectile (p)
-     struct projectile *p;
+free_projectile (struct state *st, struct projectile *p)
 {
-  p->next_free = free_projectiles;
-  free_projectiles = p;
+  p->next_free = st->free_projectiles;
+  st->free_projectiles = p;
   p->dead = True;
 }
 
 static void
-launch (xlim, ylim, g, dpy, cmap)
-     int xlim, ylim, g;
-     Display *dpy;
-     Colormap cmap;
+launch (struct state *st, 
+        int xlim, int ylim, int g)
 {
-  struct projectile *p = get_projectile ();
+  struct projectile *p = get_projectile (st);
   int x, dx, xxx;
   if (! p) return;
 
@@ -79,31 +123,37 @@ launch (xlim, ylim, g, dpy, cmap)
   p->fuse = ((((random () % 500) + 500) * abs (p->dy / g)) / 1000);
   p->primary = True;
 
+  /* cope with small windows -- those constants assume big windows. */
+  {
+    int dd = 1000000 / ylim;
+    if (dd > 1)
+      p->fuse /= dd;
+  }
+
   if (! mono_p)
     {
       hsv_to_rgb (random () % 360, 1.0, 1.0,
                  &p->color.red, &p->color.green, &p->color.blue);
       p->color.flags = DoRed | DoGreen | DoBlue;
-      if (!XAllocColor (dpy, cmap, &p->color))
+      if (!XAllocColor (st->dpy, st->cmap, &p->color))
        {
-         p->color.pixel = WhitePixel (dpy, DefaultScreen (dpy));
-         p->color.red = p->color.green = p->color.blue = 0;
+         p->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
+         p->color.red = p->color.green = p->color.blue = 0xFFFF;
        }
     }
 }
 
 static struct projectile *
-shrapnel (parent, dpy, cmap)
-     struct projectile *parent;
-     Display *dpy;
-     Colormap cmap;
+shrapnel (struct state *st, struct projectile *parent)
 {
-  struct projectile *p = get_projectile ();
+  struct projectile *p = get_projectile (st);
+  int v;
   if (! p) return 0;
   p->x = parent->x;
   p->y = parent->y;
-  p->dx = (random () % 5000) - 2500 + parent->dx;
-  p->dy = (random () % 5000) - 2500 + parent->dy;
+  v=random () % PI_2000;
+  p->dx =(st->sin_cache[v]) + parent->dx;
+  p->dy =(st->cos_cache[v]) + parent->dy;
   p->decay = (random () % 50) - 60;
   p->size = (parent->size * 2) / 3;
   p->fuse = 0;
@@ -111,75 +161,84 @@ shrapnel (parent, dpy, cmap)
 
   p->color = parent->color;
   if (! mono_p)
-    XAllocColor (dpy, cmap, &p->color); /* dup the lock */
+    XAllocColor (st->dpy, st->cmap, &p->color); /* dup the lock */
   
   return p;
 }
 
-static GC draw_gc, erase_gc;
-static unsigned int default_fg_pixel;
-
-static int how_many, frequency, scatter;
-
-static Colormap
-init_pyro (dpy, window)
-     Display *dpy;
-     Window window;
+static void *
+pyro_init (Display *dpy, Window window)
 {
+  struct state *st = (struct state *) calloc (1, sizeof(*st));
   int i;
-  Colormap cmap;
   XGCValues gcv;
   XWindowAttributes xgwa;
-  XGetWindowAttributes (dpy, window, &xgwa);
-  cmap = xgwa.colormap;
-  how_many = get_integer_resource ("count", "Integer");
-  frequency = get_integer_resource ("frequency", "Integer");
-  scatter = get_integer_resource ("scatter", "Integer");
-  if (how_many <= 0) how_many = 100;
-  if (frequency <= 0) frequency = 30;
-  if (scatter <= 0) scatter = 20;
-  projectiles = 0;
-  free_projectiles = 0;
-  projectiles = (struct projectile *)
-    calloc (how_many, sizeof (struct projectile));
-  for (i = 0; i < how_many; i++)
-    free_projectile (&projectiles [i]);
-  gcv.foreground = default_fg_pixel =
-    get_pixel_resource ("foreground", "Foreground", dpy, cmap);
-  draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
-  gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
-  erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
-  XClearWindow (dpy, window);
-  return cmap;
+  st->dpy = dpy;
+  st->window = window;
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
+  st->last_pixel = ~0;
+  st->cmap = xgwa.colormap;
+  st->delay = get_integer_resource (st->dpy, "delay", "Integer");
+  st->how_many = get_integer_resource (st->dpy, "count", "Integer");
+  st->frequency = get_integer_resource (st->dpy, "frequency", "Integer");
+  st->scatter = get_integer_resource (st->dpy, "scatter", "Integer");
+  if (st->how_many <= 0) st->how_many = 100;
+  if (st->frequency <= 0) st->frequency = 30;
+  if (st->scatter <= 0) st->scatter = 20;
+  st->projectiles = 0;
+  st->free_projectiles = 0;
+  st->projectiles = (struct projectile *)
+    calloc (st->how_many, sizeof (*st->projectiles));
+  st->sorted_projectiles = (struct projectile **)
+    calloc (st->how_many, sizeof (*st->sorted_projectiles));
+  for (i = 0; i < st->how_many; i++)
+    free_projectile (st, &st->projectiles [i]);
+  for (i = 0; i < st->how_many; i++)
+    st->sorted_projectiles[i] = &st->projectiles[i];
+  gcv.foreground = st->default_fg_pixel =
+    get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground");
+  st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
+  gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "background", "Background");
+  st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
+  XClearWindow (st->dpy, st->window);
+  cache(st);  
+
+  return st;
+}
+
+
+static int
+projectile_pixel_sorter (const void *a, const void *b)
+{
+  struct projectile *pa = *(struct projectile **) a;
+  struct projectile *pb = *(struct projectile **) b;
+  if (pa->color.pixel == pb->color.pixel) return 0;
+  else if (pa->color.pixel < pb->color.pixel) return -1;
+  else return 1;
 }
 
 static void
-pyro (dpy, window, cmap)
-     Display *dpy;
-     Window window;
-     Colormap cmap;
+sort_by_pixel (struct state *st, int length)
 {
+  qsort ((void *) st->sorted_projectiles,
+         length,
+         sizeof(*st->sorted_projectiles),
+         projectile_pixel_sorter);
+}
+
+
+static unsigned long
+pyro_draw (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
   XWindowAttributes xgwa;
-  static int xlim, ylim, real_xlim, real_ylim;
   int g = 100;
+  int resort = 0;
   int i;
-
-  if ((random () % frequency) == 0)
-    {
-      XGetWindowAttributes (dpy, window, &xgwa);
-      real_xlim = xgwa.width;
-      real_ylim = xgwa.height;
-      xlim = real_xlim * 1000;
-      ylim = real_ylim * 1000;
-      launch (xlim, ylim, g, dpy, cmap);
-    }
-
-  XSync (dpy, True);
-  usleep (10000);
-
-  for (i = 0; i < how_many; i++)
+  
+  for (i = 0; i < st->how_many; i++)
     {
-      struct projectile *p = &projectiles [i];
+      struct projectile *p = st->sorted_projectiles [i];
       int old_x, old_y, old_size;
       int size, x, y;
       if (p->dead) continue;
@@ -193,66 +252,117 @@ pyro (dpy, window, cmap)
       if (p->primary) p->fuse--;
 
       /* erase old one */
-      XFillRectangle (dpy, window, erase_gc, old_x, old_y,
-                     old_size, old_size);
+      if (old_size > 0)
+        {
+          if (old_size == 1)
+           XDrawPoint (st->dpy, st->window, st->erase_gc, old_x, old_y);
+          else
+            XFillRectangle (st->dpy, st->window, st->erase_gc, old_x, old_y,
+                            old_size, old_size);
+        }
 
       if ((p->primary ? (p->fuse > 0) : (p->size > 0)) &&
-         x < real_xlim &&
-         y < real_ylim &&
+         x < st->real_draw_xlim &&
+         y < st->real_draw_ylim &&
          x > 0 &&
          y > 0)
        {
-         if (mono_p || p->primary)
-           XSetForeground (dpy, draw_gc, default_fg_pixel);
-         else
-           XSetForeground (dpy, draw_gc, p->color.pixel);
-
-         if /*(p->primary)*/ (size > 2)
-           XFillArc (dpy, window, draw_gc, x, y, size, size, 0, 360*64);
-         else
-           XFillRectangle (dpy, window, draw_gc, x, y, size, size);
-       }
+          if (size > 0)
+            {
+              unsigned long pixel;
+
+              if (mono_p || p->primary)
+                pixel = st->default_fg_pixel;
+              else
+                pixel = p->color.pixel;
+
+              if (pixel != st->last_pixel)
+                {
+                  st->last_pixel = pixel;
+                  XSetForeground (st->dpy, st->draw_gc, pixel);
+                }
+
+              if (size == 1)
+                XDrawPoint (st->dpy, st->window, st->draw_gc, x, y);
+              else if (size < 4)
+                XFillRectangle (st->dpy, st->window, st->draw_gc, x, y, size, size);
+              else
+                XFillArc (st->dpy, st->window, st->draw_gc, x, y, size, size, 0, 360*64);
+            }
+        }
       else
        {
-         free_projectile (p);
-         if (! mono_p) XFreeColors (dpy, cmap, &p->color.pixel, 1, 0);
+         free_projectile (st, p);
+         if (! mono_p)
+           if (p->color.pixel != WhitePixel (st->dpy, DefaultScreen (st->dpy)))
+             XFreeColors (st->dpy, st->cmap, &p->color.pixel, 1, 0);
        }
 
       if (p->primary && p->fuse <= 0)
        {
-         int i = (random () % scatter) + (scatter/2);
-         while (i--)
-           shrapnel (p, dpy, cmap);
+         int j = (random () % st->scatter) + (st->scatter/2);
+         while (j--)
+           shrapnel (st, p);
+          resort = 1;
        }
     }
+
+  if ((random () % st->frequency) == 0)
+    {
+      XGetWindowAttributes (st->dpy, st->window, &xgwa);
+      st->real_draw_xlim = xgwa.width;
+      st->real_draw_ylim = xgwa.height;
+      st->draw_xlim = st->real_draw_xlim * 1000;
+      st->draw_ylim = st->real_draw_ylim * 1000;
+      launch (st, st->draw_xlim, st->draw_ylim, g);
+      resort = 1;
+    }
+
+  /* being sorted lets us avoid changing the GC's foreground color as often. */
+  if (resort)
+    sort_by_pixel (st, st->how_many);
+
+  return st->delay;
+}
+
+static void
+pyro_reshape (Display *dpy, Window window, void *closure, 
+              unsigned int w, unsigned int h)
+{
+}
+
+static Bool
+pyro_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+  return False;
+}
+
+static void
+pyro_free (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+  free (st);
 }
 
 \f
-char *progclass = "Pyro";
 
-char *defaults [] = {
-  "Pyro.background:    black",         /* to placate SGI */
-  "Pyro.foreground:    white",
-  "*count:     100",
+static const char *pyro_defaults [] = {
+  ".background:        black",
+  ".foreground:        white",
+  "*count:     600",
+  "*delay:     5000",
   "*frequency: 30",
-  "*scatter:   20",
+  "*scatter:   100",
   "*geometry:  800x500",
   0
 };
 
-XrmOptionDescRec options [] = {
+static XrmOptionDescRec pyro_options [] = {
+  { "-delay",          ".delay",       XrmoptionSepArg, 0 },
   { "-count",          ".count",       XrmoptionSepArg, 0 },
   { "-frequency",      ".frequency",   XrmoptionSepArg, 0 },
-  { "-scatter",                ".scatter",     XrmoptionSepArg, 0 }
+  { "-scatter",                ".scatter",     XrmoptionSepArg, 0 },
+  { 0, 0, 0, 0 }
 };
-int options_size = (sizeof (options) / sizeof (options[0]));
 
-void
-screenhack (dpy, window)
-     Display *dpy;
-     Window window;
-{
-  Colormap cmap = init_pyro (dpy, window);
-  while (1)
-    pyro (dpy, window, cmap);
-}
+XSCREENSAVER_MODULE ("Pyro", pyro)