/* -*- Mode: C; tab-width: 4 -*- */ /* starfish --- */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)starfish.c 5.03 2003/05/01 xlockmore"; #endif /* xscreensaver, Copyright (c) 1997 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. * * Revision History: * 01-May-2003: Changed to work in a 64x64 window. * 01-Nov-2000: Allocation checks * 24-Feb-1998: xlockmore version by Jouk Jansen * 1997: original xscreensaver version by Jamie Zawinski */ #ifdef STANDALONE #define MODE_starfish #define PROGCLASS "Starfish" #define HACK_INIT init_starfish #define HACK_DRAW draw_starfish #define starfish_opts xlockmore_opts #define DEFAULTS "*delay: 2000 \n" \ "*cycles: 1000 \n" \ "*ncolors: 200 \n" \ "*fullrandom: True \n" \ "*verbose: False \n" #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #include "color.h" #endif /* STANDALONE */ #ifdef MODE_starfish #include "spline.h" #define DEF_ROTV "-1" #define DEF_THICKNESS "-20" #define DEF_CYCLE "True" #define DEF_BLOB "False" #define DEF_CYCLESPEED "3" static int cyclespeed; static float rotv, thickness; static Bool blob, cycle_p; static XrmOptionDescRec opts[] = { {(char *) "-cyclespeed", (char *) "starfish.cyclespeed", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-rotation", (char *) "starfish.rotation", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-thickness", (char *) "starfish.thickness", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-blob", (char *) ".starfish.blob", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+blob", (char *) ".starfish.blob", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-cycle", (char *) ".starfish.cycle", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+cycle", (char *) ".starfish.cycle", XrmoptionNoArg, (caddr_t) "off"} }; static argtype vars[] = { {(void *) & cyclespeed, (char *) "cyclespeed", (char *) "CycleSpeed", (char *) DEF_CYCLESPEED, t_Int}, {(void *) & rotv, (char *) "rotation", (char *) "Rotation", (char *) DEF_ROTV, t_Float}, {(void *) & thickness, (char *) "thickness", (char *) "Thickness", (char *) DEF_THICKNESS, t_Float}, {(void *) & blob, (char *) "blob", (char *) "Blob", (char *) DEF_BLOB, t_Bool}, {(void *) & cycle_p, (char *) "cycle", (char *) "Cycle", (char *) DEF_CYCLE, t_Bool} }; static OptionStruct desc[] = { {(char *) "-cyclespeed num", (char *) "Cycling speed"}, {(char *) "-rotation num", (char *) "Rotation velocity"}, {(char *) "-thickness num", (char *) "Thickness"}, {(char *) "-/+blob", (char *) "turn on/off blob"}, {(char *) "-/+cycle", (char *) "turn on/off cycle"} }; ModeSpecOpt starfish_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct starfish_description = {"starfish", "init_starfish", "draw_starfish", "release_starfish", "init_starfish", "init_starfish", (char *) NULL, &starfish_opts, 2000, 1, 1000, 1, 64, 1.0, "", "Shows starfish", 0, NULL}; #endif #define SCALE 1000 /* fixed-point math, for sub-pixel motion */ #define RAND(n) ((long) ((LRAND() & 0x7fffffff) % ((long) (n)))) #define RANDSIGN() ((LRAND() & 1) ? 1 : -1) enum starfish_mode { pulse, zoom }; typedef struct { 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 */ double 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 *splines; XPoint *prev; int n_prev; GC gc; Colormap cmap; XColor *colors; int ncolors; Bool cycle_p, mono_p, no_colors; unsigned long fg_index; int size, winwidth, winheight, stage, counter; float thickness; unsigned long blackpixel, whitepixel, fg, bg; int direction; ModeInfo *mi; } starfishstruct; static starfishstruct *starfishes = (starfishstruct *) NULL; static void free_starfish(Display *display, starfishstruct * sp) { ModeInfo *mi = sp->mi; if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { MI_WHITE_PIXEL(mi) = sp->whitepixel; MI_BLACK_PIXEL(mi) = sp->blackpixel; #ifndef STANDALONE MI_FG_PIXEL(mi) = sp->fg; MI_BG_PIXEL(mi) = sp->bg; #endif if (sp->colors != NULL) { if (sp->ncolors && !sp->no_colors) free_colors(display, sp->cmap, sp->colors, sp->ncolors); free(sp->colors); sp->colors = (XColor *) NULL; } if (sp->cmap != None) { XFreeColormap(display, sp->cmap); sp->cmap = None; } } if (sp->gc != None) { XFreeGC(display, sp->gc); sp->gc = None; } if (sp->splines != NULL) { free_spline(sp->splines); sp->splines = (spline *) NULL; } if (sp->r != NULL) { free(sp->r); sp->r = (long *) NULL; } if (sp->prev != NULL) { free(sp->prev); sp->prev = (XPoint *) NULL; } } static void make_starfish(ModeInfo * mi) { starfishstruct *sp = &starfishes[MI_SCREEN(mi)]; int i; sp->elasticity = SCALE * sp->thickness; if (sp->elasticity == 0.0) /* bell curve from 0-15, avg 7.5 */ sp->elasticity = RAND(5 * SCALE) + RAND(5 * SCALE) + RAND(5 * SCALE); if (sp->rotv == -1) /* bell curve from 0-12 degrees, avg 6 */ sp->rotv = (double) 4.0 *((double) LRAND() / (double) MAXRAND + (double) LRAND() / (double) MAXRAND + (double) LRAND() / (double) MAXRAND); sp->rotv /= 360; /* convert degrees to ratio */ if (sp->blob_p) { sp->elasticity *= 3.0; sp->rotv *= 3; } sp->rot_max = sp->rotv * 2; sp->rota = 0.0004 + 0.0002 * (double) LRAND() / (double) MAXRAND; if (NRAND(20) == 5) sp->size = (int) (sp->size * (0.35 * ((double) LRAND() / (double) MAXRAND + (double) LRAND() / (double) MAXRAND) + 0.3)); { static char skips[] = {2, 2, 2, 2, 3, 3, 3, 6, 6, 12}; sp->skip = skips[LRAND() % sizeof (skips)]; } if (!(LRAND() % (sp->skip == 2 ? 3 : 12))) sp->mode = zoom; else sp->mode = pulse; sp->winwidth *= SCALE; sp->winheight *= SCALE; sp->size *= SCALE; sp->max_r = sp->size; sp->min_r = SCALE; /* sp->min_r = 5 * SCALE; */ /* mid = ((sp->min_r + sp->max_r) / 2); */ sp->x = sp->winwidth / 2; sp->y = sp->winheight / 2; sp->th = 2.0 * M_PI * ((double) LRAND() / (double) MAXRAND) * RANDSIGN(); { static 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 (sp->skip > 3) nsizes -= 4; sp->npoints = sp->skip * sizes[LRAND() % nsizes]; } if (sp->splines) free_spline(sp->splines); sp->splines = make_spline(sp->npoints); if (sp->r != NULL) free(sp->r); if ((sp->r = (long *) malloc(sizeof (*sp->r) * sp->npoints)) == NULL) { free_starfish(MI_DISPLAY(mi), sp); return; } for (i = 0; i < sp->npoints; i++) sp->r[i] = ((i % sp->skip) == 0) ? 0 : sp->size; } static void throb_starfish(starfishstruct * sp) { int i; double frac = ((M_PI + M_PI) / sp->npoints); for (i = 0; i < sp->npoints; i++) { double r = sp->r[i]; double ra = (r > 0.0 ? r : -r); double th = (sp->th > 0 ? sp->th : -sp->th); double x, y; double elasticity = sp->elasticity; /* place control points evenly around perimiter, shifted by theta */ x = sp->x + (ra * cos(i * frac + th)); y = sp->y + (ra * sin(i * frac + th)); sp->splines->control_x[i] = x / SCALE; sp->splines->control_y[i] = y / SCALE; if (sp->mode == zoom && ((i % sp->skip) == 0)) continue; /* Slow down near the end points: move fastest in the middle. */ { double ratio = ra / (double) (sp->max_r - sp->min_r); if (ratio > 0.5) ratio = 1 - ratio; /* flip */ ratio *= 2.0; /* normalize */ ratio = (ratio * 0.9) + 0.1; /* fudge */ elasticity = elasticity * ratio; } /* Increase/decrease radius by elasticity */ ra += (r >= 0.0 ? elasticity : -elasticity); if ((i % sp->skip) == 0) ra += (elasticity / 2.0); r = ra * (r >= 0.0 ? 1.0 : -1.0); /* If we've reached the end (too long or too short) reverse direction. */ if ((ra > sp->max_r && r >= 0.0) || (ra < sp->min_r && r < 0.0)) r = -r; sp->r[i] = (long) r; } } static void spin_starfish(starfishstruct * sp) { double th = sp->th; if (th < 0) th = -(th + sp->rotv); else th += sp->rotv; if (th > (M_PI + M_PI)) th -= (M_PI + M_PI); else if (th < 0) th += (M_PI + M_PI); sp->th = (sp->th > 0 ? th : -th); sp->rotv += sp->rota; if (sp->rotv > sp->rot_max || sp->rotv < -sp->rot_max) { sp->rota = -sp->rota; } /* If it stops, start it going in the other direction. */ else if (sp->rotv < 0) { if (LRAND() & 1) { /* keep going in the same direction */ sp->rotv = 0; if (sp->rota < 0) sp->rota = -sp->rota; } else { /* reverse gears */ sp->rotv = -sp->rotv; sp->rota = -sp->rota; sp->th = -sp->th; } } /* Alter direction of rotational acceleration randomly. */ if (!(LRAND() % 120)) sp->rota = -sp->rota; /* Change acceleration very occasionally. */ if (!(LRAND() % 200)) { if (LRAND() & 1) sp->rota *= 1.2; else sp->rota *= 0.8; } } static Bool draw1_starfish(Display * display, Drawable drawable, GC gc, starfishstruct * sp) { compute_closed_spline(sp->splines); if (sp->prev) { XPoint *points; int i = sp->splines->n_points; int j = sp->n_prev; if ((points = (XPoint *) malloc(sizeof (XPoint) * (sp->n_prev + sp->splines->n_points + 2))) == NULL) { free_starfish(display, sp); return False; } (void) memcpy((char *) points, (char *) sp->splines->points, (i * sizeof (*points))); (void) memcpy((char *) (points + i), (char *) sp->prev, (j * sizeof (*points))); if (sp->blob_p) XClearWindow(display, drawable); XFillPolygon(display, drawable, gc, points, i + j, Complex, CoordModeOrigin); free(points); free(sp->prev); sp->prev = (XPoint *) NULL; } if ((sp->prev = (XPoint *) malloc(sp->splines->n_points * sizeof (XPoint))) == NULL) { free_starfish(display, sp); return False; } (void) memcpy((char *) sp->prev, (char *) sp->splines->points, sp->splines->n_points * sizeof (XPoint)); sp->n_prev = sp->splines->n_points; #ifdef DEBUG if (sp->blob_p) { int i; for (i = 0; i < sp->npoints; i++) XDrawLine(display, drawable, gc, sp->x / SCALE, sp->y / SCALE, sp->splines->control_x[i], sp->splines->control_y[i]); } #endif return True; } #ifndef STANDALONE extern char *background; extern char *foreground; #endif void init_starfish(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); XGCValues gcv; starfishstruct *sp; /* initialize */ if (starfishes == NULL) { if ((starfishes = (starfishstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (starfishstruct))) == NULL) return; } sp = &starfishes[MI_SCREEN(mi)]; sp->mi = mi; if (MI_IS_FULLRANDOM(mi)) { if (NRAND(10) == 9) sp->blob_p = True; else sp->blob_p = False; if (NRAND(8) == 7) sp->cycle_p = False; else sp->cycle_p = True; } else { sp->blob_p = blob; sp->cycle_p = cycle_p; } sp->rotv = (double) rotv; sp->direction = (LRAND() & 1) ? 1 : -1; if (thickness < 0) sp->thickness = -thickness * (float) LRAND() / (float) MAXRAND; else sp->thickness = thickness; if (sp->blob_p) sp->fg_index = MI_BLACK_PIXEL(mi); else sp->fg_index = MI_WHITE_PIXEL(mi); if (sp->gc == None) { if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { XColor color; #ifndef STANDALONE sp->fg = MI_FG_PIXEL(mi); sp->bg = MI_BG_PIXEL(mi); #endif sp->blackpixel = MI_BLACK_PIXEL(mi); sp->whitepixel = MI_WHITE_PIXEL(mi); if ((sp->cmap = XCreateColormap(display, window, MI_VISUAL(mi), AllocNone)) == None) { free_starfish(display, sp); return; } XSetWindowColormap(display, window, sp->cmap); (void) XParseColor(display, sp->cmap, "black", &color); (void) XAllocColor(display, sp->cmap, &color); MI_BLACK_PIXEL(mi) = color.pixel; (void) XParseColor(display, sp->cmap, "white", &color); (void) XAllocColor(display, sp->cmap, &color); MI_WHITE_PIXEL(mi) = color.pixel; #ifndef STANDALONE (void) XParseColor(display, sp->cmap, background, &color); (void) XAllocColor(display, sp->cmap, &color); MI_BG_PIXEL(mi) = color.pixel; (void) XParseColor(display, sp->cmap, foreground, &color); (void) XAllocColor(display, sp->cmap, &color); MI_FG_PIXEL(mi) = color.pixel; #endif sp->colors = (XColor *) NULL; sp->ncolors = 0; } gcv.foreground = sp->fg_index; gcv.fill_rule = EvenOddRule; if ((sp->gc = XCreateGC(display, window, GCForeground | GCFillRule, &gcv)) == None) { free_starfish(display, sp); return; } } if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { /* Set up colour map */ if (sp->colors != NULL) { if (sp->ncolors && !sp->no_colors) free_colors(display, sp->cmap, sp->colors, sp->ncolors); free(sp->colors); sp->colors = (XColor *) NULL; } sp->ncolors = MI_NCOLORS(mi); if (sp->ncolors < 2) sp->ncolors = 2; if (sp->ncolors <= 2) sp->mono_p = True; else sp->mono_p = False; if (sp->mono_p) sp->colors = (XColor *) NULL; else if ((sp->colors = (XColor *) malloc(sizeof (*sp->colors) * (sp->ncolors + 1))) == NULL) { free_starfish(display, sp); return; } if (sp->mono_p || sp->blob_p) sp->cycle_p = False; if (!sp->mono_p) { if (LRAND() % 3) make_smooth_colormap(mi, sp->cmap, sp->colors, &sp->ncolors, True, &sp->cycle_p); else make_uniform_colormap(mi, sp->cmap, sp->colors, &sp->ncolors, True, &sp->cycle_p); } XInstallColormap(display, sp->cmap); if (sp->ncolors < 2) { sp->ncolors = 2; sp->no_colors = True; } else sp->no_colors = False; if (sp->ncolors <= 2) sp->mono_p = True; if (sp->mono_p) sp->cycle_p = False; sp->fg_index = 0; /* if (!sp->mono_p && !sp->blob_p) { gcv.foreground = sp->colors[sp->fg_index].pixel; XSetWindowBackground (display, window, gcv.foreground); } */ } else sp->fg_index = NRAND(MI_NPIXELS(mi)); sp->size = (MI_WIDTH(mi) < MI_HEIGHT(mi) ? MI_WIDTH(mi) : MI_HEIGHT(mi)); if (sp->blob_p) sp->size /= 2; else sp->size = (int) (sp->size * 1.3); sp->winwidth = MI_WIDTH(mi); sp->winheight = MI_HEIGHT(mi); MI_CLEARWINDOW(mi); if (sp->prev != NULL) { free(sp->prev); sp->prev = (XPoint *) NULL; } sp->stage = 0; sp->counter = 0; make_starfish(mi); } static Bool run_starfish(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); starfishstruct *sp = &starfishes[MI_SCREEN(mi)]; throb_starfish(sp); spin_starfish(sp); if (!draw1_starfish(display, window, sp->gc, sp)) return False; if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { if (sp->mono_p) { if (!sp->blob_p) { sp->fg_index = (sp->fg_index == MI_BLACK_PIXEL(mi) ? MI_BLACK_PIXEL(mi) : MI_BLACK_PIXEL(mi)); } XSetForeground(display, sp->gc, sp->fg_index); } else { sp->fg_index = (sp->fg_index + 1) % sp->ncolors; XSetForeground(display, sp->gc, sp->colors[sp->fg_index].pixel); } } else { if (MI_NPIXELS(mi) > 2) XSetForeground(display, sp->gc, MI_PIXEL(mi, sp->fg_index)); else if (sp->fg_index) XSetForeground(display, sp->gc, MI_BLACK_PIXEL(mi)); else XSetForeground(display, sp->gc, MI_WHITE_PIXEL(mi)); if (++sp->fg_index >= (unsigned int) MI_NPIXELS(mi)) sp->fg_index = 0; } return True; } void draw_starfish(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); starfishstruct *sp; if (starfishes == NULL) return; sp = &starfishes[MI_SCREEN(mi)]; if (sp->r == NULL) return; if (sp->no_colors) { free_starfish(display, sp); init_starfish(mi); return; } MI_IS_DRAWN(mi) = True; if (!sp->stage) { if (!run_starfish(mi)) return; } else if (sp->cycle_p) { rotate_colors(display, sp->cmap, sp->colors, sp->ncolors, sp->direction); if (!NRAND(512)) sp->direction = -sp->direction; } sp->stage++; if (sp->stage > (2 * sp->blob_p + 1) * cyclespeed) { sp->stage = 0; sp->counter++; } if (sp->counter > MI_CYCLES(mi)) { /* Every now and then, restart the mode */ init_starfish(mi); } } void release_starfish(ModeInfo * mi) { if (starfishes != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) free_starfish(MI_DISPLAY(mi), &starfishes[screen]); free(starfishes); starfishes = (starfishstruct *) NULL; } } #endif /* MODE_starfish */