summaryrefslogtreecommitdiffstats
path: root/hacks/starfish.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/starfish.c')
-rw-r--r--hacks/starfish.c559
1 files changed, 559 insertions, 0 deletions
diff --git a/hacks/starfish.c b/hacks/starfish.c
new file mode 100644
index 0000000..5e94144
--- /dev/null
+++ b/hacks/starfish.c
@@ -0,0 +1,559 @@
+/* xscreensaver, Copyright (c) 1997-2015 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include <math.h>
+#include "screenhack.h"
+#include "spline.h"
+
+#define SCALE 1000 /* fixed-point math, for sub-pixel motion */
+
+
+#define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
+#define RANDSIGN() ((random() & 1) ? 1 : -1)
+
+enum starfish_mode {
+ pulse,
+ zoom
+};
+
+struct starfish {
+ enum starfish_mode mode;
+ Bool blob_p;
+ int skip;
+ long x, y; /* position of midpoint */
+ double th; /* angle of rotation */
+ double rotv; /* rotational velocity */
+ double rota; /* rotational acceleration */
+ long elasticity; /* how fast it deforms: radial velocity */
+ double rot_max;
+ long min_r, max_r; /* radius range */
+ int npoints; /* control points */
+ long *r; /* radii */
+ spline *spline;
+ XPoint *prev;
+ int n_prev;
+};
+
+
+struct state {
+ Display *dpy;
+ Window window;
+
+ Colormap cmap;
+ XColor *colors;
+ int ncolors;
+ int fg_index;
+ GC gc;
+
+ int delay, delay2, duration, direction, blob_p;
+
+ Bool done_once;
+ Bool initted;
+ unsigned long black, white;
+
+ long tick;
+ time_t start_time;
+
+ struct starfish *starfish;
+};
+
+
+static struct starfish *
+make_starfish (struct state *st, int maxx, int maxy, int size)
+{
+ struct starfish *s = (struct starfish *) calloc(1, sizeof(*s));
+ int i;
+
+ s->blob_p = st->blob_p;
+ s->elasticity = SCALE * get_float_resource (st->dpy, "thickness", "Thickness");
+
+ if (s->elasticity == 0)
+ /* bell curve from 0-15, avg 7.5 */
+ s->elasticity = RAND(5*SCALE) + RAND(5*SCALE) + RAND(5*SCALE);
+
+ s->rotv = get_float_resource (st->dpy, "rotation", "Rotation");
+ if (s->rotv == -1)
+ /* bell curve from 0-12 degrees, avg 6 */
+ s->rotv = frand(4) + frand(4) + frand(4);
+
+ s->rotv /= 360; /* convert degrees to ratio */
+
+ if (s->blob_p)
+ {
+ s->elasticity *= 3;
+ s->rotv *= 3;
+ }
+
+ s->rot_max = s->rotv * 2;
+ s->rota = 0.0004 + frand(0.0002);
+
+
+ if (! (random() % 20))
+ size *= frand(0.35) + frand(0.35) + 0.3;
+
+ {
+ static const char skips[] = { 2, 2, 2, 2,
+ 3, 3, 3,
+ 6, 6,
+ 12 };
+ s->skip = skips[random() % sizeof(skips)];
+ }
+
+ if (! (random() % (s->skip == 2 ? 3 : 12)))
+ s->mode = zoom;
+ else
+ s->mode = pulse;
+
+ maxx *= SCALE;
+ maxy *= SCALE;
+ size *= SCALE;
+
+ s->max_r = size;
+ s->min_r = 0;
+
+ if (s->min_r < (5*SCALE)) s->min_r = (5*SCALE);
+
+ s->x = maxx/2;
+ s->y = maxy/2;
+
+ s->th = frand(M_PI+M_PI) * RANDSIGN();
+
+ {
+ static const char sizes[] = { 3, 3, 3, 3, 3,
+ 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5,
+ 8, 8, 8,
+ 10,
+ 35 };
+ int nsizes = sizeof(sizes);
+ if (s->skip > 3)
+ nsizes -= 4;
+ s->npoints = s->skip * sizes[random() % nsizes];
+ }
+
+ s->spline = make_spline (s->npoints);
+ s->r = (long *) malloc (sizeof(*s->r) * s->npoints);
+
+ for (i = 0; i < s->npoints; i++)
+ s->r[i] = ((i % s->skip) == 0) ? 0 : size;
+
+ return s;
+}
+
+
+static void
+free_starfish (struct starfish *s)
+{
+ if (s->r) free (s->r);
+ if (s->prev) free (s->prev);
+ if (s->spline)
+ {
+ if (s->spline->control_x) free (s->spline->control_x);
+ if (s->spline->control_y) free (s->spline->control_y);
+ if (s->spline->points) free (s->spline->points);
+ free (s->spline);
+ }
+ free (s);
+}
+
+
+static void
+throb_starfish (struct starfish *s)
+{
+ int i;
+ double frac = ((M_PI+M_PI) / s->npoints);
+
+ for (i = 0; i < s->npoints; i++)
+ {
+ long r = s->r[i];
+ long ra = (r > 0 ? r : -r);
+ double th = (s->th > 0 ? s->th : -s->th);
+ long x, y;
+ long elasticity = s->elasticity;
+
+ /* place control points evenly around perimiter, shifted by theta */
+ x = s->x + ra * cos (i * frac + th);
+ y = s->y + ra * sin (i * frac + th);
+
+ s->spline->control_x[i] = x / SCALE;
+ s->spline->control_y[i] = y / SCALE;
+
+ if (s->mode == zoom && ((i % s->skip) == 0))
+ continue;
+
+ /* Slow down near the end points: move fastest in the middle. */
+ {
+ double ratio = (double)ra / (double)(s->max_r - s->min_r);
+ if (ratio > 0.5) ratio = 1-ratio; /* flip */
+ ratio *= 2; /* normalize */
+ ratio = (ratio * 0.9) + 0.1; /* fudge */
+ elasticity *= ratio;
+ }
+
+
+ /* Increase/decrease radius by elasticity */
+ ra += (r >= 0 ? elasticity : -elasticity);
+ if ((i % s->skip) == 0)
+ ra += (elasticity / 2);
+
+ r = ra * (r >= 0 ? 1 : -1);
+
+ /* If we've reached the end (too long or too short) reverse direction. */
+ if ((ra > s->max_r && r >= 0) ||
+ (ra < s->min_r && r < 0))
+ r = -r;
+
+ s->r[i] = r;
+ }
+}
+
+
+static void
+spin_starfish (struct starfish *s)
+{
+ double th = s->th;
+ if (th < 0)
+ th = -(th + s->rotv);
+ else
+ th += s->rotv;
+
+ if (th > (M_PI+M_PI))
+ th -= (M_PI+M_PI);
+ else if (th < 0)
+ th += (M_PI+M_PI);
+
+ s->th = (s->th > 0 ? th : -th);
+
+ s->rotv += s->rota;
+
+ if (s->rotv > s->rot_max ||
+ s->rotv < -s->rot_max)
+ {
+ s->rota = -s->rota;
+ }
+ /* If it stops, start it going in the other direction. */
+ else if (s->rotv < 0)
+ {
+ if (random() & 1)
+ {
+ /* keep going in the same direction */
+ s->rotv = 0;
+ if (s->rota < 0)
+ s->rota = -s->rota;
+ }
+ else
+ {
+ /* reverse gears */
+ s->rotv = -s->rotv;
+ s->rota = -s->rota;
+ s->th = -s->th;
+ }
+ }
+
+
+ /* Alter direction of rotational acceleration randomly. */
+ if (! (random() % 120))
+ s->rota = -s->rota;
+
+ /* Change acceleration very occasionally. */
+ if (! (random() % 200))
+ {
+ if (random() & 1)
+ s->rota *= 1.2;
+ else
+ s->rota *= 0.8;
+ }
+}
+
+
+static void
+draw_starfish (struct state *st, Drawable drawable, GC this_gc, struct starfish *s,
+ Bool fill_p)
+{
+ compute_closed_spline (s->spline);
+ if (s->prev)
+ {
+ XPoint *points = (XPoint *)
+ malloc (sizeof(XPoint) * (s->n_prev + s->spline->n_points + 2));
+ int i = s->spline->n_points;
+ int j = s->n_prev;
+ memcpy (points, s->spline->points, (i * sizeof(*points)));
+ memcpy (points+i, s->prev, (j * sizeof(*points)));
+
+ if (s->blob_p)
+ XClearWindow (st->dpy, drawable);
+ XFillPolygon (st->dpy, drawable, this_gc, points, i+j, Complex, CoordModeOrigin);
+ free (points);
+
+ free (s->prev);
+ s->prev = 0;
+ }
+
+ s->prev = (XPoint *) malloc (s->spline->n_points * sizeof(XPoint));
+ memcpy (s->prev, s->spline->points, s->spline->n_points * sizeof(XPoint));
+ s->n_prev = s->spline->n_points;
+
+#ifdef DEBUG
+ if (s->blob_p)
+ {
+ int i;
+ for (i = 0; i < s->npoints; i++)
+ XDrawLine (st->dpy, drawable, this_gc, s->x/SCALE, s->y/SCALE,
+ s->spline->control_x[i], s->spline->control_y[i]);
+ }
+#endif
+}
+
+
+static struct starfish *
+make_window_starfish (struct state *st)
+{
+ XWindowAttributes xgwa;
+ int size;
+ XGetWindowAttributes (st->dpy, st->window, &xgwa);
+ size = (xgwa.width < xgwa.height ? xgwa.width : xgwa.height);
+ if (st->blob_p) size /= 2;
+ else size *= 1.3;
+
+ if (xgwa.width < 100 || xgwa.height < 100) /* tiny window */
+ {
+ size = (xgwa.width > xgwa.height ? xgwa.width : xgwa.height);
+ if (size < 100) size = 100;
+ }
+
+ return make_starfish (st, xgwa.width, xgwa.height, size);
+}
+
+
+static struct starfish *
+reset_starfish (struct state *st)
+{
+ XGCValues gcv;
+ unsigned int flags = 0;
+ XWindowAttributes xgwa;
+ XGetWindowAttributes (st->dpy, st->window, &xgwa);
+
+ st->cmap = xgwa.colormap;
+
+ if (st->done_once)
+ {
+ if (st->colors && st->ncolors)
+ free_colors (xgwa.screen, st->cmap, st->colors, st->ncolors);
+ if (st->colors)
+ free (st->colors);
+ st->colors = 0;
+ XFreeGC (st->dpy, st->gc);
+ st->gc = 0;
+ }
+
+ st->ncolors = get_integer_resource (st->dpy, "colors", "Colors");
+ if (st->ncolors < 2) st->ncolors = 2;
+ if (st->ncolors <= 2) mono_p = True;
+
+ if (mono_p)
+ st->colors = 0;
+ else
+ st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
+
+ if (mono_p)
+ ;
+ else if (random() % 3)
+ make_smooth_colormap (xgwa.screen, xgwa.visual, st->cmap,
+ st->colors, &st->ncolors,
+ True, 0, True);
+ else
+ make_uniform_colormap (xgwa.screen, xgwa.visual, st->cmap,
+ st->colors, &st->ncolors,
+ True, 0, True);
+
+ if (st->ncolors < 2) st->ncolors = 2;
+ if (st->ncolors <= 2) mono_p = True;
+
+ st->fg_index = 0;
+
+ if (!mono_p && !st->blob_p)
+ {
+ flags |= GCForeground;
+ gcv.foreground = st->colors[st->fg_index].pixel;
+ XSetWindowBackground (st->dpy, st->window, gcv.foreground);
+ }
+
+ if (!st->done_once)
+ {
+ XClearWindow (st->dpy, st->window);
+ st->done_once = 1;
+ }
+
+ flags |= GCFillRule;
+ gcv.fill_rule = EvenOddRule;
+ st->gc = XCreateGC (st->dpy, st->window, flags, &gcv);
+#ifdef HAVE_JWXYZ
+ if (!st->blob_p)
+ jwxyz_XSetAntiAliasing (st->dpy, st->gc, False);
+#endif
+
+ return make_window_starfish (st);
+}
+
+
+
+static void
+run_starfish (struct state *st, struct starfish *s)
+{
+ throb_starfish (s);
+ spin_starfish (s);
+ draw_starfish (st, st->window, st->gc, s, False);
+
+ if (mono_p)
+ {
+ if (!st->initted)
+ {
+ st->black = get_pixel_resource (st->dpy, st->cmap, "background", "Background");
+ st->white = get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground");
+ st->initted = True;
+ st->fg_index = st->white;
+ XSetForeground (st->dpy, st->gc, st->fg_index);
+ }
+ else if (!s->blob_p)
+ {
+ st->fg_index = (st->fg_index == st->black ? st->white : st->black);
+ XSetForeground (st->dpy, st->gc, st->fg_index);
+ }
+ }
+ else
+ {
+ st->fg_index = (st->fg_index + 1) % st->ncolors;
+ XSetForeground (st->dpy, st->gc, st->colors [st->fg_index].pixel);
+ }
+}
+
+
+static void *
+starfish_init (Display *dpy, Window window)
+{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
+ char *s;
+ st->dpy = dpy;
+ st->window = window;
+ st->delay = get_integer_resource (st->dpy, "delay", "Delay");
+ st->delay2 = get_integer_resource (st->dpy, "delay2", "Delay") * 1000000;
+/* st->duration = get_seconds_resource (st->dpy, "duration", "Seconds");*/
+ st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
+ st->direction = (random() & 1) ? 1 : -1;
+
+ s = get_string_resource (st->dpy, "mode", "Mode");
+ if (s && !strcasecmp (s, "blob"))
+ st->blob_p = True;
+ else if (s && !strcasecmp (s, "zoom"))
+ st->blob_p = False;
+ else if (!s || !*s || !strcasecmp (s, "random"))
+ st->blob_p = !(random() % 3);
+ else
+ fprintf (stderr, "%s: mode must be blob, zoom, or random", progname);
+
+ if (st->blob_p)
+ st->delay *= 3;
+
+ st->starfish = reset_starfish (st);
+ return st;
+}
+
+static unsigned long
+starfish_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ struct starfish *s = st->starfish;
+ time_t now;
+
+ run_starfish (st, s);
+
+ if (st->duration > 0)
+ {
+ if (st->start_time == 0)
+ st->start_time = time ((time_t *) 0);
+ now = time ((time_t *) 0);
+ if (st->start_time + st->duration < now)
+ {
+ st->start_time = now;
+
+ free_starfish (s);
+
+ /* Every now and then, pick new colors; otherwise, just build
+ a new starfish with the current colors. */
+ if (! (random () % 10))
+ s = reset_starfish (st);
+ else
+ s = make_window_starfish (st);
+
+ st->starfish = s;
+ }
+ }
+
+ return st->delay;
+}
+
+static void
+starfish_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ struct state *st = (struct state *) closure;
+ free_starfish (st->starfish);
+ st->starfish = 0;
+ st->starfish = reset_starfish (st);
+}
+
+static Bool
+starfish_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ return False;
+}
+
+static void
+starfish_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ free (st);
+}
+
+
+
+
+static const char *starfish_defaults [] = {
+ ".background: black",
+ ".foreground: white",
+ "*fpsSolid: true",
+ "*delay: 10000",
+ "*thickness: 0", /* pixels, 0 = random */
+ "*rotation: -1", /* degrees, -1 = "random" */
+ "*colors: 200",
+ "*duration: 30",
+ "*delay2: 5",
+ "*mode: random",
+#ifdef HAVE_MOBILE
+ "*ignoreRotation: True",
+#endif
+ 0
+};
+
+static XrmOptionDescRec starfish_options [] = {
+ { "-delay", ".delay", XrmoptionSepArg, 0 },
+ { "-delay2", ".delay2", XrmoptionSepArg, 0 },
+ { "-thickness", ".thickness", XrmoptionSepArg, 0 },
+ { "-rotation", ".rotation", XrmoptionSepArg, 0 },
+ { "-colors", ".colors", XrmoptionSepArg, 0 },
+ { "-duration", ".duration", XrmoptionSepArg, 0 },
+ { "-mode", ".mode", XrmoptionSepArg, 0 },
+ { "-blob", ".mode", XrmoptionNoArg, "blob" },
+ { "-zoom", ".mode", XrmoptionNoArg, "zoom" },
+ { 0, 0, 0, 0 }
+};
+
+XSCREENSAVER_MODULE ("Starfish", starfish)