X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fifs.c;h=63cb1a8c7f9082fd106a6b2d2414abc4f84753cd;hb=6afd6db0ae9396cd7ff897ade597cd5483f49b0e;hp=60d4d9cc503093e329c0e884fd5ea1659fa57069;hpb=447db08c956099b3b183886729108bf5b364c4b8;p=xscreensaver diff --git a/hacks/ifs.c b/hacks/ifs.c index 60d4d9cc..63cb1a8c 100644 --- a/hacks/ifs.c +++ b/hacks/ifs.c @@ -1,4 +1,4 @@ -/*Copyright © Chris Le Sueur (thefishface@gmail.com) February 2005 +/* Copyright © Chris Le Sueur and Robby Griffin, 2005-2006 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -24,6 +24,9 @@ xscreensaver hack, and inspired me with it's swirly goodness. This version adds things like variable quality, number of functions and also a groovier colouring mode. +This version by Chris Le Sueur , Feb 2005 +Many improvements by Robby Griffin , Mar 2006 +Multi-coloured mode added by Jack Grahl , Jan 2007 */ #include @@ -34,315 +37,513 @@ a groovier colouring mode. #include "screenhack.h" -static float myrandom(float up) +#undef countof +#define countof(x) (sizeof((x)) / sizeof(*(x))) + +typedef struct { + float r, s, tx, ty; /* Rotation, Scale, Translation X & Y */ + float ro, rt, rc; /* Old Rotation, Rotation Target, Rotation Counter */ + float so, st, sc; /* Old Scale, Scale Target, Scale Counter */ + float sa, txa, tya; /* Scale change, Translation change */ + + int ua, ub, utx; /* Precomputed combined r,s,t values */ + int uc, ud, uty; /* Precomputed combined r,s,t values */ + +} Lens; + +struct state { + Display *dpy; + Window window; + GC gc; + Drawable backbuffer; + XColor *colours; + int ncolours; + int ccolour; + int blackColor, whiteColor; + + int width, widthb, height; + int width8, height8; + unsigned int *board; + XPoint pointbuf[1000]; + int npoints; + int xmin, xmax, ymin, ymax; + int x, y; + + int delay; + + int lensnum; + Lens *lenses; + int length; + int mode; + Bool recurse; + Bool multi; + Bool translate, scale, rotate; +}; + +#define getdot(x,y) (st->board[((y)*st->widthb)+((x)>>5)] & (1<<((x) & 31))) +#define setdot(x,y) (st->board[((y)*st->widthb)+((x)>>5)] |= (1<<((x) & 31))) + +static float +myrandom(float up) { - return(((float)random()/RAND_MAX)*up); + return (((float)random() / RAND_MAX) * up); } -static int delay; -static int lensnum; -static int length; -static int mode; -static Bool notranslate, noscale, norotate; -float ht,wt; -float hs,ws; -int width,height; -float r; -long wcol; -int coloffset; - -float nx,ny; - -int count; - -/*X stuff*/ -GC gc; -Window w; -Display *dpy; -Pixmap backbuffer; -XColor *colours; -int ncolours; -int screen_num; - -int blackColor, whiteColor; - -char *progclass = "IFS"; - -char *defaults [] = { - ".lensnum: 3", - ".length: 9", - ".mode: 0", - ".colors: 200", - "*delay: 10000", - "*notranslate: False", - "*noscale: False", - "*norotate: False", +static const char *ifs_defaults [] = { + ".background: Black", + "*lensnum: 3", + "*fpsSolid: true", + "*length: 9", + "*mode: 0", + "*colors: 200", + "*delay: 20000", + "*translate: True", + "*scale: True", + "*rotate: True", + "*recurse: False", + "*multi: True", +# ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */ + "*doubleBuffer: False", +#else + "*doubleBuffer: True", +#endif +#ifdef USE_IPHONE + "*ignoreRotation: True", +#endif 0 }; -XrmOptionDescRec options [] = { - { "-detail", ".length", XrmoptionSepArg, 0 }, - { "-delay", ".delay", XrmoptionSepArg, 0 }, - { "-mode", ".mode", XrmoptionSepArg, 0 }, - { "-colors", ".colors", XrmoptionSepArg, 0 }, - { "-functions", ".lensnum", XrmoptionSepArg, 0 }, - { "-notranslate", ".notranslate", XrmoptionNoArg, "True" }, - { "-noscale", ".noscale", XrmoptionNoArg, "True" }, - { "-norotate", ".norotate", XrmoptionNoArg, "True" }, +static XrmOptionDescRec ifs_options [] = { + { "-detail", ".length", XrmoptionSepArg, 0 }, + { "-delay", ".delay", XrmoptionSepArg, 0 }, + { "-mode", ".mode", XrmoptionSepArg, 0 }, + { "-colors", ".colors", XrmoptionSepArg, 0 }, + { "-functions", ".lensnum", XrmoptionSepArg, 0 }, + { "-no-translate", ".translate", XrmoptionNoArg, "False" }, + { "-no-scale", ".scale", XrmoptionNoArg, "False" }, + { "-no-rotate", ".rotate", XrmoptionNoArg, "False" }, + { "-recurse", ".recurse", XrmoptionNoArg, "True" }, + { "-iterate", ".recurse", XrmoptionNoArg, "False" }, + { "-multi", ".multi", XrmoptionNoArg, "True" }, + { "-no-multi", ".multi", XrmoptionNoArg, "False" }, + { "-db", ".doubleBuffer",XrmoptionNoArg, "True" }, + { "-no-db", ".doubleBuffer",XrmoptionNoArg, "False" }, { 0, 0, 0, 0 } }; -/*Takes the average of two colours, with some nifty bit-shifting*/ -static long blend(long c1, long c2) -{ - long R1=(c1 & 0xFF0000) >> 16; - long R2=(c2 & 0xFF0000) >> 16; - long G1=(c1 & 0x00FF00) >> 8; - long G2=(c2 & 0x00FF00) >> 8; - long B1=(c1 & 0x0000FF); - long B2=(c2 & 0x0000FF); - - return (((R1+R2)/2 << 16) | ((G1+G2)/2 << 8) | ((B1+B2)/2)); -} -/*Draw a point on the backbuffer*/ -static void sp(float x, float y, long c) +/* Draw all the queued points on the backbuffer */ +static void +drawpoints(struct state *st) { - x+=16; x*=wt; - y=16.5-y; y*=ht; - if(x<0 || x>=width || y<0 || y>=height) return; - XSetForeground(dpy, gc, c); - XDrawPoint(dpy, backbuffer, gc, (int)x, (int)y); + XDrawPoints(st->dpy, st->backbuffer, st->gc, st->pointbuf, st->npoints, + CoordModeOrigin); + st->npoints = 0; } -/*Copy backbuffer to front buffer and clear backbuffer*/ -static void draw(void) +/* Set a point to be drawn, if it hasn't been already. + * Expects coordinates in 256ths of a pixel. */ +static void +sp(struct state *st, int x, int y) { - XCopyArea( dpy, - backbuffer, w, - gc, - 0, 0, - width, height, - 0, 0); - - XSetForeground(dpy, gc, blackColor); - XFillRectangle( dpy, - backbuffer, - gc, - 0, 0, - width, height); -} + if (x < 0 || x >= st->width8 || y < 0 || y >= st->height8) + return; -typedef struct { - float r,s,tx,ty; - /*Rotation, Scale, Translation X & Y*/ - float ra,raa,sa,txa,tya; - - int co; -} Lens; + x >>= 8; + y >>= 8; + if (getdot(x, y)) return; + setdot(x, y); -static void CreateLens(float nr, - float ns, - float nx, - float ny, - int nco, - Lens *newlens) -{ - newlens->ra=newlens->raa=newlens->sa=newlens->txa=newlens->tya=0; - if(!norotate) newlens->r=nr; - else newlens->r=0; - - if(!noscale) newlens->s=ns; - else newlens->s=0.5; - - if(!notranslate) { - newlens->tx=nx; - newlens->ty=ny; - } else { - newlens->tx=nx; - newlens->tx=ny; + if (x < st->xmin) st->xmin = x; + if (x > st->xmax) st->xmax = x; + if (y < st->ymin) st->ymin = y; + if (y > st->ymax) st->ymax = y; + + st->pointbuf[st->npoints].x = x; + st->pointbuf[st->npoints].y = y; + st->npoints++; + + if (st->npoints >= countof(st->pointbuf)) { + drawpoints(st); } - - newlens->co=nco; } - -static float stepx(float x, float y, Lens *l) + + +/* Precompute integer values for matrix multiplication and vector + * addition. The matrix multiplication will go like this (see iterate()): + * |x2| |ua ub| |x| |utx| + * | | = | | * | | + | | + * |y2| |uc ud| |y| |uty| + * + * There is an extra factor of 2^10 in these values, and an extra factor of + * 2^8 in the coordinates, in order to implement fixed-point arithmetic. + */ +static void +lensmatrix(struct state *st, Lens *l) { - return l->s*cos(l->r)*x+l->s*sin(l->r)*y+l->tx; + l->ua = 1024.0 * l->s * cos(l->r); + l->ub = -1024.0 * l->s * sin(l->r); + l->uc = -l->ub; + l->ud = l->ua; + l->utx = 131072.0 * st->width * (l->s * (sin(l->r) - cos(l->r)) + + l->tx / 16 + 1); + l->uty = -131072.0 * st->height * (l->s * (sin(l->r) + cos(l->r)) + + l->ty / 16 - 1); } -static float stepy(float x, float y, Lens *l) +static void +CreateLens(struct state *st, + float nr, + float ns, + float nx, + float ny, + Lens *newlens) { - return l->s*sin(l->r)*-x+l->s*cos(l->r)*y+l->ty; -} + newlens->sa = newlens->txa = newlens->tya = 0; + if (st->rotate) { + newlens->r = newlens->ro = newlens->rt = nr; + newlens->rc = 1; + } + else newlens->r = 0; + + if (st->scale) { + newlens->s = newlens->so = newlens->st = ns; + newlens->sc = 1; + } + else newlens->s = 0.5; -static void mutate(Lens *l) + newlens->tx = nx; + newlens->ty = ny; + + lensmatrix(st, newlens); +} + +static void +mutate(struct state *st, Lens *l) { - if(!norotate) { - l->raa+=myrandom(0.002)-0.001; - l->ra+=l->raa; - l->r +=l->ra; - if(l->ra>0.07 || l->ra<-0.07) l->ra/=1.4; - if(l->raa>0.005 || l->raa<-0.005) l->raa/=1.2; + if (st->rotate) { + float factor; + if(l->rc >= 1) { + l->rc = 0; + l->ro = l->rt; + l->rt = myrandom(4) - 2; + } + factor = (sin((-M_PI / 2.0) + M_PI * l->rc) + 1.0) / 2.0; + l->r = l->ro + (l->rt - l->ro) * factor; + l->rc += 0.01; } - if(!noscale) { - l->sa+=myrandom(0.01)-0.005; - l->s +=l->sa; - if(l->s>0.4) l->sa -=0.004; - if(l->s<-0.4) l->sa +=0.004; - if(l->sa>0.07 || l->sa<-0.07) l->sa/=1.4; + if (st->scale) { + float factor; + if (l->sc >= 1) { + /* Reset counter, obtain new target value */ + l->sc = 0; + l->so = l->st; + l->st = myrandom(2) - 1; + } + factor = (sin((-M_PI / 2.0) + M_PI * l->sc) + 1.0) / 2.0; + /* Take average of old target and new target, using factor to * + * weight. It's computed sinusoidally, resulting in smooth, * + * rhythmic transitions. */ + l->s = l->so + (l->st - l->so) * factor; + l->sc += 0.01; } - if(!notranslate) { - l->txa+=myrandom(0.004)-0.002; - l->tya+=myrandom(0.004)-0.002; - l->tx+=l->txa; - l->ty+=l->tya; - if(l->tx>6) l->txa-=0.004; - if(l->ty>6) l->tya-=0.004; - if(l->tx<-6) l->txa+=0.004; - if(l->ty<-6) l->tya+=0.004; - if(l->txa>0.05 || l->txa<-0.05) l->txa/=1.7; - if(l->tya>0.05 || l->tya<-0.05) l->tya/=1.7; + if (st->translate) { + l->txa += myrandom(0.004) - 0.002; + l->tya += myrandom(0.004) - 0.002; + l->tx += l->txa; + l->ty += l->tya; + if (l->tx > 6) l->txa -= 0.004; + if (l->ty > 6) l->tya -= 0.004; + if (l->tx < -6) l->txa += 0.004; + if (l->ty < -6) l->tya += 0.004; + if (l->txa > 0.05 || l->txa < -0.05) l->txa /= 1.7; + if (l->tya > 0.05 || l->tya < -0.05) l->tya /= 1.7; + } + if (st->rotate || st->scale || st->translate) { + lensmatrix(st, l); } - - /*Groovy, colour-shifting functions!*/ - l->co++; - l->co %= ncolours; } -Lens **lenses; + +#define STEPX(l,x,y) (((l)->ua * (x) + (l)->ub * (y) + (l)->utx) >> 10) +#define STEPY(l,x,y) (((l)->uc * (x) + (l)->ud * (y) + (l)->uty) >> 10) +/*#define STEPY(l,x,y) (((l)->ua * (y) - (l)->ub * (x) + (l)->uty) >> 10)*/ /* Calls itself times - with results from each lens/function. * * After calls to itself, it stops iterating and draws a point. */ -static void iterate(float x, float y, long curcol, int length) +static void +recurse(struct state *st, int x, int y, int length, int p) { int i; - if(length == 0) { - sp(x,y,curcol); - } else { - /*iterate(lenses[0].stepx(x,y),lenses[0].stepy(x,y),length-1); - iterate(lenses[1].stepx(x,y),lenses[1].stepy(x,y),length-1); - iterate(lenses[2].stepx(x,y),lenses[2].stepy(x,y),length-1);*/ - for(i=0;ico].pixel ), length-1); break; - case 1 : iterate(stepx( x, y, lenses[i]), stepy( x, y, lenses[i]), colours[(int)lenses[i]->co].pixel, length-1); break; - case 2 : iterate(stepx( x, y, lenses[i]), stepy( x, y, lenses[i]), curcol, length-1); break; - default: exit(0); - } + Lens *l; + + if (length == 0) { + if (p == 0) + sp(st, x, y); + else { + l = &st->lenses[p]; + sp(st, STEPX(l, x, y), STEPY(l, x, y)); + } + } + else { + for (i = 0; i < st->lensnum; i++) { + l = &st->lenses[i]; + recurse(st, STEPX(l, x, y), STEPY(l, x, y), length - 1, p); } } - count++; +} + +/* Performs random lens transformations, drawing a point at each + * iteration after the first 10. + */ +static void +iterate(struct state *st, int count, int p) +{ + int i; + Lens *l; + int x = st->x; + int y = st->y; + int tx; + +# define STEP() \ + l = &st->lenses[random() % st->lensnum]; \ + tx = STEPX(l, x, y); \ + y = STEPY(l, x, y); \ + x = tx + + for (i = 0; i < 10; i++) { + STEP(); + } + + for ( ; i < count; i++) { + STEP(); + if (p == 0) + sp(st, x, y); + else + { + l = &st->lenses[p]; + sp(st, STEPX(l, x, y), STEPY(l, x, y)); + } + } + +# undef STEP + + st->x = x; + st->y = y; } /* Come on and iterate, iterate, iterate and sing... * * Yeah, this function just calls iterate, mutate, * * and then draws everything. */ -static void step(void) +static unsigned long +ifs_draw (Display *dpy, Window window, void *closure) { + struct state *st = (struct state *) closure; int i; - if(mode == 2) { - wcol++; - wcol %= ncolours; - iterate(0,0,colours[wcol].pixel,length); - } else { - iterate(0,0,0xFFFFFF,length); - } + int xmin = st->xmin, xmax = st->xmax, ymin = st->ymin, ymax = st->ymax; + int partcolor, x, y; + + /* erase whatever was drawn in the previous frame */ + if (xmin <= xmax && ymin <= ymax) { + XSetForeground(st->dpy, st->gc, st->blackColor); + XFillRectangle(st->dpy, st->backbuffer, st->gc, + xmin, ymin, + xmax - xmin + 1, ymax - ymin + 1); + st->xmin = st->width + 1; + st->xmax = st->ymax = -1; + st->ymin = st->height + 1; + } + + st->ccolour++; + st->ccolour %= st->ncolours; + + /* calculate and draw points for this frame */ + x = st->width << 7; + y = st->height << 7; - count=0; + if (st->multi) { + for (i = 0; i < st->lensnum; i++) { + partcolor = st->ccolour * (i+1); + partcolor %= st->ncolours; + XSetForeground(st->dpy, st->gc, st->colours[partcolor].pixel); + memset(st->board, 0, st->widthb * st->height * sizeof(*st->board)); + if (st->recurse) + recurse(st, x, y, st->length - 1, i); + else + iterate(st, pow(st->lensnum, st->length - 1), i); + if (st->npoints) + drawpoints(st); + } + } + else { + + XSetForeground(st->dpy, st->gc, st->colours[st->ccolour].pixel); + memset(st->board, 0, st->widthb * st->height * sizeof(*st->board)); + if (st->recurse) + recurse(st, x, y, st->length, 0); + else + iterate(st, pow(st->lensnum, st->length), 0); + if (st->npoints) + drawpoints(st); + } - for(i=0;ibackbuffer != st->window + && ((st->xmin <= st->xmax && st->ymin <= st->ymax) + || (xmin <= xmax && ymin <= ymax))) { + if (st->xmin < xmin) xmin = st->xmin; + if (st->xmax > xmax) xmax = st->xmax; + if (st->ymin < ymin) ymin = st->ymin; + if (st->ymax > ymax) ymax = st->ymax; + XCopyArea(st->dpy, st->backbuffer, st->window, st->gc, + xmin, ymin, + xmax - xmin + 1, ymax - ymin + 1, + xmin, ymin); + } + + for(i = 0; i < st->lensnum; i++) { + mutate(st, &st->lenses[i]); } - draw(); + + return st->delay; } -static void init_ifs(void) +static void +ifs_reshape (Display *, Window, void *, unsigned int, unsigned int); + +static void * +ifs_init (Display *d_arg, Window w_arg) { - Window rw; + struct state *st = (struct state *) calloc (1, sizeof(*st)); int i; XWindowAttributes xgwa; - - delay = get_integer_resource("delay", "Delay"); - length = get_integer_resource("length", "Detail"); - mode = get_integer_resource("mode", "Mode"); + + /* Initialise all this X shizzle */ + st->dpy = d_arg; + st->window = w_arg; + + st->blackColor = BlackPixel(st->dpy, DefaultScreen(st->dpy)); + st->whiteColor = WhitePixel(st->dpy, DefaultScreen(st->dpy)); + st->gc = XCreateGC(st->dpy, st->window, 0, NULL); + + XGetWindowAttributes (st->dpy, st->window, &xgwa); + ifs_reshape(st->dpy, st->window, st, xgwa.width, xgwa.height); + + st->ncolours = get_integer_resource(st->dpy, "colors", "Colors"); + if (st->ncolours < st->lensnum) + st->ncolours = st->lensnum; + if (st->colours) free(st->colours); + st->colours = (XColor *)calloc(st->ncolours, sizeof(XColor)); + if (!st->colours) exit(1); + make_smooth_colormap (xgwa.screen, xgwa.visual, xgwa.colormap, + st->colours, &st->ncolours, + True, 0, False); + + /* Initialize IFS data */ + + st->delay = get_integer_resource(st->dpy, "delay", "Delay"); + st->length = get_integer_resource(st->dpy, "length", "Detail"); + if (st->length < 0) st->length = 0; + st->mode = get_integer_resource(st->dpy, "mode", "Mode"); + + st->rotate = get_boolean_resource(st->dpy, "rotate", "Boolean"); + st->scale = get_boolean_resource(st->dpy, "scale", "Boolean"); + st->translate = get_boolean_resource(st->dpy, "translate", "Boolean"); + st->recurse = get_boolean_resource(st->dpy, "recurse", "Boolean"); + st->multi = get_boolean_resource(st->dpy, "multi", "Boolean"); + + st->lensnum = get_integer_resource(st->dpy, "lensnum", "Functions"); + if (st->lenses) free (st->lenses); + st->lenses = (Lens *)calloc(st->lensnum, sizeof(Lens)); + if (!st->lenses) exit(1); + for (i = 0; i < st->lensnum; i++) { + CreateLens(st, + myrandom(1)-0.5, + myrandom(1), + myrandom(4)-2, + myrandom(4)+2, + &st->lenses[i]); + } - norotate = get_boolean_resource("norotate", "NoRotate"); - noscale = get_boolean_resource("noscale", "NoScale"); - notranslate = get_boolean_resource("notranslate", "NoTranslate"); + return st; +} - lensnum = get_integer_resource("lensnum", "Functions"); - - lenses = malloc(sizeof(Lens)*lensnum); - - for(i=0;idpy, st->window, &xgwa); + + st->width = xgwa.width; + st->widthb = ((xgwa.width + 31) >> 5); + st->height = xgwa.height; + st->width8 = xgwa.width << 8; + st->height8 = xgwa.height << 8; + + if (!st->xmax && !st->ymax && !st->xmin && !st->ymin) { + st->xmin = xgwa.width + 1; + st->xmax = st->ymax = -1; + st->ymin = xgwa.height + 1; } - - /*Thanks go to Dad for teaching me how to allocate memory for struct**s . */ - - XGetWindowAttributes (dpy, w, &xgwa); - width=xgwa.width; - height=xgwa.height; - - /*Initialise all this X shizzle*/ - blackColor = BlackPixel(dpy, DefaultScreen(dpy)); - whiteColor = WhitePixel(dpy, DefaultScreen(dpy)); - rw = RootWindow(dpy, screen_num); - screen_num = DefaultScreen(dpy); - gc = XCreateGC(dpy, rw, 0, NULL); - - /* Do me some colourmap magic. If we're using blend mode, this is just * - * for the nice colours - we're still using true/hicolour. Screw me if * - * I'm going to work out how to blend with colourmaps - I'm too young to * - * die!! On a sidenote, this is mostly stolen from halftone because I * - * don't really know what the hell I'm doing, here. */ - ncolours = get_integer_resource("colors", "Colors"); - if(ncolours < lensnum) ncolours=lensnum; /*apparently you're allowed to do this kind of thing...*/ - colours = (XColor *)calloc(ncolours, sizeof(XColor)); - make_smooth_colormap ( dpy, - xgwa.visual, - xgwa.colormap, - colours, - &ncolours, - True, 0, False); - /*No, I didn't have a clue what that really did... hopefully I have some colours in an array, now.*/ - wcol = (int)myrandom(ncolours); - - /*Double buffering - I can't be bothered working out the XDBE thingy*/ - backbuffer = XCreatePixmap(dpy, w, width, height, XDefaultDepth(dpy, screen_num)); - - /*Scaling factor*/ - wt=width/32; - ht=height/24; - - ws=400; - hs=400; - - /*Colourmapped colours for the general prettiness*/ - for(i=0;ibackbuffer != None && st->backbuffer != st->window) { + XFreePixmap(st->dpy, st->backbuffer); + st->backbuffer = None; + } + + if (get_boolean_resource (st->dpy, "doubleBuffer", "Boolean")) { + st->backbuffer = XCreatePixmap(st->dpy, st->window, st->width, st->height, xgwa.depth); + XSetForeground(st->dpy, st->gc, st->blackColor); + XFillRectangle(st->dpy, st->backbuffer, st->gc, + 0, 0, st->width, st->height); + } else { + st->backbuffer = st->window; + XClearWindow(st->dpy, st->window); } + + if (st->board) free(st->board); + st->board = (unsigned int *)calloc(st->widthb * st->height, sizeof(unsigned int)); + if (!st->board) exit(1); } -void -screenhack (Display *display, Window window) +static Bool +ifs_event (Display *dpy, Window window, void *closure, XEvent *event) { - dpy = display; - w = window; - - init_ifs(); - - while (1) { - step(); - screenhack_handle_events(dpy); - if (delay) usleep(delay); - } + struct state *st = (struct state *)closure; + if (screenhack_event_helper (dpy, window, event)) + { + int i; + for (i = 0; i < st->lensnum; i++) { + CreateLens(st, + myrandom(1)-0.5, + myrandom(1), + myrandom(4)-2, + myrandom(4)+2, + &st->lenses[i]); + } + return True; + } + return False; } + +static void +ifs_free (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + + if (st->board) free(st->board); + if (st->lenses) free(st->lenses); + if (st->colours) free(st->colours); + if (st->backbuffer != None && st->backbuffer != st->window) + XFreePixmap(st->dpy, st->backbuffer); + free(st); +} + +XSCREENSAVER_MODULE ("IFS", ifs)