ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.22.tar.gz
[xscreensaver] / hacks / ifs.c
1 /*Copyright © Chris Le Sueur (thefishface@gmail.com) February 2005
2
3 Permission is hereby granted, free of charge, to any person obtaining
4 a copy of this software and associated documentation files (the
5 "Software"), to deal in the Software without restriction, including
6 without limitation the rights to use, copy, modify, merge, publish,
7 distribute, sublicense, and/or sell copies of the Software, and to
8 permit persons to whom the Software is furnished to do so, subject to
9 the following conditions:
10
11 The above copyright notice and this permission notice shall be included
12 in all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 OTHER DEALINGS IN THE SOFTWARE.
21
22 Ultimate thanks go to Massimino Pascal, who created the original
23 xscreensaver hack, and inspired me with it's swirly goodness. This
24 version adds things like variable quality, number of functions and also
25 a groovier colouring mode.
26
27 */
28
29 #include <assert.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <math.h>
34
35 #include "screenhack.h"
36
37 static float
38 myrandom(float up)
39 {
40         return(((float)random()/RAND_MAX)*up);
41 }
42
43 static int delay;
44 static int lensnum;
45 static int length;
46 static int mode;
47 static Bool notranslate, noscale, norotate;
48 static float ht,wt;
49 static float hs,ws;
50 static int width,height;
51 static long wcol;
52
53 static int count;
54
55 /*X stuff*/
56 static GC gc;
57 static Window w;
58 static Display *dpy;
59 static Pixmap backbuffer;
60 static XColor *colours;
61 static int ncolours;
62 static int screen_num;
63
64 static int blackColor, whiteColor;
65
66 char *progclass = "IFS";
67
68 char *defaults [] = {
69   ".lensnum:            3",
70   ".length:                     9",
71   ".mode:                       0",
72   ".colors:                     200",
73   "*delay:                      10000",
74   "*notranslate:        False",
75   "*noscale:            False",
76   "*norotate:           False",
77   0
78 };
79
80 XrmOptionDescRec options [] = {
81   { "-detail",          ".length",              XrmoptionSepArg, 0 },
82   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
83   { "-mode",            ".mode",                XrmoptionSepArg, 0 },
84   { "-colors",          ".colors",              XrmoptionSepArg, 0 },
85   { "-functions",       ".lensnum",     XrmoptionSepArg, 0 },
86   { "-notranslate",     ".notranslate", XrmoptionNoArg,  "True" },
87   { "-noscale",         ".noscale",             XrmoptionNoArg,  "True" },
88   { "-norotate",        ".norotate",    XrmoptionNoArg,  "True" },
89   { 0, 0, 0, 0 }
90 };
91
92 /*Takes the average of two colours, with some nifty bit-shifting*/
93 static long
94 blend(long c1, long c2)
95 {
96         long R1=(c1 & 0xFF0000) >> 16;
97         long R2=(c2 & 0xFF0000) >> 16;
98         long G1=(c1 & 0x00FF00) >> 8;
99         long G2=(c2 & 0x00FF00) >> 8;
100         long B1=(c1 & 0x0000FF);
101         long B2=(c2 & 0x0000FF);
102         
103         return (((R1+R2)/2 << 16) | ((G1+G2)/2 << 8) | ((B1+B2)/2));
104 }
105
106 /*Draw a point on the backbuffer*/
107 static void
108 sp(float x, float y, long c)
109 {
110         x+=16;    x*=wt;
111         y=16.5-y; y*=ht;
112         if(x<0 || x>=width || y<0 || y>=height) return;
113         XSetForeground(dpy, gc, c);
114         XDrawPoint(dpy, backbuffer, gc, (int)x, (int)y);
115 }
116
117 /*Copy backbuffer to front buffer and clear backbuffer*/
118 static void
119 draw(void)
120 {
121         XCopyArea( dpy,
122                    backbuffer, w,
123                    gc,
124                    0, 0,
125                    width, height,
126                    0, 0);
127         
128         XSetForeground(dpy, gc, blackColor);
129         XFillRectangle( dpy,
130                         backbuffer,
131                         gc,
132                         0, 0,
133                         width, height);
134 }
135
136 typedef struct {
137         float r,s,tx,ty;
138         /*Rotation, Scale, Translation X & Y*/
139         float ro,rt,rc;
140         /*Old Rotation, Rotation Target, Rotation Counter*/
141         float so,st,sc;
142         /*Old Scale, Scale Target, Scale Counter*/
143         float sa,txa,tya;
144         /*Scale change, Translation change*/
145         
146         int co;
147 } Lens; 
148
149
150 static void
151 CreateLens( float nr, 
152             float ns, 
153             float nx, 
154             float ny, 
155             int nco,
156             Lens *newlens)
157 {
158         newlens->sa=newlens->txa=newlens->tya=0;
159         if(!norotate) newlens->r=nr;
160         else newlens->r=0;
161         
162         if(!noscale) newlens->s=ns;
163         else newlens->s=0.5;
164         
165         if(!notranslate) {
166                 newlens->tx=nx;
167                 newlens->ty=ny;
168         } else {
169                 newlens->tx=nx;
170                 newlens->tx=ny;
171         }
172
173         newlens->rc=newlens->sc=1;
174         newlens->co=nco;
175 }
176         
177 static float
178 stepx(float x, float y, Lens *l)
179 {
180         return l->s*cos(l->r)*x+l->s*sin(l->r)*y+l->tx;
181 }
182 static float
183 stepy(float x, float y, Lens *l)
184 {
185         return l->s*sin(l->r)*-x+l->s*cos(l->r)*y+l->ty;
186 }
187 static void
188 mutate(Lens *l)
189 {
190         if(!norotate) {
191                 float factor;
192                 if(l->rc >= 1) {
193                         l->rc= 0;
194                         l->ro = l->rt;
195                         l->rt = myrandom(4)-2;
196                 }
197                 factor = (sin((-M_PI / 2.0) + M_PI * l->rc) + 1.0) / 2.0;
198                 l->r=l->ro + (l->rt - l->ro) * factor;
199                 l->rc+=0.01;
200                 
201         }
202         if(!noscale) {
203                 float factor;
204                 if(l->sc >= 1) {
205                         /*Reset counter, obtain new target value*/
206                         l->sc= 0;
207                         l->so = l->st;
208                         l->st = myrandom(2)-1;
209                 }
210                 factor = (sin((-M_PI / 2.0) + M_PI * l->sc) + 1.0) / 2.0;
211                 /* Take average of old target and new target, using factor to *
212                  * weight. It's computed sinusoidally, resulting in smooth,   *
213                  * rhythmic transitions.                                      */
214                 l->s=l->so + (l->st - l->so) * factor;
215                 l->sc+=0.01;
216         }
217         if(!notranslate) {
218                 l->txa+=myrandom(0.004)-0.002;
219                 l->tya+=myrandom(0.004)-0.002;
220                 l->tx+=l->txa;
221                 l->ty+=l->tya;
222                 if(l->tx>6)  l->txa-=0.004;
223                 if(l->ty>6)  l->tya-=0.004;
224                 if(l->tx<-6)  l->txa+=0.004;
225                 if(l->ty<-6)  l->tya+=0.004;
226                 if(l->txa>0.05 || l->txa<-0.05) l->txa/=1.7;
227                 if(l->tya>0.05 || l->tya<-0.05) l->tya/=1.7;
228         }
229         
230         /*Groovy, colour-shifting functions!*/
231         l->co++;
232         l->co %= ncolours;
233 }
234
235 Lens **lenses;
236
237 /* Calls itself <lensnum> times - with results from each lens/function.  *
238  * After <length> calls to itself, it stops iterating and draws a point. */
239 static void
240 iterate(float x, float y, long curcol, int length)
241 {
242         int i;
243         if(length == 0) {
244                 sp(x,y,curcol);
245         } else {
246                 for(i=0;i<lensnum;i++) {
247                         switch(mode) {
248                                 case 0 : iterate(stepx( x, y, lenses[i]), stepy( x, y, lenses[i]), blend( curcol,colours[(int)lenses[i]->co].pixel ), length-1); break;
249                                 case 1 : iterate(stepx( x, y, lenses[i]), stepy( x, y, lenses[i]), colours[(int)lenses[i]->co].pixel, length-1); break;
250                                 case 2 : iterate(stepx( x, y, lenses[i]), stepy( x, y, lenses[i]), curcol, length-1); break;
251                                 default: exit(0);
252                         }
253                 }
254         }
255         count++;
256 }
257
258 /* Come on and iterate, iterate, iterate and sing... *
259  * Yeah, this function just calls iterate, mutate,   *
260  * and then draws everything.                        */
261 static void
262 step(void)
263 {
264         int i;
265         if(mode == 2) {
266                 wcol++;
267                 wcol %= ncolours;
268                 iterate(0,0,colours[wcol].pixel,length);
269         } else {
270                 iterate(0,0,0xFFFFFF,length);
271         }
272         
273         
274         count=0;
275         
276         for(i=0;i<lensnum;i++) {
277                 mutate(lenses[i]);
278         }
279         draw();
280 }
281
282 static void
283 init_ifs(void)
284 {
285         Window rw;
286         int i;
287         XWindowAttributes xgwa;
288         
289         delay = get_integer_resource("delay", "Delay");
290         length = get_integer_resource("length", "Detail");
291         mode = get_integer_resource("mode", "Mode");
292
293         norotate    = get_boolean_resource("norotate", "NoRotate");
294         noscale     = get_boolean_resource("noscale", "NoScale");
295         notranslate = get_boolean_resource("notranslate", "NoTranslate");
296
297         lensnum = get_integer_resource("lensnum", "Functions");
298         
299         lenses = malloc(sizeof(Lens)*lensnum);
300         
301         for(i=0;i<lensnum;i++) {
302                 lenses[i]=malloc(sizeof(Lens));
303         }
304         
305         /*Thanks go to Dad for teaching me how to allocate memory for struct**s . */
306         
307         XGetWindowAttributes (dpy, w, &xgwa);
308         width=xgwa.width;
309         height=xgwa.height;
310         
311         /*Initialise all this X shizzle*/
312         blackColor = BlackPixel(dpy, DefaultScreen(dpy));
313         whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
314         rw = RootWindow(dpy, screen_num);
315         screen_num = DefaultScreen(dpy);
316         gc = XCreateGC(dpy, rw, 0, NULL);
317         
318         /* Do me some colourmap magic. If we're using blend mode, this is just   *
319          * for the nice colours - we're still using true/hicolour. Screw me if   *
320          * I'm going to work out how to blend with colourmaps - I'm too young to *
321          * die!! On a sidenote, this is mostly stolen from halftone because I    *
322          * don't really know what the hell I'm doing, here.                      */
323         ncolours = get_integer_resource("colors", "Colors");
324         if(ncolours < lensnum) ncolours=lensnum; /*apparently you're allowed to do this kind of thing...*/
325         colours = (XColor *)calloc(ncolours, sizeof(XColor));
326         make_smooth_colormap ( dpy,
327                                xgwa.visual,
328                                xgwa.colormap,
329                            colours,
330                            &ncolours,
331                            True, 0, False);
332         /*No, I didn't have a clue what that really did... hopefully I have some colours in an array, now.*/
333         wcol = (int)myrandom(ncolours);
334                 
335         /*Double buffering - I can't be bothered working out the XDBE thingy*/
336         backbuffer = XCreatePixmap(dpy, w, width, height, XDefaultDepth(dpy, screen_num));
337         
338         /*Scaling factor*/
339         wt=width/32;
340         ht=height/24;
341         
342         ws=400;
343         hs=400;
344
345         /*Colourmapped colours for the general prettiness*/
346         for(i=0;i<lensnum;i++) {
347                 CreateLens(     myrandom(1)-0.5,
348                             myrandom(1),
349                             myrandom(4)-2,
350                             myrandom(4)+2,
351                             myrandom(ncolours),
352                             lenses[i]);
353         }
354 }
355
356 void
357 screenhack (Display *display, Window window)
358 {
359         dpy = display;
360         w   = window;
361         
362         init_ifs();
363         
364         while (1) {
365                 step();
366                 screenhack_handle_events(dpy);
367                 if (delay) usleep(delay);
368         }
369 }