ftp://ftp.swin.edu.au/slackware/slackware-9.1/source/xap/xscreensaver/xscreensaver...
[xscreensaver] / hacks / interference.c
1 /* interference.c --- colored fields via decaying sinusoidal waves.
2  * An entry for the RHAD Labs Screensaver Contest.
3  *
4  * Author: Hannu Mallat <hmallat@cs.hut.fi>
5  *
6  * Copyright (C) 1998 Hannu Mallat.
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation.  No representations are made about the suitability of this
13  * software for any purpose.  It is provided "as is" without express or 
14  * implied warranty.
15  *
16  * decaying sinusoidal waves, which extend spherically from their
17  * respective origins, move around the plane. a sort of interference
18  * between them is calculated and the resulting twodimensional wave
19  * height map is plotted in a grid, using softly changing colours.
20  *
21  * not physically (or in any sense) accurate, but fun to look at for 
22  * a while. you may tune the speed/resolution/interestingness tradeoff 
23  * with X resources, see below.
24  *
25  * Created      : Wed Apr 22 09:30:30 1998, hmallat
26  * Last modified: Wed Apr 22 09:30:30 1998, hmallat
27  * Last modified: Sun Aug 31 23:40:14 2003, 
28  *              david slimp <rock808@DavidSlimp.com>
29  *              added -hue option to specify base color hue
30  *
31  * TODO:
32  *
33  *    This really needs to be sped up.
34  *
35  *    I've tried making it use XPutPixel/XPutImage instead of XFillRectangle,
36  *    but that doesn't seem to help (it's the same speed at gridsize=1, and
37  *    it actually makes it slower at larger sizes.)
38  *
39  *    I played around with shared memory, but clearly I still don't know how
40  *    to use the XSHM extension properly, because it doesn't work yet.
41  *
42  *    Hannu had put in code to use the double-buffering extension, but that
43  *    code didn't work for me on Irix.  I don't think it would help anyway,
44  *    since it's not the swapping of frames that is the bottleneck (or a source
45  *    of visible flicker.)
46  *
47  *     -- jwz, 4-Jun-98
48  */
49
50 #include <math.h>
51
52 #include "screenhack.h"
53
54 # include <X11/Xutil.h>
55
56 /* I thought it would be faster this way, but it turns out not to be... -jwz */
57 #undef USE_XIMAGE
58
59 #ifndef USE_XIMAGE
60 # undef HAVE_XSHM_EXTENSION  /* only applicable when using XImages */
61 #endif /* USE_XIMAGE */
62
63
64 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
65 # include "xdbe.h"
66 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
67
68 #ifdef HAVE_XSHM_EXTENSION
69 # include "xshm.h"
70 #endif /* HAVE_XSHM_EXTENSION */
71
72 char *progclass="Interference";
73
74 char *defaults [] = {
75   ".background:  black",
76   ".foreground:  white",
77   "*count:       3",     /* number of waves */
78   "*gridsize:    4",     /* pixel size, smaller values for better resolution */
79   "*ncolors:     128",   /* number of colours used */
80   "*hue:         0",     /* hue to use for base color (0-360) */
81   "*speed:       30",    /* speed of wave origins moving around */
82   "*delay:       30000", /* or something */
83   "*color-shift: 60",    /* h in hsv space, smaller values for smaller
84                           * color gradients */
85   "*radius:      800",   /* wave extent */
86   "*gray:        false", /* color or grayscale */
87   "*mono:        false", /* monochrome, not very much fun */
88
89   "*doubleBuffer: True",
90 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
91   "*useDBE:      True", /* use double buffering extension */
92 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
93
94 #ifdef HAVE_XSHM_EXTENSION
95   "*useSHM:      True", /* use shared memory extension */
96 #endif /*  HAVE_XSHM_EXTENSION */
97   0
98 };
99
100 XrmOptionDescRec options [] = {
101   { "-count",       ".count",       XrmoptionSepArg, 0 }, 
102   { "-ncolors",     ".ncolors",     XrmoptionSepArg, 0 }, 
103   { "-gridsize",    ".gridsize",    XrmoptionSepArg, 0 }, 
104   { "-hue",         ".hue",         XrmoptionSepArg, 0 },
105   { "-speed",       ".speed",       XrmoptionSepArg, 0 },
106   { "-delay",       ".delay",       XrmoptionSepArg, 0 },
107   { "-color-shift", ".color-shift", XrmoptionSepArg, 0 },
108   { "-radius",      ".radius",      XrmoptionSepArg, 0 },
109   { "-gray",        ".gray",        XrmoptionNoArg,  "True" },
110   { "-mono",        ".mono",        XrmoptionNoArg,  "True" },
111   { "-db",          ".doubleBuffer", XrmoptionNoArg,  "True" },
112   { "-no-db",       ".doubleBuffer", XrmoptionNoArg,  "False" },
113 #ifdef HAVE_XSHM_EXTENSION
114   { "-shm",     ".useSHM",      XrmoptionNoArg, "True" },
115   { "-no-shm",  ".useSHM",      XrmoptionNoArg, "False" },
116 #endif /*  HAVE_XSHM_EXTENSION */
117   { 0, 0, 0, 0 }
118 };
119
120 int options_size = (sizeof (options) / sizeof (XrmOptionDescRec));
121
122 struct inter_source {
123   int x; 
124   int y;
125   double x_theta;
126   double y_theta;
127 };
128
129 struct inter_context {
130   /*
131    * Display-related entries 
132    */
133   Display* dpy;
134   Window   win;
135
136 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
137   XdbeBackBuffer back_buf;
138 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
139   Pixmap   pix_buf;
140
141   GC       copy_gc;
142 #ifdef USE_XIMAGE
143   XImage  *ximage;
144 #endif /* USE_XIMAGE */
145
146 #ifdef HAVE_XSHM_EXTENSION
147   Bool use_shm;
148   XShmSegmentInfo shm_info;
149 #endif /* HAVE_XSHM_EXTENSION */
150
151   /*
152    * Resources
153    */
154   int count;
155   int grid_size;
156   int colors;
157   float hue;
158   int speed;
159   int delay;
160   int shift;
161   int radius;
162
163   /*
164    * Drawing-related entries
165    */
166   int w;
167   int h;
168   Colormap cmap;
169   XColor* pal;
170   GC* gcs;
171
172   /*
173    * lookup tables
174    */
175   int* wave_height;
176     
177   /*
178    * Interference sources
179    */
180   struct inter_source* source;
181 };
182
183 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
184 # define TARGET(c) ((c)->back_buf ? (c)->back_buf : \
185                     (c)->pix_buf ? (c)->pix_buf : (c)->win)
186 #else  /* HAVE_DOUBLE_BUFFER_EXTENSION */
187 # define TARGET(c) ((c)->pix_buf ? (c)->pix_buf : (c)->win)
188 #endif /* !HAVE_DOUBLE_BUFFER_EXTENSION */
189
190 void inter_init(Display* dpy, Window win, struct inter_context* c) 
191 {
192   XWindowAttributes xgwa;
193   double H[3], S[3], V[3];
194   int i;
195   int mono;
196   int gray;
197   XGCValues val;
198   unsigned long valmask = 0;
199   Bool dbuf = get_boolean_resource ("doubleBuffer", "Boolean");
200
201   memset (c, 0, sizeof(*c));
202
203   c->dpy = dpy;
204   c->win = win;
205
206   XGetWindowAttributes(c->dpy, c->win, &xgwa);
207   c->w = xgwa.width;
208   c->h = xgwa.height;
209   c->cmap = xgwa.colormap;
210
211 #ifdef HAVE_XSHM_EXTENSION
212   c->use_shm = get_boolean_resource("useSHM", "Boolean");
213 #endif /*  HAVE_XSHM_EXTENSION */
214
215   if (dbuf)
216     {
217 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
218       c->back_buf = xdbe_get_backbuffer (c->dpy, c->win, XdbeUndefined);
219 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
220
221 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
222       if (!c->back_buf)
223 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
224         c->pix_buf = XCreatePixmap (dpy, win, xgwa.width, xgwa.height,
225                                     xgwa.depth);
226     }
227
228   val.function = GXcopy;
229   c->copy_gc = XCreateGC(c->dpy, TARGET(c), GCFunction, &val);
230
231   c->count = get_integer_resource("count", "Integer");
232   if(c->count < 1)
233     c->count = 1;
234   c->grid_size = get_integer_resource("gridsize", "Integer");
235   if(c->grid_size < 1)
236     c->grid_size = 1;
237   mono = get_boolean_resource("mono", "Boolean");
238   if(!mono) {
239     c->colors = get_integer_resource("ncolors", "Integer");
240     if(c->colors < 2)
241       c->colors = 2;
242   }
243   c->hue = get_integer_resource("hue", "Float");
244   while (c->hue < 0 || c->hue >= 360)
245     c->hue = frand(360.0);
246   c->speed = get_integer_resource("speed", "Integer");
247   c->shift = get_float_resource("color-shift", "Float");
248   while(c->shift >= 360.0)
249     c->shift -= 360.0;
250   while(c->shift <= -360.0)
251     c->shift += 360.0;
252   c->radius = get_integer_resource("radius", "Integer");;
253   if(c->radius < 1)
254     c->radius = 1;
255
256 #ifdef USE_XIMAGE
257
258   c->ximage = 0;
259
260 # ifdef HAVE_XSHM_EXTENSION
261   if (c->use_shm)
262     {
263       c->ximage = create_xshm_image(dpy, xgwa.visual, xgwa.depth,
264                                     ZPixmap, 0, &c->shm_info,
265                                     xgwa.width, c->grid_size);
266       if (!c->ximage)
267         c->use_shm = False;
268     }
269 # endif /* HAVE_XSHM_EXTENSION */
270
271   if (!c->ximage)
272     {
273       c->ximage =
274         XCreateImage (dpy, xgwa.visual,
275                       xgwa.depth, ZPixmap, 0, 0, /* depth, fmt, offset, data */
276                       xgwa.width, c->grid_size,  /* width, height */
277                       8, 0);                     /* pad, bpl */
278       c->ximage->data = (unsigned char *)
279         calloc(c->ximage->height, c->ximage->bytes_per_line);
280     }
281 #endif /* USE_XIMAGE */
282
283   if(!mono) {
284     c->pal = calloc(c->colors, sizeof(XColor));
285
286     gray = get_boolean_resource("gray", "Boolean");
287     if(!gray) {
288       H[0] = c->hue;
289       H[1] = H[0] + c->shift < 360.0 ? H[0]+c->shift : H[0] + c->shift-360.0; 
290       H[2] = H[1] + c->shift < 360.0 ? H[1]+c->shift : H[1] + c->shift-360.0; 
291       S[0] = S[1] = S[2] = 1.0;
292       V[0] = V[1] = V[2] = 1.0;
293     } else {
294       H[0] = H[1] = H[2] = 0.0;
295       S[0] = S[1] = S[2] = 0.0;
296       V[0] = 1.0; V[1] = 0.5; V[2] = 0.0;
297     }
298
299     make_color_loop(c->dpy, c->cmap, 
300                     H[0], S[0], V[0], 
301                     H[1], S[1], V[1], 
302                     H[2], S[2], V[2], 
303                     c->pal, &(c->colors), 
304                     True, False);
305     if(c->colors < 2) { /* color allocation failure */
306       mono = 1;
307       free(c->pal);
308     }
309   }
310
311   if(mono) { /* DON'T else this with the previous if! */
312     c->colors = 2;
313     c->pal = calloc(2, sizeof(XColor));
314     c->pal[0].pixel = BlackPixel(c->dpy, DefaultScreen(c->dpy));
315     c->pal[1].pixel = WhitePixel(c->dpy, DefaultScreen(c->dpy));
316   }    
317
318   valmask = GCForeground;
319   c->gcs = calloc(c->colors, sizeof(GC));
320   for(i = 0; i < c->colors; i++) {
321     val.foreground = c->pal[i].pixel;    
322     c->gcs[i] = XCreateGC(c->dpy, TARGET(c), valmask, &val);
323   }
324
325   c->wave_height = calloc(c->radius, sizeof(int));
326   for(i = 0; i < c->radius; i++) {
327     float max = 
328       ((float)c->colors) * 
329       ((float)c->radius - (float)i) /
330       ((float)c->radius);
331     c->wave_height[i] = 
332       (int)
333       ((max + max*cos((double)i/50.0)) / 2.0);
334   }
335
336   c->source = calloc(c->count, sizeof(struct inter_source));
337   for(i = 0; i < c->count; i++) {
338     c->source[i].x_theta = frand(2.0)*3.14159;
339     c->source[i].y_theta = frand(2.0)*3.14159;
340   }
341
342 }
343
344 #define source_x(c, i) \
345   (c->w/2 + ((int)(cos(c->source[i].x_theta)*((float)c->w/2.0))))
346 #define source_y(c, i) \
347   (c->h/2 + ((int)(cos(c->source[i].y_theta)*((float)c->h/2.0))))
348
349 /*
350  * this is rather suboptimal. the sqrt() doesn't seem to be a big
351  * performance hit, but all those separate XFillRectangle()'s are.
352  * hell, it's just a quick hack anyway -- if someone wishes to tune
353  * it, go ahead! 
354  */
355
356 void do_inter(struct inter_context* c) 
357 {
358   int i, j, k;
359   int result;
360   int dist;
361   int g;
362
363   int dx, dy;
364
365   for(i = 0; i < c->count; i++) {
366     c->source[i].x_theta += (c->speed/1000.0);
367     if(c->source[i].x_theta > 2.0*3.14159)
368       c->source[i].x_theta -= 2.0*3.14159;
369     c->source[i].y_theta += (c->speed/1000.0);
370     if(c->source[i].y_theta > 2.0*3.14159)
371       c->source[i].y_theta -= 2.0*3.14159;
372     c->source[i].x = source_x(c, i);
373     c->source[i].y = source_y(c, i);
374   }
375
376   g = c->grid_size;
377
378   for(j = 0; j < c->h/g; j++) {
379     for(i = 0; i < c->w/g; i++) {
380       result = 0;
381       for(k = 0; k < c->count; k++) {
382         dx = i*g + g/2 - c->source[k].x;
383         dy = j*g + g/2 - c->source[k].y;
384         dist = sqrt(dx*dx + dy*dy); /* what's the performance penalty here? */
385         result += (dist > c->radius ? 0 : c->wave_height[dist]);
386       }
387       result %= c->colors;
388
389 #ifdef USE_XIMAGE
390       /* Fill in these `gridsize' horizontal bits in the scanline */
391       for(k = 0; k < g; k++)
392         XPutPixel(c->ximage, (g*i)+k, 0, c->pal[result].pixel);
393
394 #else  /* !USE_XIMAGE */
395       XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g); 
396 #endif /* !USE_XIMAGE */
397     }
398
399 #ifdef USE_XIMAGE
400
401     /* Only the first scanline of the image has been filled in; clone that
402        scanline to the rest of the `gridsize' lines in the ximage */
403     for(k = 0; k < (g-1); k++)
404       memcpy(c->ximage->data + (c->ximage->bytes_per_line * (k + 1)),
405              c->ximage->data + (c->ximage->bytes_per_line * k),
406              c->ximage->bytes_per_line);
407
408     /* Move the bits for this horizontal stripe to the server. */
409 # ifdef HAVE_XSHM_EXTENSION
410     if (c->use_shm)
411       XShmPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
412                    0, 0, 0, g*j, c->ximage->width, c->ximage->height,
413                    False);
414     else
415 # endif /*  HAVE_XSHM_EXTENSION */
416       XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
417                 0, 0, 0, g*j, c->ximage->width, c->ximage->height);
418
419 #endif /* USE_XIMAGE */
420   }
421
422 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
423   if (c->back_buf)
424     {
425       XdbeSwapInfo info[1];
426       info[0].swap_window = c->win;
427       info[0].swap_action = XdbeUndefined;
428       XdbeSwapBuffers(c->dpy, info, 1);
429     }
430   else
431 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
432     if (c->pix_buf)
433       {
434         XCopyArea (c->dpy, c->pix_buf, c->win, c->copy_gc,
435                    0, 0, c->w, c->h, 0, 0);
436       }
437
438   XSync(c->dpy, False);
439 }
440
441 void screenhack(Display *dpy, Window win) 
442 {
443   struct inter_context c;
444   int delay;
445
446   delay = get_integer_resource("delay", "Integer");
447
448   inter_init(dpy, win, &c);
449   while(1) {
450     do_inter(&c); 
451     screenhack_handle_events (dpy);
452     if(delay) usleep(delay);
453   }
454 }