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