/* The Spiral Generator, Copyright (c) 2000 * by Rohit Singh * * Contains code from / To be used with: * xscreensaver, Copyright (c) 1992, 1995, 1996, 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 notices appear in all copies and that both that * copyright notices 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. * * Modified (Dec 2001) by Matthew Strait * Added -subdelay and -alwaysfinish * Prevented redrawing over existing lines */ #include #include "screenhack.h" #include "erase.h" struct state { Display *dpy; Window window; XWindowAttributes xgwa; GC draw_gc; int long_delay; int sub_sleep_time; int num_layers; unsigned int default_fg_pixel; Bool always_finish_p; XColor color; int got_color; int theta; float firstx, firsty; int x1, y1, x2, y2; int counter; int distance; int radius1, radius2; double divisor; enum curstate { NEW_LAYER, DRAW, ERASE1, ERASE2 } drawstate; eraser_state *eraser; }; static void init_tsg (struct state *st) { XGCValues gcv; Colormap cmap; XGetWindowAttributes (st->dpy, st->window, &st->xgwa); cmap = st->xgwa.colormap; gcv.foreground = st->default_fg_pixel = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground"); st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv); gcv.foreground = get_pixel_resource (st->dpy, cmap, "background", "Background"); } static Bool go (struct state *st, int radius1, int radius2, int d) { int width, height; int xmid, ymid; float tmpx, tmpy; int delta; width = st->xgwa.width; height = st->xgwa.height; delta = 1; xmid = width / 2; ymid = height / 2; if (st->theta == 1) { st->x1 = xmid + radius1 - radius2 + d; st->y1 = ymid; } /* for (theta = 1; / * theta < ( 360 * 100 ) * /; theta++) */ /* see below about alwaysfinish */ { tmpx = xmid + (( radius1 /* * * * * */ - radius2 ) /* This algo simulates */ * cos(( st->theta /* the rotation of a */ * M_PI ) /* circular disk inside */ / 180 )) /* a hollow circular */ + ( d /* rim. A point on the */ * cos(((( radius1 /* disk dist d from the */ * st->theta ) /* centre, traces the */ - delta ) /* path given by this */ / radius2 ) /* equation. */ * M_PI /* A deviation (error) */ / 180 ) /* of delta needs to be */ ); /* given, which greatly */ /* adds to the beauty */ tmpy = ymid + ( /* of the figure. */ ( radius1 - radius2 /* */ ) * sin /* Imperfection adds to */ ( /* beauty, symbolically */ ( st->theta * M_PI /* ... */ ) / 180 /* Algo deduced by */ ) /* Rohit Singh, Jan'00 */ ) + /* based on a toy he */ ( d * sin /* used to play with */ ( /* when he was a kid. */ ( /* * * * * */ ( ( radius1 * st->theta ) - delta ) / radius2 ) * M_PI / 180 ) ); /*makes integers from the calculated values to do the drawing*/ st->x2 = tmpx; st->y2 = tmpy; /*stores the first values for later reference*/ if(st->theta == 1) { st->firstx = tmpx; st->firsty = tmpy; } if (st->theta != 1) XDrawLine (st->dpy, st->window, st->draw_gc, st->x1, st->y1, st->x2, st->y2); st->x1 = st->x2; st->y1 = st->y2; /* compares the exact values calculated to the first exact values calculated */ /* this will break when nothing new is being drawn */ if(tmpx == st->firstx && tmpy == st->firsty && st->theta != 1) { st->firstx = st->firsty = 0; st->theta = 1; return True; } /* this will break after 36000 iterations if the -alwaysfinish option is not specified */ if(!st->always_finish_p && st->theta > ( 360 * 100 ) ) { st->firstx = st->firsty = 0; st->theta = 1; return True; } } st->theta++; return False; } #define min(a,b) ((a)<(b)?(a):(b)) static void pick_new (struct state *st) { int radius = min (st->xgwa.width, st->xgwa.height) / 2; st->divisor = ((frand (3.0) + 1) * (((random() & 1) * 2) - 1)); st->radius1 = radius; st->radius2 = radius / st->divisor + 5; st->distance = 100 + (random() % 200); st->theta = 1; } static void * xspirograph_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); st->dpy = dpy; st->window = window; st->long_delay = get_integer_resource(st->dpy, "delay", "Integer"); st->sub_sleep_time = get_integer_resource(st->dpy, "subdelay", "Integer"); st->num_layers = get_integer_resource(st->dpy, "layers", "Integer"); st->always_finish_p = get_boolean_resource (st->dpy, "alwaysfinish", "Boolean"); XGetWindowAttributes (st->dpy, st->window, &st->xgwa); init_tsg (st); st->theta = 1; st->drawstate = NEW_LAYER; return st; } static void new_colors (struct state *st) { if (mono_p) XSetForeground (st->dpy, st->draw_gc, st->default_fg_pixel); else { hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5, &st->color.red, &st->color.green, &st->color.blue); if ((st->got_color = XAllocColor (st->dpy, st->xgwa.colormap, &st->color))) XSetForeground (st->dpy, st->draw_gc, st->color.pixel); else XSetForeground (st->dpy, st->draw_gc, st->default_fg_pixel); } } static unsigned long xspirograph_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; Bool free_color = False; Bool flip_p = (st->counter & 1); int i; switch (st->drawstate) { case ERASE1: /* 5 sec delay before starting the erase */ st->drawstate = ERASE2; /* shouldn't this use the configured long_delay value??? */ return (st->long_delay == 0 ? 0 : 5000000); case ERASE2: /* erase, delaying 1/50th sec between frames */ st->eraser = erase_window(st->dpy, st->window, st->eraser); if (st->eraser) /* shouldn't this be a configured pause??? */ return 20000; st->drawstate = NEW_LAYER; /* just finished erasing -- leave screen black for 1 sec */ return (st->long_delay == 0 ? 0 : 1000000); case DRAW: /* most common case put in front */ for (i = 0; i < 1000; i++) { if (go(st, st->radius1, (flip_p ? st->radius2 : -st->radius2), st->distance)) { st->drawstate = NEW_LAYER; break; } } /* Next draw is delayed sleep_time (initialized value)*/ return st->sub_sleep_time; case NEW_LAYER: /* Increment counter */ st->counter++; if (st->counter > (2 * st->num_layers)) { /* reset to zero, free, and erase next time through */ st->counter = 0; if (free_color) XFreeColors (st->dpy, st->xgwa.colormap, &st->color.pixel, 1, 0); st->drawstate = ERASE1; } else { /* first, third, fifth, ... time through */ if (!flip_p) pick_new (st); new_colors (st); st->drawstate = DRAW; } /* No delay to the next draw */ return 0; default: /* OOPS!! */ fprintf(stderr, "%s: invalid state\n", progname); exit(1); } return st->sub_sleep_time; /* notreached */ } static void xspirograph_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { struct state *st = (struct state *) closure; XGetWindowAttributes (st->dpy, st->window, &st->xgwa); } static Bool xspirograph_event (Display *dpy, Window window, void *closure, XEvent *event) { return False; } static void xspirograph_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; XFreeGC (dpy, st->draw_gc); free (st); } static const char *xspirograph_defaults [] = { ".background: black", ".foreground: white", "*fpsSolid: true", "*delay: 5", "*subdelay: 20000", "*layers: 2", "*alwaysfinish: false", #ifdef HAVE_MOBILE "*ignoreRotation: True", #endif 0 }; static XrmOptionDescRec xspirograph_options [] = { { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-subdelay", ".subdelay", XrmoptionSepArg, 0 }, { "-layers", ".layers", XrmoptionSepArg, 0 }, { "-alwaysfinish", ".alwaysfinish", XrmoptionNoArg, "true"}, { "-noalwaysfinish", ".alwaysfinish", XrmoptionNoArg, "false"}, { 0, 0, 0, 0 } }; XSCREENSAVER_MODULE ("XSpirograph", xspirograph)