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