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