0d0f5abf4f56472a2e32529d898aa293c84e1c7f
[xscreensaver] / hacks / swirl.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * swirl --- swirly color-cycling patterns.
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)swirl.c       4.00 97/01/01 xlockmore";
6 #endif
7
8 /* Copyright (c) 1994 M.Dobie <mrd@ecs.soton.ac.uk>
9  *
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * 13-May-97: jwz@netscape.com: turned into a standalone program.
23  * 21-Apr-95: improved startup time for TrueColour displays
24  *            (limited to 16bpp to save memory) S.Early <sde1000@cam.ac.uk>
25  * 09-Jan-95: fixed colour maps (more colourful) and the image now spirals
26  *            outwards from the centre with a fixed number of points drawn
27  *            every iteration. Thanks to M.Dobie <mrd@ecs.soton.ac.uk>.
28  * 1994:      written.   Copyright (c) 1994 M.Dobie <mrd@ecs.soton.ac.uk>
29  *            based on original code by R.Taylor
30  */
31
32 #ifdef STANDALONE
33 # define PROGCLASS                                      "Swirl"
34 # define HACK_INIT                                      init_swirl
35 # define HACK_DRAW                                      draw_swirl
36 # define swirl_opts                                     xlockmore_opts
37 # define DEFAULTS       "*count:                5       \n"                     \
38                                         "*delay:                10000   \n"                     \
39                                         "*ncolors:              200     \n"
40 # define SMOOTH_COLORS
41 # define WRITABLE_COLORS
42 # include "xlockmore.h"                         /* from the xscreensaver distribution */
43 # include <X11/Xutil.h>
44 #else  /* !STANDALONE */
45 # include "xlock.h"                                     /* from the xlockmore distribution */
46 #endif /* !STANDALONE */
47
48 ModeSpecOpt swirl_opts = {
49   0, NULL, 0, NULL, NULL };
50
51 #include <time.h>
52
53 /****************************************************************/
54
55 #define MASS            4       /* maximum mass of a knot */
56 #define MIN_RES         5       /* minimim resolution (>= MIN_RES) */
57 #define MAX_RES         1       /* maximum resolution (>0) */
58 #define TWO_PLANE_PCNT  30      /* probability for two plane mode (0-100) */
59 #define RESTART         2500    /* number of cycles before restart */
60 #define BATCH_DRAW      100     /* points to draw per iteration */
61
62 /* knot types */
63 typedef enum {
64         NONE = 0,
65         ORBIT = (1 << 0),
66         WHEEL = (1 << 1),
67         PICASSO = (1 << 2),
68         RAY = (1 << 3),
69         HOOK = (1 << 4),
70         ALL = (1 << 5)
71 } KNOT_T;
72
73 /* a knot */
74 typedef struct Knot {
75         int         x, y;       /* position */
76         int         m;          /* mass */
77         KNOT_T      t;          /* type in the first (or only) plane */
78         KNOT_T      T;          /* type in second plane if there is one */
79         int         M;          /* mass in second plane if there is one */
80 } KNOT     , *KNOT_P;
81
82 /* a colour specification */
83 typedef struct Colour {
84         unsigned short r, g, b;
85 } COLOUR   , *COLOUR_P;
86
87 /* drawing direction */
88 typedef enum {
89         DRAW_RIGHT, DRAW_DOWN, DRAW_LEFT, DRAW_UP
90 } DIR_T;
91
92 /****************************************************************/
93
94 /* data associated with a swirl window */
95 typedef struct swirl_data {
96         /* window paramaters */
97         Window      win;        /* the window */
98         int         width, height;      /* window size */
99         int         depth;      /* depth */
100         int         rdepth;     /* real depth (for XImage) */
101         Visual     *visual;     /* visual */
102
103         /* swirl drawing parameters */
104         int         n_knots;    /* number of knots */
105         KNOT_P      knots;      /* knot details */
106         KNOT_T      knot_type;  /* general type of knots */
107         int         resolution; /* drawing resolution, 1..5 */
108         int         max_resolution;     /* maximum resolution, MAX_RES */
109         int         r;          /* pixel step */
110         Bool        two_plane;  /* two plane mode? */
111         Bool        first_plane;        /* doing first plane? */
112         int         start_again;        /* when to restart */
113
114         /* spiral drawing parameters */
115         int         x, y;       /* current point */
116         DIR_T       direction;  /* current direction */
117         int         dir_todo, dir_done;         /* how many points in current direction? */
118         int         batch_todo, batch_done;     /* how many points in this batch */
119         Bool        started, drawing;   /* are we drawing? */
120
121         /* image stuff */
122         unsigned char *image;   /* image data */
123         XImage     *ximage;
124
125         /* colours stuff */
126         int         colours;    /* how many colours possible */
127         int         dcolours;   /* how many colours for shading */
128 #ifndef STANDALONE
129         Bool        fixed_colourmap;    /* fixed colourmap? */
130 #endif /* !STANDALONE */
131         Bool        monochrome; /* monochrome? */
132         Colormap    cmap;       /* colour map for the window */
133         XColor     *rgb_values; /* colour definitions array */
134 #ifndef STANDALONE
135         int         current_map;        /* current colour map, 0..dcolours-1 */
136         unsigned long fg, bg, white, black;     /* black and white pixel values */
137         int         shift;      /* colourmap shift */
138         int         dshift;     /* colourmap shift while drawing */
139         XColor      fgcol, bgcol;       /* foreground and background colour specs */
140 #endif /* !STANDALONE */
141         Bool       off_screen;
142 } SWIRL    , *SWIRL_P;
143
144 #define SWIRLCOLOURS 13
145
146 #ifndef STANDALONE
147 /* basic colours */
148 static COLOUR basic_colours[SWIRLCOLOURS];
149 #endif /* !STANDALONE */
150
151 /* an array of swirls for each screen */
152 static SWIRL_P swirls = NULL;
153
154 /* 
155    random_no
156
157    Return a random integer between 0 and n inclusive
158
159    -      n is the maximum number
160
161    Returns a random integer */
162
163 static int
164 random_no(unsigned int n)
165 {
166         return ((int) ((n + 1) * (double) LRAND() / MAXRAND));
167 }
168
169 /****************************************************************/
170
171 /* 
172    initialise_swirl
173
174    Initialise all the swirl data
175
176    -      swirl is the swirl data */
177
178 static void
179 initialise_swirl(ModeInfo * mi, SWIRL_P swirl)
180 {
181 #ifndef STANDALONE
182         Display    *display = MI_DISPLAY(mi);
183 #endif /* !STANDALONE */
184
185         swirl->width = 0;       /* width and height of window */
186         swirl->height = 0;
187         swirl->depth = 1;
188         swirl->rdepth = 1;
189         swirl->visual = NULL;
190         swirl->resolution = MIN_RES + 1;        /* current resolution */
191         swirl->max_resolution = MAX_RES;        /* maximum resolution */
192         swirl->n_knots = 0;     /* number of knots */
193         swirl->knot_type = ALL; /* general type of knots */
194         swirl->two_plane = False;       /* two plane mode? */
195         swirl->first_plane = False;     /* doing first plane? */
196         swirl->start_again = -1;        /* restart counter */
197
198         /* drawing parameters */
199         swirl->x = 0;
200         swirl->y = 0;
201         swirl->started = False;
202         swirl->drawing = False;
203
204         /* image stuff */
205         swirl->image = NULL;    /* image data */
206         swirl->ximage = NULL;
207
208         /* colours stuff */
209         swirl->colours = 0;     /* how many colours possible */
210         swirl->dcolours = 0;    /* how many colours for shading */
211         swirl->cmap = (Colormap) NULL;
212         swirl->rgb_values = NULL;       /* colour definitions array */
213 #ifndef STANDALONE
214         swirl->current_map = 0; /* current colour map, 0..dcolours-1 */
215
216         /* set up fg fb colour specs */
217         swirl->white = MI_WIN_WHITE_PIXEL(mi);
218         swirl->black = MI_WIN_BLACK_PIXEL(mi);
219 #endif /* !STANDALONE */
220
221
222 #ifdef STANDALONE
223 # define MI_COLORMAP MI_WIN_COLORMAP
224 #else /* !STANDALONE */
225         swirl->fg = MI_FG_COLOR(mi);
226         swirl->bg = MI_BG_COLOR(mi);
227         swirl->fgcol.pixel = swirl->fg;
228         swirl->bgcol.pixel = swirl->bg;
229         XQueryColor(display, MI_COLORMAP(mi), &(swirl->fgcol));
230         XQueryColor(display, MI_COLORMAP(mi), &(swirl->bgcol));
231 #endif /* !STANDALONE */
232 }
233
234 /****************************************************************/
235
236 /* 
237  * initialise_image
238  *
239  * Initialise the image for drawing to
240  *
241  * -      swirl is the swirl data
242  */
243 static void
244 initialise_image(ModeInfo * mi, SWIRL_P swirl)
245 {
246   Display *dpy = MI_DISPLAY(mi);
247
248   if (swirl->ximage != NULL)
249         XDestroyImage(swirl->ximage);
250
251   swirl->ximage = XCreateImage(dpy, swirl->visual, swirl->rdepth, ZPixmap,
252                                                            0, 0, swirl->width, swirl->height,
253                                                            8, 0);
254   swirl->ximage->data = swirl->image =
255         (unsigned char *) calloc(swirl->height, swirl->ximage->bytes_per_line);
256 }
257
258 /****************************************************************/
259
260 #ifndef STANDALONE
261 /* 
262  * initialise_colours
263  *
264  * Initialise the list of colours from which the colourmaps are derived
265  *
266  * -      colours is the array to initialise
267  * -      saturation is the saturation value to use 0->grey,
268  *            1.0->full saturation
269  */
270 static void
271 initialise_colours(COLOUR * colours, float saturate)
272 {
273         int         i;
274
275         /* start off fully saturated, medium and bright colours */
276         colours[0].r = 0xA000;
277         colours[0].g = 0x0000;
278         colours[0].b = 0x0000;
279         colours[1].r = 0xD000;
280         colours[1].g = 0x0000;
281         colours[1].b = 0x0000;
282         colours[2].r = 0x0000;
283         colours[2].g = 0x6000;
284         colours[2].b = 0x0000;
285         colours[3].r = 0x0000;
286         colours[3].g = 0x9000;
287         colours[3].b = 0x0000;
288         colours[4].r = 0x0000;
289         colours[4].g = 0x0000;
290         colours[4].b = 0xC000;
291         colours[5].r = 0x0000;
292         colours[5].g = 0x0000;
293         colours[5].b = 0xF000;
294         colours[6].r = 0xA000;
295         colours[6].g = 0x6000;
296         colours[6].b = 0x0000;
297         colours[7].r = 0xD000;
298         colours[7].g = 0x9000;
299         colours[7].b = 0x0000;
300         colours[8].r = 0xA000;
301         colours[8].g = 0x0000;
302         colours[8].b = 0xC000;
303         colours[9].r = 0xD000;
304         colours[9].g = 0x0000;
305         colours[9].b = 0xF000;
306         colours[10].r = 0x0000;
307         colours[10].g = 0x6000;
308         colours[10].b = 0xC000;
309         colours[11].r = 0x0000;
310         colours[11].g = 0x9000;
311         colours[11].b = 0xF000;
312         colours[12].r = 0xA000;
313         colours[12].g = 0xA000;
314         colours[12].b = 0xA000;
315
316         /* add white for low saturation */
317         for (i = 0; i < SWIRLCOLOURS - 1; i++) {
318                 unsigned short max_rg, max;
319
320                 /* what is the max intensity for this colour? */
321                 max_rg = (colours[i].r > colours[i].g) ? colours[i].r : colours[i].g;
322                 max = (max_rg > colours[i].b) ? max_rg : colours[i].b;
323
324                 /* bring elements up to max as saturation approaches 0.0 */
325                 colours[i].r += (unsigned short) ((float) (1.0 - saturate) *
326                                                ((float) max - colours[i].r));
327                 colours[i].g += (unsigned short) ((float) (1.0 - saturate) *
328                                                ((float) max - colours[i].g));
329                 colours[i].b += (unsigned short) ((float) (1.0 - saturate) *
330                                                ((float) max - colours[i].b));
331         }
332 }
333 #endif /* !STANDALONE */
334
335 /****************************************************************/
336
337 #ifndef STANDALONE
338 /* 
339  * set_black_and_white
340  *
341  * Set the entries for foreground & background pixels and
342  * WhitePixel & BlackPixel in an array of colour specifications.
343  *
344  * -      swirl is the swirl data
345  * -      values is the array of specifications 
346  */
347 static void
348 set_black_and_white(SWIRL_P swirl, XColor * values)
349 {
350         unsigned long white, black;
351
352         /* where is black and white? */
353         white = swirl->white;
354         black = swirl->black;
355
356         /* set black and white up */
357         values[white].flags = DoRed | DoGreen | DoBlue;
358         values[white].pixel = white;
359         values[white].red = 0xFFFF;
360         values[white].green = 0xFFFF;
361         values[white].blue = 0xFFFF;
362         values[black].flags = DoRed | DoGreen | DoBlue;
363         values[black].pixel = black;
364         values[black].red = 0;
365         values[black].green = 0;
366         values[black].blue = 0;
367
368         /* copy the colour specs from the original entries */
369         values[swirl->fg] = swirl->fgcol;
370         values[swirl->bg] = swirl->bgcol;
371 }
372
373 /****************************************************************/
374
375 /* 
376  * set_colour
377  *
378  * Set an entry in an array of XColor specifications. The given entry will be
379  * set to the given colour. If the entry corresponds to the foreground,
380  * background, WhitePixel, or BlackPixel it is ignored and the given colour
381  * is is put in the next entry.
382  *
383  * Therefore, the given colour may be placed up to four places after the
384  * specified entry in the array, if foreground, background, white, or black
385  * intervene.
386  *
387  * -      swirl is the swirl data
388  * -      value points to a pointer to the array entry. It gets updated to
389  *            point to the next free entry.
390  * -      pixel points to the current pixel number. It gets updated.
391  * -      c points to the colour to add
392  */
393 static void
394 set_colour(SWIRL_P swirl, XColor ** value, unsigned long *pixel, COLOUR_P c)
395 {
396         Bool        done;
397         unsigned long fg, bg, white, black;
398
399         /* where are foreground, background, white, and black? */
400         fg = swirl->fg;
401         bg = swirl->bg;
402         white = swirl->white;
403         black = swirl->black;
404
405         /* haven't set it yet */
406         done = False;
407
408         /* try and set the colour */
409         while (!done) {
410                 (**value).flags = DoRed | DoGreen | DoBlue;
411                 (**value).pixel = *pixel;
412
413                 /* white, black, fg, bg, or a colour? */
414                 if ((*pixel != fg) && (*pixel != bg) &&
415                     (*pixel != white) && (*pixel != black)) {
416                         (**value).red = c->r;
417                         (**value).green = c->g;
418                         (**value).blue = c->b;
419
420                         /* now we've done it */
421                         done = True;
422                 }
423                 /* next pixel */
424                 (*value)++;
425                 (*pixel)++;
426         }
427 }
428
429 /****************************************************************/
430
431 /* 
432  * get_colour
433  *
434  * Get an entry from an array of XColor specifications. The next colour from
435  * the array will be returned. Foreground, background, WhitePixel, or
436  * BlackPixel will be ignored.
437  *
438  * -      swirl is the swirl data
439  * -      value points the array entry. It is updated to point to the entry
440  *            following the one returned.
441  * -      c is set to the colour found
442  */
443 static void
444 get_colour(SWIRL_P swirl, XColor ** value, COLOUR_P c)
445 {
446         Bool        done;
447         unsigned long fg, bg, white, black;
448
449         /* where is white and black? */
450         fg = swirl->fg;
451         bg = swirl->bg;
452         white = swirl->white;
453         black = swirl->black;
454
455         /* haven't set it yet */
456         done = False;
457
458         /* try and set the colour */
459         while (!done) {
460                 /* black, white or a colour? */
461                 if (((*value)->pixel != fg) && ((*value)->pixel != bg) &&
462                   ((*value)->pixel != white) && ((*value)->pixel != black)) {
463                         c->r = (*value)->red;
464                         c->g = (*value)->green;
465                         c->b = (*value)->blue;
466
467                         /* now we've done it */
468                         done = True;
469                 }
470                 /* next value */
471                 (*value)++;
472         }
473 }
474 #endif /* !STANDALONE */
475
476 /****************************************************************/
477
478 #ifndef STANDALONE
479 /* 
480  *  interpolate
481  *
482  * Generate n colours between c1 and c2.  n XColors at *value are set up with
483  * ascending pixel values.
484  *
485  * If the pixel range includes BlackPixel or WhitePixel they are set to black
486  * and white respectively but otherwise ignored. Therefore, up to n+2 colours
487  * may actually be set by this function.
488  *
489  * -      swirl is the swirl data
490  * -      values points a pointer to an array of XColors to update
491  * -      pixel points to the pixel number to start at
492  * -      k n is the number of colours to generate
493  * -      c1, c2 are the colours to interpolate between
494  */
495 static void
496 interpolate(SWIRL_P swirl, XColor ** values, unsigned long *pixel, int n, COLOUR_P c1, COLOUR_P c2)
497 {
498         int         i, r, g, b;
499         COLOUR      c;
500         unsigned short maxv;
501
502         /* maximum value */
503         maxv = (255 << 8);
504
505         for (i = 0; i < n / 2 && (int) *pixel < swirl->colours; i++) {
506                 /* work out the colour */
507                 r = c1->r + 2 * i * ((int) c2->r) / n;
508                 c.r = (r > (int) maxv) ? maxv : r;
509                 g = c1->g + 2 * i * ((int) c2->g) / n;
510                 c.g = (g > (int) maxv) ? maxv : g;
511                 b = c1->b + 2 * i * ((int) c2->b) / n;
512                 c.b = (b > (int) maxv) ? maxv : b;
513
514                 /* set it up */
515                 set_colour(swirl, values, pixel, &c);
516         }
517         for (i = n / 2; i >= 0 && (int) *pixel < swirl->colours; i--) {
518                 r = c2->r + 2 * i * ((int) c1->r) / n;
519                 c.r = (r > (int) maxv) ? maxv : r;
520                 g = c2->g + 2 * i * ((int) c1->g) / n;
521                 c.g = (g > (int) maxv) ? maxv : g;
522                 b = c2->b + 2 * i * ((int) c1->b) / n;
523                 c.b = (b > (int) maxv) ? maxv : b;
524
525                 /* set it up */
526                 set_colour(swirl, values, pixel, &c);
527         }
528 }
529
530 /****************************************************************/
531
532 /* 
533  * basic_map
534  *
535  * Generate a `random' closed loop colourmap that occupies the whole colour
536  * map.
537  *
538  * -      swirl is the swirl data
539  * -      values is the array of colour definitions to set up
540  */
541 static void
542 basic_map(SWIRL_P swirl, XColor * values)
543 {
544         COLOUR      c[3];
545         int         i;
546         unsigned short r1, g1, b1, r2, g2, b2, r3, g3, b3;
547         int         L1, L2, L3, L;
548         unsigned long pixel;
549         XColor     *value;
550
551         /* start at the beginning of the colour map */
552         pixel = 0;
553         value = values;
554
555         /* choose 3 different basic colours at random */
556         for (i = 0; i < 3;) {
557                 int         j;
558                 Bool        same;
559
560                 /* choose colour i */
561                 c[i] = basic_colours[random_no(SWIRLCOLOURS - 1)];
562
563                 /* assume different */
564                 same = False;
565
566                 /* different from the rest? */
567                 for (j = 0; j < i; j++)
568                         if ((c[i].r == c[j].r) &&
569                             (c[i].g == c[j].g) &&
570                             (c[i].b == c[j].b))
571                                 same = True;
572
573                 /* ready for the next colour? */
574                 if (!same)
575                         i++;
576         }
577
578         /* extract components into variables */
579         r1 = c[0].r;
580         g1 = c[0].g;
581         b1 = c[0].b;
582         r2 = c[1].r;
583         g2 = c[1].g;
584         b2 = c[1].b;
585         r3 = c[2].r;
586         g3 = c[2].g;
587         b3 = c[2].b;
588
589         /* work out the lengths of each side of the triangle */
590         L1 = (int) sqrt((((double) r1 - r2) * ((double) r1 - r2) +
591                          ((double) g1 - g2) * ((double) g1 - g2) +
592                          ((double) b1 - b2) * ((double) b1 - b2)));
593
594         L2 = (int) sqrt((((double) r3 - r2) * ((double) r3 - r2) +
595                          ((double) g3 - g2) * ((double) g3 - g2) +
596                          ((double) b3 - b2) * ((double) b3 - b2)));
597
598         L3 = (int) sqrt((((double) r1 - r3) * ((double) r1 - r3) +
599                          ((double) g1 - g3) * ((double) g1 - g3) +
600                          ((double) b1 - b3) * ((double) b1 - b3)));
601
602         L = L1 + L2 + L3;
603
604         /* allocate colours in proportion to the lengths of the sides */
605         interpolate(swirl, &value, &pixel,
606                     (int) ((double) swirl->dcolours * ((double) L1 / (double) L)) + 1, c, c + 1);
607         interpolate(swirl, &value, &pixel,
608                     (int) ((double) swirl->dcolours * ((double) L2 / (double) L)) + 1, c + 1, c + 2);
609         interpolate(swirl, &value, &pixel,
610                     (int) ((double) swirl->dcolours * ((double) L3 / (double) L)) + 1, c + 2, c);
611
612         /* fill up any remaining slots (due to rounding) */
613         while ((int) pixel < swirl->colours) {
614                 /* repeat the last colour */
615                 set_colour(swirl, &value, &pixel, c);
616         }
617
618         /* ensure black and white are correct */
619         if (!swirl->fixed_colourmap)
620                 set_black_and_white(swirl, values);
621 }
622
623 /****************************************************************/
624
625 /* 
626  * pre_rotate
627  *
628  * Generate pre-rotated versions of the colour specifications
629  *
630  * -      swirl is the swirl data
631  * -      values is an array of colour specifications
632  */
633 static void
634 pre_rotate(SWIRL_P swirl, XColor * values)
635 {
636         int         i, j;
637         XColor     *src, *dest;
638         int         dcolours;
639         unsigned long pixel;
640
641         /* how many colours to display? */
642         dcolours = swirl->dcolours;
643
644         /* start at the first map */
645         src = values;
646         dest = values + swirl->colours;
647
648         /* generate dcolours-1 rotated maps */
649         for (i = 0; i < dcolours - 1; i++) {
650                 COLOUR      first;
651
652                 /* start at the first pixel */
653                 pixel = 0;
654
655                 /* remember the first one and skip it */
656                 get_colour(swirl, &src, &first);
657
658                 /* put a rotated version of src at dest */
659                 for (j = 0; j < dcolours - 1; j++) {
660                         COLOUR      c;
661
662                         /* get the source colour */
663                         get_colour(swirl, &src, &c);
664
665                         /* set the colour */
666                         set_colour(swirl, &dest, &pixel, &c);
667                 }
668
669                 /* put the first one at the end */
670                 set_colour(swirl, &dest, &pixel, &first);
671
672                 /* NB: src and dest should now be ready for the next table */
673
674                 /* ensure black and white are properly set */
675                 set_black_and_white(swirl, src);
676         }
677 }
678
679 /****************************************************************/
680
681 /* 
682  * create_colourmap
683  *
684  * Create a read/write colourmap to use
685  *
686  * -      swirl is the swirl data
687  */
688
689 static void
690 create_colourmap(ModeInfo * mi, SWIRL_P swirl)
691 {
692         Display    *display = MI_DISPLAY(mi);
693         int         preserve;
694         int         n_rotations;
695         int         i;
696         Bool        truecolor;
697   unsigned long redmask, greenmask, bluemask;
698
699         swirl->fixed_colourmap = !setupColormap(mi, &(swirl->colours),
700     &truecolor, &redmask, &greenmask, &bluemask);
701         preserve = preserveColors(swirl->fg, swirl->bg, swirl->white, swirl->black);
702
703         /* how many colours should we animate? */
704         swirl->dcolours = (swirl->colours > preserve + 1) ?
705                 swirl->colours - preserve : swirl->colours;
706
707         if (MI_NPIXELS(mi) < 2)
708                 return;
709
710         /* how fast to shift the colourmap? */
711         swirl->shift = (swirl->colours > 64) ? swirl->colours / 64 : 1;
712         swirl->dshift = (swirl->shift > 1) ? swirl->shift * 2 : 1;
713
714         /* how may colour map rotations are there? */
715         n_rotations = (swirl->fixed_colourmap) ? 1 : swirl->dcolours;
716
717         /* allocate space for colour definitions (if not already there) */
718         if (swirl->rgb_values == NULL) {
719                 swirl->rgb_values = (XColor *) calloc((swirl->colours + 3) * n_rotations,
720                                                       sizeof (XColor));
721
722                 /* create a colour map */
723                 if (!swirl->fixed_colourmap)
724                         swirl->cmap =
725                                 XCreateColormap(display, swirl->win, swirl->visual, AllocAll);
726         }
727         /* select a set of colours for the colour map */
728         basic_map(swirl, swirl->rgb_values);
729
730         /* are we rotating them? */
731         if (!swirl->fixed_colourmap) {
732                 /* generate rotations of the colour maps */
733                 pre_rotate(swirl, swirl->rgb_values);
734
735                 /* store the colours in the colour map */
736                 XStoreColors(display, swirl->cmap, swirl->rgb_values, swirl->colours);
737         } else {
738                 if (truecolor) {
739                         int         rsh, gsh, bsh;
740                         unsigned long int t;
741
742                         t = redmask;
743                         for (i = 0; (int) t > 0; i++, t >>= 1);
744                         rsh = 16 - i;
745                         t = greenmask;
746                         for (i = 0; (int) t > 0; i++, t >>= 1);
747                         gsh = 16 - i;
748                         t = bluemask;
749                         for (i = 0; (int) t > 0; i++, t >>= 1);
750                         bsh = 16 - i;
751                         for (i = 0; i < swirl->colours; i++)
752                                 swirl->rgb_values[i].pixel =
753                                         ((rsh > 0 ? (swirl->rgb_values[i].red) >> rsh :
754                                           (swirl->rgb_values[i].red) << (-rsh)) & redmask) |
755                                         ((gsh > 0 ? (swirl->rgb_values[i].green) >> gsh :
756                                           (swirl->rgb_values[i].green) << (-gsh)) & greenmask) |
757                                         ((bsh > 0 ? (swirl->rgb_values[i].blue) >> bsh :
758                                           (swirl->rgb_values[i].blue) << (-bsh)) & bluemask);
759                 } else {
760                         /* lookup the colours in the fixed colour map */
761                         for (i = 0; i < swirl->colours; i++)
762                                 (void) XAllocColor(display, MI_COLORMAP(mi),
763                                                    &(swirl->rgb_values[i]));
764                 }
765         }
766 }
767
768 /****************************************************************/
769
770 /* 
771  * install_map
772  *
773  * Install a new set of colours into the colour map
774  *
775  * -      dpy is the display
776  * -      swirl is the swirl data
777  * -      shift is the amount to rotate the colour map by
778  */
779 static void
780 install_map(Display * dpy, SWIRL_P swirl, int shift)
781 {
782         if (!swirl->fixed_colourmap) {
783                 /* shift the colour map */
784                 swirl->current_map = (swirl->current_map + shift) %
785                         swirl->dcolours;
786
787                 /* store it */
788                 XStoreColors(dpy, swirl->cmap,
789                              swirl->rgb_values +
790                              swirl->current_map * swirl->colours,
791                              swirl->colours);
792         }
793 }
794 #endif /* !STANDALONE */
795
796 /****************************************************************/
797
798 /* 
799  * create_knots
800  *
801  * Initialise the array of knot
802  *
803  * swirl is the swirl data
804  */
805 static void
806 create_knots(SWIRL_P swirl)
807 {
808         int         k;
809         Bool        orbit, wheel, picasso, ray, hook;
810         KNOT_P      knot;
811
812         /* create array for knots */
813         if (swirl->knots)
814                 (void) free((void *) swirl->knots);
815         swirl->knots = (KNOT_P) calloc(swirl->n_knots, sizeof (KNOT));
816
817         /* no knots yet */
818         orbit = wheel = picasso = ray = hook = False;
819
820         /* what types do we have? */
821         if ((int) swirl->knot_type & (int) ALL) {
822                 orbit = wheel = ray = hook = True;
823         } else {
824                 if ((int) swirl->knot_type & (int) ORBIT)
825                         orbit = True;
826                 if ((int) swirl->knot_type & (int) WHEEL)
827                         wheel = True;
828                 if ((int) swirl->knot_type & (int) PICASSO)
829                         picasso = True;
830                 if ((int) swirl->knot_type & (int) RAY)
831                         ray = True;
832                 if ((int) swirl->knot_type & (int) HOOK)
833                         hook = True;
834         }
835
836         /* initialise each knot */
837         knot = swirl->knots;
838         for (k = 0; k < swirl->n_knots; k++) {
839                 /* position */
840                 knot->x = random_no((unsigned int) swirl->width);
841                 knot->y = random_no((unsigned int) swirl->height);
842
843                 /* mass */
844                 knot->m = random_no(MASS) + 1;
845
846                 /* can be negative */
847                 if (random_no(100) > 50)
848                         knot->m *= -1;
849
850                 /* type */
851                 knot->t = NONE;
852                 while (knot->t == NONE) {
853                         /* choose a random one from the types available */
854                         switch (random_no(4)) {
855                                 case 0:
856                                         if (orbit)
857                                                 knot->t = ORBIT;
858                                         break;
859                                 case 1:
860                                         if (wheel)
861                                                 knot->t = WHEEL;
862                                         break;
863                                 case 2:
864                                         if (picasso)
865                                                 knot->t = PICASSO;
866                                         break;
867                                 case 3:
868                                         if (ray)
869                                                 knot->t = RAY;
870                                         break;
871                                 case 4:
872                                         if (hook)
873                                                 knot->t = HOOK;
874                                         break;
875                         }
876                 }
877
878                 /* if two planes, do same for second plane */
879                 if (swirl->two_plane) {
880                         knot->T = NONE;
881                         while (knot->T == NONE || knot->T == knot->t) {
882                                 /* choose a different type */
883                                 switch (random_no(4)) {
884                                         case 0:
885                                                 if (orbit)
886                                                         knot->T = ORBIT;
887                                                 break;
888                                         case 1:
889                                                 if (wheel)
890                                                         knot->T = WHEEL;
891                                                 break;
892                                         case 2:
893                                                 if (picasso)
894                                                         knot->T = PICASSO;
895                                                 break;
896                                         case 3:
897                                                 if (ray)
898                                                         knot->T = RAY;
899                                                 break;
900                                         case 4:
901                                                 if (hook)
902                                                         knot->T = HOOK;
903                                                 break;
904                                 }
905                         }
906                 }
907                 /* next knot */
908                 knot++;
909         }
910 }
911
912 /****************************************************************/
913
914 /* 
915  * do_point
916  *
917  * Work out the pixel value at i, j. Ensure it does not clash with BlackPixel
918  * or WhitePixel.
919  *
920  * -      swirl is the swirl data
921  * -      i, j is the point to calculate
922  *
923  * Returns the value of the point
924  */
925 static unsigned long
926 do_point(SWIRL_P swirl, int i, int j)
927 {
928         int         tT, k, value, add;
929         double      dx, dy, theta, dist;
930         int         dcolours, qcolours;
931         double      rads;
932         KNOT_P      knot;
933
934         /* how many colours? */
935         dcolours = swirl->dcolours;
936         qcolours = dcolours / 4;
937
938         /* colour step round a circle */
939         rads = (double) dcolours / (2.0 * M_PI);
940
941         /* start at zero */
942         value = 0;
943
944         /* go through all the knots */
945         knot = swirl->knots;
946         for (k = 0; k < swirl->n_knots; k++) {
947                 dx = i - knot->x;
948                 dy = j - knot->y;
949
950                 /* in two_plane mode get the appropriate knot type */
951                 if (swirl->two_plane)
952                         tT = (int) ((swirl->first_plane) ? knot->t : knot->T);
953                 else
954                         tT = (int) knot->t;
955
956                 /* distance from knot */
957                 dist = sqrt(dx * dx + dy * dy);
958
959                 /* nothing to add at first */
960                 add = 0;
961
962                 /* work out the contribution (if close enough) */
963                 if (dist > 0.1)
964                         switch (tT) {
965                                 case ORBIT:
966                                         add = (int) (dcolours / (1.0 + 0.01 * abs(knot->m) * dist));
967                                         break;
968                                 case WHEEL:
969                                         /* Avoid atan2: DOMAIN error message */
970                                         if (dy == 0.0 && dx == 0.0)
971                                                 theta = 1.0;
972                                         else
973                                                 theta = (atan2(dy, dx) + M_PI) / M_PI;
974                                         if (theta < 1.0)
975                                                 add = (int) (dcolours * theta +
976                                                   sin(0.1 * knot->m * dist) *
977                                                 qcolours * exp(-0.01 * dist));
978                                         else
979                                                 add = (int) (dcolours * (theta - 1.0) +
980                                                   sin(0.1 * knot->m * dist) *
981                                                 qcolours * exp(-0.01 * dist));
982                                         break;
983                                 case PICASSO:
984                                         add = (int) (dcolours *
985                                           fabs(cos(0.002 * knot->m * dist)));
986                                         break;
987                                 case RAY:
988                                         /* Avoid atan2: DOMAIN error message */
989                                         if (dy == 0.0 && dx == 0.0)
990                                                 add = 0;
991                                         else
992                                                 add = (int) (dcolours * fabs(sin(2.0 * atan2(dy, dx))));
993
994                                         break;
995                                 case HOOK:
996                                         /* Avoid atan2: DOMAIN error message */
997                                         if (dy == 0.0 && dx == 0.0)
998                                                 add = (int) (0.05 * (abs(knot->m) - 1) * dist);
999                                         else
1000                                                 add = (int) (rads * atan2(dy, dx) +
1001                                                              0.05 * (abs(knot->m) - 1) * dist);
1002                                         break;
1003                         }
1004                 /* for a +ve mass add on the contribution else take it off */
1005                 if (knot->m > 0)
1006                         value += add;
1007                 else
1008                         value -= add;
1009
1010                 /* next knot */
1011                 knot++;
1012         }
1013
1014         /* toggle plane */
1015         swirl->first_plane = (!swirl->first_plane);
1016
1017         /* make sure we handle -ve values properly */
1018         if (value >= 0)
1019                 value = (value % dcolours) + 2;
1020         else
1021                 value = dcolours - (abs(value) % (dcolours - 1));
1022
1023 #ifndef STANDALONE
1024         /* if fg and bg are 1 and 0 we should be OK, but just in case */
1025         while ((dcolours > 2) &&
1026                (((value % swirl->colours) == (int) swirl->fg) ||
1027                 ((value % swirl->colours) == (int) swirl->bg) ||
1028                 ((value % swirl->colours) == (int) swirl->white) ||
1029                 ((value % swirl->colours) == (int) swirl->black))) {
1030                 value++;
1031         }
1032 #endif /* !STANDALONE */
1033
1034         /* definitely make sure it is in range */
1035         value = value % swirl->colours;
1036
1037         /* lookup the pixel value if necessary */
1038 #ifndef STANDALONE
1039         if (swirl->fixed_colourmap && swirl->dcolours > 2)
1040 #endif
1041                 value = swirl->rgb_values[value].pixel;
1042
1043         /* return it */
1044         return ((unsigned long) value);
1045 }
1046
1047 /****************************************************************/
1048
1049 /* 
1050  * draw_block
1051  *
1052  * Draw a square block of points with the same value.
1053  *
1054  * -      ximage is the XImage to draw on.
1055  * -      x, y is the top left corner
1056  * -      s is the length of each side
1057  * -      v is the value
1058  */
1059 static void
1060 draw_block(XImage * ximage, int x, int y, int s, unsigned long v)
1061 {
1062         int         a, b;
1063
1064         for (a = 0; a < s; a++)
1065                 for (b = 0; b < s; b++) {
1066                         XPutPixel(ximage, x + b, y + a, v);
1067                 }
1068 }
1069
1070 /****************************************************************/
1071
1072 /* 
1073  * draw_point  Draw the current point in a swirl pattern onto the XImage
1074  *
1075  * -    swirl is the swirl
1076  * -    win is the window to update
1077  */
1078 static void
1079 draw_point(ModeInfo * mi, SWIRL_P swirl)
1080 {
1081         int         r;
1082         int         x, y;
1083
1084         /* get current point coordinates and resolution */
1085         x = swirl->x;
1086         y = swirl->y;
1087         r = swirl->r;
1088
1089         /* check we are within the window */
1090         if ((x < 0) || (x > swirl->width - r) || (y < 0) || (y > swirl->height - r))
1091                 return;
1092
1093         /* what style are we drawing? */
1094         if (swirl->two_plane) {
1095                 int         r2;
1096
1097                 /* halve the block size */
1098                 r2 = r / 2;
1099
1100                 /* interleave blocks at half r */
1101                 draw_block(swirl->ximage, x, y, r2, do_point(swirl, x, y));
1102                 draw_block(swirl->ximage, x + r2, y, r2, do_point(swirl, x + r2, y));
1103                 draw_block(swirl->ximage, x + r2, y + r2, r2, do_point(swirl,
1104                         x + r2, y + r2));
1105                 draw_block(swirl->ximage, x, y + r2, r2, do_point(swirl, x, y + r2));
1106         } else
1107                 draw_block(swirl->ximage, x, y, r, do_point(swirl, x, y));
1108
1109         /* update the screen */
1110 /* PURIFY 4.0.1 on SunOS4 and on Solaris 2 reports a 256 byte memory leak on * 
1111    the next line. */
1112         XPutImage(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), swirl->ximage,
1113                   x, y, x, y, r, r);
1114 }
1115
1116 /****************************************************************/
1117
1118 /* 
1119  * next_point  Move to the next point in the spiral pattern
1120  *  -    swirl is the swirl
1121  *  -    win is the window to update
1122  */
1123 static void
1124 next_point(SWIRL_P swirl)
1125 {
1126         /* more to do in this direction? */
1127         if (swirl->dir_done < swirl->dir_todo) {
1128                 /* move in the current direction */
1129                 switch (swirl->direction) {
1130                         case DRAW_RIGHT:
1131                                 swirl->x += swirl->r;
1132                                 break;
1133                         case DRAW_DOWN:
1134                                 swirl->y += swirl->r;
1135                                 break;
1136                         case DRAW_LEFT:
1137                                 swirl->x -= swirl->r;
1138                                 break;
1139                         case DRAW_UP:
1140                                 swirl->y -= swirl->r;
1141                                 break;
1142                 }
1143
1144                 /* done another point */
1145                 swirl->dir_done++;
1146         } else {
1147                 /* none drawn yet */
1148                 swirl->dir_done = 0;
1149
1150                 /* change direction - check and record if off screen */
1151                 switch (swirl->direction) {
1152                         case DRAW_RIGHT:
1153                                 swirl->direction = DRAW_DOWN;
1154                                 if (swirl->x > swirl->width - swirl->r) {
1155                                         /* skip these points */
1156                                         swirl->dir_done = swirl->dir_todo;
1157                                         swirl->y += (swirl->dir_todo * swirl->r);
1158
1159                                         /* check for finish */
1160                                         if (swirl->off_screen)
1161                                                 swirl->drawing = False;
1162                                         swirl->off_screen = True;
1163                                 } else
1164                                         swirl->off_screen = False;
1165                                 break;
1166                         case DRAW_DOWN:
1167                                 swirl->direction = DRAW_LEFT;
1168                                 swirl->dir_todo++;
1169                                 if (swirl->y > swirl->height - swirl->r) {
1170                                         /* skip these points */
1171                                         swirl->dir_done = swirl->dir_todo;
1172                                         swirl->x -= (swirl->dir_todo * swirl->r);
1173
1174                                         /* check for finish */
1175                                         if (swirl->off_screen)
1176                                                 swirl->drawing = False;
1177                                         swirl->off_screen = True;
1178                                 } else
1179                                         swirl->off_screen = False;
1180                                 break;
1181                         case DRAW_LEFT:
1182                                 swirl->direction = DRAW_UP;
1183                                 if (swirl->x < 0) {
1184                                         /* skip these points */
1185                                         swirl->dir_done = swirl->dir_todo;
1186                                         swirl->y -= (swirl->dir_todo * swirl->r);
1187
1188                                         /* check for finish */
1189                                         if (swirl->off_screen)
1190                                                 swirl->drawing = False;
1191                                         swirl->off_screen = True;
1192                                 } else
1193                                         swirl->off_screen = False;
1194                                 break;
1195                         case DRAW_UP:
1196                                 swirl->direction = DRAW_RIGHT;
1197                                 swirl->dir_todo++;
1198                                 if (swirl->y < 0) {
1199                                         /* skip these points */
1200                                         swirl->dir_done = swirl->dir_todo;
1201                                         swirl->x += (swirl->dir_todo * swirl->r);
1202
1203                                         /* check for finish */
1204                                         if (swirl->off_screen)
1205                                                 swirl->drawing = False;
1206                                         swirl->off_screen = True;
1207                                 } else
1208                                         swirl->off_screen = False;
1209                                 break;
1210                 }
1211         }
1212 }
1213
1214 /****************************************************************/
1215
1216 /* 
1217  * init_swirl
1218  *
1219  * Initialise things for swirling
1220  *
1221  * -      win is the window to draw in
1222  */
1223 void
1224 init_swirl(ModeInfo * mi)
1225 {
1226         Display    *display = MI_DISPLAY(mi);
1227         Window      window = MI_WINDOW(mi);
1228         SWIRL_P     swirl;
1229
1230         /* does the swirls array exist? */
1231         if (swirls == NULL) {
1232                 int         i;
1233
1234                 /* allocate an array, one entry for each screen */
1235                 swirls = (SWIRL_P) calloc(ScreenCount(display), sizeof (SWIRL));
1236
1237                 /* initialise them all */
1238                 for (i = 0; i < ScreenCount(display); i++)
1239                         initialise_swirl(mi, &swirls[i]);
1240         }
1241         /* get a pointer to this swirl */
1242         swirl = &(swirls[MI_SCREEN(mi)]);
1243
1244         /* get window parameters */
1245         swirl->win = window;
1246         swirl->width = MI_WIN_WIDTH(mi);
1247         swirl->height = MI_WIN_HEIGHT(mi);
1248         swirl->depth = MI_WIN_DEPTH(mi);
1249         swirl->rdepth = swirl->depth;
1250         swirl->visual = MI_VISUAL(mi);
1251
1252         if (swirl->depth > 16)
1253                 swirl->depth = 16;
1254
1255         /* initialise image for speeding up drawing */
1256         initialise_image(mi, swirl);
1257
1258         /* clear the window (before setting the colourmap) */
1259         XClearWindow(display, MI_WINDOW(mi));
1260
1261 #ifdef STANDALONE
1262
1263         swirl->rgb_values = mi->colors;
1264         swirl->colours = mi->npixels;
1265         swirl->dcolours = swirl->colours;
1266 /*      swirl->fixed_colourmap = !mi->writable_p;*/
1267
1268 #else /* !STANDALONE */
1269
1270         /* initialise the colours from which the colourmap is derived */
1271         initialise_colours(basic_colours, MI_SATURATION(mi));
1272
1273         /* set up the colour map */
1274         create_colourmap(mi, swirl);
1275
1276         /* attach the colour map to the window (if we have one) */
1277         if (!swirl->fixed_colourmap) {
1278 #if 1
1279                 setColormap(display, window, swirl->cmap, MI_WIN_IS_INWINDOW(mi));
1280 #else
1281                 XSetWindowColormap(display, window, swirl->cmap);
1282                 (void) XSetWMColormapWindows(display, window, &window, 1);
1283                 XInstallColormap(display, swirl->cmap);
1284 #endif
1285         }
1286 #endif /* STANDALONE */
1287
1288         /* resolution starts off chunky */
1289         swirl->resolution = MIN_RES + 1;
1290
1291         /* calculate the pixel step for this resulution */
1292         swirl->r = (1 << (swirl->resolution - 1));
1293
1294         /* how many knots? */
1295         swirl->n_knots = random_no((unsigned int) MI_BATCHCOUNT(mi) / 2) +
1296                 MI_BATCHCOUNT(mi) + 1;
1297
1298         /* what type of knots? */
1299         swirl->knot_type = ALL; /* for now */
1300
1301         /* use two_plane mode occaisionally */
1302         if (random_no(100) <= TWO_PLANE_PCNT) {
1303                 swirl->two_plane = swirl->first_plane = True;
1304                 swirl->max_resolution = 2;
1305         } else
1306                 swirl->two_plane = False;
1307
1308         /* fix the knot values */
1309         create_knots(swirl);
1310
1311         /* we are off */
1312         swirl->started = True;
1313         swirl->drawing = False;
1314 }
1315
1316 /****************************************************************/
1317
1318 /* 
1319  * draw_swirl
1320  *
1321  * Draw one iteration of swirling
1322  *
1323  * -      win is the window to draw in
1324  */
1325 void
1326 draw_swirl(ModeInfo * mi)
1327 {
1328         SWIRL_P     swirl = &(swirls[MI_SCREEN(mi)]);
1329
1330         /* are we going? */
1331         if (swirl->started) {
1332                 /* in the middle of drawing? */
1333                 if (swirl->drawing) {
1334 #ifdef STANDALONE
1335                   if (mi->writable_p)
1336                         rotate_colors(MI_DISPLAY(mi), MI_COLORMAP(mi),
1337                                                   swirl->rgb_values, swirl->colours, 1);
1338 #else  /* !STANDALONE */
1339                         /* rotate the colours */
1340                         install_map(MI_DISPLAY(mi), swirl, swirl->dshift);
1341 #endif /* !STANDALONE */
1342
1343                         /* draw a batch of points */
1344                         swirl->batch_todo = BATCH_DRAW;
1345                         while ((swirl->batch_todo > 0) && swirl->drawing) {
1346                                 /* draw a point */
1347                                 draw_point(mi, swirl);
1348
1349                                 /* move to the next point */
1350                                 next_point(swirl);
1351
1352                                 /* done a point */
1353                                 swirl->batch_todo--;
1354                         }
1355                 } else {
1356 #ifdef STANDALONE
1357                   if (mi->writable_p)
1358                         rotate_colors(MI_DISPLAY(mi), MI_COLORMAP(mi),
1359                                                   swirl->rgb_values, swirl->colours, 1);
1360 #else  /* !STANDALONE */
1361                         /* rotate the colours */
1362                         install_map(MI_DISPLAY(mi), swirl, swirl->shift);
1363 #endif /* !STANDALONE */
1364
1365                         /* time for a higher resolution? */
1366                         if (swirl->resolution > swirl->max_resolution) {
1367                                 /* move to higher resolution */
1368                                 swirl->resolution--;
1369
1370                                 /* calculate the pixel step for this resulution */
1371                                 swirl->r = (1 << (swirl->resolution - 1));
1372
1373                                 /* start drawing again */
1374                                 swirl->drawing = True;
1375
1376                                 /* start in the middle of the screen */
1377                                 swirl->x = (swirl->width - swirl->r) / 2;
1378                                 swirl->y = (swirl->height - swirl->r) / 2;
1379
1380                                 /* initialise spiral drawing parameters */
1381                                 swirl->direction = DRAW_RIGHT;
1382                                 swirl->dir_todo = 1;
1383                                 swirl->dir_done = 0;
1384                         } else {
1385                                 /* all done, decide when to restart */
1386                                 if (swirl->start_again == -1) {
1387                                         /* start the counter */
1388                                         swirl->start_again = RESTART;
1389                                 } else if (swirl->start_again == 0) {
1390                                         /* reset the counter */
1391                                         swirl->start_again = -1;
1392
1393 #ifdef STANDALONE
1394                                         /* Pick a new colormap! */
1395                                         XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1396                                         free_colors (MI_DISPLAY(mi), MI_COLORMAP(mi),
1397                                                                  mi->colors, mi->npixels);
1398                                         make_smooth_colormap (MI_DISPLAY(mi),
1399                                                                                   MI_VISUAL(mi),
1400                                                                                   MI_COLORMAP(mi),
1401                                                                                   mi->colors, &mi->npixels, True,
1402                                                                                   &mi->writable_p, True);
1403                                         swirl->colours = mi->npixels;
1404 #endif /* STANDALONE */
1405
1406                                         /* start again */
1407                                         init_swirl(mi);
1408                                 } else
1409                                         /* decrement the counter */
1410                                         swirl->start_again--;
1411                         }
1412                 }
1413         }
1414 }
1415
1416 /****************************************************************/
1417
1418 void
1419 release_swirl(ModeInfo * mi)
1420 {
1421         /* does the swirls array exist? */
1422         if (swirls != NULL) {
1423                 int         i;
1424
1425                 /* free them all */
1426                 for (i = 0; i < MI_NUM_SCREENS(mi); i++) {
1427                         SWIRL_P     swirl = &(swirls[i]);
1428
1429                         if (swirl->cmap != (Colormap) NULL)
1430                                 XFreeColormap(MI_DISPLAY(mi), swirl->cmap);
1431                         if (swirl->rgb_values != NULL)
1432                                 XFree((void *) swirl->rgb_values);
1433                         if (swirl->ximage != NULL)
1434                                 XDestroyImage(swirl->ximage);
1435                         if (swirl->knots)
1436                                 (void) free((void *) swirl->knots);
1437                 }
1438                 /* deallocate an array, one entry for each screen */
1439                 (void) free((void *) swirls);
1440                 swirls = NULL;
1441         }
1442 }
1443
1444 /****************************************************************/
1445
1446 void
1447 refresh_swirl(ModeInfo * mi)
1448 {
1449         SWIRL_P     swirl = &(swirls[MI_SCREEN(mi)]);
1450
1451         if (swirl->started) {
1452                 if (swirl->drawing)
1453                         swirl->resolution = swirl->resolution + 1;
1454                 swirl->drawing = False;
1455         }
1456 }