e5eb9d554c251da78c72459142780bbd39af433c
[xscreensaver] / hacks / whirlygig.c
1 /*  Whirlygig -- an experiment in X programming
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  *  When I was in trigonometry class as a kid, I remember being fascinated
12  *  by the beauty of the shapes one receives when playing with sine waves
13  *  Here is a little experiment to show that beauty is simple
14  */
15
16 #include <stdio.h>
17 #include <math.h>
18 #include "screenhack.h"
19
20 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
21 # include "xdbe.h"
22 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
23
24 #define NCOLORS 100
25 #define FULL_CYCLE 429496729
26 #define START_ARC 0
27 #define END_ARC 23040
28
29 struct info {
30 /*    Bool writable; / * Is the screen writable */
31     double xspeed; /* A factor to modify the horizontal movement */
32     double yspeed; /* A factor to modify vertical movement */
33     double xamplitude;
34     double yamplitude;
35     int whirlies; /*  How many whirlies per line do you want? */
36     int nlines;   /*  How many lines of whirlies do you want? */
37     int half_width;         /* 1/2 the width of the screen */
38     int half_height;
39     int speed;
40     int trail;
41     int color_modifier;
42     double xoffset;
43     double yoffset;
44     double offset_period;
45     Bool wrap;
46 };
47
48 enum object_mode {
49     spin_mode, funky_mode, circle_mode, linear_mode, test_mode, fun_mode, innie_mode, lissajous_mode
50 };
51
52 struct state {
53   Display *dpy;
54   Window window;
55
56     XGCValues gcv;      /* The structure to hold the GC data */
57     XWindowAttributes xgwa;       /*  A structure to hold window data */
58     Pixmap b, ba;       /* double-buffer to reduce flicker */
59 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
60     Bool dbeclear_p;
61     XdbeBackBuffer backb;
62 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
63
64     GC fgc, bgc;
65     int screen;
66     Bool dbuf;
67
68     unsigned long int current_time; /* The global int telling the current time */
69     unsigned long int start_time;
70     struct info  *info;
71     char *xmode_str, *ymode_str; /* holds the current mode for x and y computation */
72
73  /* pos is the current position x,y -- last_x contains one cell of
74     every x coordinate for every position of every whirly in every
75     line up to 100 whirlies in 100 lines -- lasy_y and last_size hold
76     the same information for y and size respectively */
77
78     int pos[2], last_x[100][100], last_y[100][100], last_size[100][100];
79     int current_color;
80     Bool wrap;
81     int xmode, ymode;
82     double modifier;    /* for innie */
83
84    XColor colors[NCOLORS];
85    int ncolors;
86    int explaining;
87 };
88
89 static void draw_explain_string(struct state *, int, int, Display *, Window, GC);
90 static void spin(struct state *, unsigned long int, struct info *, int *, int);
91 static void funky(struct state *, unsigned long int, struct info *, int *, int);
92 static void circle(struct state *, unsigned long int, struct info *, int *, int);
93 static void fun(struct state *, unsigned long int, struct info *, int *, int);
94 static void linear(struct state *, unsigned long int, struct info *, int *, int);
95 static void lissajous(struct state *, unsigned long int, struct info *, int *, int);
96 static void test(struct state *, unsigned long int, struct info *, int *, int);
97 static void innie(struct state *, unsigned long int, struct info *, int *, int, double);
98
99
100
101 static const char spin_explanation[] =
102 "Spin mode is a simple sin/cos with every argument modified";
103
104 static const char funky_explanation[] =
105 "Funky mode is me goofing off.";
106
107 static const char circle_explanation[] =
108 "Circle mode graphs the x and y positions as you trace the edge of a circle over time.";
109
110 static const char linear_explanation[] =
111 "Linear mode draws a straight line";
112
113 static const char test_explanation[] =
114 "Test mode is a mode that I play around with ideas in.";
115
116 static const char fun_explanation[] =
117 "Fun mode is the coolest.";
118
119 static const char innie_explanation[] =
120 "Innie mode does something or other. Looks cool, though.";
121
122 static const char lissajous_explanation[] =
123 "Lissajous mode draws a slightly modified lissajous curve";
124
125 static void
126 draw_explain_string(struct state *st, int mode, int offset, Display *dpy, Window window, GC fgc) 
127 {
128   switch(mode) {
129   case spin_mode:
130     XDrawString(st->dpy, st->window, st->fgc, 50, offset, 
131                 (char*) spin_explanation, strlen(spin_explanation));
132     break;
133   case funky_mode:
134     XDrawString(st->dpy, st->window, st->fgc, 50, offset, 
135                 (char*) funky_explanation, strlen(funky_explanation));
136     break;
137   case circle_mode:
138     XDrawString(st->dpy, st->window, st->fgc, 50, offset, 
139                 (char*) circle_explanation, strlen(circle_explanation));
140     break;
141   case linear_mode:
142     XDrawString(st->dpy, st->window, st->fgc, 50, offset, 
143                 (char*) linear_explanation, strlen(linear_explanation));
144     break;
145   case test_mode:
146     XDrawString(st->dpy, st->window, st->fgc, 50, offset, 
147                 (char*) test_explanation, strlen(test_explanation));
148     break;
149   case fun_mode:
150     XDrawString(st->dpy, st->window, st->fgc, 50, offset, 
151                 (char*) fun_explanation, strlen(fun_explanation));
152     break;
153   case innie_mode:
154     XDrawString(st->dpy, st->window, st->fgc, 50, offset, 
155                 (char*) innie_explanation, strlen(innie_explanation));
156     break;
157   case lissajous_mode:
158     XDrawString(st->dpy, st->window, st->fgc, 50, offset, 
159                 (char*) lissajous_explanation, strlen(linear_explanation));    
160   }
161 }
162
163 static void
164 funky(struct state *st, unsigned long int the_time, struct info *info, int pos[], int index)
165 {
166     double new_time = ((the_time % 360 ) / 180.0) * M_PI;
167     if (index == 0) {
168         double time_modifier = cos(new_time / 180.0);
169         double the_cos = cos((new_time * (double)st->info->xspeed) + (time_modifier * 80.0));
170         double dist_mod_x = cos(new_time) * (st->info->half_width - 50);
171         st->pos[index]= st->info->xamplitude * (the_cos * dist_mod_x) + st->info->half_width;
172     }
173     else {
174         double new_time = ((the_time % 360 ) / 180.0) * M_PI;
175         double time_modifier = sin(new_time / 180.0);
176         double the_sin = sin((new_time * (double)st->info->yspeed) + (time_modifier * 80.0));
177         double dist_mod_y = sin(new_time) * (st->info->half_height - 50);
178         st->pos[index] = st->info->yamplitude * (the_sin * dist_mod_y) + st->info->half_height;
179     }
180 }
181
182 static void
183 innie(struct state *st, unsigned long int the_time, struct info *info, int pos[], int index, double modifier)
184 {
185     double frequency = 2000000.0 + (st->modifier * cos(((double)the_time / 100.0)));
186     double arg = (double)the_time / frequency;
187     double amplitude = 200.0 * cos(arg);
188     double fun = 150.0 * cos((double)the_time / 2000.0);
189     int vert_mod, horiz_mod;
190     if (index == 0) {
191         horiz_mod = (int)(fun * cos((double)the_time / 100.0)) + st->info->half_width;
192         st->pos[index] = (amplitude * cos((double)the_time / 100.0 * st->info->xspeed)) + horiz_mod;
193     }
194     else {
195         vert_mod = (int)(fun * sin((double)the_time / 100.0)) + st->info->half_height;
196         st->pos[index] = (amplitude * sin((double)the_time / 100.0 * st->info->yspeed)) + vert_mod;
197     }
198 }
199
200 static void
201 lissajous(struct state *st, unsigned long int the_time, struct info *info, int pos[], int index)
202 {
203       /*  This is a pretty standard lissajous curve
204            x = a sin(nt + c) 
205            y = b sin(t) 
206            The n and c modifiers are cyclic as well, however... */
207   int result;
208   double time = (double)the_time / 100.0;
209   double fun = 15.0 * cos((double)the_time / 800.0);
210   double weird = cos((time / 1100000.0) / 1000.0);
211
212   if (index == 0) {
213       result = st->info->xamplitude * 200.0 * sin((weird * time) + fun) + st->info->half_width;
214   }
215   else {
216       result = st->info->yamplitude * 200.0 * sin(time) + st->info->half_height;
217   }
218   st->pos[index] = result;
219
220
221 static void
222 circle(struct state *st, unsigned long int the_time, struct info *info, int pos[], int index)
223 {
224     int result;
225     if (index == 0) {
226         result = st->info->xamplitude * (cos((double)the_time / 100.0 * st->info->xspeed) * (st->info->half_width / 2)) + st->info->half_width;
227     }
228     else {
229         result = st->info->yamplitude * (sin((double)the_time / 100.0 * st->info->yspeed) * (st->info->half_height / 2)) + st->info->half_height;
230     }
231     st->pos[index] = result;
232 }
233
234 #if 0
235 static void
236 mod(unsigned long int the_time, struct info *info, int pos[], int index)
237 {
238     int amplitude;
239     int max = st->info->half_width;
240     if ((the_time % (max * 2)) < max)
241         amplitude = max - ((the_time % (max * 2)) - max);
242     else
243         amplitude = the_time % (max * 2);
244     amplitude = amplitude - max;
245 }
246 #endif
247
248 static void
249 spin(struct state *st, unsigned long int the_time, struct info *info, int pos[], int index)
250 {
251     double funky = (((the_time % 360) * 1.0) / 180.0) * M_PI;
252     if (index ==0) {
253     double the_cos = cos((double)the_time / (180.0 * st->info->xspeed));
254     double dist_mod_x = cos((double)funky) * (st->info->half_width - 50);
255     st->pos[index] = st->info->xamplitude * (the_cos * dist_mod_x) + st->info->half_width;
256     }
257     else {
258     double the_sin = sin((double)the_time / (180.0 * st->info->yspeed));
259     double dist_mod_y = sin((double)funky) * (st->info->half_height - 50);
260     st->pos[index] = st->info->yamplitude * (the_sin * dist_mod_y) + st->info->half_height;
261     }
262 }
263
264 static void
265 fun(struct state *st, unsigned long int the_time, struct info *info, int pos[], int index)
266 {
267     int amplitude;
268     int max = st->info->half_width;
269     if ((the_time % (max * 2)) < max)
270         amplitude = max - ((the_time % (max * 2)) - max);
271     else
272         amplitude = the_time % (max * 2);
273     amplitude = amplitude - max;
274     if (index ==0) {
275         st->pos[index] = (amplitude * cos((double)the_time / 100.0 * st->info->xspeed)) + st->info->half_width;
276     }
277     else {
278         st->pos[index] = (amplitude * sin((double)the_time / 100.0 * st->info->yspeed)) + st->info->half_height;
279     }
280 }
281
282 static void
283 linear(struct state *st, unsigned long int the_time, struct info *info, int pos[], int index)
284 {
285     if (index == 0)   /* Calculate for the x axis */
286         st->pos[index] = ((the_time / 2) % (st->info->half_width * 2));
287     else
288         st->pos[index] = ((the_time / 2) % (st->info->half_height * 2));
289 }
290
291 static void
292 test(struct state *st, unsigned long int the_time, struct info *info, int pos[], int index)
293 {
294     if (index == 0) {
295         st->pos[index] = st->info->xamplitude * (cos((double)the_time / 100.0 * st->info->xspeed) * (st->info->half_width / 2)) + st->info->half_width;
296     }
297     else {
298         st->pos[index] = st->info->yamplitude * (sin((double)the_time / 100.0 * st->info->yspeed) * (st->info->half_height / 2)) + st->info->half_height;
299     }   
300 }
301
302 static int preen(int current, int max) {
303     if (current > max)
304         current=current-max;
305     if (current < 0)
306         current=current+max;
307     return(current);
308 }
309
310 #if 0
311 static void
312 smoothen(struct state *st, int x, int lastx, int y, int lasty, int size, int last_color, XColor *colors, Display *dpy, Window window, GC bgc, int screen, struct info *info)
313 {
314     double xdistance = abs((double)x-(double)lastx);
315     double ydistance = abs((double)y-(double)lasty);
316     double distance = sqrt(((xdistance * xdistance) + (ydistance * ydistance)) );
317     double slope = (((double)y-(double)lasty) / ((double)x-(double)lastx));
318     printf("Starting smoothen with values: %f, %f, %f, %f\n", xdistance, ydistance, distance, slope);
319     if (distance > 2.0) {
320         int newx = (int)((xdistance / distance) * slope);
321         int newy = (int)((ydistance / distance) * slope);
322         if (! st->info->trail) {
323             XSetForeground(st->dpy, st->bgc, BlackPixel(st->dpy, st->screen));
324             XFillArc(st->dpy, st->window, st->bgc, lastx, lasty, size, size, START_ARC, END_ARC);
325         }
326         XSetForeground(st->dpy, st->bgc, st->colors[last_color].pixel);
327         XFillArc(st->dpy, st->window, st->bgc, newx, newy, size, size, START_ARC, END_ARC);
328         smoothen(st, newx, x, newy, y, size, last_color, st->colors, st->dpy, st->window, st->bgc, st->screen, st->info);
329     }
330 }
331 #endif
332
333
334 static void *
335 whirlygig_init (Display *dpy, Window window)
336 {
337   struct state *st = (struct state *) calloc (1, sizeof(*st));
338   st->dpy = dpy;
339   st->window = window;
340
341   st->ncolors = NCOLORS;
342
343     st->dbuf = get_boolean_resource (st->dpy, "doubleBuffer", "Boolean");
344
345 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
346     st->dbuf = False;
347 # endif
348
349     st->start_time = st->current_time;
350     st->info = (struct info *)malloc(sizeof(struct info));
351
352     st->screen = DefaultScreen(st->dpy);
353     XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
354     if (st->dbuf)
355       {
356 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
357         if (get_boolean_resource(st->dpy,"useDBE","Boolean"))
358           {
359             st->dbeclear_p = get_boolean_resource (st->dpy, "useDBEClear",
360                                                    "Boolean");
361             if (st->dbeclear_p)
362               st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeBackground);
363             else
364               st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeUndefined);
365             st->backb = st->b;
366           }
367 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
368
369         if (!st->b)
370           {
371             st->ba = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height,st->xgwa.depth);
372             st->b = st->ba;
373           }
374       }
375     else
376       {
377         st->b = st->window;
378       }
379
380     st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap, "foreground", "Foreground");
381     st->fgc = XCreateGC (st->dpy, st->b, GCForeground, &st->gcv);
382     st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap, "background", "Background");
383     st->bgc = XCreateGC (st->dpy, st->b, GCForeground, &st->gcv);
384
385 #ifdef HAVE_COCOA  /* #### should turn off double-buffering instead */
386     jwxyz_XSetAntiAliasing (dpy, st->fgc, False);
387     jwxyz_XSetAntiAliasing (dpy, st->bgc, False);
388 #endif
389
390     {
391       Bool writable_p = False;
392     make_uniform_colormap (st->dpy, st->xgwa.visual, st->xgwa.colormap, st->colors, &st->ncolors, True, &writable_p, True);
393     }
394
395     if (st->ba) XFillRectangle (st->dpy, st->ba, st->bgc, 0, 0, st->xgwa.width, st->xgwa.height);
396
397         /* info is a structure holding all the random pieces of information I may want to 
398            pass to my baby functions -- much of it I may never use, but it is nice to
399            have around just in case I want it to make a funky function funkier */
400 /*    info->writable = get_boolean_resource (dpy, "cycle", "Boolean"); */
401     st->info->xspeed = get_float_resource(st->dpy, "xspeed" , "Float");
402     st->info->yspeed = get_float_resource(st->dpy, "yspeed" , "Float");
403     st->info->xamplitude = get_float_resource(st->dpy, "xamplitude", "Float");
404     st->info->yamplitude = get_float_resource(st->dpy, "yamplitude", "Float");
405     st->info->offset_period = get_float_resource(st->dpy, "offset_period", "Float");
406     st->info->whirlies = get_integer_resource(st->dpy, "whirlies", "Integer");
407     st->info->nlines = get_integer_resource(st->dpy, "nlines", "Integer");
408     st->info->half_width = st->xgwa.width / 2;
409     st->info->half_height = st->xgwa.height / 2;
410     st->info->speed = get_integer_resource(st->dpy, "speed" , "Integer");
411     st->info->trail = get_boolean_resource(st->dpy, "trail", "Integer");
412     st->info->color_modifier = get_integer_resource(st->dpy, "color_modifier", "Integer");
413     st->info->xoffset = get_float_resource(st->dpy, "xoffset", "Float");
414     st->info->yoffset = get_float_resource(st->dpy, "yoffset", "Float");
415     st->xmode_str = get_string_resource(st->dpy, "xmode", "Mode");
416     st->ymode_str = get_string_resource(st->dpy, "ymode", "Mode");
417     st->wrap = get_boolean_resource(st->dpy, "wrap", "Boolean");
418     st->modifier = 3000.0 + frand(1500.0);
419     if (! st->xmode_str) st->xmode = spin_mode;
420     else if (! strcmp (st->xmode_str, "spin")) st->xmode = spin_mode;
421     else if (! strcmp (st->xmode_str, "funky")) st->xmode = funky_mode;
422     else if (! strcmp (st->xmode_str, "circle")) st->xmode = circle_mode;
423     else if (! strcmp (st->xmode_str, "linear")) st->xmode = linear_mode;
424     else if (! strcmp (st->xmode_str, "test")) st->xmode = test_mode;
425     else if (! strcmp (st->xmode_str, "fun")) st->xmode = fun_mode;
426     else if (! strcmp (st->xmode_str, "innie")) st->xmode = innie_mode;
427     else if (! strcmp (st->xmode_str, "lissajous")) st->xmode = lissajous_mode;
428     else {
429         st->xmode = random() % (int) lissajous_mode;
430     }
431     if (! st->ymode_str) st->ymode = spin_mode;
432     else if (! strcmp (st->ymode_str, "spin")) st->ymode = spin_mode;
433     else if (! strcmp (st->ymode_str, "funky")) st->ymode = funky_mode;
434     else if (! strcmp (st->ymode_str, "circle")) st->ymode = circle_mode;
435     else if (! strcmp (st->ymode_str, "linear")) st->ymode = linear_mode;
436     else if (! strcmp (st->ymode_str, "test")) st->ymode = test_mode;
437     else if (! strcmp (st->ymode_str, "fun")) st->ymode = fun_mode;
438     else if (! strcmp (st->ymode_str, "innie")) st->ymode = innie_mode;
439     else if (! strcmp (st->ymode_str, "lissajous")) st->ymode = lissajous_mode;
440     else {
441         st->ymode = random() % (int) lissajous_mode;
442     }
443
444     if (get_integer_resource(st->dpy, "start_time", "Integer") == -1)
445         st->current_time = (unsigned long int)(random());
446     else
447         st->current_time = get_integer_resource(st->dpy, "start_time", "Integer");
448     if (st->info->whirlies == -1)
449         st->info->whirlies = 1 + (random() % 15);
450     if (st->info->nlines == -1)
451         st->info->nlines = 1 + (random() % 5);
452     if (st->info->color_modifier == -1)
453         st->info->color_modifier = 1 + (random() % 25);
454     if (get_boolean_resource(st->dpy, "explain", "Integer"))
455       st->explaining = 1;
456     st->current_color = 1 + (random() % NCOLORS);
457
458   return st;
459 }
460
461 static unsigned long
462 whirlygig_draw (Display *dpy, Window window, void *closure)
463 {
464   struct state *st = (struct state *) closure;
465   int wcount;  /* wcount is a counter incremented for every whirly take note of
466                   internal_time before you mess with it */
467   int change_time = 4000;
468
469   if (st->explaining == 1) {
470     XClearWindow (st->dpy, st->window);
471     draw_explain_string(st, st->xmode, st->info->half_height-100, 
472                         st->dpy, st->window, st->fgc);
473     st->explaining++;
474     return 3000000;
475   } else if (st->explaining == 2) {
476     XClearWindow (st->dpy, st->window);
477     st->explaining = 0;
478   }
479
480   if (! strcmp (st->xmode_str, "change") && ! strcmp (st->ymode_str, "change")) {
481     if ((st->current_time - st->start_time) > change_time) {
482       st->start_time = st->current_time;
483       st->xmode = 1 + (random() % 4);
484       st->ymode = 1 + (random() % 4);
485     }
486   }
487   else if (! strcmp (st->xmode_str, "change")) {
488     if ((st->current_time - st->start_time) > change_time) {
489       st->start_time = st->current_time;
490       st->xmode = 1 + (random() % 4);
491     }
492   }
493   else if (! strcmp (st->ymode_str, "change")) {
494     if ((st->current_time - st->start_time) > change_time) {
495       st->start_time = st->current_time;
496       st->ymode = 1 + (random() % 3);
497       printf("Changing ymode to %d\n", st->ymode);
498     }
499   }
500   if (++st->current_color >= NCOLORS)
501     st->current_color = 0;
502   for (wcount = 0; wcount < st->info->whirlies; wcount++) {
503     int lcount; /* lcount is a counter for every line -- take note of the offsets changing */
504     int internal_time = st->current_time;
505     int color_offset = (st->current_color + (st->info->color_modifier * wcount)) % NCOLORS;
506     if (st->current_time == 0)
507       internal_time = 0;
508     else
509       /* I want the distance between whirlies to increase more each whirly */
510       internal_time = st->current_time + (10 * wcount) + (wcount * wcount); 
511     switch (st->xmode) {
512       /* All these functions expect an int time, the struct info,
513          a pointer to an array of positions, and the index that the 
514          the function will fill of the array */
515     case spin_mode:
516       spin(st, internal_time, st->info, st->pos, 0);
517       break;
518     case funky_mode:
519       funky(st, internal_time, st->info, st->pos, 0);
520       break;
521     case circle_mode:
522       circle(st, internal_time, st->info, st->pos, 0);
523       break;
524     case linear_mode:
525       linear(st, internal_time, st->info, st->pos, 0);
526       break;
527     case fun_mode:
528       fun(st, internal_time, st->info, st->pos, 0);
529       break;
530     case test_mode:
531       test(st, internal_time, st->info, st->pos, 0);
532       break;
533     case innie_mode:
534       innie(st, internal_time, st->info, st->pos, 0, st->modifier);
535       break;
536     case lissajous_mode:
537       lissajous(st, internal_time, st->info, st->pos, 0);
538       break;
539     default:
540       spin(st, internal_time, st->info, st->pos, 0);
541       break;
542     }   /* End of the switch for the x position*/
543     switch (st->ymode) {
544     case spin_mode:
545       spin(st, internal_time, st->info, st->pos, 1);
546       break;
547     case funky_mode:
548       funky(st, internal_time, st->info, st->pos, 1);
549       break;
550     case circle_mode:
551       circle(st, internal_time, st->info, st->pos, 1);
552       break;
553     case linear_mode:
554       linear(st, internal_time, st->info, st->pos, 1);
555       break;
556     case fun_mode:
557       fun(st, internal_time, st->info, st->pos, 1);
558       break;
559     case test_mode:
560       test(st, internal_time, st->info, st->pos, 1);
561       break;
562     case innie_mode:
563       innie(st, internal_time, st->info, st->pos, 1, st->modifier);
564       break;
565     case lissajous_mode:
566       lissajous(st, internal_time, st->info, st->pos, 1);
567       break;
568     default:
569       spin(st, internal_time, st->info, st->pos, 1);
570       break;
571     } /* End of the switch for the y position*/
572     for (lcount = 0; lcount < st->info->nlines; lcount++) {
573       double arg = (double)((internal_time * st->info->offset_period) / 90.0); 
574       double line_offset = 20.0 * (double)lcount * sin(arg); 
575       int size;
576       size = (int)(15.0 + 5.0 * sin((double)internal_time / 180.0));
577       /* First delete the old circle... */
578       if (!st->info->trail
579 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
580           && ( !st->dbeclear_p || !st->backb)
581 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
582           ) {
583         XSetForeground(st->dpy, st->bgc, BlackPixel(st->dpy, st->screen));
584         XFillArc(st->dpy, st->b, st->bgc, st->last_x[wcount][lcount], st->last_y[wcount][lcount], st->last_size[wcount][lcount], st->last_size[wcount][lcount], START_ARC, END_ARC);
585       }
586       /* Now, lets draw in the new circle */
587       {  /* Starting new scope for local x_pos and y_pos */
588         int xpos, ypos;
589         if (st->wrap) {
590           xpos = preen((int)(st->info->xoffset*line_offset)+st->pos[0], st->info->half_width * 2);
591           ypos = preen((int)(st->info->yoffset*line_offset)+st->pos[1], st->info->half_height * 2);
592         }
593         else {
594           xpos = (int)(st->info->xoffset*line_offset)+st->pos[0];
595           ypos = (int)(st->info->yoffset*line_offset)+st->pos[1]; 
596         }
597         if (st->start_time == st->current_time) {
598           /* smoothen should move from one mode to another prettily... */
599
600           /* Note: smoothen has not been modified to take the double
601              buffering code into account, and needs to be hacked on
602              before uncommenting.
603           */
604           /* 
605              smoothen(xpos, last_x[wcount][lcount], ypos, last_y[wcount][lcount], size, color_offset, colors, dpy, window, bgc, screen, info);
606           */
607         }
608         st->last_x[wcount][lcount] = xpos;
609         st->last_y[wcount][lcount] = ypos;
610         st->last_size[wcount][lcount] = size;
611         XSetForeground(st->dpy, st->bgc, st->colors[color_offset].pixel);
612         XFillArc(st->dpy, st->b, st->bgc, xpos, ypos, size, size, START_ARC, END_ARC);
613       } /* End of my temporary scope for xpos and ypos */
614     }  /* End of the for each line in nlines */
615   } /* End of the for each whirly in whirlies */
616
617
618 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
619   if (st->backb)
620     {
621       XdbeSwapInfo info[1];
622       info[0].swap_window = st->window;
623       info[0].swap_action = (st->dbeclear_p ? XdbeBackground : XdbeUndefined);
624       XdbeSwapBuffers (st->dpy, info, 1);
625     }
626   else
627 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
628     if (st->dbuf)
629       {
630         XCopyArea (st->dpy, st->b, st->window, st->bgc, 0, 0,
631                    st->xgwa.width, st->xgwa.height, 0, 0);
632       }
633
634   if (st->current_time == FULL_CYCLE)
635     st->current_time = 1;
636   else
637     st->current_time = st->current_time + st->info->speed;
638
639   return 10000;
640 }
641
642
643 static void
644 whirlygig_reshape (Display *dpy, Window window, void *closure, 
645                  unsigned int w, unsigned int h)
646 {
647 }
648
649 static Bool
650 whirlygig_event (Display *dpy, Window window, void *closure, XEvent *event)
651 {
652   return False;
653 }
654
655 static void
656 whirlygig_free (Display *dpy, Window window, void *closure)
657 {
658 }
659
660
661 static const char *whirlygig_defaults [] = {
662   ".background: black",
663   ".foreground: white",
664   "*fpsSolid:   true",
665   "*xspeed: 1.0",
666   "*yspeed: 1.0",
667   "*xamplitude: 1.0",
668   "*yamplitude: 1.0",
669   "*whirlies: -1",
670   "*nlines: -1",
671   "*xmode: change",
672   "*ymode: change",
673   "*speed: 1",
674   "*trail: false",
675   "*color_modifier: -1",
676   "*start_time: -1",
677   "*explain: False",
678   "*xoffset: 1.0",
679   "*yoffset: 1.0",
680   "*offset_period:    1",
681   "*wrap:               False",
682   "*doubleBuffer:       True",
683 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
684   "*useDBEClear:        True",
685   "*useDBE:             True",
686 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
687   0
688 };
689
690 static XrmOptionDescRec whirlygig_options [] = {
691   { "-xspeed",          ".xspeed", XrmoptionSepArg, 0 },
692       /* xspeed is a modifier of the argument to cos -- changing it thus
693          changes the frequency of cos */
694   { "-yspeed",          ".yspeed", XrmoptionSepArg, 0 },
695       /*  Similiarly, yspeed changes the frequency of sin */
696   { "-xamplitude",      ".xamplitude", XrmoptionSepArg, 0 },
697       /* A factor by which to increase/decrease the amplitude of the sin */
698   { "-yamplitude",      ".yamplitude", XrmoptionSepArg, 0 },
699       /* A factor by which to increase/decrease the amplitude of the cos */
700   { "-whirlies",         ".whirlies",XrmoptionSepArg, 0 },
701       /*  whirlies defines the number of circles to draw per line */
702   { "-nlines",         ".nlines",XrmoptionSepArg, 0 },
703       /* nlines is the number of lines of whirlies to draw */
704   { "-xmode",        ".xmode", XrmoptionSepArg, 0 },
705       /*  There are a few different modes that I have written -- each mode
706           is in theory a different experiment with the possible modifiers to sin/cos */
707   { "-ymode",       ".ymode", XrmoptionSepArg, 0 },
708   { "-speed",        ".speed", XrmoptionSepArg, 0 },
709       /*  This will modify how often it should draw, changing it will probably suck */
710   { "-trail",           ".trail", XrmoptionNoArg, "True" },
711       /* Control whether or not you want the old circles to be erased */
712   { "-color_modifier",          ".color_modifier", XrmoptionSepArg, 0 },
713       /*  How many colors away from the current should the next whirly be? */
714   { "-start_time",                ".start_time", XrmoptionSepArg, 0 },
715       /*  Specify exactly at what time to start graphing...  */
716   { "-xoffset",                    ".xoffset", XrmoptionSepArg, 0 },
717       /*  Tell the whirlies to be offset by this factor of a sin */
718   { "-yoffset",                    ".yoffset", XrmoptionSepArg, 0 },
719       /*  Tell the whirlies to be offset by this factor of a cos */
720   { "-offset_period",          ".offset_period", XrmoptionSepArg, 0 },
721       /*  Change the period of an offset cycle */
722   { "-explain",                    ".explain", XrmoptionNoArg, "True" },
723       /*  Specify whether or not to print an explanation of the function used. */
724   { "-wrap",                      ".wrap", XrmoptionNoArg, "True" },
725   { "-no-wrap",                   ".wrap", XrmoptionNoArg, "False" },
726       /* Specify if you want whirlies which are out of the boundary of the screen to be
727          wrapped around to the other side */
728   { "-db",              ".doubleBuffer", XrmoptionNoArg,  "True" },
729   { "-no-db",           ".doubleBuffer", XrmoptionNoArg,  "False" },
730   { 0, 0, 0, 0 }
731 };
732
733 XSCREENSAVER_MODULE ("Whirlygig", whirlygig)