From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / lisa.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lisa --- animated full-loop lissajous figures */
3
4 #if 0
5 static const char sccsid[] = "@(#)lisa.c        5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1997, 2006 by Caleb Cullen.
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * Revision History:
24  * 23-Feb-2006: fixed color-cycling issues
25  * 01-Nov-2000: Allocation checks
26  * 10-May-1997: Compatible with xscreensaver
27  *
28  * The inspiration for this program, Lasp, was written by Adam B. Roach
29  * in 1990, assisted by me, Caleb Cullen.  It was written first in C, then
30  * in assembly, and used pre-calculated data tables to graph lissajous
31  * figures on 386 machines and lower.  This version bears only superficial
32  * resemblances to the original Lasp.
33  *
34  * The `lissie' module's source code was studied as an example of how
35  * to incorporate a new module into xlock.  Resemblances to it are
36  * expected, but not intended to be plaigiaristic.
37  *
38  * February, 2006: 21st Century Update for Lisa
39  * + fixed color-mapping: the 'beginning' of the loop always uses the
40  *     same (starting) pixel value, causing the loop's coloration to
41  *     appear solid rather than flickering as in the previous version
42  * + all lines/points in a single color are drawn at once using XDrawLines()
43  *     or XDrawPoints(); the artifacting evident in the previous version
44  *     has been masked by the use of CapNotLast to separate individual drawn
45  *     areas with intentional "whitespace" (typically black)
46  * + added many new elements to the Function[] array
47  * + randomized selection of next function
48  * + introduced concept of "rarely-chosen" functions
49  * + cleaned up code somewhat, standardized capitalization, commented all
50  *     #directives with block labels
51  */
52
53 #ifdef STANDALONE
54 # define MODE_lisa
55 # define DEFAULTS       "*delay: 17000 \n" \
56                                         "*count: 1 \n" \
57                                         "*cycles: 768 \n" \
58                                         "*size: 500 \n" \
59                                         "*ncolors: 64 \n" \
60                                         "*fpsSolid: true \n" \
61
62 # define UNIFORM_COLORS
63 # define reshape_lisa 0
64 # define lisa_handle_event 0
65 # include "xlockmore.h"         /* in xscreensaver distribution */
66 #else /* STANDALONE */
67 #include "xlock.h"          /* in xlockmore distribution */
68
69 #endif /* STANDALONE */
70
71 #ifdef MODE_lisa
72
73 #define  DEF_ADDITIVE     "True"
74
75 static Bool additive;
76
77 static XrmOptionDescRec opts[] =
78 {
79         {"-additive", ".lisa.additive", XrmoptionNoArg, "True"},
80         {"+additive", ".lisa.additive", XrmoptionNoArg, "False"}
81 };
82
83 static argtype vars[] =
84 {
85         {&additive, "additive", "Additive", DEF_ADDITIVE, t_Bool}
86 };
87
88 static OptionStruct desc[] =
89 {
90         {"-/+additive", "turn on/off additive functions mode"}
91 };
92
93 ENTRYPOINT ModeSpecOpt lisa_opts =
94 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
95
96 #ifdef USE_MODULES
97 ModStruct   lisa_description =
98 {"lisa", "init_lisa", "draw_lisa", "release_lisa",
99  "refresh_lisa", "change_lisa", (char *) NULL, &lisa_opts,
100  17000, 1, 768, -1, 64, 1.0, "",
101  "Shows animated lissajous figures", 0, NULL};
102
103 #endif
104
105 #define  DRAWLINES    1
106 /* #define  FOLLOW_FUNC_ORDER 1 */
107 #define  TWOLOOPS     1
108 #define  XVMAX        10        /* Maximum velocities */
109 #define  YVMAX        10
110 #define  LISAMAXFUNCS 2
111 #define  NUMSTDFUNCS  28
112 #define  RAREFUNCMIN  25
113 #define  RAREFUNCODDS 4     /* 1:n chance a rare function will be re-randomized */
114 #define  MAXCYCLES    3
115 #define  MINLISAS     1
116 #define  STARTCOLOR   0
117 #define  STARTFUNC    24    /* if negative, is upper-bound on randomization */
118 #define  LINEWIDTH    -8    /* if negative, is upper-bound on randomization */
119 #define  LINESTYLE    LineSolid  /* an insane man might have fun with this :) */
120 #define  LINECAP      CapNotLast /* anything else looks pretty crappy */
121 #define  LINEJOIN     JoinBevel  /* this ought to be fastest */
122 #define  SET_COLOR() \
123          if (MI_NPIXELS(mi) > 2) { \
124            XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, loop->color)); \
125          if (loop->cstep \
126              && pctr % loop->cstep == 0 \
127              && ++(loop->color) >= (unsigned) MI_NPIXELS(mi)) \
128                { loop->color=STARTCOLOR; } \
129          } else { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); } 
130 #define  GET_RADIUS(context) \
131          ((context->width > context->height)?context->height:context->width) * 3 / 8
132 #define  CHECK_RADIUS(loop, context) \
133          if ((context->height / 2 > MI_SIZE(mi)) && (context->width / 2 > MI_SIZE(mi))) \
134            loop->radius = MI_SIZE(mi); \
135          if ((loop->radius < 0) || \
136            (loop->radius > loop->center.x) || \
137            (loop->radius > loop->center.y)) loop->radius = GET_RADIUS(context)
138 #define  PRINT_FUNC(funcptr) \
139                  printf("new function -- #%d:\n\tx = sin(%gs) * sin(%gs)\n\ty = sin(%gt) * sin(%gt)\n", \
140                             funcptr->index, \
141                             funcptr->xcoeff[0], funcptr->xcoeff[1], \
142                             funcptr->ycoeff[0], funcptr->ycoeff[1])
143
144
145 typedef struct lisafunc_struct {
146         double      xcoeff[2], ycoeff[2];
147         int         nx, ny;
148         int         index;
149 } lisafuncs;
150
151 typedef struct lisa_struct {
152     unsigned long color;
153     int         radius, dx, dy, nsteps, nfuncs, melting, cstep;
154     double      pistep, phi, theta;
155     XPoint      center, *lastpoint;
156     lisafuncs  *function[LISAMAXFUNCS];
157     int         linewidth;
158 } lisas;
159
160 typedef struct lisacontext_struct {
161         lisas      *lissajous;
162         int         width, height, nlissajous, loopcount;
163         int         maxcycles;
164         Bool        painted;
165 } lisacons;
166
167 static lisacons *Lisa = (lisacons *) NULL;
168
169 static lisafuncs Function[NUMSTDFUNCS] =
170   {
171         {
172           {1.0, 2.0},
173           {1.0, 2.0}, 2, 2, 0},
174         {
175           {1.0, 2.0},
176           {1.0, 1.0}, 2, 2, 1},
177         {
178           {1.0, 3.0},
179           {1.0, 2.0}, 2, 2, 2},
180         {
181           {1.0, 3.0},
182           {1.0, 3.0}, 2, 2, 3},
183         {
184           {2.0, 4.0},
185           {1.0, 2.0}, 2, 2, 4},
186         {
187           {1.0, 4.0},
188           {1.0, 3.0}, 2, 2, 5},
189         {
190           {1.0, 4.0},
191           {1.0, 4.0}, 2, 2, 6},
192         {
193           {1.0, 5.0},
194           {1.0, 5.0}, 2, 2, 7},
195         {
196           {2.0, 5.0},
197           {2.0, 5.0}, 2, 2, 8},
198         {
199           {1.0, 2.0},
200           {2.0, 5.0}, 2, 2, 9},
201         {
202           {1.0, 2.0},
203           {3.0, 5.0}, 2, 2, 10},
204         {
205           {1.0, 2.0},
206           {2.0, 3.0}, 2, 2, 11},
207         {
208           {1.0, 3.0},
209           {2.0, 3.0}, 2, 2, 12},
210         {
211           {2.0, 3.0},
212           {1.0, 3.0}, 2, 2, 13},
213         {
214           {2.0, 4.0},
215           {1.0, 3.0}, 2, 2, 14},
216         {
217           {1.0, 4.0},
218           {2.0, 3.0}, 2, 2, 15},
219         {
220           {2.0, 4.0},
221           {2.0, 3.0}, 2, 2, 16},
222         {
223           {1.0, 5.0},
224           {2.0, 3.0}, 2, 2, 17},
225         {
226           {2.0, 5.0},
227           {2.0, 3.0}, 2, 2, 18},        
228         {
229           {1.0, 5.0},
230           {2.0, 5.0}, 2, 2, 19},
231         {
232           {1.0, 3.0},
233           {2.0, 7.0}, 2, 2, 20},
234         {
235           {2.0, 3.0},
236           {5.0, 7.0}, 2, 2, 21},
237         {
238           {1.0, 2.0},
239           {3.0, 7.0}, 2, 2, 22},
240         {
241           {2.0, 5.0},
242           {5.0, 7.0}, 2, 2, 23},
243         {
244           {5.0, 7.0},
245           {5.0, 7.0}, 2, 2, 24},
246         {                          /* functions past here are 'rare' and won't  */
247           {2.0, 7.0},              /* show up as often.  tweak the #defines above */
248           {1.0, 7.0}, 2, 2, 25},   /* to see them more frequently */
249         {
250           {2.0, 9.0},
251           {1.0, 7.0}, 2, 2, 26},
252         {
253           {5.0, 11.0},
254           {2.0, 9.0}, 2, 2, 27}
255 };
256
257 int xMaxLines;
258
259 static void
260 free_lisa(lisacons *lc)
261 {
262         while (lc->lissajous) {
263                 int    lctr;
264
265                 for (lctr = 0; lctr < lc->nlissajous; lctr++) {
266                         (void) free((void *) lc->lissajous[lctr].lastpoint);
267                 }
268                 (void) free((void *) lc->lissajous);
269                 lc->lissajous = (lisas *) NULL;
270         }
271 }
272
273 static Bool
274 drawlisa(ModeInfo * mi, lisas * loop)
275 {
276         XPoint     *np;
277         XPoint     *lp = loop->lastpoint;
278         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
279         lisafuncs **lf = loop->function;
280         int         phase = lc->loopcount % loop->nsteps;
281         int         pctr, fctr, xctr, yctr, extra_points;
282         double      xprod, yprod, xsum, ysum;
283
284     /* why carry this around in the struct when we can calculate it on demand? */
285     extra_points = loop->cstep - (loop->nsteps % loop->cstep);
286
287         /* Allocate the np (new point) array (with padding) */
288         if ((np = (XPoint *) calloc(loop->nsteps+extra_points, sizeof (XPoint))) == NULL) {
289                 free_lisa(lc);
290                 return False;
291         }
292
293         /* Update the center */
294         loop->center.x += loop->dx;
295         loop->center.y += loop->dy;
296         CHECK_RADIUS(loop, lc);
297
298   /* check for overlaps -- where the figure might go off the screen */
299
300         if ((loop->center.x - loop->radius) <= 0) {
301                 loop->center.x = loop->radius;
302                 loop->dx = NRAND(XVMAX);
303         } else if ((loop->center.x + loop->radius) >= lc->width) {
304                 loop->center.x = lc->width - loop->radius;
305                 loop->dx = -NRAND(XVMAX);
306         };
307         if ((loop->center.y - loop->radius) <= 0) {
308                 loop->center.y = loop->radius;
309                 loop->dy = NRAND(YVMAX);
310         } else if ((loop->center.y + loop->radius) >= lc->height) {
311                 loop->center.y = lc->height - loop->radius;
312                 loop->dy = -NRAND(YVMAX);
313         };
314
315         /* Now draw the points, and erase the ones from the last cycle */
316
317         for (pctr = 0; pctr < loop->nsteps; pctr++) {
318                 fctr = loop->nfuncs;
319                 loop->phi = (double) (pctr - phase) * loop->pistep;
320                 loop->theta = (double) (pctr + phase) * loop->pistep;
321                 xsum = ysum = 0;
322                 while (fctr--) {
323                         xctr = lf[fctr]->nx;
324                         yctr = lf[fctr]->ny;
325                         if (additive) {
326                                 xprod = yprod = 0.0;
327                                 while (xctr--)
328                                         xprod += sin(lf[fctr]->xcoeff[xctr] * loop->theta);
329                                 while (yctr--)
330                                         yprod += sin(lf[fctr]->ycoeff[yctr] * loop->phi);
331                                 if (loop->melting) {
332                                         if (fctr) {
333                                                 xsum += xprod * (double) (loop->nsteps - loop->melting) /
334                                                         (double) loop->nsteps;
335                                                 ysum += yprod * (double) (loop->nsteps - loop->melting) /
336                                                         (double) loop->nsteps;
337                                         } else {
338                                                 xsum += xprod * (double) loop->melting / (double) loop->nsteps;
339                                                 ysum += yprod * (double) loop->melting / (double) loop->nsteps;
340                                         }
341                                 } else {
342                                         xsum = xprod;
343                                         ysum = yprod;
344                                 }
345                                 if (!fctr) {
346                                         xsum = xsum * (double) loop->radius / (double) lf[fctr]->nx;
347                                         ysum = ysum * (double) loop->radius / (double) lf[fctr]->ny;
348                                 }
349                         } else {
350                                 if (loop->melting) {
351                                         if (fctr) {
352                                                 yprod = xprod = (double) loop->radius *
353                                                         (double) (loop->nsteps - loop->melting) /
354                                                         (double) (loop->nsteps);
355                                         } else {
356                                                 yprod = xprod = (double) loop->radius *
357                                                         (double) (loop->melting) / (double) (loop->nsteps);
358                                         }
359                                 } else {
360                                         xprod = yprod = (double) loop->radius;
361                                 }
362                                 while (xctr--)
363                                         xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
364                                 while (yctr--)
365                                         yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
366                                 xsum += xprod;
367                                 ysum += yprod;
368                         }
369                 }
370                 if ((loop->nfuncs > 1) && (!loop->melting)) {
371                         xsum /= (double) loop->nfuncs;
372                         ysum /= (double) loop->nfuncs;
373                 }
374                 xsum += (double) loop->center.x;
375                 ysum += (double) loop->center.y;
376
377                 np[pctr].x = (int) ceil(xsum);
378                 np[pctr].y = (int) ceil(ysum);
379         }
380     /* fill in extra points */
381     for (pctr=loop->nsteps; pctr < loop->nsteps+extra_points; pctr++) {
382           np[pctr].x = np[pctr - loop->nsteps].x;
383           np[pctr].y = np[pctr - loop->nsteps].y;
384     }
385         if (loop->melting) {
386                 if (!--loop->melting) {
387                         loop->nfuncs = 1;
388                         loop->function[0] = loop->function[1];
389                 }
390         }
391
392     /* reset starting color each time to prevent ass-like appearance */ 
393     loop->color = STARTCOLOR;
394
395   if (loop->cstep < xMaxLines) {
396         /*      printf("Drawing dashes\n"); */
397         for (pctr = 0; pctr < loop->nsteps; pctr+=loop->cstep) {
398 #if defined DRAWLINES
399                 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
400                                    LINESTYLE, LINECAP, LINEJOIN);
401                 /* erase the last cycle's point */
402                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
403                 XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), &lp[pctr], 
404                                    loop->cstep, CoordModeOrigin);
405
406                 /* Set the new color */
407                 SET_COLOR();
408
409                 /* plot this cycle's point */
410                 XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), 
411                                    &np[pctr], loop->cstep, CoordModeOrigin);
412                 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
413                                                    LINESTYLE, LINECAP, LINEJOIN);
414 #else
415                 /* erase the last cycle's point */
416                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
417                 XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
418                                         &lp[pctr], loop->cstep, CoordModeOrigin);
419
420                 /* Set the new color */
421                 SET_COLOR();
422
423                 /* plot this cycle's point */
424                 XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi),
425                                         MI_GC(mi), &np[pctr], loop->cstep, CoordModeOrigin);
426 #endif
427         }
428   } else { /* on my system, cstep is larger than 65532/2 if we get here */
429         for (pctr = 0; pctr < loop->nsteps; pctr++) {
430 #if defined DRAWLINES
431           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
432                                                  LINESTYLE, LINECAP, LINEJOIN);
433           /* erase the last cycle's point */
434           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
435           XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi),
436                                 MI_GC(mi), lp[pctr].x, lp[pctr].y,
437                                 lp[(pctr + 1) % loop->nsteps].x,
438                                 lp[(pctr + 1) % loop->nsteps].y);
439           
440           /* Set the new color */
441           SET_COLOR();
442           
443           /* plot this cycle's point */
444           XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi),
445                                 MI_GC(mi), np[pctr].x, np[pctr].y,
446                                 np[(pctr + 1) % loop->nsteps].x,
447                                 np[(pctr + 1) % loop->nsteps].y);
448           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
449                                                  LINESTYLE, LINECAP, LINEJOIN);
450 #else  /* DRAWLINES */
451           /* erase the last cycle's point */
452           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
453           XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
454                                  MI_GC(mi), lp[pctr].x, lp[pctr].y);
455           
456           /* Set the new color */
457           SET_COLOR();
458           
459           /* plot this cycle's point */
460           XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
461                                  MI_GC(mi), np[pctr].x, np[pctr].y);
462 #endif  /* DRAWLINES */
463         }
464   }
465         (void) free((void *) lp);
466         loop->lastpoint = np;
467         return True;
468 }
469
470 static Bool 
471 initlisa(ModeInfo * mi, lisas * loop)
472 {
473   lisacons   *lc = &Lisa[MI_SCREEN(mi)];
474   lisafuncs **lf = loop->function;
475   XPoint     *lp;
476   int         phase, pctr, fctr, xctr, yctr, extra_points;
477   double      xprod, yprod, xsum, ysum;
478   
479   xMaxLines = (XMaxRequestSize(MI_DISPLAY(mi))-3)/2;
480   /*  printf("Got xMaxLines = %d\n", xMaxLines); */
481   loop->nsteps = MI_CYCLES(mi);
482   if (loop->nsteps == 0)
483         loop->nsteps = 1;
484   if (MI_NPIXELS(mi) > 2) {
485         loop->color = STARTCOLOR;
486         loop->cstep = (loop->nsteps > MI_NPIXELS(mi)) ? loop->nsteps / MI_NPIXELS(mi) : 1;
487   } else {
488         loop->color = MI_WHITE_PIXEL(mi);
489         loop->cstep = 0;
490   }
491   extra_points = loop->cstep - (loop->nsteps % loop->cstep);
492   lc->maxcycles = (MAXCYCLES * loop->nsteps) - 1;
493   loop->cstep = ( loop->nsteps > MI_NPIXELS(mi) ) ? loop->nsteps / MI_NPIXELS(mi) : 1;
494   /*  printf("Got cstep = %d\n", loop->cstep); */
495   loop->melting = 0;
496   loop->nfuncs = 1;
497   loop->pistep = 2.0 * M_PI / (double) loop->nsteps;
498   loop->center.x = lc->width / 2;
499   loop->center.y = lc->height / 2;
500   loop->radius = (int) MI_SIZE(mi);
501   CHECK_RADIUS(loop, lc);
502   loop->dx = NRAND(XVMAX);
503   loop->dy = NRAND(YVMAX);
504   loop->dx++;
505   loop->dy++;
506 #if defined STARTFUNC
507   lf[0] = &Function[STARTFUNC];
508 #else  /* STARTFUNC */
509   lf[0] = &Function[NRAND(NUMSTDFUNCS)];
510 #endif  /* STARTFUNC */ 
511   
512   if ((lp = loop->lastpoint = (XPoint *)
513            calloc(loop->nsteps+extra_points, sizeof (XPoint))) == NULL) {
514         free_lisa(lc);
515         return False;
516   }
517   phase = lc->loopcount % loop->nsteps;
518   
519 #if defined DEBUG
520   printf( "nsteps = %d\tcstep = %d\tmrs = %d\textra_points = %d\n", 
521                   loop->nsteps, loop->cstep, xMaxLines, extra_points );
522   PRINT_FUNC(lf[0]);
523 #endif  /* DEBUG */
524   
525   for (pctr = 0; pctr < loop->nsteps; pctr++) {
526         loop->phi = (double) (pctr - phase) * loop->pistep;
527         loop->theta = (double) (pctr + phase) * loop->pistep;
528         fctr = loop->nfuncs;
529         xsum = ysum = 0.0;
530         while (fctr--) {
531           xprod = yprod = (double) loop->radius;
532           xctr = lf[fctr]->nx;
533           yctr = lf[fctr]->ny;
534           while (xctr--)
535                 xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
536           while (yctr--)
537                 yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
538           xsum += xprod;
539           ysum += yprod;
540         }
541         if (loop->nfuncs > 1) {
542           xsum /= 2.0;
543           ysum /= 2.0;
544         }
545         xsum += (double) loop->center.x;
546         ysum += (double) loop->center.y;
547         
548         lp[pctr].x = (int) ceil(xsum);
549         lp[pctr].y = (int) ceil(ysum);
550   }
551   /* this fills in the extra points, so we can use segment-drawing calls */
552   for (pctr = loop->nsteps; pctr < loop->nsteps + extra_points; pctr++) {
553         lp[pctr].x=lp[pctr - loop->nsteps].x;
554         lp[pctr].y=lp[pctr - loop->nsteps].y;
555   }
556 #if defined DRAWLINES
557   loop->linewidth = LINEWIDTH;  /* #### make this a resource */
558   
559   if (loop->linewidth == 0)
560         loop->linewidth = 1;
561   if (loop->linewidth < 0)
562         loop->linewidth = NRAND(-loop->linewidth) + 1;
563   XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
564                                          LINESTYLE, LINECAP, LINEJOIN);
565 #endif  /* DRAWLINES */
566   
567   if ( loop->cstep < xMaxLines ) { 
568         /* we can send each color segment in a single request 
569          * because the max request length is long enough 
570          * and because we have padded out the array to have extra elements 
571          * to support calls which would otherwise fall off the end*/
572         for (pctr = 0; pctr < loop->nsteps; pctr+=loop->cstep) {
573           /* Set the color */
574           SET_COLOR();
575           
576 #if defined DRAWLINES
577           XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi),
578                                  MI_GC(mi), &lp[pctr], loop->cstep, CoordModeOrigin );
579 #else  /* DRAWLINES */
580           XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
581                                   &lp[pctr], loop->cstep, CoordModeOrigin );
582 #endif  /* DRAWLINES */
583         }
584   } else { /* do it one by one as before */
585         for (pctr = 0; pctr < loop->nsteps; pctr++ ) {
586           SET_COLOR();
587           
588 #if defined DRAWLINES
589           XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
590                                 lp[pctr].x, lp[pctr].y,
591                                 lp[pctr+1 % loop->nsteps].x, lp[pctr+1 % loop->nsteps].y);
592 #else  /* DRAWLINES */
593           XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
594                                  lp[pctr].x, lp[pctr].y);
595 #endif  /* DRAWLINES */
596         }
597   }
598   
599 #if defined DRAWLINES
600   XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
601                                          LINESTYLE, LINECAP, LINEJOIN);
602 #endif  /* DRAWLINES */
603   return True;
604 }
605
606 static void
607 refreshlisa(ModeInfo * mi)
608 {
609         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
610         int         lctr;
611
612         for (lctr = 0; lctr < lc->nlissajous; lctr++) {
613                 if (!drawlisa(mi, &lc->lissajous[lctr]))
614                         return;
615         }
616 }
617
618 ENTRYPOINT void
619 refresh_lisa(ModeInfo * mi)
620 {
621         lisacons   *lc;
622
623         if (Lisa == NULL)
624                 return;
625         lc = &Lisa[MI_SCREEN(mi)];
626         if (lc->lissajous == NULL)
627                 return;
628
629         if (lc->painted) {
630                 lc->painted = False;
631                 MI_CLEARWINDOW(mi);
632                 refreshlisa(mi);
633         }
634 }
635
636 static void
637 change_lisa(ModeInfo * mi)
638 {
639         lisas      *loop;
640         int         lctr, newfunc;
641         lisacons   *lc;
642
643         if (Lisa == NULL)
644                 return;
645         lc = &Lisa[MI_SCREEN(mi)];
646         if (lc->lissajous == NULL)
647                 return;
648
649         lc->loopcount = 0;
650         for (lctr = 0; lctr < lc->nlissajous; lctr++) {
651                 loop = &lc->lissajous[lctr];       /* count through the loops we're drawing */
652         newfunc = NRAND(NUMSTDFUNCS);     /* choose a new function at random */
653 #if defined FOLLOW_FUNC_ORDER
654         loop->function[1] =
655           &Function[(loop->function[0]->index + 1) % NUMSTDFUNCS];
656 #else  /* FOLLOW_FUNC_ORDER */
657         if (newfunc == loop->function[0]->index) {
658           ++newfunc;
659           newfunc %= NUMSTDFUNCS;       /* take the next if we got the one we have */
660         }
661         if (newfunc >= RAREFUNCMIN \
662                 && !(random() % RAREFUNCODDS) \
663                 && (newfunc = NRAND(NUMSTDFUNCS)) == loop->function[0]->index) {
664           ++newfunc;
665           newfunc %= NUMSTDFUNCS;
666         }
667         loop->function[1] =               /* set 2nd function pointer on the loop */
668           &Function[newfunc];             /* to the new function we just chose */
669 #endif  /* FOLLOW_FUNC_ORDER */         
670 #if defined DEBUG
671         PRINT_FUNC(loop->function[1]);
672 #endif /* DEBUG */
673         loop->melting = loop->nsteps - 1; /* melt the two functions together */ 
674         loop->nfuncs = 2;                 /* simultaneously for a full cycle */  
675   }
676 }
677
678 ENTRYPOINT void
679 init_lisa (ModeInfo * mi)
680 {
681         int         lctr;
682         lisacons   *lc;
683
684         if (Lisa == NULL) {
685                 if ((Lisa = (lisacons *) calloc(MI_NUM_SCREENS(mi),
686                                  sizeof (lisacons))) == NULL)
687                         return;
688         }
689         lc = &Lisa[MI_SCREEN(mi)];
690         lc->width = MI_WIDTH(mi);
691         lc->height = MI_HEIGHT(mi);
692         lc->loopcount = 0;
693         lc->nlissajous = MI_COUNT(mi);
694         if (lc->nlissajous <= 0)
695                 lc->nlissajous = 1;
696         MI_CLEARWINDOW(mi);
697         lc->painted = False;
698
699         if (lc->lissajous == NULL) {
700                 if ((lc->lissajous = (lisas *) calloc(lc->nlissajous,
701                                 sizeof (lisas))) == NULL)
702                         return;
703                 for (lctr = 0; lctr < lc->nlissajous; lctr++) {
704                         if (!initlisa(mi, &lc->lissajous[lctr]))
705                                 return;
706                         lc->loopcount++;
707                 }
708         } else {
709                 refreshlisa(mi);
710         }
711 }
712
713 ENTRYPOINT void
714 draw_lisa (ModeInfo * mi)
715 {
716         lisacons   *lc;
717
718         if (Lisa == NULL)
719                 return;
720         lc = &Lisa[MI_SCREEN(mi)];
721         if (lc->lissajous == NULL)
722                 return;
723
724 #ifdef HAVE_COCOA       /* Don't second-guess Quartz's double-buffering */
725     XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
726 #endif
727
728         MI_IS_DRAWN(mi) = True;
729         lc->painted = True;
730         if (++lc->loopcount > lc->maxcycles) {
731                 change_lisa(mi);
732         }
733         refreshlisa(mi);
734 }
735
736 ENTRYPOINT void
737 release_lisa (ModeInfo * mi)
738 {
739         if (Lisa) {
740                 int    screen;
741
742                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
743                         free_lisa(&Lisa[screen]);
744                 (void) free(Lisa);
745                 Lisa = (lisacons *) NULL;
746         }
747 }
748
749 XSCREENSAVER_MODULE ("Lisa", lisa)
750
751 #endif /* MODE_lisa */