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