http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.01.tar.gz
[xscreensaver] / hacks / starfish.c
1 /* xscreensaver, Copyright (c) 1997, 1998 Jamie Zawinski <jwz@jwz.org>
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
12 #include <math.h>
13 #include <time.h>
14 #include <sys/time.h> /* for gettimeofday() */
15 #include "screenhack.h"
16 #include "spline.h"
17
18
19 static Colormap cmap;
20 static Bool cycle_p;
21 static XColor *colors;
22 static int ncolors;
23 static int fg_index;
24 static GC gc;
25
26 #define SCALE        1000       /* fixed-point math, for sub-pixel motion */
27
28
29 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
30 #define RANDSIGN() ((random() & 1) ? 1 : -1)
31
32 enum starfish_mode {
33   pulse,
34   zoom
35 };
36   
37
38 struct starfish {
39   enum starfish_mode mode;
40   Bool blob_p;
41   int skip;
42   long x, y;            /* position of midpoint */
43   double th;            /* angle of rotation */
44   double rotv;          /* rotational velocity */
45   double rota;          /* rotational acceleration */
46   long elasticity;      /* how fast it deforms: radial velocity */
47   double rot_max;
48   long min_r, max_r;    /* radius range */
49   int npoints;          /* control points */
50   long *r;              /* radii */
51   spline *spline;
52   XPoint *prev;
53   int n_prev;
54 };
55
56 static struct starfish *
57 make_starfish (int maxx, int maxy, int size)
58 {
59   struct starfish *s = (struct starfish *) calloc(1, sizeof(*s));
60   int i;
61   int mid;
62
63   s->blob_p = get_boolean_resource ("blob", "Blob");
64   s->elasticity = SCALE * get_float_resource ("thickness", "Thickness");
65
66   if (s->elasticity == 0)
67     /* bell curve from 0-15, avg 7.5 */
68     s->elasticity = RAND(5*SCALE) + RAND(5*SCALE) + RAND(5*SCALE);
69
70   s->rotv = get_float_resource ("rotation", "Rotation");
71   if (s->rotv == -1)
72     /* bell curve from 0-12 degrees, avg 6 */
73     s->rotv = frand(4) + frand(4) + frand(4);
74
75   s->rotv /= 360;  /* convert degrees to ratio */
76
77   if (s->blob_p)
78     {
79       s->elasticity *= 3;
80       s->rotv *= 3;
81     }
82
83   s->rot_max = s->rotv * 2;
84   s->rota = 0.0004 + frand(0.0002);
85
86
87   if (! (random() % 20))
88     size *= frand(0.35) + frand(0.35) + 0.3;
89
90   {
91     static char skips[] = { 2, 2, 2, 2,
92                             3, 3, 3,
93                             6, 6,
94                             12 };
95     s->skip = skips[random() % sizeof(skips)];
96   }
97
98   if (! (random() % (s->skip == 2 ? 3 : 12)))
99     s->mode = zoom;
100   else
101     s->mode = pulse;
102
103   maxx *= SCALE;
104   maxy *= SCALE;
105   size *= SCALE;
106
107   s->max_r = size;
108   s->min_r = 0;
109
110   if (s->min_r < (5*SCALE)) s->min_r = (5*SCALE);
111   mid = ((s->min_r + s->max_r) / 2);
112
113   s->x = maxx/2;
114   s->y = maxy/2;
115
116   s->th = frand(M_PI+M_PI) * RANDSIGN();
117
118   {
119     static char sizes[] = { 3, 3, 3, 3, 3,
120                             4, 4, 4, 4,
121                             5, 5, 5, 5, 5, 5,
122                             8, 8, 8,
123                             10,
124                             35 };
125     int nsizes = sizeof(sizes);
126     if (s->skip > 3)
127       nsizes -= 4;
128     s->npoints = s->skip * sizes[random() % nsizes];
129   }
130
131   s->spline = make_spline (s->npoints);
132   s->r = (long *) malloc (sizeof(*s->r) * s->npoints);
133
134   for (i = 0; i < s->npoints; i++)
135     s->r[i] = ((i % s->skip) == 0) ? 0 : size;
136
137   return s;
138 }
139
140
141 static void
142 free_starfish (struct starfish *s)
143 {
144   if (s->r) free (s->r);
145   if (s->prev) free (s->prev);
146   if (s->spline)
147     {
148       if (s->spline->control_x) free (s->spline->control_x);
149       if (s->spline->control_y) free (s->spline->control_y);
150       if (s->spline->points) free (s->spline->points);
151       free (s->spline);
152     }
153   free (s);
154 }
155
156
157 static void 
158 throb_starfish (struct starfish *s)
159 {
160   int i;
161   double frac = ((M_PI+M_PI) / s->npoints);
162
163   for (i = 0; i < s->npoints; i++)
164     {
165       long r = s->r[i];
166       long ra = (r > 0 ? r : -r);
167       double th = (s->th > 0 ? s->th : -s->th);
168       long x, y;
169       long elasticity = s->elasticity;
170
171       /* place control points evenly around perimiter, shifted by theta */
172       x = s->x + ra * cos (i * frac + th);
173       y = s->y + ra * sin (i * frac + th);
174
175       s->spline->control_x[i] = x / SCALE;
176       s->spline->control_y[i] = y / SCALE;
177
178       if (s->mode == zoom && ((i % s->skip) == 0))
179         continue;
180
181       /* Slow down near the end points: move fastest in the middle. */
182       {
183         double ratio = (double)ra / (double)(s->max_r - s->min_r);
184         if (ratio > 0.5) ratio = 1-ratio;       /* flip */
185         ratio *= 2;                             /* normalize */
186         ratio = (ratio * 0.9) + 0.1;            /* fudge */
187         elasticity *= ratio;
188       }
189
190
191       /* Increase/decrease radius by elasticity */
192       ra += (r >= 0 ? elasticity : -elasticity);
193       if ((i % s->skip) == 0)
194         ra += (elasticity / 2);
195
196       r = ra * (r >= 0 ? 1 : -1);
197
198       /* If we've reached the end (too long or too short) reverse direction. */
199       if ((ra > s->max_r && r >= 0) ||
200           (ra < s->min_r && r < 0))
201         r = -r;
202
203       s->r[i] = r;
204     }
205 }
206
207
208 static void
209 spin_starfish (struct starfish *s)
210 {
211   double th = s->th;
212   if (th < 0)
213     th = -(th + s->rotv);
214   else
215     th += s->rotv;
216
217   if (th > (M_PI+M_PI))
218     th -= (M_PI+M_PI);
219   else if (th < 0)
220     th += (M_PI+M_PI);
221
222   s->th = (s->th > 0 ? th : -th);
223
224   s->rotv += s->rota;
225
226   if (s->rotv > s->rot_max || 
227       s->rotv < -s->rot_max)
228     {
229       s->rota = -s->rota;
230     }
231   /* If it stops, start it going in the other direction. */
232   else if (s->rotv < 0)
233     {
234       if (random() & 1)
235         {
236           /* keep going in the same direction */
237           s->rotv = 0;
238           if (s->rota < 0)
239             s->rota = -s->rota;
240         }
241       else
242         {
243           /* reverse gears */
244           s->rotv = -s->rotv;
245           s->rota = -s->rota;
246           s->th = -s->th;
247         }
248     }
249
250
251   /* Alter direction of rotational acceleration randomly. */
252   if (! (random() % 120))
253     s->rota = -s->rota;
254
255   /* Change acceleration very occasionally. */
256   if (! (random() % 200))
257     {
258       if (random() & 1)
259         s->rota *= 1.2;
260       else
261         s->rota *= 0.8;
262     }
263 }
264
265
266 static void
267 draw_starfish (Display *dpy, Drawable drawable, GC gc, struct starfish *s,
268            Bool fill_p)
269 {
270   compute_closed_spline (s->spline);
271   if (s->prev)
272     {
273       XPoint *points = (XPoint *)
274         malloc (sizeof(XPoint) * (s->n_prev + s->spline->n_points + 2));
275       int i = s->spline->n_points;
276       int j = s->n_prev;
277       memcpy (points, s->spline->points, (i * sizeof(*points)));
278       memcpy (points+i, s->prev, (j * sizeof(*points)));
279
280       if (s->blob_p)
281         XClearWindow (dpy, drawable);
282       XFillPolygon (dpy, drawable, gc, points, i+j, Complex, CoordModeOrigin);
283       free (points);
284
285       free (s->prev);
286       s->prev = 0;
287     }
288
289   s->prev = (XPoint *) malloc (s->spline->n_points * sizeof(XPoint));
290   memcpy (s->prev, s->spline->points, s->spline->n_points * sizeof(XPoint));
291   s->n_prev = s->spline->n_points;
292
293 #ifdef DEBUG
294   if (s->blob_p)
295     {
296       int i;
297       for (i = 0; i < s->npoints; i++)
298         XDrawLine (dpy, drawable, gc, s->x/SCALE, s->y/SCALE,
299                    s->spline->control_x[i], s->spline->control_y[i]);
300     }
301 #endif
302 }
303
304
305 static struct starfish *
306 make_window_starfish (Display *dpy, Window window)
307 {
308   XWindowAttributes xgwa;
309   int size;
310   Bool blob_p = get_boolean_resource ("blob", "Blob");
311   XGetWindowAttributes (dpy, window, &xgwa);
312   size = (xgwa.width < xgwa.height ? xgwa.width : xgwa.height);
313   if (blob_p) size /= 2;
314   else size *= 1.3;
315   return make_starfish (xgwa.width, xgwa.height, size);
316 }
317
318
319 static struct starfish *
320 init_starfish (Display *dpy, Window window)
321 {
322   static Bool first_time = True;
323   XGCValues gcv;
324   XWindowAttributes xgwa;
325   Bool blob_p = get_boolean_resource ("blob", "Blob");
326   XGetWindowAttributes (dpy, window, &xgwa);
327
328   cmap = xgwa.colormap;
329   cycle_p = get_boolean_resource ("cycle", "Cycle");
330
331   if (!first_time)
332     {
333       if (colors && ncolors)
334         free_colors (dpy, cmap, colors, ncolors);
335       if (colors)
336         free (colors);
337       colors = 0;
338     }
339
340   ncolors = get_integer_resource ("colors", "Colors");
341   if (ncolors < 2) ncolors = 2;
342   if (ncolors <= 2) mono_p = True;
343
344   if (mono_p)
345     colors = 0;
346   else
347     colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
348
349   if (mono_p || blob_p)
350     cycle_p = False;
351
352   if (mono_p)
353     ;
354   else if (random() % 3)
355     make_smooth_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
356                           True, &cycle_p, True);
357   else
358     make_uniform_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
359                            True, &cycle_p, True);
360
361   if (ncolors < 2) ncolors = 2;
362   if (ncolors <= 2) mono_p = True;
363
364   if (mono_p) cycle_p = False;
365
366   fg_index = 0;
367
368   if (!mono_p && !blob_p)
369     {
370       gcv.foreground = colors[fg_index].pixel;
371       XSetWindowBackground (dpy, window, gcv.foreground);
372     }
373
374   if (first_time)
375     {
376       XClearWindow (dpy, window);
377       first_time = False;
378     }
379
380   gcv.fill_rule = EvenOddRule;
381   gc = XCreateGC (dpy, window, GCForeground | GCFillRule, &gcv);
382
383   return make_window_starfish (dpy, window);
384 }
385
386
387
388 static void
389 run_starfish (Display *dpy, Window window, struct starfish *s)
390 {
391   throb_starfish (s);
392   spin_starfish (s);
393   draw_starfish (dpy, window, gc, s, False);
394
395   if (mono_p)
396     {
397       static Bool init = False;
398       static unsigned long black, white;
399       if (!init)
400         {
401           black = get_pixel_resource ("background", "Background", dpy, cmap);
402           white = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
403           init = True;
404           fg_index = white;
405           XSetForeground (dpy, gc, fg_index);
406         }
407       else if (!s->blob_p)
408         {
409           fg_index = (fg_index == black ? white : black);
410           XSetForeground (dpy, gc, fg_index);
411         }
412     }
413   else
414     {
415       fg_index = (fg_index + 1) % ncolors;
416       XSetForeground (dpy, gc, colors [fg_index].pixel);
417     }
418 }
419
420
421
422 \f
423 char *progclass = "Starfish";
424
425 char *defaults [] = {
426   ".background:         black",
427   ".foreground:         white",
428   "*delay:              10000",
429   "*cycleDelay:         100000",
430   "*thickness:          0",             /* pixels, 0 = random */
431   "*rotation:           -1",            /* degrees, -1 = "random" */
432   "*colors:             200",
433   "*cycle:              true",
434   "*duration:           30",
435   "*delay2:             5",
436   "*blob:               false",
437   0
438 };
439
440 XrmOptionDescRec options [] = {
441   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
442   { "-delay2",          ".delay2",      XrmoptionSepArg, 0 },
443   { "-cycle-delay",     ".cycleDelay",  XrmoptionSepArg, 0 },
444   { "-thickness",       ".thickness",   XrmoptionSepArg, 0 },
445   { "-rotation",        ".rotation",    XrmoptionSepArg, 0 },
446   { "-colors",          ".colors",      XrmoptionSepArg, 0 },
447   { "-cycle",           ".cycle",       XrmoptionNoArg, "True" },
448   { "-no-cycle",        ".cycle",       XrmoptionNoArg, "False" },
449   { "-duration",        ".duration",    XrmoptionSepArg, 0 },
450   { "-blob",            ".blob",        XrmoptionNoArg, "True" },
451   { "-no-blob",         ".blob",        XrmoptionNoArg, "False" },
452   { 0, 0, 0, 0 }
453 };
454
455 void
456 screenhack (Display *dpy, Window window)
457 {
458   struct starfish *s = init_starfish (dpy, window);
459   int delay = get_integer_resource ("delay", "Delay");
460   int delay2 = get_integer_resource ("delay2", "Delay") * 1000000;
461   int cycle_delay = get_integer_resource ("cycleDelay", "Delay");
462   int duration = get_seconds_resource ("duration", "Seconds");
463   Bool blob_p = get_boolean_resource ("blob", "Blob");
464   time_t start = time ((time_t) 0);
465   time_t now;
466   int direction = (random() % 1) ? 1 : -1;
467
468   if (blob_p)
469     delay *= 3;
470
471   while (1)
472     {
473       run_starfish (dpy, window, s);
474       XSync (dpy, False);
475
476       screenhack_handle_events (dpy);
477       if (cycle_p && cycle_delay)
478         {
479           if (cycle_delay <= delay)
480             {
481               int i = 0;
482               while (i < delay)
483                 {
484                   rotate_colors (dpy, cmap, colors, ncolors, direction);
485                   usleep(cycle_delay);
486                   i += cycle_delay;
487                 }
488             }
489           else
490             {
491               static long tick = 0;
492               if (tick >= cycle_delay)
493                 {
494                   rotate_colors (dpy, cmap, colors, ncolors, direction);
495                   tick = 0;
496                 }
497               if (delay)
498                 usleep(delay);
499               tick += delay;
500             }
501
502           if (! (random() % 1000))
503             direction = -direction;
504         }
505       else if (delay)
506         usleep (delay);
507
508       if (duration > 0)
509         {
510           now = time ((time_t) 0);
511           if (start + duration < now)
512             {
513               start = now;
514
515               free_starfish (s);
516
517               if (delay2 && !blob_p && cycle_p)
518                 {
519                   int i = 0;
520                   while (i < delay2)
521                     {
522                       rotate_colors (dpy, cmap, colors, ncolors, direction);
523                       screenhack_handle_events (dpy);
524                       usleep(cycle_delay);
525                       i += cycle_delay;
526                     }
527                 }
528
529               /* Every now and then, pick new colors; otherwise, just build
530                  a new starfish with the current colors. */
531               if (! (random () % 10))
532                 s = init_starfish (dpy, window);
533               else
534                 s = make_window_starfish(dpy, window);
535             }
536         }
537     }
538 }