http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.01.tar.gz
[xscreensaver] / hacks / twang.c
diff --git a/hacks/twang.c b/hacks/twang.c
new file mode 100644 (file)
index 0000000..8b975dc
--- /dev/null
@@ -0,0 +1,763 @@
+/* twang, twist around screen bits, v1.3
+ * by Dan Bornstein, danfuzz@milk.com
+ * Copyright (c) 2003 Dan Bornstein. All rights reserved.
+ *
+ * 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.
+ *
+ * See the included man page for more details.
+ */
+
+#include <math.h>
+#include "screenhack.h"
+#include <X11/Xutil.h>
+
+#ifdef HAVE_XSHM_EXTENSION
+#include "xshm.h"
+#endif
+
+#define FLOAT double
+
+/* random float in the range (-1..1) */
+#define RAND_FLOAT_PM1 \
+        (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000) * 2 - 1)
+
+/* random float in the range (0..1) */
+#define RAND_FLOAT_01 \
+        (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000))
+
+
+
+/* parameters that are user configurable */
+
+/* whether or not to use xshm */
+#ifdef HAVE_XSHM_EXTENSION
+static Bool useShm;
+#endif
+
+/* delay (usec) between iterations */
+static int delay;
+
+/* the maximum number of columns of tiles */
+static int maxColumns;
+
+/* the maximum number of rows of tiles */
+static int maxRows;
+
+/* the size (width and height) of a tile */
+static int tileSize;
+
+/* the width of the border around each tile */
+static int borderWidth;
+
+/* the chance, per iteration, of an interesting event happening */
+static FLOAT eventChance;
+
+/* friction: the fraction (0..1) by which velocity decreased per iteration */
+static FLOAT friction;
+
+/* springiness: the fraction (0..1) of the orientation that turns into 
+ * velocity towards the center */
+static FLOAT springiness;
+
+/* transference: the fraction (0..1) of the orientations of orthogonal
+ * neighbors that turns into velocity (in the same direction as the
+ * orientation) */
+static FLOAT transference;
+
+
+
+/* non-user-modifiable immutable definitions */
+
+/* width and height of the window */
+static int windowWidth;
+static int windowHeight;
+
+static Display *display;        /* the display to draw on */
+static Window window;           /* the window to draw on */
+static Screen *screen;          /* the screen to draw on */
+
+static XImage *sourceImage;     /* image source of stuff to draw */
+static XImage *workImage;       /* work area image, used when rendering */
+static XImage *backgroundImage; /* image filled with background pixels */
+
+static GC backgroundGC;         /* GC for the background color */
+static GC foregroundGC;         /* GC for the foreground color */
+static GC borderGC;             /* GC for the border color */
+unsigned long backgroundPixel;  /* background color as a pixel value */
+unsigned long borderPixel;      /* border color as a pixel value */
+
+#ifdef HAVE_XSHM_EXTENSION
+XShmSegmentInfo shmInfo;
+#endif
+
+
+
+/* the model */
+
+typedef struct
+{
+    int x;        /* x coordinate of the center of the tile */
+    int y;        /* y coordinate of the center of the tile */
+    FLOAT angle;  /* angle of the tile (-pi..pi) */
+    FLOAT zoom;   /* log of the zoom of the tile (-1..1) */
+    FLOAT vAngle; /* angular velocity (-pi/4..pi/4) */
+    FLOAT vZoom;  /* zoomular velocity (-0.25..0.25) */
+}
+Tile;
+
+static Tile *tiles;  /* array of tiles (left->right, top->bottom, row major) */
+static int rows;     /* number of rows of tiles */
+static int columns;  /* number of columns of tiles */
+
+static Tile **sortedTiles; /* array of tile pointers, sorted by zoom */
+static int tileCount;     /* total number of tiles */
+
+#define TILE_AT(col,row) (&tiles[(row) * columns + (col)])
+
+#define MAX_VANGLE (M_PI / 4.0)
+#define MAX_VZOOM 0.25
+
+#define RAND_ANGLE (RAND_FLOAT_PM1 * M_PI)
+#define RAND_ZOOM (RAND_FLOAT_PM1)
+#define RAND_VANGLE (RAND_FLOAT_PM1 * MAX_VANGLE)
+#define RAND_VZOOM (RAND_FLOAT_PM1 * MAX_VZOOM)
+
+
+
+/*
+ * overall setup stuff
+ */
+
+/* grab the source image */
+static void grabImage (XWindowAttributes *xwa)
+{
+    XFillRectangle (display, window, backgroundGC, 0, 0, 
+                   windowWidth, windowHeight);
+    backgroundImage = 
+       XGetImage (display, window, 0, 0, windowWidth, windowHeight,
+                  ~0L, ZPixmap);
+
+    grab_screen_image (screen, window);
+    sourceImage = XGetImage (display, window, 0, 0, windowWidth, windowHeight,
+                            ~0L, ZPixmap);
+
+#ifdef HAVE_XSHM_EXTENSION
+    workImage = NULL;
+    if (useShm) 
+    {
+       workImage = create_xshm_image (display, xwa->visual, xwa->depth,
+                                      ZPixmap, 0, &shmInfo, 
+                                      windowWidth, windowHeight);
+       if (!workImage) 
+       {
+           useShm = False;
+           fprintf (stderr, "create_xshm_image failed\n");
+       }
+    }
+
+    if (workImage == NULL)
+#endif /* HAVE_XSHM_EXTENSION */
+
+       /* just use XSubImage to acquire the right visual, depth, etc;
+        * easier than the other alternatives */
+       workImage = XSubImage (sourceImage, 0, 0, windowWidth, windowHeight);
+}
+
+/* set up the system */
+static void setup (void)
+{
+    XWindowAttributes xgwa;
+    XGCValues gcv;
+
+    XGetWindowAttributes (display, window, &xgwa);
+
+    screen = xgwa.screen;
+    windowWidth = xgwa.width;
+    windowHeight = xgwa.height;
+
+    gcv.line_width = borderWidth;
+    gcv.foreground = get_pixel_resource ("borderColor", "BorderColor",
+                                        display, xgwa.colormap);
+    borderPixel = gcv.foreground;
+    borderGC = XCreateGC (display, window, GCForeground | GCLineWidth, 
+                         &gcv);
+
+    gcv.foreground = get_pixel_resource ("background", "Background",
+                                        display, xgwa.colormap);
+    backgroundPixel = gcv.foreground;
+    backgroundGC = XCreateGC (display, window, GCForeground, &gcv);
+
+    gcv.foreground = get_pixel_resource ("foreground", "Foreground",
+                                        display, xgwa.colormap);
+    foregroundGC = XCreateGC (display, window, GCForeground, &gcv);
+
+    grabImage (&xgwa);
+}
+
+
+
+/*
+ * the simulation
+ */
+
+/* event: randomize all the angular velocities */
+static void randomizeAllAngularVelocities (void)
+{
+    int c;
+    int r;
+
+    for (r = 0; r < rows; r++)
+    {
+       for (c = 0; c < columns; c++)
+       {
+           TILE_AT (c, r)->vAngle = RAND_VANGLE;
+       }
+    }
+}
+
+/* event: randomize all the zoomular velocities */
+static void randomizeAllZoomularVelocities (void)
+{
+    int c;
+    int r;
+
+    for (r = 0; r < rows; r++)
+    {
+       for (c = 0; c < columns; c++)
+       {
+           TILE_AT (c, r)->vZoom = RAND_VZOOM;
+       }
+    }
+}
+
+/* event: randomize all the velocities */
+static void randomizeAllVelocities (void)
+{
+    randomizeAllAngularVelocities ();
+    randomizeAllZoomularVelocities ();
+}
+
+/* event: randomize all the angular orientations */
+static void randomizeAllAngularOrientations (void)
+{
+    int c;
+    int r;
+
+    for (r = 0; r < rows; r++)
+    {
+       for (c = 0; c < columns; c++)
+       {
+           TILE_AT (c, r)->angle = RAND_ANGLE;
+       }
+    }
+}
+
+/* event: randomize all the zoomular orientations */
+static void randomizeAllZoomularOrientations (void)
+{
+    int c;
+    int r;
+
+    for (r = 0; r < rows; r++)
+    {
+       for (c = 0; c < columns; c++)
+       {
+           TILE_AT (c, r)->zoom = RAND_ZOOM;
+       }
+    }
+}
+
+/* event: randomize all the orientations */
+static void randomizeAllOrientations (void)
+{
+    randomizeAllAngularOrientations ();
+    randomizeAllZoomularOrientations ();
+}
+
+/* event: randomize everything */
+static void randomizeEverything (void)
+{
+    randomizeAllVelocities ();
+    randomizeAllOrientations ();
+}
+
+/* event: pick one tile and randomize all its stats */
+static void randomizeOneTile (void)
+{
+    int c = RAND_FLOAT_01 * columns;
+    int r = RAND_FLOAT_01 * rows;
+
+    Tile *t = TILE_AT (c, r);
+    t->angle = RAND_ANGLE;
+    t->zoom = RAND_ZOOM;
+    t->vAngle = RAND_VANGLE;
+    t->vZoom = RAND_VZOOM;
+}
+
+/* event: pick one row and randomize everything about each of its tiles */
+static void randomizeOneRow (void)
+{
+    int c;
+    int r = RAND_FLOAT_01 * rows;
+
+    for (c = 0; c < columns; c++)
+    {
+       Tile *t = TILE_AT (c, r);
+       t->angle = RAND_ANGLE;
+       t->zoom = RAND_ZOOM;
+       t->vAngle = RAND_VANGLE;
+       t->vZoom = RAND_VZOOM;
+    }
+}
+
+/* event: pick one column and randomize everything about each of its tiles */
+static void randomizeOneColumn (void)
+{
+    int c = RAND_FLOAT_01 * columns;
+    int r;
+
+    for (r = 0; r < rows; r++)
+    {
+       Tile *t = TILE_AT (c, r);
+       t->angle = RAND_ANGLE;
+       t->zoom = RAND_ZOOM;
+       t->vAngle = RAND_VANGLE;
+       t->vZoom = RAND_VZOOM;
+    }
+}
+
+/* do model event processing */
+static void modelEvents (void)
+{
+    int which;
+
+    if (RAND_FLOAT_01 > eventChance)
+    {
+       return;
+    }
+
+    which = RAND_FLOAT_01 * 10;
+
+    switch (which)
+    {
+       case 0: randomizeAllAngularVelocities ();    break;
+       case 1: randomizeAllZoomularVelocities ();   break;
+       case 2: randomizeAllVelocities ();           break;
+       case 3: randomizeAllAngularOrientations ();  break;
+       case 4: randomizeAllZoomularOrientations (); break;
+       case 5: randomizeAllOrientations ();         break;
+       case 6: randomizeEverything ();              break;
+       case 7: randomizeOneTile ();                 break;
+       case 8: randomizeOneColumn ();               break;
+       case 9: randomizeOneRow ();                  break;
+    }
+}
+
+/* update the model for one iteration */
+static void updateModel (void)
+{
+    int r;
+    int c;
+
+    /* for each tile, decrease its velocities according to the friction,
+     * and increase them based on its current orientation and the orientations
+     * of its orthogonal neighbors */
+    for (r = 0; r < rows; r++)
+    {
+       for (c = 0; c < columns; c++)
+       {
+           Tile *t = TILE_AT (c, r);
+           FLOAT a = t->angle;
+           FLOAT z = t->zoom;
+           FLOAT va = t->vAngle;
+           FLOAT vz = t->vZoom;
+
+           va -= t->angle * springiness;
+           vz -= t->zoom * springiness;
+
+           if (c > 0)
+           {
+               Tile *t2 = TILE_AT (c - 1, r);
+               va += (t2->angle - a) * transference;
+               vz += (t2->zoom - z) * transference;
+           }
+
+           if (c < (columns - 1))
+           {
+               Tile *t2 = TILE_AT (c + 1, r);
+               va += (t2->angle - a) * transference;
+               vz += (t2->zoom - z) * transference;
+           }
+
+           if (r > 0)
+           {
+               Tile *t2 = TILE_AT (c, r - 1);
+               va += (t2->angle - a) * transference;
+               vz += (t2->zoom - z) * transference;
+           }
+
+           if (r < (rows - 1))
+           {
+               Tile *t2 = TILE_AT (c, r + 1);
+               va += (t2->angle - a) * transference;
+               vz += (t2->zoom - z) * transference;
+           }
+
+           va *= (1.0 - friction);
+           vz *= (1.0 - friction);
+
+           if (va > MAX_VANGLE) va = MAX_VANGLE;
+           else if (va < -MAX_VANGLE) va = -MAX_VANGLE;
+           t->vAngle = va;
+
+           if (vz > MAX_VZOOM) vz = MAX_VZOOM;
+           else if (vz < -MAX_VZOOM) vz = -MAX_VZOOM;
+           t->vZoom = vz;
+       }
+    }
+
+    /* for each tile, update its orientation based on its velocities */
+    for (r = 0; r < rows; r++)
+    {
+       for (c = 0; c < columns; c++)
+       {
+           Tile *t = TILE_AT (c, r);
+           FLOAT a = t->angle + t->vAngle;
+           FLOAT z = t->zoom + t->vZoom;
+
+           if (a > M_PI) a = M_PI;
+           else if (a < -M_PI) a = -M_PI;
+           t->angle = a;
+
+           if (z > 1.0) z = 1.0;
+           else if (z < -1.0) z = -1.0;
+           t->zoom = z;
+       }
+    }
+}
+
+/* the comparator to us to sort the tiles (used immediately below); it'd
+ * sure be nice if C allowed inner functions (or jeebus-forbid *real
+ * closures*!) */
+static int sortTilesComparator (const void *v1, const void *v2)
+{
+    Tile *t1 = *(Tile **) v1;
+    Tile *t2 = *(Tile **) v2;
+    
+    if (t1->zoom < t2->zoom)
+    {
+       return -1;
+    }
+
+    if (t1->zoom > t2->zoom)
+    {
+       return 1;
+    }
+
+    return 0;
+}
+
+/* sort the tiles in sortedTiles by zoom */
+static void sortTiles (void)
+{
+    qsort (sortedTiles, tileCount, sizeof (Tile *), sortTilesComparator);
+}
+
+/* render the given tile */
+static void renderTile (Tile *t)
+{
+    /* note: the zoom as stored per tile is log-based (centered on 0, with
+     * 0 being no zoom, but the range for zoom-as-drawn is 0.4..2.5,
+     * hence the alteration of t->zoom, below */
+
+    int x, y;
+
+    int tx = t->x;
+    int ty = t->y;
+
+    FLOAT zoom = pow (2.5, t->zoom);
+    FLOAT ang = -t->angle;
+    FLOAT sinAng = sin (ang);
+    FLOAT cosAng = cos (ang);
+
+    FLOAT innerBorder = (tileSize - borderWidth) / 2.0;
+    FLOAT outerBorder = innerBorder + borderWidth;
+
+    int maxCoord = outerBorder * zoom * (fabs (sinAng) + fabs (cosAng));
+    int minX = tx - maxCoord;
+    int maxX = tx + maxCoord;
+    int minY = ty - maxCoord;
+    int maxY = ty + maxCoord;
+
+    FLOAT prey;
+
+    if (minX < 0) minX = 0;
+    if (maxX > windowWidth) maxX = windowWidth;
+    if (minY < 0) minY = 0;
+    if (maxY > windowHeight) maxY = windowHeight;
+
+    sinAng /= zoom;
+    cosAng /= zoom;
+
+    for (y = minY, prey = y - ty; y < maxY; y++, prey++)
+    {
+       FLOAT prex = minX - tx;
+       FLOAT srcx = prex * cosAng - prey * sinAng;
+       FLOAT srcy = prex * sinAng + prey * cosAng;
+
+       for (x = minX; 
+            x < maxX; 
+            x++, srcx += cosAng, srcy += sinAng)
+       {
+           if ((srcx < -innerBorder) || (srcx >= innerBorder) ||
+               (srcy < -innerBorder) || (srcy >= innerBorder))
+           {
+               if ((srcx < -outerBorder) || (srcx >= outerBorder) ||
+                   (srcy < -outerBorder) || (srcy >= outerBorder))
+               {
+                   continue;
+               }
+               XPutPixel (workImage, x, y, borderPixel);
+           }
+           else
+           {
+               unsigned long p = 
+                   XGetPixel (sourceImage, srcx + tx, srcy + ty);
+               XPutPixel (workImage, x, y, p);
+           }
+       }
+    }
+}
+
+/* render and display the current model */
+static void renderFrame (void)
+{
+    int n;
+
+    memcpy (workImage->data, backgroundImage->data, 
+           workImage->bytes_per_line * workImage->height);
+
+    sortTiles ();
+
+    for (n = 0; n < tileCount; n++)
+    {
+       renderTile (sortedTiles[n]);
+    }
+
+#ifdef HAVE_XSHM_EXTENSION
+    if (useShm)
+       XShmPutImage (display, window, backgroundGC, workImage, 0, 0, 0, 0,
+                     windowWidth, windowHeight, False);
+    else
+#endif /* HAVE_XSHM_EXTENSION */
+       XPutImage (display, window, backgroundGC, workImage, 
+                  0, 0, 0, 0, windowWidth, windowHeight);
+}
+
+/* set up the model */
+static void setupModel (void)
+{
+    int c;
+    int r;
+
+    int leftX; /* x of the center of the top-left tile */
+    int topY;  /* y of the center of the top-left tile */
+
+    if (tileSize > (windowWidth / 2))
+    {
+       tileSize = windowWidth / 2;
+    }
+
+    if (tileSize > (windowHeight / 2))
+    {
+       tileSize = windowHeight / 2;
+    }
+
+    columns = windowWidth / tileSize;
+    rows = windowHeight / tileSize;
+
+    if ((maxColumns != 0) && (columns > maxColumns))
+    {
+       columns = maxColumns;
+    }
+
+    if ((maxRows != 0) && (rows > maxRows))
+    {
+       rows = maxRows;
+    }
+
+    tileCount = rows * columns;
+
+    leftX = (windowWidth - (columns * tileSize) + tileSize) / 2;
+    topY = (windowHeight - (rows * tileSize) + tileSize) / 2;
+
+    tiles = calloc (tileCount, sizeof (Tile));
+    sortedTiles = calloc (tileCount, sizeof (Tile *));
+
+    for (r = 0; r < rows; r++)
+    {
+       for (c = 0; c < columns; c++)
+       {
+           Tile *t = TILE_AT (c, r);
+           t->x = leftX + c * tileSize;
+           t->y = topY + r * tileSize;
+           sortedTiles[c + r * columns] = t;
+       }
+    }
+
+    randomizeEverything ();
+}
+
+/* do one iteration */
+static void oneIteration (void)
+{
+    modelEvents ();
+    updateModel ();
+    renderFrame ();
+}
+
+
+
+/* main and options and stuff */
+
+char *progclass = "Twang";
+
+char *defaults [] = {
+    ".background:      black",
+    ".foreground:      white",
+    "*borderColor:      blue",
+    "*borderWidth:     3",
+    "*delay:           10000",
+    "*eventChance:      0.01",
+    "*friction:                0.05",
+    "*maxColumns:       0",
+    "*maxRows:          0",
+    "*springiness:     0.1",
+    "*tileSize:                120",
+    "*transference:    0.025",
+#ifdef HAVE_XSHM_EXTENSION
+    "*useSHM: True",
+#endif
+    0
+};
+
+XrmOptionDescRec options [] = {
+  { "-border-color",     ".borderColor",    XrmoptionSepArg, 0 },
+  { "-border-width",     ".borderWidth",    XrmoptionSepArg, 0 },
+  { "-delay",            ".delay",          XrmoptionSepArg, 0 },
+  { "-event-chance",     ".eventChance",    XrmoptionSepArg, 0 },
+  { "-friction",         ".friction",       XrmoptionSepArg, 0 },
+  { "-max-columns",      ".maxColumns",     XrmoptionSepArg, 0 },
+  { "-max-rows",         ".maxRows",        XrmoptionSepArg, 0 },
+  { "-springiness",      ".springiness",    XrmoptionSepArg, 0 },
+  { "-tile-size",        ".tileSize",       XrmoptionSepArg, 0 },
+  { "-transference",     ".transference",   XrmoptionSepArg, 0 },
+#ifdef HAVE_XSHM_EXTENSION
+  { "-shm",              ".useSHM",         XrmoptionNoArg, "True" },
+  { "-no-shm",           ".useSHM",         XrmoptionNoArg, "False" },
+#endif
+  { 0, 0, 0, 0 }
+};
+
+/* initialize the user-specifiable params */
+static void initParams (void)
+{
+    int problems = 0;
+
+    borderWidth = get_integer_resource ("borderWidth", "Integer");
+    if (borderWidth < 0)
+    {
+       fprintf (stderr, "error: border width must be at least 0\n");
+       problems = 1;
+    }
+
+    delay = get_integer_resource ("delay", "Delay");
+    if (delay < 0)
+    {
+       fprintf (stderr, "error: delay must be at least 0\n");
+       problems = 1;
+    }
+
+    eventChance = get_float_resource ("eventChance", "Double");
+    if ((eventChance < 0.0) || (eventChance > 1.0))
+    {
+       fprintf (stderr, "error: eventChance must be in the range 0..1\n");
+       problems = 1;
+    }
+
+    friction = get_float_resource ("friction", "Double");
+    if ((friction < 0.0) || (friction > 1.0))
+    {
+       fprintf (stderr, "error: friction must be in the range 0..1\n");
+       problems = 1;
+    }
+
+    maxColumns = get_integer_resource ("maxColumns", "Integer");
+    if (maxColumns < 0)
+    {
+       fprintf (stderr, "error: max columns must be at least 0\n");
+       problems = 1;
+    }
+
+    maxRows = get_integer_resource ("maxRows", "Integer");
+    if (maxRows < 0)
+    {
+       fprintf (stderr, "error: max rows must be at least 0\n");
+       problems = 1;
+    }
+
+    springiness = get_float_resource ("springiness", "Double");
+    if ((springiness < 0.0) || (springiness > 1.0))
+    {
+       fprintf (stderr, "error: springiness must be in the range 0..1\n");
+       problems = 1;
+    }
+
+    tileSize = get_integer_resource ("tileSize", "Integer");
+    if (tileSize < 1)
+    {
+       fprintf (stderr, "error: tile size must be at least 1\n");
+       problems = 1;
+    }
+    
+    transference = get_float_resource ("transference", "Double");
+    if ((transference < 0.0) || (transference > 1.0))
+    {
+       fprintf (stderr, "error: transference must be in the range 0..1\n");
+       problems = 1;
+    }
+
+#ifdef HAVE_XSHM_EXTENSION
+    useShm = get_boolean_resource ("useSHM", "Boolean");
+#endif
+
+    if (problems)
+    {
+       exit (1);
+    }
+}
+
+/* main function */
+void screenhack (Display *dpy, Window win)
+{
+    display = dpy;
+    window = win;
+
+    initParams ();
+    setup ();
+    setupModel ();
+
+    for (;;) 
+    {
+       oneIteration ();
+        XSync (dpy, False);
+        screenhack_handle_events (dpy);
+       usleep (delay);
+    }
+}