/* xscreensaver, Copyright (c) 1997-2015 Jamie Zawinski * * 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 #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 (s) free (s); 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; XFreeGC (dpy, st->gc); free_starfish (st->starfish); free (st->colors); 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)