ftp://ftp.demon.nl/disk1/redhat-contrib/libc5/SRPMS/xscreensaver-2.14-1.src.rpm
[xscreensaver] / hacks / worm.c
diff --git a/hacks/worm.c b/hacks/worm.c
new file mode 100644 (file)
index 0000000..7eb5fdf
--- /dev/null
@@ -0,0 +1,441 @@
+
+/* -*- Mode: C; tab-width: 4 -*- */
+/* worm --- draw wiggly worms */
+
+#if !defined( lint ) && !defined( SABER )
+static const char sccsid[] = "@(#)worm.c       4.04 97/07/28 xlockmore";
+
+#endif
+
+/*-
+ * Copyright (c) 1991 by Patrick J. Naughton.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind.  The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof.  In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ * Revision History:
+ * 10-May-97: Compatible with xscreensaver
+ * 03-Sep-96: fixed bug in allocation of space for worms, added 3d support
+ *            Henrik Theiling <theiling@coli.uni-sb.de>
+ * 27-Sep-95: put back malloc
+ * 23-Sep-93: got rid of "rint". (David Bagley)
+ * 27-Sep-91: got rid of all malloc calls since there were no calls to free().
+ * 25-Sep-91: Integrated into X11R5 contrib xlock.
+ *
+ * Adapted from a concept in the Dec 87 issue of Scientific American p. 142.
+ *
+ * SunView version: Brad Taylor <brad@sun.com>
+ * X11 version: Dave Lemke <lemke@ncd.com>
+ * xlock version: Boris Putanec <bp@cs.brown.edu>
+ */
+
+#ifdef STANDALONE
+# define PROGCLASS "Worm"
+# define HACK_INIT init_worm
+# define HACK_DRAW draw_worm
+# define worm_opts xlockmore_opts
+# define DEFAULTS      "*delay:  17000 \n"     \
+                                       "*count:  -20 \n"               \
+                                       "*cycles:  10 \n"               \
+                                       "*size:   -3 \n"                \
+                                       "*ncolors: 150 \n"              \
+                                       "*use3d:   False \n"    \
+                                       "*delta3d: 1.5 \n"              \
+                                       "*right3d: red \n"              \
+                                       "*left3d:  blue \n"             \
+                                       "*both3d:  magenta \n"  \
+                                       "*none3d:  black \n     "
+# define SMOOTH_COLORS
+# include "xlockmore.h"                /* in xscreensaver distribution */
+#else /* STANDALONE */
+# include "xlock.h"            /* in xlockmore distribution */
+#endif /* STANDALONE */
+
+ModeSpecOpt worm_opts =
+{0, NULL, 0, NULL, NULL};
+
+#define MINSIZE 1
+
+#define SEGMENTS  36
+#define MINWORMS 1
+
+#define MAXZ      750
+#define MINZ      100
+#define SCREENZ   200
+#define GETZDIFF(z) (MI_DELTA3D(mi)*20.0*(1.0-(SCREENZ)/((float)(z)+MINZ)))
+#define IRINT(x) ((int)(((x)>0.0)?(x)+0.5:(x)-0.5))
+
+/* How many segments to draw per cycle when redrawing */
+#define REDRAWSTEP 3
+
+typedef struct {
+       XPoint     *circ;
+       int        *diffcirc;
+       int         dir, dir2;
+       int         tail;
+       int         x, y, z;
+       int         redrawing, redrawpos;
+} wormstuff;
+
+typedef struct {
+       int         xsize, ysize, zsize;
+       int         wormlength;
+       int         nc;
+       int         nw;
+       int         circsize;
+       wormstuff  *worm;
+       XRectangle *rects;      /* [NUMCOLORS * batchcount/NUMCOLORS+1] */
+       int         maxsize;
+       int        *size;
+       unsigned int chromo;
+} wormstruct;
+
+static float sintab[SEGMENTS];
+static float costab[SEGMENTS];
+static int  init_table = 0;
+
+static wormstruct *worms = NULL;
+
+static void
+worm_doit(ModeInfo * mi, int which, unsigned long color)
+{
+       Display    *display = MI_DISPLAY(mi);
+       Window      window = MI_WINDOW(mi);
+       GC          gc = MI_GC(mi);
+       wormstruct *wp = &worms[MI_SCREEN(mi)];
+       wormstuff  *ws = &wp->worm[which];
+       int         x, y, z;
+       int         diff;
+
+       ws->tail++;
+       if (ws->tail == wp->wormlength)
+               ws->tail = 0;
+
+       x = ws->circ[ws->tail].x;
+       y = ws->circ[ws->tail].y;
+
+       if (MI_WIN_IS_USE3D(mi)) {
+               diff = ws->diffcirc[ws->tail];
+               if (MI_WIN_IS_INSTALL(mi)) {
+                       XSetForeground(display, gc, MI_NONE_COLOR(mi));
+                       XFillRectangle(display, window, gc, x - diff, y,
+                                      wp->circsize, wp->circsize);
+                       XFillRectangle(display, window, gc, x + diff, y,
+                                      wp->circsize, wp->circsize);
+               } else {
+                       XClearArea(display, window, x - diff, y,
+                                  wp->circsize, wp->circsize, False);
+                       XClearArea(display, window, x + diff, y,
+                                  wp->circsize, wp->circsize, False);
+               }
+       } else
+               XClearArea(display, window, x, y, wp->circsize, wp->circsize, False);
+
+       if (LRAND() & 1)
+               ws->dir = (ws->dir + 1) % SEGMENTS;
+       else
+               ws->dir = (ws->dir + SEGMENTS - 1) % SEGMENTS;
+
+       x = (ws->x + IRINT((float) wp->circsize * costab[ws->dir]) +
+            wp->xsize) % wp->xsize;
+       y = (ws->y + IRINT((float) wp->circsize * sintab[ws->dir]) +
+            wp->ysize) % wp->ysize;
+
+       ws->circ[ws->tail].x = x;
+       ws->circ[ws->tail].y = y;
+       ws->x = x;
+       ws->y = y;
+
+       if (MI_WIN_IS_USE3D(mi)) {
+               if (LRAND() & 1)
+                       ws->dir2 = (ws->dir2 + 1) % SEGMENTS;
+               else
+                       ws->dir2 = (ws->dir2 + SEGMENTS - 1) % SEGMENTS;
+               /* for the z-axis the wrap-around looks bad, so worms should just turn around. */
+               z = (int) (ws->z + wp->circsize * sintab[ws->dir2]);
+               if (z < 0 || z >= wp->zsize)
+                       z = (int) (ws->z - wp->circsize * sintab[ws->dir2]);
+
+               diff = (int) (GETZDIFF(z) + 0.5);       /* ROUND */
+               ws->diffcirc[ws->tail] = diff;
+
+               ws->z = z;
+
+               /* right eye */
+               color = 0;
+               wp->rects[color * wp->maxsize + wp->size[color]].x = x + diff;
+               wp->rects[color * wp->maxsize + wp->size[color]].y = y;
+               wp->size[color]++;
+
+               /* left eye */
+               color = 1;
+               wp->rects[color * wp->maxsize + wp->size[color]].x = x - diff;
+               wp->rects[color * wp->maxsize + wp->size[color]].y = y;
+               wp->size[color]++;
+
+#if 0
+               if (ws->redrawing) {    /* Too hard for now */
+                       int         j;
+
+                       for (j = 0; j < REDRAWSTEP; j++) {
+                               int         k = (ws->tail - ws->redrawpos + wp->wormlength)
+                               % wp->wormlength;
+
+                               color = 0;
+                               wp->rects[color * wp->maxsize + wp->size[color]].x =
+                                       ws->circ[k].x + ws->diffcirc[k];
+                               wp->rects[color * wp->maxsize + wp->size[color]].y =
+                                       ws->circ[k].y;
+                               wp->size[color]++;
+
+                               color = 1;
+                               wp->rects[color * wp->maxsize + wp->size[color]].x =
+                                       ws->circ[k].x - ws->diffcirc[k];
+                               wp->rects[color * wp->maxsize + wp->size[color]].y =
+                                       ws->circ[k].y;
+                               wp->size[color]++;
+
+                               if (++(ws->redrawpos) >= wp->wormlength) {
+                                       ws->redrawing = 0;
+                                       break;
+                               }
+                       }
+               }
+#endif
+
+       } else {
+
+               wp->rects[color * wp->maxsize + wp->size[color]].x = x;
+               wp->rects[color * wp->maxsize + wp->size[color]].y = y;
+               wp->size[color]++;
+               if (ws->redrawing) {
+                       int         j;
+
+                       ws->redrawpos++;
+                       /* Compensates for the changed ws->tail
+                          since the last callback. */
+
+                       for (j = 0; j < REDRAWSTEP; j++) {
+                               int         k = (ws->tail - ws->redrawpos + wp->wormlength)
+                               % wp->wormlength;
+
+                               wp->rects[color * wp->maxsize + wp->size[color]].x = ws->circ[k].x;
+                               wp->rects[color * wp->maxsize + wp->size[color]].y = ws->circ[k].y;
+                               wp->size[color]++;
+
+                               if (++(ws->redrawpos) >= wp->wormlength) {
+                                       ws->redrawing = 0;
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
+static void
+free_worms(wormstruct * wp)
+{
+       int         wn;
+
+       if (wp->worm) {
+               for (wn = 0; wn < wp->nw; wn++) {
+                       if (wp->worm[wn].circ)
+                               (void) free((void *) wp->worm[wn].circ);
+                       if (wp->worm[wn].diffcirc)
+                               (void) free((void *) wp->worm[wn].diffcirc);
+               }
+               (void) free((void *) wp->worm);
+               wp->worm = NULL;
+       }
+       if (wp->rects) {
+               (void) free((void *) wp->rects);
+               wp->rects = NULL;
+       }
+       if (wp->size) {
+               (void) free((void *) wp->size);
+               wp->size = NULL;
+       }
+}
+
+void
+init_worm(ModeInfo * mi)
+{
+       wormstruct *wp;
+       int         size = MI_SIZE(mi);
+       int         i, j;
+
+       if (worms == NULL) {
+               if ((worms = (wormstruct *) calloc(MI_NUM_SCREENS(mi),
+                                              sizeof (wormstruct))) == NULL)
+                       return;
+       }
+       wp = &worms[MI_SCREEN(mi)];
+       if (MI_NPIXELS(mi) <= 2 || MI_WIN_IS_USE3D(mi))
+               wp->nc = 2;
+       else
+               wp->nc = MI_NPIXELS(mi);
+       if (wp->nc > NUMCOLORS)
+               wp->nc = NUMCOLORS;
+
+       free_worms(wp);
+       wp->nw = MI_BATCHCOUNT(mi);
+       if (wp->nw < -MINWORMS)
+               wp->nw = NRAND(-wp->nw - MINWORMS + 1) + MINWORMS;
+       else if (wp->nw < MINWORMS)
+               wp->nw = MINWORMS;
+       if (!wp->worm)
+               wp->worm = (wormstuff *) malloc(wp->nw * sizeof (wormstuff));
+
+       if (!wp->size)
+               wp->size = (int *) malloc(NUMCOLORS * sizeof (int));
+
+       wp->maxsize = (REDRAWSTEP + 1) * wp->nw;        /*  / wp->nc + 1; */
+       if (!wp->rects)
+               wp->rects =
+                       (XRectangle *) malloc(wp->maxsize * NUMCOLORS * sizeof (XRectangle));
+
+
+       if (!init_table) {
+               init_table = 1;
+               for (i = 0; i < SEGMENTS; i++) {
+                       sintab[i] = SINF(i * 2.0 * M_PI / SEGMENTS);
+                       costab[i] = COSF(i * 2.0 * M_PI / SEGMENTS);
+               }
+       }
+       wp->xsize = MI_WIN_WIDTH(mi);
+       wp->ysize = MI_WIN_HEIGHT(mi);
+       wp->zsize = MAXZ - MINZ + 1;
+       if (MI_NPIXELS(mi) > 2)
+               wp->chromo = NRAND(MI_NPIXELS(mi));
+
+       if (size < -MINSIZE)
+               wp->circsize = NRAND(-size - MINSIZE + 1) + MINSIZE;
+       else if (size < MINSIZE)
+               wp->circsize = MINSIZE;
+       else
+               wp->circsize = size;
+
+       for (i = 0; i < wp->nc; i++) {
+               for (j = 0; j < wp->maxsize; j++) {
+                       wp->rects[i * wp->maxsize + j].width = wp->circsize;
+                       wp->rects[i * wp->maxsize + j].height = wp->circsize;
+
+               }
+       }
+       (void) memset((char *) wp->size, 0, wp->nc * sizeof (int));
+
+       wp->wormlength = (int) sqrt(wp->xsize + wp->ysize) *
+               MI_CYCLES(mi) / 8;      /* Fudge this to something reasonable */
+       for (i = 0; i < wp->nw; i++) {
+               wp->worm[i].circ = (XPoint *) malloc(wp->wormlength * sizeof (XPoint));
+               wp->worm[i].diffcirc = (int *) malloc(wp->wormlength * sizeof (int));
+
+               for (j = 0; j < wp->wormlength; j++) {
+                       wp->worm[i].circ[j].x = wp->xsize / 2;
+                       wp->worm[i].circ[j].y = wp->ysize / 2;
+                       if (MI_WIN_IS_USE3D(mi))
+                               wp->worm[i].diffcirc[j] = 0;
+               }
+               wp->worm[i].dir = NRAND(SEGMENTS);
+               wp->worm[i].dir2 = NRAND(SEGMENTS);
+               wp->worm[i].tail = 0;
+               wp->worm[i].x = wp->xsize / 2;
+               wp->worm[i].y = wp->ysize / 2;
+               wp->worm[i].z = SCREENZ - MINZ;
+               wp->worm[i].redrawing = 0;
+       }
+
+       if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi)) {
+               XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_NONE_COLOR(mi));
+               XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
+                              0, 0, wp->xsize, wp->ysize);
+       } else
+               XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
+}
+
+void
+draw_worm(ModeInfo * mi)
+{
+       Display    *display = MI_DISPLAY(mi);
+       Window      window = MI_WINDOW(mi);
+       GC          gc = MI_GC(mi);
+       wormstruct *wp = &worms[MI_SCREEN(mi)];
+       unsigned long wcolor;
+       int         i;
+
+       (void) memset((char *) wp->size, 0, wp->nc * sizeof (int));
+
+       for (i = 0; i < wp->nw; i++) {
+               if (MI_NPIXELS(mi) > 2) {
+                       wcolor = (i + wp->chromo) % wp->nc;
+
+                       worm_doit(mi, i, wcolor);
+               } else
+                       worm_doit(mi, i, (unsigned long) 0);
+       }
+
+       if (MI_WIN_IS_USE3D(mi)) {
+               if (MI_WIN_IS_INSTALL(mi))
+                       XSetFunction(display, gc, GXor);
+               XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
+               XFillRectangles(display, window, gc, &(wp->rects[0]), wp->size[0]);
+
+               XSetForeground(display, gc, MI_LEFT_COLOR(mi));
+               XFillRectangles(display, window, gc, &(wp->rects[wp->maxsize]), wp->size[1]);
+               if (MI_WIN_IS_INSTALL(mi))
+                       XSetFunction(display, gc, GXcopy);
+       } else if (MI_NPIXELS(mi) > 2) {
+               for (i = 0; i < wp->nc; i++) {
+                       XSetForeground(display, gc, MI_PIXEL(mi, i));
+                       XFillRectangles(display, window, gc, &(wp->rects[i * wp->maxsize]), wp->size[i]);
+               }
+       } else {
+               XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
+               XFillRectangles(display, window, gc,
+                               &(wp->rects[0]), wp->size[0]);
+       }
+
+       if (++wp->chromo == (unsigned long) wp->nc)
+               wp->chromo = 0;
+}
+
+void
+release_worm(ModeInfo * mi)
+{
+       if (worms != NULL) {
+               int         screen;
+
+               for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
+                       free_worms(&worms[screen]);
+               (void) free((void *) worms);
+               worms = NULL;
+       }
+}
+
+void
+refresh_worm(ModeInfo * mi)
+{
+       if (MI_WIN_IS_USE3D(mi))
+               /* The 3D code does drawing&clearing by XORing.  We do not
+                  want to go to too much trouble here to make it redraw
+                  correctly. */
+               XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
+       else if (worms != NULL) {
+               wormstruct *wp = &worms[MI_SCREEN(mi)];
+               int         i;
+
+               for (i = 0; i < wp->nw; i++) {
+                       wp->worm[i].redrawing = 1;
+                       wp->worm[i].redrawpos = 0;
+               }
+       }
+}