diff options
Diffstat (limited to 'hacks/starfish.c')
-rw-r--r-- | hacks/starfish.c | 559 |
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) |