From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / lisa.c
index 5859b77ec5ed4a8c31294f71209da65c64d6dc38..1a1159f62cdfadc9d4840033b6801d98d2b89497 100644 (file)
@@ -1,11 +1,12 @@
-/* -*- Mode: C; tab-width: 4 -*-
- * lisa.c --- animated full-loop lisajous figures
- */
-#if !defined( lint ) && !defined( SABER )
-static const char sccsid[] = "@(#)lisa.c       4.03 97/05/10 xlockmore";
+/* -*- Mode: C; tab-width: 4 -*- */
+/* lisa --- animated full-loop lissajous figures */
+
+#if 0
+static const char sccsid[] = "@(#)lisa.c       5.00 2000/11/01 xlockmore";
 #endif
 
-/* Copyright (c) 1997 by Caleb Cullen.
+/*-
+ * Copyright (c) 1997, 2006 by Caleb Cullen.
  *
  * Permission to use, copy, modify, and distribute this software and its
  * documentation for any purpose and without fee is hereby granted,
@@ -20,59 +21,125 @@ static const char sccsid[] = "@(#)lisa.c   4.03 97/05/10 xlockmore";
  * other special, indirect and consequential damages.
  *
  * Revision History:
- * 10-May-97: Compatible with xscreensaver
+ * 23-Feb-2006: fixed color-cycling issues
+ * 01-Nov-2000: Allocation checks
+ * 10-May-1997: Compatible with xscreensaver
  *
- * The inspiration for this program, Lasp, was written by Adam B. Roach 
+ * The inspiration for this program, Lasp, was written by Adam B. Roach
  * in 1990, assisted by me, Caleb Cullen.  It was written first in C, then
- * in assembly, and used pre-calculated data tables to graph lisajous
+ * in assembly, and used pre-calculated data tables to graph lissajous
  * figures on 386 machines and lower.  This version bears only superficial
  * resemblances to the original Lasp.
  *
  * The `lissie' module's source code was studied as an example of how
  * to incorporate a new module into xlock.  Resemblances to it are
  * expected, but not intended to be plaigiaristic.
+ *
+ * February, 2006: 21st Century Update for Lisa
+ * + fixed color-mapping: the 'beginning' of the loop always uses the
+ *     same (starting) pixel value, causing the loop's coloration to
+ *     appear solid rather than flickering as in the previous version
+ * + all lines/points in a single color are drawn at once using XDrawLines()
+ *     or XDrawPoints(); the artifacting evident in the previous version
+ *     has been masked by the use of CapNotLast to separate individual drawn
+ *     areas with intentional "whitespace" (typically black)
+ * + added many new elements to the Function[] array
+ * + randomized selection of next function
+ * + introduced concept of "rarely-chosen" functions
+ * + cleaned up code somewhat, standardized capitalization, commented all
+ *     #directives with block labels
  */
 
 #ifdef STANDALONE
-# define PROGCLASS                                     "Lisa"
-# define HACK_INIT                                     init_lisa
-# define HACK_DRAW                                     draw_lisa
-# define lisa_opts                                     xlockmore_opts
-# define DEFAULTS      "*count:                1       \n"                     \
-                                       "*cycles:               256     \n"                     \
-                                       "*delay:                25000   \n"                     \
-                                       "*size:                 -1      \n"                     \
-                                       "*ncolors:              200     \n"
+# define MODE_lisa
+# define DEFAULTS      "*delay: 17000 \n" \
+                                       "*count: 1 \n" \
+                                       "*cycles: 768 \n" \
+                                       "*size: 500 \n" \
+                                       "*ncolors: 64 \n" \
+                                       "*fpsSolid: true \n" \
+
 # define UNIFORM_COLORS
-# include "xlockmore.h"                                /* from the xscreensaver distribution */
-#else  /* !STANDALONE */
-# include "xlock.h"                                    /* from the xlockmore distribution */
-#endif /* !STANDALONE */
+# define reshape_lisa 0
+# define lisa_handle_event 0
+# include "xlockmore.h"                /* in xscreensaver distribution */
+#else /* STANDALONE */
+#include "xlock.h"          /* in xlockmore distribution */
+
+#endif /* STANDALONE */
+
+#ifdef MODE_lisa
+
+#define  DEF_ADDITIVE     "True"
+
+static Bool additive;
+
+static XrmOptionDescRec opts[] =
+{
+       {"-additive", ".lisa.additive", XrmoptionNoArg, "True"},
+       {"+additive", ".lisa.additive", XrmoptionNoArg, "False"}
+};
+
+static argtype vars[] =
+{
+       {&additive, "additive", "Additive", DEF_ADDITIVE, t_Bool}
+};
+
+static OptionStruct desc[] =
+{
+       {"-/+additive", "turn on/off additive functions mode"}
+};
 
-ModeSpecOpt lisa_opts = { 
-  0, NULL, 0, NULL, NULL };
+ENTRYPOINT ModeSpecOpt lisa_opts =
+{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
+
+#ifdef USE_MODULES
+ModStruct   lisa_description =
+{"lisa", "init_lisa", "draw_lisa", "release_lisa",
+ "refresh_lisa", "change_lisa", (char *) NULL, &lisa_opts,
+ 17000, 1, 768, -1, 64, 1.0, "",
+ "Shows animated lissajous figures", 0, NULL};
+
+#endif
 
 #define  DRAWLINES    1
+/* #define  FOLLOW_FUNC_ORDER 1 */
 #define  TWOLOOPS     1
-#define  ADDITIVE     "True"
 #define  XVMAX        10       /* Maximum velocities */
 #define  YVMAX        10
 #define  LISAMAXFUNCS 2
-#define  NUMSTDFUNCS  10
+#define  NUMSTDFUNCS  28
+#define  RAREFUNCMIN  25
+#define  RAREFUNCODDS 4     /* 1:n chance a rare function will be re-randomized */
 #define  MAXCYCLES    3
-#define  lisasetcolor() \
-if (MI_NPIXELS(mi) > 2) { \
-  XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, loop->color)); \
-  if (++(loop->color) >= MI_NPIXELS(mi)) { loop->color=0; } \
-  } else { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_WHITE_PIXEL(mi)); }
-#define getRadius(context) \
-  ((context->width > context->height)?context->height:context->width) * 3 / 8
-#define checkRadius(loop, context) \
-  if ((context->height / 2 > MI_SIZE(mi)) && (context->width / 2 > MI_SIZE(mi))) \
-      loop->radius = MI_SIZE(mi); \
-  if ((loop->radius < 0) || \
-      (loop->radius > loop->center.x) || \
-      (loop->radius > loop->center.y)) loop->radius = getRadius(context)
+#define  MINLISAS     1
+#define  STARTCOLOR   0
+#define  STARTFUNC    24    /* if negative, is upper-bound on randomization */
+#define  LINEWIDTH    -8    /* if negative, is upper-bound on randomization */
+#define  LINESTYLE    LineSolid  /* an insane man might have fun with this :) */
+#define  LINECAP      CapNotLast /* anything else looks pretty crappy */
+#define  LINEJOIN     JoinBevel  /* this ought to be fastest */
+#define  SET_COLOR() \
+         if (MI_NPIXELS(mi) > 2) { \
+           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, loop->color)); \
+         if (loop->cstep \
+             && pctr % loop->cstep == 0 \
+             && ++(loop->color) >= (unsigned) MI_NPIXELS(mi)) \
+               { loop->color=STARTCOLOR; } \
+         } else { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); } 
+#define  GET_RADIUS(context) \
+         ((context->width > context->height)?context->height:context->width) * 3 / 8
+#define  CHECK_RADIUS(loop, context) \
+         if ((context->height / 2 > MI_SIZE(mi)) && (context->width / 2 > MI_SIZE(mi))) \
+           loop->radius = MI_SIZE(mi); \
+         if ((loop->radius < 0) || \
+           (loop->radius > loop->center.x) || \
+           (loop->radius > loop->center.y)) loop->radius = GET_RADIUS(context)
+#define  PRINT_FUNC(funcptr) \
+                printf("new function -- #%d:\n\tx = sin(%gs) * sin(%gs)\n\ty = sin(%gt) * sin(%gt)\n", \
+                           funcptr->index, \
+                           funcptr->xcoeff[0], funcptr->xcoeff[1], \
+                           funcptr->ycoeff[0], funcptr->ycoeff[1])
 
 
 typedef struct lisafunc_struct {
@@ -82,82 +149,128 @@ typedef struct lisafunc_struct {
 } lisafuncs;
 
 typedef struct lisa_struct {
-       int         radius, color, dx, dy, nsteps, nfuncs, melting;
-       double      pistep, phi, theta;
-       XPoint      center, *lastpoint;
-       lisafuncs  *function[LISAMAXFUNCS];
+    unsigned long color;
+    int         radius, dx, dy, nsteps, nfuncs, melting, cstep;
+    double      pistep, phi, theta;
+    XPoint      center, *lastpoint;
+    lisafuncs  *function[LISAMAXFUNCS];
+    int         linewidth;
 } lisas;
 
 typedef struct lisacontext_struct {
-       lisas      *lisajous;
-       int         width, height, nlisajous, loopcount;
+       lisas      *lissajous;
+       int         width, height, nlissajous, loopcount;
        int         maxcycles;
+       Bool        painted;
 } lisacons;
 
-static lisacons *Lisa = NULL;
-static Bool additive;
-
-#ifndef STANDALONE
-static XrmOptionDescRec lisa_xrm_opts[] =
-{
-       {"-additive", ".lisa.additive", XrmoptionNoArg, (caddr_t) "True"},
-       {"+additive", ".lisa.additive", XrmoptionNoArg, (caddr_t) "False"}
-};
-
-static argtype lisa_vars[] =
-{
-       {(caddr_t *) & additive, "additive", "Additive", ADDITIVE, t_Bool}
-};
-
-static OptionStruct lisa_vars_desc[] =
-{
-       {"-/+additive", "turn on/off additive functions mode"}
-};
-
-ModeSpecOpt lisa_opts =
-{2, lisa_xrm_opts, 1, lisa_vars, lisa_vars_desc};
-#endif /* STANDALONE */
-
-
-void refresh_lisa(ModeInfo * mi);
-void change_lisa(ModeInfo * mi);
-
+static lisacons *Lisa = (lisacons *) NULL;
 
 static lisafuncs Function[NUMSTDFUNCS] =
-{
+  {
        {
-               {1.0, 2.0},
-               {1.0, 2.0}, 2, 2, 0},
+         {1.0, 2.0},
+         {1.0, 2.0}, 2, 2, 0},
        {
-               {1.0, 2.0},
-               {1.0, 1.0}, 2, 2, 1},
+         {1.0, 2.0},
+         {1.0, 1.0}, 2, 2, 1},
        {
-               {1.0, 3.0},
-               {1.0, 2.0}, 2, 2, 2},
+         {1.0, 3.0},
+         {1.0, 2.0}, 2, 2, 2},
        {
-               {1.0, 3.0},
-               {1.0, 3.0}, 2, 2, 3},
+         {1.0, 3.0},
+         {1.0, 3.0}, 2, 2, 3},
        {
-               {2.0, 4.0},
-               {1.0, 2.0}, 2, 2, 4},
+         {2.0, 4.0},
+         {1.0, 2.0}, 2, 2, 4},
        {
-               {1.0, 4.0},
-               {1.0, 3.0}, 2, 2, 5},
+         {1.0, 4.0},
+         {1.0, 3.0}, 2, 2, 5},
        {
-               {1.0, 4.0},
-               {1.0, 4.0}, 2, 2, 6},
+         {1.0, 4.0},
+         {1.0, 4.0}, 2, 2, 6},
        {
-               {1.0, 5.0},
-               {1.0, 5.0}, 2, 2, 7},
+         {1.0, 5.0},
+         {1.0, 5.0}, 2, 2, 7},
        {
-               {2.0, 5.0},
-               {2.0, 5.0}, 2, 2, 8},
+         {2.0, 5.0},
+         {2.0, 5.0}, 2, 2, 8},
        {
-               {1.0, 0.0},
-               {1.0, 0.0}, 1, 1, 9}
+         {1.0, 2.0},
+         {2.0, 5.0}, 2, 2, 9},
+       {
+         {1.0, 2.0},
+         {3.0, 5.0}, 2, 2, 10},
+       {
+         {1.0, 2.0},
+         {2.0, 3.0}, 2, 2, 11},
+       {
+         {1.0, 3.0},
+         {2.0, 3.0}, 2, 2, 12},
+       {
+         {2.0, 3.0},
+         {1.0, 3.0}, 2, 2, 13},
+       {
+         {2.0, 4.0},
+         {1.0, 3.0}, 2, 2, 14},
+       {
+         {1.0, 4.0},
+         {2.0, 3.0}, 2, 2, 15},
+       {
+         {2.0, 4.0},
+         {2.0, 3.0}, 2, 2, 16},
+       {
+         {1.0, 5.0},
+         {2.0, 3.0}, 2, 2, 17},
+       {
+         {2.0, 5.0},
+         {2.0, 3.0}, 2, 2, 18},        
+       {
+         {1.0, 5.0},
+         {2.0, 5.0}, 2, 2, 19},
+       {
+         {1.0, 3.0},
+         {2.0, 7.0}, 2, 2, 20},
+       {
+         {2.0, 3.0},
+         {5.0, 7.0}, 2, 2, 21},
+       {
+         {1.0, 2.0},
+         {3.0, 7.0}, 2, 2, 22},
+       {
+         {2.0, 5.0},
+         {5.0, 7.0}, 2, 2, 23},
+       {
+         {5.0, 7.0},
+         {5.0, 7.0}, 2, 2, 24},
+       {                          /* functions past here are 'rare' and won't  */
+         {2.0, 7.0},              /* show up as often.  tweak the #defines above */
+         {1.0, 7.0}, 2, 2, 25},   /* to see them more frequently */
+       {
+         {2.0, 9.0},
+         {1.0, 7.0}, 2, 2, 26},
+       {
+         {5.0, 11.0},
+         {2.0, 9.0}, 2, 2, 27}
 };
 
+int xMaxLines;
+
 static void
+free_lisa(lisacons *lc)
+{
+       while (lc->lissajous) {
+               int    lctr;
+
+               for (lctr = 0; lctr < lc->nlissajous; lctr++) {
+                       (void) free((void *) lc->lissajous[lctr].lastpoint);
+               }
+               (void) free((void *) lc->lissajous);
+               lc->lissajous = (lisas *) NULL;
+       }
+}
+
+static Bool
 drawlisa(ModeInfo * mi, lisas * loop)
 {
        XPoint     *np;
@@ -165,16 +278,25 @@ drawlisa(ModeInfo * mi, lisas * loop)
        lisacons   *lc = &Lisa[MI_SCREEN(mi)];
        lisafuncs **lf = loop->function;
        int         phase = lc->loopcount % loop->nsteps;
-       int         pctr, fctr, xctr, yctr;
+       int         pctr, fctr, xctr, yctr, extra_points;
        double      xprod, yprod, xsum, ysum;
 
-       /* Allocate the np array */
-       np = (XPoint *) calloc(loop->nsteps, sizeof (XPoint));
+    /* why carry this around in the struct when we can calculate it on demand? */
+    extra_points = loop->cstep - (loop->nsteps % loop->cstep);
+
+       /* Allocate the np (new point) array (with padding) */
+       if ((np = (XPoint *) calloc(loop->nsteps+extra_points, sizeof (XPoint))) == NULL) {
+               free_lisa(lc);
+               return False;
+       }
 
        /* Update the center */
        loop->center.x += loop->dx;
        loop->center.y += loop->dy;
-       checkRadius(loop, lc);
+       CHECK_RADIUS(loop, lc);
+
+  /* check for overlaps -- where the figure might go off the screen */
+
        if ((loop->center.x - loop->radius) <= 0) {
                loop->center.x = loop->radius;
                loop->dx = NRAND(XVMAX);
@@ -208,42 +330,31 @@ drawlisa(ModeInfo * mi, lisas * loop)
                                        yprod += sin(lf[fctr]->ycoeff[yctr] * loop->phi);
                                if (loop->melting) {
                                        if (fctr) {
-                                               xsum += xprod \
-                                                       *(double) (loop->nsteps - loop->melting) \
-                                                       /(double) loop->nsteps;
-                                               ysum += yprod \
-                                                       *(double) (loop->nsteps - loop->melting) \
-                                                       /(double) loop->nsteps;
+                                               xsum += xprod * (double) (loop->nsteps - loop->melting) /
+                                                       (double) loop->nsteps;
+                                               ysum += yprod * (double) (loop->nsteps - loop->melting) /
+                                                       (double) loop->nsteps;
                                        } else {
-                                               xsum += xprod \
-                                                       *(double) loop->melting \
-                                                       /(double) loop->nsteps;
-                                               ysum += yprod \
-                                                       *(double) loop->melting \
-                                                       /(double) loop->nsteps;
+                                               xsum += xprod * (double) loop->melting / (double) loop->nsteps;
+                                               ysum += yprod * (double) loop->melting / (double) loop->nsteps;
                                        }
                                } else {
                                        xsum = xprod;
                                        ysum = yprod;
                                }
                                if (!fctr) {
-                                       xsum = xsum \
-                                               *(double) loop->radius \
-                                               /(double) lf[fctr]->nx;
-                                       ysum = ysum \
-                                               *(double) loop->radius \
-                                               /(double) lf[fctr]->ny;
+                                       xsum = xsum * (double) loop->radius / (double) lf[fctr]->nx;
+                                       ysum = ysum * (double) loop->radius / (double) lf[fctr]->ny;
                                }
                        } else {
                                if (loop->melting) {
                                        if (fctr) {
-                                               yprod = xprod = (double) loop->radius \
-                                                       *(double) (loop->nsteps - loop->melting) \
-                                                       /(double) (loop->nsteps);
+                                               yprod = xprod = (double) loop->radius *
+                                                       (double) (loop->nsteps - loop->melting) /
+                                                       (double) (loop->nsteps);
                                        } else {
-                                               yprod = xprod = (double) loop->radius \
-                                                       *(double) (loop->melting) \
-                                                       /(double) (loop->nsteps);
+                                               yprod = xprod = (double) loop->radius *
+                                                       (double) (loop->melting) / (double) (loop->nsteps);
                                        }
                                } else {
                                        xprod = yprod = (double) loop->radius;
@@ -266,212 +377,371 @@ drawlisa(ModeInfo * mi, lisas * loop)
                np[pctr].x = (int) ceil(xsum);
                np[pctr].y = (int) ceil(ysum);
        }
+    /* fill in extra points */
+    for (pctr=loop->nsteps; pctr < loop->nsteps+extra_points; pctr++) {
+         np[pctr].x = np[pctr - loop->nsteps].x;
+         np[pctr].y = np[pctr - loop->nsteps].y;
+    }
        if (loop->melting) {
                if (!--loop->melting) {
                        loop->nfuncs = 1;
                        loop->function[0] = loop->function[1];
                }
        }
-       for (pctr = 0; pctr < loop->nsteps; pctr++) {
 
+    /* reset starting color each time to prevent ass-like appearance */        
+    loop->color = STARTCOLOR;
+
+  if (loop->cstep < xMaxLines) {
+       /*      printf("Drawing dashes\n"); */
+       for (pctr = 0; pctr < loop->nsteps; pctr+=loop->cstep) {
 #if defined DRAWLINES
+               XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
+                                  LINESTYLE, LINECAP, LINEJOIN);
                /* erase the last cycle's point */
-               XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
-               XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), \
-                         MI_GC(mi), lp[pctr].x, lp[pctr].y, \
-                         lp[(pctr + 1) % loop->nsteps].x, \
-                         lp[(pctr + 1) % loop->nsteps].y);
+               XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
+               XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), &lp[pctr], 
+                                  loop->cstep, CoordModeOrigin);
 
                /* Set the new color */
-               lisasetcolor();
+               SET_COLOR();
 
                /* plot this cycle's point */
-               XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), \
-                         MI_GC(mi), np[pctr].x, np[pctr].y, \
-                         np[(pctr + 1) % loop->nsteps].x, \
-                         np[(pctr + 1) % loop->nsteps].y);
+               XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), 
+                                  &np[pctr], loop->cstep, CoordModeOrigin);
+               XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
+                                                  LINESTYLE, LINECAP, LINEJOIN);
 #else
                /* erase the last cycle's point */
-               XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
-               XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), \
-                          MI_GC(mi), lp[pctr].x, lp[pctr].y);
+               XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
+               XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
+                                       &lp[pctr], loop->cstep, CoordModeOrigin);
 
                /* Set the new color */
-               lisasetcolor();
+               SET_COLOR();
 
                /* plot this cycle's point */
-               XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), \
-                          MI_GC(mi), np[pctr].x, np[pctr].y);
+               XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi),
+                                       MI_GC(mi), &np[pctr], loop->cstep, CoordModeOrigin);
 #endif
        }
+  } else { /* on my system, cstep is larger than 65532/2 if we get here */
+       for (pctr = 0; pctr < loop->nsteps; pctr++) {
+#if defined DRAWLINES
+         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
+                                                LINESTYLE, LINECAP, LINEJOIN);
+         /* erase the last cycle's point */
+         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
+         XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi),
+                               MI_GC(mi), lp[pctr].x, lp[pctr].y,
+                               lp[(pctr + 1) % loop->nsteps].x,
+                               lp[(pctr + 1) % loop->nsteps].y);
+         
+         /* Set the new color */
+         SET_COLOR();
+         
+         /* plot this cycle's point */
+         XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi),
+                               MI_GC(mi), np[pctr].x, np[pctr].y,
+                               np[(pctr + 1) % loop->nsteps].x,
+                               np[(pctr + 1) % loop->nsteps].y);
+         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
+                                                LINESTYLE, LINECAP, LINEJOIN);
+#else  /* DRAWLINES */
+         /* erase the last cycle's point */
+         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
+         XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
+                                MI_GC(mi), lp[pctr].x, lp[pctr].y);
+         
+         /* Set the new color */
+         SET_COLOR();
+         
+         /* plot this cycle's point */
+         XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
+                                MI_GC(mi), np[pctr].x, np[pctr].y);
+#endif  /* DRAWLINES */
+       }
+  }
        (void) free((void *) lp);
        loop->lastpoint = np;
+       return True;
 }
 
-static void
+static Bool 
 initlisa(ModeInfo * mi, lisas * loop)
 {
-       lisacons   *lc = &Lisa[MI_SCREEN(mi)];
-       lisafuncs **lf = loop->function;
-       XPoint     *lp;
-       int         phase, pctr, fctr, xctr, yctr;
-       double      xprod, yprod, xsum, ysum;
-
-       if (MI_NPIXELS(mi) > 2) {
-               loop->color = 0;
-       } else
-               loop->color = MI_WIN_WHITE_PIXEL(mi);
-       loop->nsteps = MI_CYCLES(mi);
-       if (loop->nsteps == 0)
-               loop->nsteps = 1;
-       lc->maxcycles = (MAXCYCLES * loop->nsteps) - 1;
-       loop->melting = 0;
-       loop->nfuncs = 1;
-       loop->pistep = 2.0 * M_PI / (double) loop->nsteps;
-       loop->center.x = lc->width / 2;
-       loop->center.y = lc->height / 2;
-       loop->radius = MI_SIZE(mi);
-       checkRadius(loop, lc);
-       loop->dx = NRAND(XVMAX);
-       loop->dy = NRAND(YVMAX);
-       loop->dx++;
-       loop->dy++;
-       lf[0] = &Function[lc->loopcount % NUMSTDFUNCS];
-       if ((lp = loop->lastpoint = (XPoint *)
-            calloc(loop->nsteps, sizeof (XPoint))) == NULL)
-               return;
-       phase = lc->loopcount % loop->nsteps;
-
-       for (pctr = 0; pctr < loop->nsteps; pctr++) {
-               loop->phi = (double) (pctr - phase) * loop->pistep;
-               loop->theta = (double) (pctr + phase) * loop->pistep;
-               fctr = loop->nfuncs;
-               xsum = ysum = 0.0;
-               while (fctr--) {
-                       xprod = yprod = (double) loop->radius;
-                       xctr = lf[fctr]->nx;
-                       yctr = lf[fctr]->ny;
-                       while (xctr--)
-                               xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
-                       while (yctr--)
-                               yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
-                       xsum += xprod;
-                       ysum += yprod;
-               }
-               if (loop->nfuncs > 1) {
-                       xsum /= 2.0;
-                       ysum /= 2.0;
-               }
-               xsum += (double) loop->center.x;
-               ysum += (double) loop->center.y;
-
-               lp[pctr].x = (int) ceil(xsum);
-               lp[pctr].y = (int) ceil(ysum);
+  lisacons   *lc = &Lisa[MI_SCREEN(mi)];
+  lisafuncs **lf = loop->function;
+  XPoint     *lp;
+  int         phase, pctr, fctr, xctr, yctr, extra_points;
+  double      xprod, yprod, xsum, ysum;
+  
+  xMaxLines = (XMaxRequestSize(MI_DISPLAY(mi))-3)/2;
+  /*  printf("Got xMaxLines = %d\n", xMaxLines); */
+  loop->nsteps = MI_CYCLES(mi);
+  if (loop->nsteps == 0)
+       loop->nsteps = 1;
+  if (MI_NPIXELS(mi) > 2) {
+       loop->color = STARTCOLOR;
+       loop->cstep = (loop->nsteps > MI_NPIXELS(mi)) ? loop->nsteps / MI_NPIXELS(mi) : 1;
+  } else {
+       loop->color = MI_WHITE_PIXEL(mi);
+       loop->cstep = 0;
+  }
+  extra_points = loop->cstep - (loop->nsteps % loop->cstep);
+  lc->maxcycles = (MAXCYCLES * loop->nsteps) - 1;
+  loop->cstep = ( loop->nsteps > MI_NPIXELS(mi) ) ? loop->nsteps / MI_NPIXELS(mi) : 1;
+  /*  printf("Got cstep = %d\n", loop->cstep); */
+  loop->melting = 0;
+  loop->nfuncs = 1;
+  loop->pistep = 2.0 * M_PI / (double) loop->nsteps;
+  loop->center.x = lc->width / 2;
+  loop->center.y = lc->height / 2;
+  loop->radius = (int) MI_SIZE(mi);
+  CHECK_RADIUS(loop, lc);
+  loop->dx = NRAND(XVMAX);
+  loop->dy = NRAND(YVMAX);
+  loop->dx++;
+  loop->dy++;
+#if defined STARTFUNC
+  lf[0] = &Function[STARTFUNC];
+#else  /* STARTFUNC */
+  lf[0] = &Function[NRAND(NUMSTDFUNCS)];
+#endif  /* STARTFUNC */        
+  
+  if ((lp = loop->lastpoint = (XPoint *)
+          calloc(loop->nsteps+extra_points, sizeof (XPoint))) == NULL) {
+       free_lisa(lc);
+       return False;
+  }
+  phase = lc->loopcount % loop->nsteps;
+  
+#if defined DEBUG
+  printf( "nsteps = %d\tcstep = %d\tmrs = %d\textra_points = %d\n", 
+                 loop->nsteps, loop->cstep, xMaxLines, extra_points );
+  PRINT_FUNC(lf[0]);
+#endif  /* DEBUG */
+  
+  for (pctr = 0; pctr < loop->nsteps; pctr++) {
+       loop->phi = (double) (pctr - phase) * loop->pistep;
+       loop->theta = (double) (pctr + phase) * loop->pistep;
+       fctr = loop->nfuncs;
+       xsum = ysum = 0.0;
+       while (fctr--) {
+         xprod = yprod = (double) loop->radius;
+         xctr = lf[fctr]->nx;
+         yctr = lf[fctr]->ny;
+         while (xctr--)
+               xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
+         while (yctr--)
+               yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
+         xsum += xprod;
+         ysum += yprod;
        }
-       for (pctr = 0; pctr < loop->nsteps; pctr++) {
-               /* Set the color */
-               lisasetcolor();
+       if (loop->nfuncs > 1) {
+         xsum /= 2.0;
+         ysum /= 2.0;
+       }
+       xsum += (double) loop->center.x;
+       ysum += (double) loop->center.y;
+       
+       lp[pctr].x = (int) ceil(xsum);
+       lp[pctr].y = (int) ceil(ysum);
+  }
+  /* this fills in the extra points, so we can use segment-drawing calls */
+  for (pctr = loop->nsteps; pctr < loop->nsteps + extra_points; pctr++) {
+       lp[pctr].x=lp[pctr - loop->nsteps].x;
+       lp[pctr].y=lp[pctr - loop->nsteps].y;
+  }
 #if defined DRAWLINES
-               XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), \
-                         MI_GC(mi), lp[pctr].x, lp[pctr].y, \
-                         lp[(pctr + 1) % loop->nsteps].x, \
-                         lp[(pctr + 1) % loop->nsteps].y);
-#else
-               XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), \
-                          lp[pctr].x, lp[pctr].y);
-#endif
+  loop->linewidth = LINEWIDTH; /* #### make this a resource */
+  
+  if (loop->linewidth == 0)
+       loop->linewidth = 1;
+  if (loop->linewidth < 0)
+       loop->linewidth = NRAND(-loop->linewidth) + 1;
+  XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
+                                        LINESTYLE, LINECAP, LINEJOIN);
+#endif  /* DRAWLINES */
+  
+  if ( loop->cstep < xMaxLines ) { 
+       /* we can send each color segment in a single request 
+        * because the max request length is long enough 
+        * and because we have padded out the array to have extra elements 
+        * to support calls which would otherwise fall off the end*/
+       for (pctr = 0; pctr < loop->nsteps; pctr+=loop->cstep) {
+         /* Set the color */
+         SET_COLOR();
+         
+#if defined DRAWLINES
+         XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi),
+                                MI_GC(mi), &lp[pctr], loop->cstep, CoordModeOrigin );
+#else  /* DRAWLINES */
+         XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
+                                 &lp[pctr], loop->cstep, CoordModeOrigin );
+#endif  /* DRAWLINES */
        }
+  } else { /* do it one by one as before */
+       for (pctr = 0; pctr < loop->nsteps; pctr++ ) {
+         SET_COLOR();
+         
+#if defined DRAWLINES
+         XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
+                               lp[pctr].x, lp[pctr].y,
+                               lp[pctr+1 % loop->nsteps].x, lp[pctr+1 % loop->nsteps].y);
+#else  /* DRAWLINES */
+         XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
+                                lp[pctr].x, lp[pctr].y);
+#endif  /* DRAWLINES */
+       }
+  }
+  
+#if defined DRAWLINES
+  XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
+                                        LINESTYLE, LINECAP, LINEJOIN);
+#endif  /* DRAWLINES */
+  return True;
 }
 
-void
-init_lisa(ModeInfo * mi)
+static void
+refreshlisa(ModeInfo * mi)
 {
-       lisacons   *lc;
+       lisacons   *lc = &Lisa[MI_SCREEN(mi)];
        int         lctr;
 
-       if (Lisa == NULL) {
-               if ((Lisa = (lisacons *) calloc(MI_NUM_SCREENS(mi), sizeof (lisacons))) \
-                   == NULL)
+       for (lctr = 0; lctr < lc->nlissajous; lctr++) {
+               if (!drawlisa(mi, &lc->lissajous[lctr]))
                        return;
        }
+}
+
+ENTRYPOINT void
+refresh_lisa(ModeInfo * mi)
+{
+       lisacons   *lc;
+
+       if (Lisa == NULL)
+               return;
        lc = &Lisa[MI_SCREEN(mi)];
-       lc->width = MI_WIN_WIDTH(mi);
-       lc->height = MI_WIN_HEIGHT(mi);
-       lc->nlisajous = MI_BATCHCOUNT(mi);
-       lc->loopcount = 0;
+       if (lc->lissajous == NULL)
+               return;
 
-       if (lc->lisajous == NULL) {
-               if ((lc->lisajous = (lisas *) calloc(lc->nlisajous, sizeof (lisas))) \
-                   == NULL)
-                       return;
-               XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
-               for (lctr = 0; lctr < lc->nlisajous; lctr++) {
-                       initlisa(mi, &lc->lisajous[lctr]);
-                       lc->loopcount++;
-               }
-       } else {
-               refresh_lisa(mi);
+       if (lc->painted) {
+               lc->painted = False;
+               MI_CLEARWINDOW(mi);
+               refreshlisa(mi);
        }
-       XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
 }
 
-void
-draw_lisa(ModeInfo * mi)
+static void
+change_lisa(ModeInfo * mi)
 {
-       lisacons   *lc = &Lisa[MI_SCREEN(mi)];
+       lisas      *loop;
+       int         lctr, newfunc;
+       lisacons   *lc;
 
-       if (++lc->loopcount > lc->maxcycles) {
-               change_lisa(mi);
+       if (Lisa == NULL)
+               return;
+       lc = &Lisa[MI_SCREEN(mi)];
+       if (lc->lissajous == NULL)
+               return;
+
+       lc->loopcount = 0;
+       for (lctr = 0; lctr < lc->nlissajous; lctr++) {
+               loop = &lc->lissajous[lctr];       /* count through the loops we're drawing */
+       newfunc = NRAND(NUMSTDFUNCS);     /* choose a new function at random */
+#if defined FOLLOW_FUNC_ORDER
+       loop->function[1] =
+         &Function[(loop->function[0]->index + 1) % NUMSTDFUNCS];
+#else  /* FOLLOW_FUNC_ORDER */
+       if (newfunc == loop->function[0]->index) {
+         ++newfunc;
+         newfunc %= NUMSTDFUNCS;       /* take the next if we got the one we have */
        }
-       refresh_lisa(mi);
+       if (newfunc >= RAREFUNCMIN \
+               && !(random() % RAREFUNCODDS) \
+               && (newfunc = NRAND(NUMSTDFUNCS)) == loop->function[0]->index) {
+         ++newfunc;
+         newfunc %= NUMSTDFUNCS;
+       }
+       loop->function[1] =               /* set 2nd function pointer on the loop */
+         &Function[newfunc];             /* to the new function we just chose */
+#endif  /* FOLLOW_FUNC_ORDER */                
+#if defined DEBUG
+       PRINT_FUNC(loop->function[1]);
+#endif /* DEBUG */
+       loop->melting = loop->nsteps - 1; /* melt the two functions together */ 
+       loop->nfuncs = 2;                 /* simultaneously for a full cycle */  
+  }
 }
 
-void
-refresh_lisa(ModeInfo * mi)
+ENTRYPOINT void
+init_lisa (ModeInfo * mi)
 {
-       lisacons   *lc = &Lisa[MI_SCREEN(mi)];
        int         lctr;
+       lisacons   *lc;
 
-       for (lctr = 0; lctr < lc->nlisajous; lctr++) {
-               drawlisa(mi, &lc->lisajous[lctr]);
+       MI_INIT (mi, Lisa, 0);
+       lc = &Lisa[MI_SCREEN(mi)];
+       lc->width = MI_WIDTH(mi);
+       lc->height = MI_HEIGHT(mi);
+       lc->loopcount = 0;
+       lc->nlissajous = MI_COUNT(mi);
+       if (lc->nlissajous <= 0)
+               lc->nlissajous = 1;
+       MI_CLEARWINDOW(mi);
+       lc->painted = False;
+
+       if (lc->lissajous == NULL) {
+               if ((lc->lissajous = (lisas *) calloc(lc->nlissajous,
+                               sizeof (lisas))) == NULL)
+                       return;
+               for (lctr = 0; lctr < lc->nlissajous; lctr++) {
+                       if (!initlisa(mi, &lc->lissajous[lctr]))
+                               return;
+                       lc->loopcount++;
+               }
+       } else {
+               refreshlisa(mi);
        }
 }
 
-void
-release_lisa(ModeInfo * mi)
+ENTRYPOINT void
+draw_lisa (ModeInfo * mi)
 {
        lisacons   *lc;
-       int         lctr, sctr;
 
-       if (Lisa) {
-               for (sctr = 0; sctr < MI_NUM_SCREENS(mi); sctr++) {
-                       lc = &Lisa[sctr];
-                       while (lc->lisajous) {
-                               for (lctr = 0; lctr < lc->nlisajous; lctr++) {
-                                       (void) free(lc->lisajous[lctr].lastpoint);
-                               }
-                               (void) free(lc->lisajous);
-                               lc->lisajous = NULL;
-                       }
-               }
-               (void) free(Lisa);
-               Lisa = NULL;
+       if (Lisa == NULL)
+               return;
+       lc = &Lisa[MI_SCREEN(mi)];
+       if (lc->lissajous == NULL)
+               return;
+
+#ifdef HAVE_JWXYZ      /* Don't second-guess Quartz's double-buffering */
+    XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
+#endif
+
+       MI_IS_DRAWN(mi) = True;
+       lc->painted = True;
+       if (++lc->loopcount > lc->maxcycles) {
+               change_lisa(mi);
        }
+       refreshlisa(mi);
 }
 
-void
-change_lisa(ModeInfo * mi)
+ENTRYPOINT void
+release_lisa (ModeInfo * mi)
 {
-       lisacons   *lc = &Lisa[MI_SCREEN(mi)];
-       lisas      *loop;
-       int         lctr;
+       if (Lisa) {
+               int    screen;
 
-       lc->loopcount = 0;
-       for (lctr = 0; lctr < lc->nlisajous; lctr++) {
-               loop = &lc->lisajous[lctr];
-               loop->function[1] = &Function[(loop->function[0]->index + 1) %
-                                             NUMSTDFUNCS];
-               loop->melting = loop->nsteps - 1;
-               loop->nfuncs = 2;
+               for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
+                       free_lisa(&Lisa[screen]);
+               (void) free(Lisa);
+               Lisa = (lisacons *) NULL;
        }
 }
+
+XSCREENSAVER_MODULE ("Lisa", lisa)
+
+#endif /* MODE_lisa */