http://ftp.x.org/contrib/applications/xscreensaver-3.20.tar.gz
[xscreensaver] / hacks / ripples.c
diff --git a/hacks/ripples.c b/hacks/ripples.c
new file mode 100644 (file)
index 0000000..f8996c4
--- /dev/null
@@ -0,0 +1,846 @@
+/* ripples, Copyright (c) 1999 Ian McConnell <ian@emit.demon.co.uk>
+ *
+ * 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.
+ */
+
+/*
+ * "Water" ripples that can cross and interfere with each other.
+ *
+ * I can't remember where I got this idea from, but it's been around for a
+ * while in various demos. Some inspiration from
+ *      water.txt by Tom Hammersley,tomh@globalnet.co.uk
+ *
+ * Options
+ * -delay      usleep every iteration
+ * -rate       Add one drop every "rate" iterations
+ * -water      Ripples on a grabbed background image
+ * -foreground  Interpolate ripples between these two colors
+ * -background
+ * -oily       Psychedelic colours like man
+ * -stir       Add a regular pattern of drops
+ * -fluidity   Between 0 and 16. 16 = big drops
+ * -light      Hack to add lighting effect (2 for 16bbp, 6 for 32bpp)
+ *
+ * Code mainly hacked from xflame and decayscreen.
+ */
+
+/* Version history:
+ * Speeded up graphics with dirty buffer. Returned to using putpixel for
+ * greater portability
+ * Added a variety of methods for splashing screen.
+ */
+
+
+#include <math.h>
+#include "screenhack.h"
+#include <X11/Xutil.h>
+
+typedef enum {ripple_drop, ripple_blob, ripple_box, ripple_stir} ripple_mode;
+
+#ifdef HAVE_XSHM_EXTENSION
+#include "xshm.h"
+static Bool use_shm;
+static XShmSegmentInfo shm_info;
+#endif /* HAVE_XSHM_EXTENSION */
+
+static Window window;
+static Display *display;
+static GC gc;
+static Visual *visual;
+
+static XImage *orig_map, *buffer_map;
+static int ctab[256];
+static Colormap colormap;
+static int ncolors;
+static int light;
+
+static int width, height; /* ripple size */
+static int bigwidth, bigheight; /* screen size */
+
+static Bool transparent;
+static short *bufferA, *bufferB, *temp;
+static char *dirty_buffer;
+
+#define TABLE 256
+static double cos_tab[TABLE];
+
+static double drop_dist[] = 
+{0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2, 0.6};
+
+/* How hard to hit the water */
+#define SPLASH 512
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#define DIRTY 2 /* 2 = dirty, 1 = restore original pixel, 0 = leave alone */
+
+
+/*      -------------------------------------------             */
+
+
+static int
+map_color(int grey)
+{
+  /* Clip it */
+  grey = ncolors * abs(grey) / (SPLASH/4);
+  if (grey > ncolors)
+    grey = ncolors;
+
+  /* Display it */
+  return ctab[grey];
+}
+
+
+static void
+draw_ripple(short *src)
+{
+  int across, down;
+  char *dirty = dirty_buffer;
+
+  for (down = 0; down < height - 1; down++, src += 1, dirty += 1)
+    for (across = 0; across < width - 1; across++, src++, dirty++) {
+      int v1, v2, v3, v4;
+      v1 = (int)*src;
+      v2 = (int)*(src + 1);
+      v3 = (int)*(src + width);
+      v4 = (int)*(src + width + 1);
+      if ((v1 == 0 && v2 == 0 && v3 == 0 && v4 == 0)) {
+        if (*dirty > 0)
+          (*dirty)--;
+      } else
+        *dirty = DIRTY;
+
+      if (*dirty > 0) {
+        int dx;
+        if (light > 0) {
+          /* dx = ((v2 - v1) + (v4 - v3)) << light; */ /* light from left */
+          dx = ((v3 - v1) + (v4 - v2)) << light; /* light from top */
+        } else
+          dx = 0;
+        XPutPixel(buffer_map,(across<<1),  (down<<1),  map_color(dx + v1));
+        XPutPixel(buffer_map,(across<<1)+1,(down<<1),  map_color(dx + ((v1 + v2) >> 1)));
+        XPutPixel(buffer_map,(across<<1),  (down<<1)+1,map_color(dx + ((v1 + v3) >> 1)));
+        XPutPixel(buffer_map,(across<<1)+1,(down<<1)+1,map_color(dx + ((v1 + v4) >> 1)));
+      }
+    }
+}
+
+
+/*      -------------------------------------------             */
+
+
+static unsigned long (*bright)(int dx, unsigned long color);
+static unsigned long rmask;
+static unsigned long gmask;
+static unsigned long bmask;
+static int rshift;
+static int gshift;
+static int bshift;
+
+
+static void 
+set_mask(unsigned long color, unsigned long *mask, int *shift)
+{
+  *shift = 0;
+  while (color != 0 && (color & 1) == 0) {
+    (*shift)++;
+    color >>= 1;
+  }
+  *mask = color;
+}
+
+
+static unsigned long
+cadd(unsigned long color, int dx, unsigned long mask, int shift)
+{
+  int x;
+  color >>= shift;
+  x = (color & mask);
+  x += dx;
+  if (x < 0) x = 0;
+  else if (x > (int)mask) x = mask;
+  color = x;
+  return color << shift;
+}
+
+
+static unsigned long
+dobright(int dx, unsigned long color)
+{
+  return (cadd(color, dx, rmask, rshift) |
+          cadd(color, dx, gmask, gshift) |
+          cadd(color, dx, bmask, bshift));
+}
+
+
+static unsigned long
+nobright(int dx, unsigned long color)
+{
+  return color;
+}
+
+
+static void
+draw_transparent(short *src)
+{
+  int across, down, pixel;
+  char *dirty = dirty_buffer;
+
+  pixel = 0;
+  for (down = 0; down < height - 2; down++, pixel += 2)
+    for (across = 0; across < width-2; across++, pixel++) {
+      int gradx, grady, gradx1, grady1;
+      int x0, x1, x2, y1, y2;
+
+      x0 = src[pixel];
+      x1 = src[pixel + 1];
+      x2 = src[pixel + 2];
+      y1 = src[pixel + width];
+      y2 = src[pixel + 2*width];
+
+      gradx = (x1 - x0);
+      grady = (y1 - x0);
+      gradx1= (x2 - x1);
+      grady1= (y2 - y1);
+      gradx1 = 1 + (gradx + gradx1) / 2;
+      grady1 = 1 + (grady + grady1) / 2;
+
+      if ((2*across+MIN(gradx,gradx1) < 0) ||
+          (2*across+MAX(gradx,gradx1) >= bigwidth)) {
+        gradx = 0;
+        gradx1= 1;
+      }
+      if ((2*down+MIN(grady,grady1) < 0) ||
+          (2*down+MAX(grady,grady1) >= bigheight)) {
+        grady = 0;
+        grady1 = 1;
+      }
+
+      if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
+        if (dirty[pixel] > 0)
+          dirty[pixel]--;
+      } else
+        dirty[pixel] = DIRTY;
+
+      if (dirty[pixel] > 0) {
+        int dx;
+        if (light > 0) {
+          if (4-light >= 0)
+            dx = (grady + (src[pixel+width+1]-x1)) >> (4-light); /* light from top */
+          else
+            dx = (grady + (src[pixel+width+1]-x1)) << (light-4); /* light from top */
+        } else
+          dx = 0;
+        XPutPixel(buffer_map, (across<<1),  (down<<1),
+                  bright(dx, XGetPixel(orig_map, (across<<1) + gradx, (down<<1) + grady)));
+        XPutPixel(buffer_map, (across<<1)+1,(down<<1),
+                  bright(dx, XGetPixel(orig_map, (across<<1) + gradx1,(down<<1) + grady)));
+        XPutPixel(buffer_map, (across<<1),  (down<<1)+1,
+                  bright(dx, XGetPixel(orig_map, (across<<1) + gradx, (down<<1) + grady1)));
+        XPutPixel(buffer_map, (across<<1)+1,(down<<1)+1,
+                  bright(dx, XGetPixel(orig_map, (across<<1) + gradx1,(down<<1) + grady1)));
+      }
+    }
+}
+
+
+/*      -------------------------------------------             */
+
+
+/*
+static void
+draw_ripple16(short *src)
+{
+  int across, down;
+  unsigned short *dest;
+
+  dest = (unsigned short *) buffer_map->data;
+
+  for (down = 0; down < height - 1; down++, src += 1, dest += bigwidth+2)
+    for (across = 0; across < width - 1; across++, src++) {
+      int v1, v2, v3, v4;
+      v1 = (int)*src;
+      v2 = (int)*(src + 1);
+      v3 = (int)*(src + width);
+      v4 = (int)*(src + width + 1);
+      if (!(v1 == 0 && v2 == 0 && v3 == 0 && v4 == 0)) {
+        *dest++ = map_color(v1);
+        *dest   = map_color((v1 + v2) >> 1);
+        dest   += bigwidth - 1;
+        *dest++ = map_color((v1 + v3) >> 1);
+        *dest   = map_color((v1 + v4) >> 1);
+        dest   -= bigwidth - 1;
+      } else
+        dest += 2;
+    }
+}
+*/
+
+
+/*      -------------------------------------------             */
+
+
+static void
+setup_X(Display * disp, Window win)
+{
+  XWindowAttributes xwa;
+  int depth;
+
+  XGetWindowAttributes(disp, win, &xwa);
+  window = win;
+  display = disp;
+  depth = xwa.depth;
+  colormap = xwa.colormap;
+  bigwidth = xwa.width;
+  bigheight = xwa.height;
+  visual = xwa.visual;
+
+#if 1 /* I'm not entirely sure if I need this */
+  if (bigwidth % 2)
+    bigwidth++;
+  if (bigheight % 2)
+    bigheight++;
+#endif
+  width = bigwidth / 2;
+  height = bigheight / 2;
+
+  if (transparent) {
+    XGCValues gcv;
+    long gcflags;
+
+    gcv.function = GXcopy;
+    gcv.subwindow_mode = IncludeInferiors;
+
+    gcflags = GCForeground | GCFunction;
+    if (use_subwindow_mode_p(xwa.screen, window))      /* see grabscreen.c */
+      gcflags |= GCSubwindowMode;
+    gc = XCreateGC(display, window, gcflags, &gcv);
+
+    grab_screen_image(xwa.screen, window);
+
+    orig_map = XGetImage(display, window, 0, 0, xwa.width, xwa.height,
+                        ~0L, ZPixmap);
+  } else {
+    XGCValues gcv;
+
+    gc = XCreateGC(display, window, 0, &gcv);
+    if (!gc) {
+      fprintf(stderr, "XCreateGC failed\n");
+      exit(1);
+    }
+
+    orig_map = 0;
+  }
+
+  buffer_map = 0;
+
+#ifdef HAVE_XSHM_EXTENSION
+  if (use_shm) {
+    buffer_map = create_xshm_image(display, xwa.visual, depth,
+                                  ZPixmap, 0, &shm_info, bigwidth, bigheight);
+    if (!buffer_map) {
+      use_shm = False;
+      fprintf(stderr, "create_xshm_image failed\n");
+    }
+  }
+#endif /* HAVE_XSHM_EXTENSION */
+
+  if (!buffer_map) {
+    buffer_map = XCreateImage(display, xwa.visual,
+                             depth, ZPixmap, 0, 0,
+                             bigwidth, bigheight, 8, 0);
+    buffer_map->data = (char *)
+      calloc(buffer_map->height, buffer_map->bytes_per_line);
+  }
+}
+
+
+static void
+DisplayImage(void)
+{
+#ifdef HAVE_XSHM_EXTENSION
+  if (use_shm)
+    XShmPutImage(display, window, gc, buffer_map, 0, 0, 0, 0,
+                bigwidth, bigheight, False);
+  else
+#endif /* HAVE_XSHM_EXTENSION */
+    XPutImage(display, window, gc, buffer_map, 0, 0, 0, 0,
+             bigwidth, bigheight);
+}
+
+
+/*      -------------------------------------------             */
+
+
+static int
+cinterp(double a, int bg, int fg)
+{
+  int result;
+  result = (int)((1-a) * bg + a * fg + 0.5);
+  if (result < 0) result = 0;
+  if (result > 255) result = 255;
+  return result;
+}
+
+
+/* Interpolate the ripple colours between the background colour and
+   foreground colour */
+static void
+init_linear_colors(void)
+{
+  int i, j, red, green, blue, bred, bgreen, bblue;
+  XColor fg, bg;
+
+  if (ncolors < 2 || mono_p)
+    ncolors = 2;
+  if (ncolors <= 2)
+    mono_p = True;
+
+  /* Make it possible to set the color of the ripples,
+     Based on work by Raymond Medeiros <ray@stommel.marine.usf.edu> and jwz.
+   */
+  fg.pixel = get_pixel_resource("foreground", "Foreground",
+                               display, colormap);
+  XQueryColor(display, colormap, &fg);
+  red = (fg.red >> 8);
+  green = (fg.green >> 8);
+  blue = (fg.blue >> 8);
+
+  bg.pixel = get_pixel_resource("background", "Background",
+                               display, colormap);
+  XQueryColor(display, colormap, &bg);
+  bred = (bg.red >> 8);
+  bgreen = (bg.green >> 8);
+  bblue = (bg.blue >> 8);
+
+  j = 0;
+  for (i = 0; i < ncolors+1; i++) {
+    XColor xcl;
+    double a = (double)i / ncolors;
+    int r = cinterp(a, bred, red);
+    int g = cinterp(a, bgreen, green);
+    int b = cinterp(a, bblue, blue);
+
+    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
+init_oily_colors(void)
+{
+  XColor *colors = NULL;
+
+  if (ncolors < 2 || mono_p)
+    ncolors = 2;
+  if (ncolors <= 2)
+    mono_p = True;
+  colors = 0;
+
+  if (!mono_p) {
+    colors = (XColor *)malloc(sizeof(*colors) * (ncolors+1));
+    make_smooth_colormap(display, visual, colormap, colors, &ncolors,
+                         True, /* allocate */
+                         False, /* not writable */
+                         True); /* verbose (complain about failure) */
+    if (ncolors <= 2) {
+      if (colors)
+        free (colors);
+      colors = 0;
+      mono_p = True;
+    }
+  }
+  if (!mono_p) {
+    int i, j = 0;
+    for (i = 0; i < ncolors+1; i++) {
+      XAllocColor(display, colormap, colors+i);
+      ctab[j++] = (int) colors[i].pixel;
+    }
+    free (colors);
+  } else {
+    ncolors = 2;
+    ctab[1] = get_pixel_resource("foreground", "Foreground", display, colormap);
+    ctab[0] = get_pixel_resource("background", "Background", display, colormap);
+  }
+}
+
+
+/*      -------------------------------------------             */
+
+
+/* Shape of drop to add */
+static double
+sinc(double x)
+{
+#if 1
+  int i;
+  i = (int)(x * TABLE + 0.5);
+  if (i >= TABLE) i = (TABLE-1) - (i-(TABLE-1));
+  return cos_tab[i];
+#elif 0
+  return cos(x * M_PI/2);
+#else
+  if (fabs(x) < 0.1)
+    return 1 - x*x;
+  else
+    return sin(x) / x;
+#endif
+}
+
+
+static void
+add_circle_drop(int x, int y, int radius, int dheight)
+{
+  int i, r2, cx, cy;
+  short *buf = (random()&1) ? bufferA : bufferB;
+
+  i = y * width + x;
+  r2 = radius * radius;
+
+  for (cy = -radius; cy <= radius; cy++)
+    for (cx = -radius; cx <= radius; cx++) {
+      int r = cx*cx + cy*cy;
+      if (r <= r2) {
+        buf[i + cx + cy*width] =
+          (short)(dheight * sinc(sqrt(r)/radius));
+      }
+    }
+}
+
+
+static void
+add_drop(ripple_mode mode, int drop)
+{
+  int newx, newy, dheight;
+  int radius = MIN(width, height) / 50;
+  /* Don't put drops too near the edge of the screen or they get stuck */
+  int border = 6;
+
+  switch (mode) {
+  default:
+  case ripple_drop: {
+    int x;
+
+    dheight = 1 + (random() % drop);
+    newx = border + (random() % (width - 2*border));
+    newy = border + (random() % (height - 2*border));
+    x = newy * width + newx;
+    bufferA[x + 1] = bufferA[x] = bufferA[x + width] = bufferA[x + width + 1] =
+      bufferB[x + 1] = bufferB[x] = bufferB[x + width] = bufferB[x + width + 1] =
+      dheight;
+  }
+  break;
+  case ripple_blob: {
+    double power;
+
+    power = drop_dist[random() % (sizeof(drop_dist)/sizeof(drop_dist[0]))]; /* clumsy */
+    dheight = (int)(drop * (power + 0.01));
+    newx = radius + border + (random() % (int)(width - 2*border - 2*radius*power));
+    newy = radius + border + (random() % (int)(height - 2*border - 2*radius*power));
+    add_circle_drop(newx, newy, radius, dheight);
+  }
+  break;
+  /* Adding too many boxes too quickly (-box 1) doesn't give the waves time
+     to disperse and the waves build up (and overflow) */
+  case ripple_box: {
+    int x;
+    int cx, cy;
+    short *buf = (random()&1) ? bufferA : bufferB;
+
+    radius = (1 + (random() % 5)) * (1 + (random() % 5));
+    dheight = drop / 128;
+    if (random() & 1) dheight = -dheight;
+    newx = radius + border + (random() % (width - 2*border - 2*radius));
+    newy = radius + border + (random() % (height - 2*border - 2*radius));
+    x = newy * width + newx;
+    for (cy = -radius; cy <= radius; cy++)
+      for (cx = -radius; cx <= radius; cx++)
+        buf[x + cx + cy*width] = (short)(dheight);
+  }
+  break;
+  case ripple_stir: {
+    static double ang = 0;
+    border += radius;
+    newx = border + (int)((width-2*border) * (1+cos(3*ang)) / 2);
+    newy = border + (int)((height-2*border) * (1+sin(2*ang)) / 2);
+    add_circle_drop(newx, newy, radius, drop / 10);
+    ang += 0.02;
+    if (ang > 12*M_PI) ang = 0;
+  }
+  break;
+  }
+}
+
+
+static void
+init_ripples(int ndrops, int splash)
+{
+  int i;
+
+  bufferA = (short *)calloc(width * height, sizeof(*bufferA));
+  bufferB = (short *)calloc(width * height, sizeof(*bufferB));
+  temp = (short *)calloc(width * height, sizeof(*temp));
+
+  dirty_buffer = (char *)malloc(width * height);
+  memset(dirty_buffer, DIRTY, width * height);
+
+  for (i = 0; i < ndrops; i++)
+    add_drop(ripple_blob, splash);
+
+  if (transparent) {
+    /* There's got to be a better way of doing this  XCopyArea? */
+    int across, down;
+    for (down = 0; down < bigheight-4; down++)
+      for (across = bigwidth-4; across < bigwidth; across++)
+       XPutPixel(buffer_map, across, down,
+                 XGetPixel(orig_map, across, down));
+    for (down = bigheight-4; down < bigheight; down++)
+      for (across = 0; across < bigwidth; across++)
+       XPutPixel(buffer_map, across, down,
+                 XGetPixel(orig_map, across, down));
+  } else {
+    int across, down, color;
+
+    color = map_color(0); /* background colour */
+    for (down = 0; down < bigheight; down++)
+      for (across = 0; across < bigwidth; across++)
+        XPutPixel(buffer_map,across,  down,  color);
+  }
+
+  DisplayImage();
+}
+
+
+/*
+ Explanation from hq_water.zip (Arturo R Montesinos (ARM) arami@fi.upm.es)
+
+ Differential equation is:  u  = a ( u  + u  )
+                             tt       xx   yy
+
+ Where a = tension * gravity / surface_density.
+
+ Approximating second derivatives by central differences:
+
+  [ u(t+1)-2u(t)+u(t-1) ] / dt = a [ u(x+1)+u(x-1)+u(y+1)+u(y-1)-4u ] / h
+
+ where dt = time step squared, h= dx*dy = mesh resolution squared.
+
+ From where u(t+1) may be calculated as:
+
+            dt  |   1   |                   dt
+ u(t+1) = a --  | 1 0 1 |u - u(t-1) + (2-4a --)u
+            h   |   1   |                    h
+
+ When a*dt/h = 1/2 last term vanishes, giving:
+
+                 1 |   1   |
+        u(t+1) = - | 1 0 1 |u - u(t-1)
+                 2 |   1   |
+
+ (note that u(t-1,x,y) is only used to calculate u(t+1,x,y) so
+  we can use the same array for both t-1 and t+1, needing only
+  two arrays, U[0] and U[1])
+
+ Dampening is simulated by subtracting 1/2^n of result.
+ n=4 gives best-looking result
+ n<4 (eg 2 or 3) thicker consistency, waves die out immediately
+ n>4 (eg 8 or 12) more fluid, waves die out slowly
+ */
+
+static void
+ripple(int fluidity)
+{
+  int across, down, pixel;
+  static int toggle;
+  static int count;
+  short *src, *dest;
+
+  if (toggle == 0) {
+    src = bufferA;
+    dest = bufferB;
+    toggle = 1;
+  } else {
+    src = bufferB;
+    dest = bufferA;
+    toggle = 0;
+  }
+
+#if 1
+  /* Traditional, squarer ripples */
+  switch (count) {
+  case 0: case 1:
+    pixel = 1 * width + 1;
+    for (down = 1; down < height - 1; down++, pixel += 2 * 1)
+      for (across = 1; across < width - 1; across++, pixel++) {
+        temp[pixel] =
+          (((src[pixel - 1] + src[pixel + 1] +
+             src[pixel - width] + src[pixel + width]) / 2)) - dest[pixel];
+      }
+
+    /* Smooth the output */
+    pixel = 1 * width + 1;
+    for (down = 1; down < height - 1; down++, pixel += 2 * 1)
+      for (across = 1; across < width - 1; across++, pixel++) {
+        int damp =
+          (temp[pixel - 1] + temp[pixel + 1] +
+           temp[pixel - width] + temp[pixel + width] +
+           temp[pixel - width - 1] + temp[pixel - width + 1] +
+           temp[pixel + width - 1] + temp[pixel + width + 1] +
+           temp[pixel]) / 9;
+        dest[pixel] = damp - (damp >> fluidity);
+      }
+    break;
+  case 2: case 3:
+    pixel = 1 * width + 1;
+    for (down = 1; down < height - 1; down++, pixel += 2 * 1)
+      for (across = 1; across < width - 1; across++, pixel++) {
+        int damp =
+          (((src[pixel - 1] + src[pixel + 1] +
+             src[pixel - width] + src[pixel + width]) / 2)) - dest[pixel];
+        dest[pixel] = damp - (damp >> fluidity);
+      }
+    break;
+  }
+  if (++count > 3) count = 0;
+#else
+  /* Rounder ripples, but with strange cross effect in center */
+  pixel = 1 * width + 1;
+  for (down = 1; down < height - 1; down++, pixel += 2 * 1)
+    for (across = 1; across < width - 1; across++, pixel++) {
+      int grad;
+      grad =
+        (src[pixel - 1] + src[pixel + 1] +
+         src[pixel - width] + src[pixel + width] +
+         src[pixel - width - 1] + src[pixel - width + 1] +
+         src[pixel + width - 1] + src[pixel + width + 1]) / 2;
+      dest[pixel] = grad/2 - dest[pixel];
+      dest[pixel] -= (dest[pixel] >> fluidity);
+    }
+#endif
+
+  if (transparent)
+    draw_transparent(dest);
+  else
+    draw_ripple(dest);
+}
+
+
+/*      -------------------------------------------             */
+
+
+char *progclass = "Ripples";
+
+char *defaults[] =
+{
+  ".background:                black",
+  ".foreground:                #FFAF5F",
+  "*colors:            200",
+  "*dontClearRoot:     True",
+  "*delay:             50000",
+  "*rate:              5",
+  "*box:               0",
+  "*water:             False",
+  "*oily:              False",
+  "*stir:              False",
+  "*fluidity:          6",
+  "*light:             0",
+#ifdef HAVE_XSHM_EXTENSION
+  "*useSHM: True",
+#endif                         /* HAVE_XSHM_EXTENSION */
+  0
+};
+
+XrmOptionDescRec options[] =
+{
+  { "-colors", ".colors",      XrmoptionSepArg, 0},
+  { "-colours",        ".colors",      XrmoptionSepArg, 0},
+  {"-delay",   ".delay",       XrmoptionSepArg, 0},
+  {"-rate",    ".rate",        XrmoptionSepArg, 0},
+  {"-box",     ".box",         XrmoptionSepArg, 0},
+  {"-water",   ".water",       XrmoptionNoArg, "True"},
+  {"-oily",    ".oily",        XrmoptionNoArg, "True"},
+  {"-stir",    ".stir",        XrmoptionNoArg, "True"},
+  {"-fluidity",        ".fluidity",    XrmoptionSepArg, 0},
+  {"-light",   ".light",       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 iterations = 0;
+  int delay = get_integer_resource("delay", "Integer");
+  int rate = get_integer_resource("rate", "Integer");
+  int box = get_integer_resource("box", "Integer");
+  int oily = get_boolean_resource("oily", "Boolean");
+  int stir = get_boolean_resource("stir", "Boolean");
+  int fluidity = get_integer_resource("fluidity", "Integer");
+  transparent = get_boolean_resource("water", "Boolean");
+  use_shm = get_boolean_resource("useSHM", "Boolean");
+  light = get_integer_resource("light", "Integer");
+
+  if (fluidity <= 1) fluidity = 1;
+  if (fluidity > 16) fluidity = 16; /* 16 = sizeof(short) */
+  if (light < 0) light = 0;
+
+  { int i;
+  for (i = 0; i < TABLE; i++)
+    cos_tab[i] = cos(i * M_PI/2 / TABLE);
+  }
+
+  setup_X(disp, win);
+
+  ncolors = get_integer_resource ("colors", "Colors");
+  if (0 == ncolors)            /* English spelling? */
+    ncolors = get_integer_resource ("colours", "Colors");
+  if (oily)
+    init_oily_colors();
+  else
+    init_linear_colors();
+
+  if (transparent && light > 0) {
+    bright = dobright;
+    set_mask(visual->red_mask,   &rmask, &rshift);
+    set_mask(visual->green_mask, &gmask, &gshift);
+    set_mask(visual->blue_mask,  &bmask, &bshift);
+    if (rmask == 0) bright = nobright;
+  } else
+    bright = nobright;
+
+  init_ripples(0, -SPLASH); /* Start off without any drops */
+
+  while (1) {
+    if (rate > 0 && (iterations % rate) == 0)
+      add_drop(ripple_blob, -SPLASH);
+    if (stir)
+      add_drop(ripple_stir, -SPLASH);
+    if (box > 0 && (random() % box) == 0)
+      add_drop(ripple_box, -SPLASH);
+
+    ripple(fluidity);
+    DisplayImage();
+
+    XSync(display,False);
+    screenhack_handle_events(display);
+    if (delay)
+      usleep(delay);
+
+    iterations++;
+  }
+}