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