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