1 /* xscreensaver, Copyright (c) 1997-2008 Jamie Zawinski <jwz@jwz.org>
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
13 #include "screenhack.h"
16 #define SCALE 1000 /* fixed-point math, for sub-pixel motion */
19 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
20 #define RANDSIGN() ((random() & 1) ? 1 : -1)
28 enum starfish_mode mode;
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 */
37 long min_r, max_r; /* radius range */
38 int npoints; /* control points */
56 int delay, delay2, duration, direction, blob_p;
60 unsigned long black, white;
65 struct starfish *starfish;
69 static struct starfish *
70 make_starfish (struct state *st, int maxx, int maxy, int size)
72 struct starfish *s = (struct starfish *) calloc(1, sizeof(*s));
76 s->blob_p = st->blob_p;
77 s->elasticity = SCALE * get_float_resource (st->dpy, "thickness", "Thickness");
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);
83 s->rotv = get_float_resource (st->dpy, "rotation", "Rotation");
85 /* bell curve from 0-12 degrees, avg 6 */
86 s->rotv = frand(4) + frand(4) + frand(4);
88 s->rotv /= 360; /* convert degrees to ratio */
96 s->rot_max = s->rotv * 2;
97 s->rota = 0.0004 + frand(0.0002);
100 if (! (random() % 20))
101 size *= frand(0.35) + frand(0.35) + 0.3;
104 static const char skips[] = { 2, 2, 2, 2,
108 s->skip = skips[random() % sizeof(skips)];
111 if (! (random() % (s->skip == 2 ? 3 : 12)))
123 if (s->min_r < (5*SCALE)) s->min_r = (5*SCALE);
124 mid = ((s->min_r + s->max_r) / 2);
129 s->th = frand(M_PI+M_PI) * RANDSIGN();
132 static const char sizes[] = { 3, 3, 3, 3, 3,
138 int nsizes = sizeof(sizes);
141 s->npoints = s->skip * sizes[random() % nsizes];
144 s->spline = make_spline (s->npoints);
145 s->r = (long *) malloc (sizeof(*s->r) * s->npoints);
147 for (i = 0; i < s->npoints; i++)
148 s->r[i] = ((i % s->skip) == 0) ? 0 : size;
155 free_starfish (struct starfish *s)
157 if (s->r) free (s->r);
158 if (s->prev) free (s->prev);
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);
171 throb_starfish (struct starfish *s)
174 double frac = ((M_PI+M_PI) / s->npoints);
176 for (i = 0; i < s->npoints; i++)
179 long ra = (r > 0 ? r : -r);
180 double th = (s->th > 0 ? s->th : -s->th);
182 long elasticity = s->elasticity;
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);
188 s->spline->control_x[i] = x / SCALE;
189 s->spline->control_y[i] = y / SCALE;
191 if (s->mode == zoom && ((i % s->skip) == 0))
194 /* Slow down near the end points: move fastest in the middle. */
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 */
204 /* Increase/decrease radius by elasticity */
205 ra += (r >= 0 ? elasticity : -elasticity);
206 if ((i % s->skip) == 0)
207 ra += (elasticity / 2);
209 r = ra * (r >= 0 ? 1 : -1);
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))
222 spin_starfish (struct starfish *s)
226 th = -(th + s->rotv);
230 if (th > (M_PI+M_PI))
235 s->th = (s->th > 0 ? th : -th);
239 if (s->rotv > s->rot_max ||
240 s->rotv < -s->rot_max)
244 /* If it stops, start it going in the other direction. */
245 else if (s->rotv < 0)
249 /* keep going in the same direction */
264 /* Alter direction of rotational acceleration randomly. */
265 if (! (random() % 120))
268 /* Change acceleration very occasionally. */
269 if (! (random() % 200))
280 draw_starfish (struct state *st, Drawable drawable, GC this_gc, struct starfish *s,
283 compute_closed_spline (s->spline);
286 XPoint *points = (XPoint *)
287 malloc (sizeof(XPoint) * (s->n_prev + s->spline->n_points + 2));
288 int i = s->spline->n_points;
290 memcpy (points, s->spline->points, (i * sizeof(*points)));
291 memcpy (points+i, s->prev, (j * sizeof(*points)));
294 XClearWindow (st->dpy, drawable);
295 XFillPolygon (st->dpy, drawable, this_gc, points, i+j, Complex, CoordModeOrigin);
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;
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]);
318 static struct starfish *
319 make_window_starfish (struct state *st)
321 XWindowAttributes xgwa;
323 XGetWindowAttributes (st->dpy, st->window, &xgwa);
324 size = (xgwa.width < xgwa.height ? xgwa.width : xgwa.height);
325 if (st->blob_p) size /= 2;
327 return make_starfish (st, xgwa.width, xgwa.height, size);
331 static struct starfish *
332 reset_starfish (struct state *st)
335 unsigned int flags = 0;
336 XWindowAttributes xgwa;
337 XGetWindowAttributes (st->dpy, st->window, &xgwa);
339 st->cmap = xgwa.colormap;
343 if (st->colors && st->ncolors)
344 free_colors (st->dpy, st->cmap, st->colors, st->ncolors);
348 XFreeGC (st->dpy, st->gc);
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;
359 st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
363 else if (random() % 3)
364 make_smooth_colormap (st->dpy, xgwa.visual, st->cmap, st->colors, &st->ncolors,
367 make_uniform_colormap (st->dpy, xgwa.visual, st->cmap, st->colors, &st->ncolors,
370 if (st->ncolors < 2) st->ncolors = 2;
371 if (st->ncolors <= 2) mono_p = True;
375 if (!mono_p && !st->blob_p)
377 flags |= GCForeground;
378 gcv.foreground = st->colors[st->fg_index].pixel;
379 XSetWindowBackground (st->dpy, st->window, gcv.foreground);
384 XClearWindow (st->dpy, st->window);
389 gcv.fill_rule = EvenOddRule;
390 st->gc = XCreateGC (st->dpy, st->window, flags, &gcv);
392 return make_window_starfish (st);
398 run_starfish (struct state *st, struct starfish *s)
402 draw_starfish (st, st->window, st->gc, s, False);
408 st->black = get_pixel_resource (st->dpy, st->cmap, "background", "Background");
409 st->white = get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground");
411 st->fg_index = st->white;
412 XSetForeground (st->dpy, st->gc, st->fg_index);
416 st->fg_index = (st->fg_index == st->black ? st->white : st->black);
417 XSetForeground (st->dpy, st->gc, st->fg_index);
422 st->fg_index = (st->fg_index + 1) % st->ncolors;
423 XSetForeground (st->dpy, st->gc, st->colors [st->fg_index].pixel);
429 starfish_init (Display *dpy, Window window)
431 struct state *st = (struct state *) calloc (1, sizeof(*st));
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;
441 s = get_string_resource (st->dpy, "mode", "Mode");
442 if (s && !strcasecmp (s, "blob"))
444 else if (s && !strcasecmp (s, "zoom"))
446 else if (!s || !*s || !strcasecmp (s, "random"))
447 st->blob_p = !(random() % 3);
449 fprintf (stderr, "%s: mode must be blob, zoom, or random", progname);
454 st->starfish = reset_starfish (st);
459 starfish_draw (Display *dpy, Window window, void *closure)
461 struct state *st = (struct state *) closure;
462 struct starfish *s = st->starfish;
465 run_starfish (st, s);
467 if (st->duration > 0)
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)
474 st->start_time = now;
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);
483 s = make_window_starfish (st);
493 starfish_reshape (Display *dpy, Window window, void *closure,
494 unsigned int w, unsigned int h)
496 struct state *st = (struct state *) closure;
497 free_starfish (st->starfish);
499 st->starfish = reset_starfish (st);
503 starfish_event (Display *dpy, Window window, void *closure, XEvent *event)
509 starfish_free (Display *dpy, Window window, void *closure)
511 struct state *st = (struct state *) closure;
518 static const char *starfish_defaults [] = {
519 ".background: black",
520 ".foreground: white",
523 "*thickness: 0", /* pixels, 0 = random */
524 "*rotation: -1", /* degrees, -1 = "random" */
532 static XrmOptionDescRec starfish_options [] = {
533 { "-delay", ".delay", XrmoptionSepArg, 0 },
534 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
535 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
536 { "-rotation", ".rotation", XrmoptionSepArg, 0 },
537 { "-colors", ".colors", XrmoptionSepArg, 0 },
538 { "-duration", ".duration", XrmoptionSepArg, 0 },
539 { "-mode", ".mode", XrmoptionSepArg, 0 },
540 { "-blob", ".mode", XrmoptionNoArg, "blob" },
541 { "-zoom", ".mode", XrmoptionNoArg, "zoom" },
545 XSCREENSAVER_MODULE ("Starfish", starfish)