*/
/* WhirlwindWarp: moving stars. Ported from QBasic by Joey.
- Version 1.2. FF parameters now driven by a
- dampened-walked velocity, making them smoother.
+ Version 1.3. Smooth with pretty colours.
This code adapted from original program by jwz/jk above.
Freely distrubtable. Please keep this tag with
Paul "Joey" Clark, hacking for humanity, Feb 99
www.cs.bris.ac.uk/~pclark | www.changetheworld.org.uk */
+/* 15/May/05: Added colour rotation, limit on max FPS, scaling size dots, and smoother drivers.
+ 4/Mar/01: Star colours are cycled when new colour can not be allocated.
+ 4/Mar/01: Stars are plotted as squares with size relative to screen.
+ 28/Nov/00: Submitted to xscreensaver as "whirlwindwarp".
+ 10/Oct/00: Ported to xscreensaver as "twinkle".
+ 19/Feb/98: Meters and interaction added for Ivor's birthday "stars11f".
+ 11/Aug/97: Original QBasic program. */
+
#include <math.h>
#include "screenhack.h"
#include "erase.h"
#include "hsv.h"
-static GC draw_gc, erase_gc;
-static unsigned int default_fg_pixel;
-
-/* Maximum number of points, tails, and fields (hard-coded) */
+/* Maximum number of points, maximum tail length, and the number of forcefields/effects (hard-coded) */
#define maxps 1000
-#define maxts 30
-#define fs 10
-
-/* Screen width and height */
-static int scrwid,scrhei;
-
-/* Current x,y of stars in realspace */
-static float cx[maxps];
-static float cy[maxps];
-/* Previous x,y plots in pixelspace for removal later */
-static int tx[maxps*maxts];
-static int ty[maxps*maxts];
-/* The force fields and their parameters */
-static char *name[fs];
-static int fon[fs]; /* Is field on or off? */
-static float var[fs]; /* Current parameter */
-static float op[fs]; /* Optimum (central/mean) value */
-static float damp[fs]; /* Dampening (how much drawn between current and optimal) */
-static float force[fs]; /* Amount of change per moment */
-static float acc[fs];
-
-/* Number of points and tails */
-static int ps=500;
-static int ts=5;
-
-/* Show meters or not? */
-static Bool meters;
-
-static Bool init_whirlwindwarp(Display *dpy, Window window) {
+#define maxts 50
+#define fs 16
+/* TODO: change ps and ts arrays into pointers, for dynamic allocation at runtime. */
+
+struct state {
+ Display *dpy;
+ Window window;
+
+ GC draw_gc, erase_gc;
+ unsigned int default_fg_pixel;
+
+ int scrwid,scrhei;
+ int starsize;
+
+ float cx[maxps]; /* Current x,y of stars in realspace */
+ float cy[maxps];
+ int tx[maxps*maxts]; /* Previous x,y plots in pixelspace for removal later */
+ int ty[maxps*maxts];
+ char *name[fs]; /* The force fields and their parameters */
+
+ int fon[fs]; /* Is field on or off? */
+ float var[fs]; /* Current parameter */
+ float op[fs]; /* Optimum (central/mean) value */
+ float acc[fs];
+ float vel[fs];
+
+ int ps; /* Number of points and tail length */
+ int ts;
+
+ Bool meters;
+
+ int initted;
+ XWindowAttributes xgwa;
+ int got_color;
+ XColor color[maxps]; /* The colour assigned to each star */
+ XColor bgcolor;
+ int p,f,nt, sx,sy, resets,lastresets,cnt;
+ int colsavailable;
+ int hue;
+
+ struct timeval lastframe;
+};
+
+
+static void *
+whirlwindwarp_init (Display *dpy, Window window)
+{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
XGCValues gcv;
Colormap cmap;
- XWindowAttributes xgwa;
- XGetWindowAttributes (dpy, window, &xgwa);
- cmap = xgwa.colormap;
- gcv.foreground = default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
- draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
- gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
- erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
-
- ps = get_integer_resource ("points", "Integer");
- ts = get_integer_resource ("tails", "Integer");
- meters = get_boolean_resource ("meters", "Show meters");
- if (ps>maxps || ts>maxts)
- return 0;
- return 1;
+
+ st->dpy = dpy;
+ st->window = window;
+
+ st->ps=500;
+ st->ts=5;
+
+ 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");
+ st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
+
+ st->ps = get_integer_resource (st->dpy, "points", "Integer");
+ st->ts = get_integer_resource (st->dpy, "tails", "Integer");
+ st->meters = get_boolean_resource (st->dpy, "meters", "Show meters");
+ if (st->ps > maxps) st->ps = maxps;
+ if (st->ts > maxts) st->ts = maxts;
+
+ return st;
}
-static float myrnd(void) { /* between -1.0 and +1.0 */
+static float myrnd(void)
+{ /* between -1.0 (inclusive) and +1.0 (exclusive) */
return 2.0*((float)((random()%10000000)/10000000.0)-0.5);
}
-float mysgn(float x) {
+#if 0
+static float mysgn(float x)
+{
return ( x < 0 ? -1 :
x > 0 ? +1 :
0 );
}
+#endif
-void stars_newp(int p) {
- cx[p]=myrnd();
- cy[p]=myrnd();
+static void stars_newp(struct state *st, int pp)
+{
+ st->cx[pp]=myrnd();
+ st->cy[pp]=myrnd();
}
/* Adjust a variable var about optimum op,
with damp = dampening about op
force = force of random perturbation */
-float stars_perturb(float var,float op,float damp,float force) {
- var=op+damp*(var-op)+force*myrnd()/4.0;
-/* if (fabs(var-op)>0.1) // (to keep within bounds)
- var=op+0.1*mysgn(var-op);*/
- return var;
-}
+/* float stars_perturb(float var,float op,float damp,float force) {
+ return op+damp*(var-op)+force*myrnd()/4.0;
+ }*/
+#define stars_perturb(var,op,damp,force) \
+ ( (op) + (damp)*((var)-(op)) + (force)*myrnd()/4.0 )
/* Get pixel coordinates of a star */
-int stars_scrpos_x(int p) {
- return scrwid*(cx[p]+1.0)/2.0;
+static int stars_scrpos_x(struct state *st, int pp)
+{
+ return st->scrwid*(st->cx[pp]+1.0)/2.0;
}
-int stars_scrpos_y(int p) {
- return scrhei*(cy[p]+1.0)/2.0;
+
+static int stars_scrpos_y(struct state *st, int pp)
+{
+ return st->scrhei*(st->cy[pp]+1.0)/2.0;
}
/* Draw a meter of a forcefield's parameter */
-void stars_draw_meter(Display *dpy,Window window,GC draw_gc,int f) {
+static void stars_draw_meter(struct state *st, int ff)
+{
int x,y,w,h;
- x=scrwid/2;
- y=f*10;
- w=(var[f]-op[f])*scrwid*4;
+ x=st->scrwid/2;
+ y=ff*10;
+ w=(st->var[ff]-st->op[ff])*st->scrwid*4;
h=7;
if (w<0) {
w=-w;
x=x-w;
}
- if (fon[f])
- XFillRectangle(dpy,window,draw_gc,x,y,w,h);
- else
- XDrawRectangle(dpy,window,draw_gc,x,y,w,h);
+ if (st->fon[ff])
+ XFillRectangle(st->dpy,st->window,st->draw_gc,x,y,w,h);
+ /* else
+ XDrawRectangle(dpy,window,draw_gc,x,y,w,h); */
}
/* Move a star according to acting forcefields */
-void stars_move(int p) {
+static void stars_move(struct state *st, int pp)
+{
float nx,ny;
- float x=cx[p];
- float y=cy[p];
- if (fon[1]) {
- x = x * var[1]; y = y * var[1];
- }
- if (fon[2]) {
- nx=x*cos(var[2])+y*sin(var[2]);
- ny=-x*sin(var[2])+y*cos(var[2]);
- x=nx;
- y=ny;
- }
- if (fon[3]) {
- y=y*var[3];
- }
- if (fon[4]) {
- x=(x-1.0)*var[3]+1.0;
- }
- if (fon[5]) {
- x=x+var[5]*x;
- }
- if (fon[6]) {
- x = mysgn(x) * pow(fabs(x),var[6]);
- y = mysgn(y) * pow(fabs(y),var[6]);
- }
- if (fon[7]) {
- if (fon[0]) {
- if (fon[9]) {
- x=x+var[7]*(-1.0+2.0*(float)(p%2));
- } else {
- x=x+var[7]*(-1.0+2.0*(float)((p%50)/49.0));
- }
- }
- }
- if (fon[8]) {
- if (fon[0]) {
- if (fon[9]) {
- y=y+var[8]*(-1.0+2.0*(float)(p%2));
- } else {
- y=y+var[8]*(-1.0+2.0*(float)((p%50)/49.0));
- }
- }
- }
- cx[p]=x;
- cy[p]=y;
+ float x=st->cx[pp];
+ float y=st->cy[pp];
+
+ /* In theory all these if checks are unneccessary,
+ since each forcefield effect should do nothing when its var = op.
+ But the if's are good for efficiency because this function
+ is called once for every point.
+
+ Squirge towards edges (makes a leaf shape, previously split the screen in 4 but now only 1 :)
+ These ones must go first, to avoid x+1.0 < 0
+ */
+ if (st->fon[6]) {
+ /* x = mysgn(x) * pow(fabs(x),var[6]);
+ y = mysgn(y) * pow(fabs(y),var[6]);*/
+ x = -1.0 + 2.0*pow((x + 1.0)/2.0,st->var[6]);
+ }
+ if (st->fon[7]) {
+ y = -1.0 + 2.0*pow((y + 1.0)/2.0,st->var[7]);
+ }
+
+ /* Warping in/out */
+ if (st->fon[1]) {
+ x = x * st->var[1]; y = y * st->var[1];
+ }
+
+ /* Rotation */
+ if (st->fon[2]) {
+ nx=x*cos(1.1*st->var[2])+y*sin(1.1*st->var[2]);
+ ny=-x*sin(1.1*st->var[2])+y*cos(1.1*st->var[2]);
+ x=nx;
+ y=ny;
+ }
+
+ /* Asymptotes (looks like a plane with a horizon; equivalent to 1D warp) */
+ if (st->fon[3]) { /* Horizontal asymptote */
+ y=y*st->var[3];
+ }
+ if (st->fon[4]) { /* Vertical asymptote */
+ x=x+st->var[4]*x; /* this is the same maths as the last, but with op=0 */
+ }
+ if (st->fon[5]) { /* Vertical asymptote at right of screen */
+ x=(x-1.0)*st->var[5]+1.0;
+ }
+
+ /* Splitting (whirlwind effect): */
+ #define num_splits ( 2 + (int) (fabs(st->var[0]) * 1000) )
+ /* #define thru ( (float)(pp%num_splits)/(float)(num_splits-1) ) */
+ #define thru ( (float)((int)(num_splits*(float)(pp)/(float)(st->ps)))/(float)(num_splits-1) )
+ if (st->fon[8]) {
+ x=x+0.5*st->var[8]*(-1.0+2.0*thru);
+ }
+ if (st->fon[9]) {
+ y=y+0.5*st->var[9]*(-1.0+2.0*thru);
+ }
+
+ /* Waves */
+ if (st->fon[10]) {
+ y = y + 0.4*st->var[10]*sin(300.0*st->var[12]*x + 600.0*st->var[11]);
+ }
+ if (st->fon[13]) {
+ x = x + 0.4*st->var[13]*sin(300.0*st->var[15]*y + 600.0*st->var[14]);
+ }
+
+ st->cx[pp]=x;
+ st->cy[pp]=y;
}
-static void do_whirlwindwarp(Display *dpy, Window window) {
- Colormap cmap;
- XWindowAttributes xgwa;
- int got_color = 0;
- XColor color[maxps];
- XColor bgcolor;
- int p,f,nt, sx,sy, resets,lastresets,cnt;
-
- XClearWindow (dpy, window);
- XGetWindowAttributes (dpy, window, &xgwa);
- cmap = xgwa.colormap;
- scrwid = xgwa.width;
- scrhei = xgwa.height;
-
- /* Setup colours */
- hsv_to_rgb (0.0, 0.0, 0.0, &bgcolor.red, &bgcolor.green, &bgcolor.blue);
- got_color = XAllocColor (dpy, cmap, &bgcolor);
- for (p=0;p<ps;p++) {
- if (!mono_p)
- hsv_to_rgb (random()%360, .6+.4*myrnd(), .6+.4*myrnd(), &color[p].red, &color[p].green, &color[p].blue);
- /* hsv_to_rgb (random()%360, 1.0, 1.0, &color[p].red, &color[p].green, &color[p].blue); for stronger colours! */
- if ((!mono_p) && (got_color = XAllocColor (dpy, cmap, &color[p]))) {
- } else {
- if (p>0)
- color[p]=color[0];
- else
- color[p].pixel=default_fg_pixel;
- }
+/* Turns a forcefield on, and ensures its vars are suitable. */
+static void turn_on_field(struct state *st, int ff)
+{
+ if (!st->fon[ff]) {
+ /* acc[ff]=0.0; */
+ st->acc[ff]=0.02 * myrnd();
+ st->vel[ff]=0.0;
+ st->var[ff]=st->op[ff];
+ }
+ st->fon[ff] = 1;
+ if (ff == 10) {
+ turn_on_field(st, 11);
+ turn_on_field(st, 12);
+ }
+ if (ff == 13) {
+ turn_on_field(st, 14);
+ turn_on_field(st, 15);
}
+}
- /* Set up parameter movements for the different forcefields */
- name[1] = "Velocity";
- op[1] = 1; damp[1] = .999; force[1] = .002;
- name[2] = "Rotation";
- op[2] = 0; damp[2] = .999; force[2] = .002;
- name[3] = "Drip";
- op[3] = 1; damp[3] = .999; force[3] = .005;
- name[4] = "Dribble";
- op[4] = 1; damp[4] = .999; force[4] = .005;
- name[5] = "Slide";
- op[5] = 0; damp[5] = .999; force[5] = .002;
- name[6] = "Accelerate";
- op[6] = 1.0; damp[6] = .999; force[6] = .005;
- name[7] = "xDisplace";
- op[7] = 0; damp[7] = .999; force[7] = .005;
- name[8] = "yDisplace";
- op[8] = 0; damp[8] = .999; force[8] = .005;
- /* 0 and 9 are options for splitting displacements [no var] */
- name[0] = "Split";
- op[0] = 0; damp[0] = 0; force[0] = 0;
- name[9] = "2d/3d split";
- op[9] = 0; damp[9] = 0; force[9] = 0;
+static unsigned long
+whirlwindwarp_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+
+ /* time_t lastframe = time((time_t) 0); */
+
+ if (!st->initted) {
+ st->initted = 1;
+
+ XClearWindow (st->dpy, st->window);
+ XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
+ st->scrwid = st->xgwa.width;
+ st->scrhei = st->xgwa.height;
+
+ st->starsize=st->scrhei/480;
+ if (st->starsize<=0)
+ st->starsize=1;
+
+ /* Setup colours */
+ hsv_to_rgb (0.0, 0.0, 0.0, &st->bgcolor.red, &st->bgcolor.green, &st->bgcolor.blue);
+ st->got_color = XAllocColor (st->dpy, st->xgwa.colormap, &st->bgcolor);
+ st->colsavailable=0;
+ for (st->p=0;st->p<st->ps;st->p++) {
+ if (!mono_p)
+ hsv_to_rgb (random()%360, .6+.4*myrnd(), .6+.4*myrnd(), &st->color[st->p].red, &st->color[st->p].green, &st->color[st->p].blue);
+ /* hsv_to_rgb (random()%360, 1.0, 1.0, &color[p].red, &color[p].green, &color[p].blue); for stronger colours! */
+ if ((!mono_p) && (st->got_color = XAllocColor (st->dpy, st->xgwa.colormap, &st->color[st->p]))) {
+ st->colsavailable=st->p;
+ } else {
+ if (st->colsavailable>0) /* assign colours from those already allocated */
+ st->color[st->p]=st->color[ st->p % st->colsavailable ];
+ else
+ st->color[st->p].pixel=st->default_fg_pixel;
+ }
+ }
+
+ /* Set up central (optimal) points for each different forcefield */
+ st->op[1] = 1; st->name[1] = "Warp";
+ st->op[2] = 0; st->name[2] = "Rotation";
+ st->op[3] = 1; st->name[3] = "Horizontal asymptote";
+ st->op[4] = 0; st->name[4] = "Vertical asymptote";
+ st->op[5] = 1; st->name[5] = "Vertical asymptote right";
+ st->op[6] = 1; st->name[6] = "Squirge x";
+ st->op[7] = 1; st->name[7] = "Squirge y";
+ st->op[0] = 0; st->name[0] = "Split number (inactive)";
+ st->op[8] = 0; st->name[8] = "Split velocity x";
+ st->op[9] = 0; st->name[9] = "Split velocity y";
+ st->op[10] = 0; st->name[10] = "Horizontal wave amplitude";
+ st->op[11] = myrnd()*3.141; st->name[11] = "Horizontal wave phase (inactive)";
+ st->op[12] = 0.01; st->name[12] = "Horizontal wave frequency (inactive)";
+ st->op[13] = 0; st->name[13] = "Vertical wave amplitude";
+ st->op[14] = myrnd()*3.141; st->name[14] = "Vertical wave phase (inactive)";
+ st->op[15] = 0.01; st->name[15] = "Vertical wave frequency (inactive)";
/* Initialise parameters to optimum, all off */
- for (f=0;f<fs;f++) {
- var[f]=op[f];
- fon[f]=0;
- acc[f]=0;
+ for (st->f=0;st->f<fs;st->f++) {
+ st->var[st->f]=st->op[st->f];
+ st->fon[st->f]=( myrnd()>0.5 ? 1 : 0 );
+ st->acc[st->f]=0.02 * myrnd();
+ st->vel[st->f]=0;
}
/* Initialise stars */
- for (p=0;p<ps;p++)
- stars_newp(p);
+ for (st->p=0;st->p<st->ps;st->p++)
+ stars_newp(st, st->p);
- /* tx[nt],ty[nt] remeber earlier screen plots (tails of stars)
+ /* tx[nt],ty[nt] remember earlier screen plots (tails of stars)
which are deleted when nt comes round again */
- nt=0;
- resets=0;
+ st->nt = 0;
+ st->resets = 0;
+
+ st->hue = 180 + 180*myrnd();
- while (1) {
+ gettimeofday(&st->lastframe, NULL);
+
+ }
+
+
+ if (myrnd()>0.75) {
+ /* Change one of the allocated colours to something near the current hue. */
+ /* By changing a random colour, we sometimes get a tight colour spread, sometime a diverse one. */
+ int pp = st->colsavailable * (0.5+myrnd()/2);
+ hsv_to_rgb (st->hue, .6+.4*myrnd(), .6+.4*myrnd(), &st->color[pp].red, &st->color[pp].green, &st->color[pp].blue);
+ if ((!mono_p) && (st->got_color = XAllocColor (st->dpy, st->xgwa.colormap, &st->color[pp]))) {
+ }
+ st->hue = st->hue + 0.5 + myrnd()*9.0;
+ if (st->hue<0) st->hue+=360;
+ if (st->hue>=360) st->hue-=360;
+ }
/* Move current points */
- lastresets=resets;
- resets=0;
- for (p=0;p<ps;p++) {
+ st->lastresets=st->resets;
+ st->resets=0;
+ for (st->p=0;st->p<st->ps;st->p++) {
/* Erase old */
- XSetForeground (dpy, draw_gc, bgcolor.pixel);
- XDrawPoint(dpy,window,draw_gc,tx[nt],ty[nt]);
+ XSetForeground (st->dpy, st->draw_gc, st->bgcolor.pixel);
+ /* XDrawPoint(dpy,window,draw_gc,tx[nt],ty[nt]); */
+ XFillRectangle(st->dpy,st->window,st->draw_gc,st->tx[st->nt],st->ty[st->nt],st->starsize,st->starsize);
/* Move */
- stars_move(p);
+ stars_move(st, st->p);
/* If moved off screen, create a new one */
- if (cx[p]<-1.0 || cx[p]>+1.0 ||
- cy[p]<-1.0 || cy[p]>+1.0 ||
- fabs(cx[p])<.0001 || fabs(cy[p])<.0001) {
- stars_newp(p);
- resets++;
+ if (st->cx[st->p]<=-0.9999 || st->cx[st->p]>=+0.9999 ||
+ st->cy[st->p]<=-0.9999 || st->cy[st->p]>=+0.9999 ||
+ fabs(st->cx[st->p])<.0001 || fabs(st->cy[st->p])<.0001) {
+ stars_newp(st, st->p);
+ st->resets++;
} else if (myrnd()>0.99) /* Reset at random */
- stars_newp(p);
+ stars_newp(st, st->p);
/* Draw point */
- sx=stars_scrpos_x(p);
- sy=stars_scrpos_y(p);
- XSetForeground (dpy, draw_gc, color[p].pixel);
- XDrawPoint(dpy,window,draw_gc,sx,sy);
+ st->sx=stars_scrpos_x(st, st->p);
+ st->sy=stars_scrpos_y(st, st->p);
+ XSetForeground (st->dpy, st->draw_gc, st->color[st->p].pixel);
+ /* XDrawPoint(dpy,window,draw_gc,sx,sy); */
+ XFillRectangle(st->dpy,st->window,st->draw_gc,st->sx,st->sy,st->starsize,st->starsize);
/* Remember it for removal later */
- tx[nt]=sx;
- ty[nt]=sy;
- nt=(nt+1)%(ps*ts);
+ st->tx[st->nt]=st->sx;
+ st->ty[st->nt]=st->sy;
+ st->nt=(st->nt+1)%(st->ps*st->ts);
}
/* Adjust force fields */
- cnt=0;
- for (f=0;f<fs;f++) {
+ st->cnt=0;
+ for (st->f=0;st->f<fs;st->f++) {
- if (meters) { /* Remove meter from display */
- XSetForeground(dpy, draw_gc, bgcolor.pixel);
- stars_draw_meter(dpy,window,draw_gc,f);
+ if (st->meters) { /* Remove meter from display */
+ XSetForeground(st->dpy, st->draw_gc, st->bgcolor.pixel);
+ stars_draw_meter(st,st->f);
}
/* Adjust forcefield's parameter */
- acc[f]=stars_perturb(acc[f],0,0.98,0.009);
- var[f]=op[f]+(var[f]-op[f])*damp[f]+force[f]*acc[f];
-
- if (myrnd()>0.998) /* Turn it on/off ? */
- fon[f]=(myrnd()<0.0);
- /* fon[f]=!fon[f]; */
+ if (st->fon[st->f]) {
+ /* This configuration produces var[f]s usually below 0.01 */
+ st->acc[st->f]=stars_perturb(st->acc[st->f],0,0.98,0.005);
+ st->vel[st->f]=stars_perturb(st->vel[st->f]+0.03*st->acc[st->f],0,0.995,0.0);
+ st->var[st->f]=st->op[st->f]+(st->var[st->f]-st->op[st->f])*0.9995+0.001*st->vel[st->f];
+ }
+ /* fprintf(stderr,"f=%i fon=%i acc=%f vel=%f var=%f\n",f,fon[f],acc[f],vel[f],var[f]); */
+
+ /* Decide whether to turn this forcefield on or off. */
+ /* prob_on makes the "splitting" effects less likely than the rest */
+ #define prob_on ( st->f==8 || st->f==9 ? 0.999975 : 0.9999 )
+ if ( st->fon[st->f]==0 && myrnd()>prob_on ) {
+ turn_on_field(st, st->f);
+ } else if ( st->fon[st->f]!=0 && myrnd()>0.99 && fabs(st->var[st->f]-st->op[st->f])<0.0005 && fabs(st->vel[st->f])<0.005 /* && fabs(acc[f])<0.01 */ ) {
+ /* We only turn it off if it has gently returned to its optimal (as opposed to rapidly passing through it). */
+ st->fon[st->f] = 0;
+ }
- if (meters) { /* Redraw the meter */
- XSetForeground(dpy, draw_gc, color[f].pixel);
- stars_draw_meter(dpy,window,draw_gc,f);
+ if (st->meters) { /* Redraw the meter */
+ XSetForeground(st->dpy, st->draw_gc, st->color[st->f].pixel);
+ stars_draw_meter(st,st->f);
}
- if (fon[f])
- cnt++;
+ if (st->fon[st->f])
+ st->cnt++;
}
- if (cnt==0) { /* Ensure at least one is on! */
- f=random() % fs;
- fon[f]=1;
+
+ /* Ensure at least three forcefields are on.
+ * BUG: Picking randomly might not be enough since 0,11,12,14 and 15 do nothing!
+ * But then what's wrong with a rare gentle twinkle?!
+ */
+ if (st->cnt<3) {
+ st->f=random() % fs;
+ turn_on_field(st, st->f);
}
- if (meters) {
- XSetForeground(dpy, draw_gc, bgcolor.pixel);
- XDrawRectangle(dpy,window,draw_gc,0,0,lastresets*5,3);
- XSetForeground(dpy, draw_gc, default_fg_pixel);
- XDrawRectangle(dpy,window,draw_gc,0,0,resets*5,3);
+
+ if (st->meters) {
+ XSetForeground(st->dpy, st->draw_gc, st->bgcolor.pixel);
+ XDrawRectangle(st->dpy,st->window,st->draw_gc,0,0,st->lastresets*5,3);
+ XSetForeground(st->dpy, st->draw_gc, st->default_fg_pixel);
+ XDrawRectangle(st->dpy,st->window,st->draw_gc,0,0,st->resets*5,3);
}
-/* if (resets*5>scrwid) {
- Turn one off a field if too many points are flying off screen */
- /* This was a problem when one of the force-fields was acting wrong,
- but not really needed any more unless we need to debug new ones ...! */
- /* for (f=0;f<fs;f++)
- if (fon[f])
- printf("%i",f);
- printf("\n");
- XSync (dpy, False);
- screenhack_handle_events (dpy);
- sleep(1);
- do { // In fact this bit might go wrong if
- f=random() % fs; // we have not ensured at least one is on (above)
- } while (!fon[f]);
- fon[f]=0;
- } */
-
- XSync (dpy, False);
- screenhack_handle_events (dpy);
- }
+ /* Cap frames per second; do not go above specified fps: */
+ {
+ unsigned long this_delay = 0;
+ int maxfps = 200;
+ long utimeperframe = 1000000/maxfps;
+ struct timeval now;
+ long timediff;
+ gettimeofday(&now, NULL);
+ /* timediff = now.tv_sec*1000000 + now.tv_usec - st->lastframe.tv_sec*1000000 - st->lastframe.tv_usec; */
+ timediff = (now.tv_sec - st->lastframe.tv_sec) * 1000000 + now.tv_usec - st->lastframe.tv_usec;
+ if (timediff < utimeperframe) {
+ /* fprintf(stderr,"sleeping for %i\n",utimeperframe-timediff); */
+ this_delay = (utimeperframe-timediff);
+ }
+ st->lastframe = now;
+ return this_delay;
+ }
+}
+
+
+static void
+whirlwindwarp_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ struct state *st = (struct state *) closure;
+ st->scrwid = w;
+ st->scrhei = h;
}
+static Bool
+whirlwindwarp_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ return False;
+}
+
+static void
+whirlwindwarp_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ free (st);
+}
-char *progclass = "WhirlwindWarp";
-char *defaults [] = {
+static const char *whirlwindwarp_defaults [] = {
".background: black",
".foreground: white",
+ "*fpsSolid: true",
"*points: 400",
- "*tails: 10",
+ "*tails: 8",
"*meters: false",
+#ifdef USE_IPHONE
+ "*ignoreRotation: True",
+#endif
0
};
-XrmOptionDescRec options [] = {
+static XrmOptionDescRec whirlwindwarp_options [] = {
{ "-points", ".points", XrmoptionSepArg, 0 },
{ "-tails", ".tails", XrmoptionSepArg, 0 },
{ "-meters", ".meters", XrmoptionNoArg, "true" },
{ 0, 0, 0, 0 }
};
-void screenhack(Display *dpy, Window window) {
- if (init_whirlwindwarp(dpy, window))
- do_whirlwindwarp(dpy, window);
-}
+XSCREENSAVER_MODULE ("WhirlWindWarp", whirlwindwarp)