http://ftp.x.org/contrib/applications/xscreensaver-3.21.tar.gz
[xscreensaver] / hacks / ripples.c
1 /* ripples, Copyright (c) 1999 Ian McConnell <ian@emit.demon.co.uk>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  */
11
12 /*
13  * "Water" ripples that can cross and interfere with each other.
14  *
15  * I can't remember where I got this idea from, but it's been around for a
16  * while in various demos. Some inspiration from
17  *      water.txt by Tom Hammersley,tomh@globalnet.co.uk
18  *
19  * Options
20  * -delay       usleep every iteration
21  * -rate        Add one drop every "rate" iterations
22  * -box         Add big square splash every "box" iters (not very good)
23  * -water       Ripples on a grabbed background image
24  * -foreground  Interpolate ripples between these two colors
25  * -background
26  * -oily        Psychedelic colours like man
27  * -stir        Add a regular pattern of drops
28  * -fluidity    Between 0 and 16. 16 = big drops
29  * -light       Hack to add lighting effect
30  *
31  * Code mainly hacked from xflame and decayscreen.
32  */
33
34 /* Version history:
35  * 13 Oct 1999: Initial hack
36  * 30 Oct 1999: Speeded up graphics with dirty buffer. Returned to using
37  *              putpixel for greater portability
38  *              Added a variety of methods for splashing screen.
39  * 31 Oct 1999: Added in lighting hack
40  * 13 Nov 1999: Speed up tweaks
41  *              Adjust "light" for different bits per colour (-water only)
42  *
43  */
44
45
46 #include <math.h>
47 #include "screenhack.h"
48 #include <X11/Xutil.h>
49
50 typedef enum {ripple_drop, ripple_blob, ripple_box, ripple_stir} ripple_mode;
51
52 #ifdef HAVE_XSHM_EXTENSION
53 #include "xshm.h"
54 static Bool use_shm;
55 static XShmSegmentInfo shm_info;
56 #endif /* HAVE_XSHM_EXTENSION */
57
58 static Window window;
59 static Display *display;
60 static GC gc;
61 static Visual *visual;
62
63 static XImage *orig_map, *buffer_map;
64 static int ctab[256];
65 static Colormap colormap;
66 static int ncolors;
67 static int light;
68
69 static int width, height; /* ripple size */
70 static int bigwidth, bigheight; /* screen size */
71
72 static Bool transparent;
73 static short *bufferA, *bufferB, *temp;
74 static char *dirty_buffer;
75
76 #define TABLE 256
77 static double cos_tab[TABLE];
78
79 /* Distribution of drops: many little ones and a few big ones. */
80 static double drop_dist[] =
81 {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2, 0.6};
82
83 static void (*draw_transparent)(short *src);
84
85 /* How hard to hit the water */
86 #define SPLASH 512
87 #define MIN(x, y) ((x) < (y) ? (x) : (y))
88 #define MAX(x, y) ((x) > (y) ? (x) : (y))
89 #define DIRTY 3 /* dirty >= 2, 1 = restore original pixel, 0 = leave alone */
90
91 /* From fortune(6) */
92 /* -- really weird C code to count the number of bits in a word */
93 #define BITCOUNT(x)     (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255)
94 #define BX_(x)          ((x) - (((x)>>1)&0x77777777) \
95                              - (((x)>>2)&0x33333333) \
96                              - (((x)>>3)&0x11111111))
97
98
99 /*      -------------------------------------------             */
100
101
102 static int
103 map_color(int grey)
104 {
105   /* Clip it */
106   grey = ncolors * abs(grey) / (SPLASH/4);
107   if (grey > ncolors)
108     grey = ncolors;
109
110   /* Display it */
111   return ctab[grey];
112 }
113
114
115 static void
116 draw_ripple(short *src)
117 {
118   int across, down;
119   char *dirty = dirty_buffer;
120
121   for (down = 0; down < height - 1; down++, src += 1, dirty += 1)
122     for (across = 0; across < width - 1; across++, src++, dirty++) {
123       int v1, v2, v3, v4;
124       v1 = (int)*src;
125       v2 = (int)*(src + 1);
126       v3 = (int)*(src + width);
127       v4 = (int)*(src + width + 1);
128       if ((v1 == 0 && v2 == 0 && v3 == 0 && v4 == 0)) {
129         if (*dirty > 0)
130           (*dirty)--;
131       } else
132         *dirty = DIRTY;
133
134       if (*dirty > 0) {
135         int dx;
136         if (light > 0) {
137           dx = ((v3 - v1) + (v4 - v2)) << light; /* light from top */
138         } else
139           dx = 0;
140         XPutPixel(buffer_map,(across<<1),  (down<<1),  map_color(dx + v1));
141         XPutPixel(buffer_map,(across<<1)+1,(down<<1),  map_color(dx + ((v1 + v2) >> 1)));
142         XPutPixel(buffer_map,(across<<1),  (down<<1)+1,map_color(dx + ((v1 + v3) >> 1)));
143         XPutPixel(buffer_map,(across<<1)+1,(down<<1)+1,map_color(dx + ((v1 + v4) >> 1)));
144       }
145     }
146 }
147
148
149 /*      -------------------------------------------             */
150
151
152 /* Uses the horizontal gradient as an offset to create a warp effect  */
153 static void
154 draw_transparent_vanilla(short *src)
155 {
156   int across, down, pixel;
157   char *dirty = dirty_buffer;
158
159   pixel = 0;
160   for (down = 0; down < height - 2; down++, pixel += 2)
161     for (across = 0; across < width-2; across++, pixel++) {
162       int gradx, grady, gradx1, grady1;
163       int x0, x1, x2, y1, y2;
164
165       x0 = src[pixel];
166       x1 = src[pixel + 1];
167       x2 = src[pixel + 2];
168       y1 = src[pixel + width];
169       y2 = src[pixel + 2*width];
170
171       gradx = (x1 - x0);
172       grady = (y1 - x0);
173       gradx1= (x2 - x1);
174       grady1= (y2 - y1);
175       gradx1 = 1 + (gradx + gradx1) / 2;
176       grady1 = 1 + (grady + grady1) / 2;
177
178       if ((2*across+MIN(gradx,gradx1) < 0) ||
179           (2*across+MAX(gradx,gradx1) >= bigwidth)) {
180         gradx = 0;
181         gradx1= 1;
182       }
183       if ((2*down+MIN(grady,grady1) < 0) ||
184           (2*down+MAX(grady,grady1) >= bigheight)) {
185         grady = 0;
186         grady1 = 1;
187       }
188
189       if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
190         if (dirty[pixel] > 0)
191           dirty[pixel]--;
192       } else
193         dirty[pixel] = DIRTY;
194
195       if (dirty[pixel] > 0) {
196         XPutPixel(buffer_map, (across<<1),  (down<<1),
197                   XGetPixel(orig_map, (across<<1) + gradx, (down<<1) + grady));
198         XPutPixel(buffer_map, (across<<1)+1,(down<<1),
199                   XGetPixel(orig_map, (across<<1) + gradx1,(down<<1) + grady));
200         XPutPixel(buffer_map, (across<<1),  (down<<1)+1,
201                   XGetPixel(orig_map, (across<<1) + gradx, (down<<1) + grady1));
202         XPutPixel(buffer_map, (across<<1)+1,(down<<1)+1,
203                   XGetPixel(orig_map, (across<<1) + gradx1,(down<<1) + grady1));
204       }
205     }
206 }
207
208
209 /*      -------------------------------------------             */
210
211
212 /* This builds on the above warp effect by adding in a lighting effect: */
213 /* brighten pixels by an amount corresponding to the vertical gradient */
214 static unsigned long rmask;
215 static unsigned long gmask;
216 static unsigned long bmask;
217 static int rshift;
218 static int gshift;
219 static int bshift;
220
221
222 static void
223 set_mask(unsigned long color, unsigned long *mask, int *shift)
224 {
225   *shift = 0;
226   while (color != 0 && (color & 1) == 0) {
227     (*shift)++;
228     color >>= 1;
229   }
230   *mask = color;
231 }
232
233
234 static unsigned long
235 cadd(unsigned long color, int dx, unsigned long mask, int shift)
236 {
237   int x;
238   color >>= shift;
239   x = (color & mask);
240   x += dx;
241   if (x < 0) x = 0;
242   else if (x > (int)mask) x = mask;
243   color = x;
244   return color << shift;
245 }
246
247
248 static unsigned long
249 bright(int dx, unsigned long color)
250 {
251   return (cadd(color, dx, rmask, rshift) |
252           cadd(color, dx, gmask, gshift) |
253           cadd(color, dx, bmask, bshift));
254 }
255
256
257 static void
258 draw_transparent_light(short *src)
259 {
260   int across, down, pixel;
261   char *dirty = dirty_buffer;
262
263   pixel = 0;
264   for (down = 0; down < height - 2; down++, pixel += 2)
265     for (across = 0; across < width-2; across++, pixel++) {
266       int gradx, grady, gradx1, grady1;
267       int x0, x1, x2, y1, y2;
268
269       x0 = src[pixel];
270       x1 = src[pixel + 1];
271       x2 = src[pixel + 2];
272       y1 = src[pixel + width];
273       y2 = src[pixel + 2*width];
274
275       gradx = (x1 - x0);
276       grady = (y1 - x0);
277       gradx1= (x2 - x1);
278       grady1= (y2 - y1);
279       gradx1 = 1 + (gradx + gradx1) / 2;
280       grady1 = 1 + (grady + grady1) / 2;
281
282       if ((2*across+MIN(gradx,gradx1) < 0) ||
283           (2*across+MAX(gradx,gradx1) >= bigwidth)) {
284         gradx = 0;
285         gradx1= 1;
286       }
287       if ((2*down+MIN(grady,grady1) < 0) ||
288           (2*down+MAX(grady,grady1) >= bigheight)) {
289         grady = 0;
290         grady1 = 1;
291       }
292
293       if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
294         if (dirty[pixel] > 0)
295           dirty[pixel]--;
296       } else
297         dirty[pixel] = DIRTY;
298
299       if (dirty[pixel] > 0) {
300         int dx;
301
302         /* light from top */
303         if (4-light >= 0)
304           dx = (grady + (src[pixel+width+1]-x1)) >> (4-light);
305         else
306           dx = (grady + (src[pixel+width+1]-x1)) << (light-4);
307
308         if (dx != 0) {
309           XPutPixel(buffer_map, (across<<1),  (down<<1),
310                     bright(dx, XGetPixel(orig_map, (across<<1) + gradx, (down<<1) + grady)));
311           XPutPixel(buffer_map, (across<<1)+1,(down<<1),
312                     bright(dx, XGetPixel(orig_map, (across<<1) + gradx1,(down<<1) + grady)));
313           XPutPixel(buffer_map, (across<<1),  (down<<1)+1,
314                     bright(dx, XGetPixel(orig_map, (across<<1) + gradx, (down<<1) + grady1)));
315           XPutPixel(buffer_map, (across<<1)+1,(down<<1)+1,
316                     bright(dx, XGetPixel(orig_map, (across<<1) + gradx1,(down<<1) + grady1)));
317         } else {
318           /* Could use XCopyArea, but XPutPixel is faster */
319           XPutPixel(buffer_map, (across<<1),  (down<<1),
320                     XGetPixel(orig_map, (across<<1) + gradx, (down<<1) + grady));
321           XPutPixel(buffer_map, (across<<1)+1,(down<<1),
322                     XGetPixel(orig_map, (across<<1) + gradx1,(down<<1) + grady));
323           XPutPixel(buffer_map, (across<<1),  (down<<1)+1,
324                     XGetPixel(orig_map, (across<<1) + gradx, (down<<1) + grady1));
325           XPutPixel(buffer_map, (across<<1)+1,(down<<1)+1,
326                     XGetPixel(orig_map, (across<<1) + gradx1,(down<<1) + grady1));
327         }
328       }
329     }
330 }
331
332
333 /*      -------------------------------------------             */
334
335
336 #if 0
337 /* Doesn't go any faster and doesn't work at all colour depths */
338 static void
339 draw_transparent16l(short *src)
340 {
341   int across, down, bigpix, pixel;
342   char *dirty = dirty_buffer;
343   unsigned short *buffer, *orig;
344
345   buffer = (unsigned short *) buffer_map->data;
346   orig = (unsigned short *) orig_map->data;
347
348   for (pixel = bigpix = down = 0;
349        down < height - 2;
350        down++, pixel += 2, bigpix += bigwidth+4)
351     for (across = 0; across < width-2; across++, pixel++, bigpix+=2) {
352       int gradx, grady, gradx1, grady1;
353       int x0, x1, x2, y1, y2;
354
355       x0 = src[pixel];
356       x1 = src[pixel + 1];
357       x2 = src[pixel + 2];
358       y1 = src[pixel + width];
359       y2 = src[pixel + 2*width];
360
361       gradx = (x1 - x0);
362       grady = (y1 - x0);
363       gradx1= (x2 - x1);
364       grady1= (y2 - y1);
365       gradx1 = 1 + (gradx + gradx1) / 2;
366       grady1 = 1 + (grady + grady1) / 2;
367
368       if ((2*across+MIN(gradx,gradx1) < 0) ||
369           (2*across+MAX(gradx,gradx1) >= bigwidth)) {
370         gradx = 0;
371         gradx1= 1;
372       }
373       if ((2*down+MIN(grady,grady1) < 0) ||
374           (2*down+MAX(grady,grady1) >= bigheight)) {
375         grady = 0;
376         grady1 = 1;
377       }
378
379       if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
380         if (dirty[pixel] > 0)
381           dirty[pixel]--;
382       } else
383         dirty[pixel] = DIRTY;
384
385       if (dirty[pixel] > 0) {
386         unsigned short *dest = buffer + bigpix;
387         unsigned short *image = orig + bigpix;
388         int dx;
389
390         /* light from top */
391         if (4-light >= 0)
392           dx = (grady + (src[pixel+width+1]-x1)) >> (4-light);
393         else
394           dx = (grady + (src[pixel+width+1]-x1)) << (light-4);
395
396         grady *= bigwidth;
397         grady1*= bigwidth;
398
399         if (dx != 0) {
400           *dest++ = dobright(dx, *(image + gradx  + grady));
401           *dest   = dobright(dx, *(image + gradx1 + grady));
402           dest   += bigwidth - 1;
403           *dest++ = dobright(dx, *(image + gradx  + grady1));
404           *dest   = dobright(dx, *(image + gradx1 + grady1));
405         } else {
406           *dest++ = *(image + gradx  + grady);
407           *dest   = *(image + gradx1 + grady);
408           dest   += bigwidth - 1;
409           *dest++ = *(image + gradx  + grady1);
410           *dest   = *(image + gradx1 + grady1);
411         }
412       }
413     }
414 }
415 #endif
416
417
418 /*      -------------------------------------------             */
419
420
421 static void
422 setup_X(Display * disp, Window win)
423 {
424   XWindowAttributes xwa;
425   int depth;
426
427   XGetWindowAttributes(disp, win, &xwa);
428   window = win;
429   display = disp;
430   depth = xwa.depth;
431   colormap = xwa.colormap;
432   bigwidth = xwa.width;
433   bigheight = xwa.height;
434   visual = xwa.visual;
435
436 #if 1 /* I'm not entirely sure if I need this */
437   if (bigwidth % 2)
438     bigwidth++;
439   if (bigheight % 2)
440     bigheight++;
441 #endif
442   width = bigwidth / 2;
443   height = bigheight / 2;
444
445   if (transparent) {
446     XGCValues gcv;
447     long gcflags;
448
449     gcv.function = GXcopy;
450     gcv.subwindow_mode = IncludeInferiors;
451
452     gcflags = GCForeground | GCFunction;
453     if (use_subwindow_mode_p(xwa.screen, window))       /* see grabscreen.c */
454       gcflags |= GCSubwindowMode;
455
456     gc = XCreateGC(display, window, gcflags, &gcv);
457
458     grab_screen_image(xwa.screen, window);
459
460     orig_map = XGetImage(display, window, 0, 0, xwa.width, xwa.height,
461                          ~0L, ZPixmap);
462   } else {
463     XGCValues gcv;
464
465     gc = XCreateGC(display, window, 0, &gcv);
466     orig_map = 0;
467   }
468
469   if (!gc) {
470     fprintf(stderr, "XCreateGC failed\n");
471     exit(1);
472   }
473
474   buffer_map = 0;
475
476 #ifdef HAVE_XSHM_EXTENSION
477   if (use_shm) {
478     buffer_map = create_xshm_image(display, xwa.visual, depth,
479                                    ZPixmap, 0, &shm_info, bigwidth, bigheight);
480     if (!buffer_map) {
481       use_shm = False;
482       fprintf(stderr, "create_xshm_image failed\n");
483     }
484   }
485 #endif /* HAVE_XSHM_EXTENSION */
486
487   if (!buffer_map) {
488     buffer_map = XCreateImage(display, xwa.visual,
489                               depth, ZPixmap, 0, 0,
490                               bigwidth, bigheight, 8, 0);
491     buffer_map->data = (char *)
492       calloc(buffer_map->height, buffer_map->bytes_per_line);
493   }
494 }
495
496
497 static void
498 DisplayImage(void)
499 {
500 #ifdef HAVE_XSHM_EXTENSION
501   if (use_shm)
502     XShmPutImage(display, window, gc, buffer_map, 0, 0, 0, 0,
503                  bigwidth, bigheight, False);
504   else
505 #endif /* HAVE_XSHM_EXTENSION */
506     XPutImage(display, window, gc, buffer_map, 0, 0, 0, 0,
507               bigwidth, bigheight);
508
509   XSync(display,False);
510   screenhack_handle_events(display);
511 }
512
513
514 /*      -------------------------------------------             */
515
516
517 static int
518 cinterp(double a, int bg, int fg)
519 {
520   int result;
521   result = (int)((1-a) * bg + a * fg + 0.5);
522   if (result < 0) result = 0;
523   if (result > 255) result = 255;
524   return result;
525 }
526
527
528 /* Interpolate the ripple colours between the background colour and
529    foreground colour */
530 static void
531 init_linear_colors(void)
532 {
533   int i, j, red, green, blue, bred, bgreen, bblue;
534   XColor fg, bg;
535
536   if (ncolors < 2 || mono_p)
537     ncolors = 2;
538   if (ncolors <= 2)
539     mono_p = True;
540
541   /* Make it possible to set the color of the ripples,
542      Based on work by Raymond Medeiros <ray@stommel.marine.usf.edu> and jwz.
543    */
544   fg.pixel = get_pixel_resource("foreground", "Foreground",
545                                 display, colormap);
546   XQueryColor(display, colormap, &fg);
547   red = (fg.red >> 8);
548   green = (fg.green >> 8);
549   blue = (fg.blue >> 8);
550
551   bg.pixel = get_pixel_resource("background", "Background",
552                                 display, colormap);
553   XQueryColor(display, colormap, &bg);
554   bred = (bg.red >> 8);
555   bgreen = (bg.green >> 8);
556   bblue = (bg.blue >> 8);
557
558   j = 0;
559   for (i = 0; i < ncolors+1; i++) {
560     XColor xcl;
561     double a = (double)i / ncolors;
562     int r = cinterp(a, bred, red);
563     int g = cinterp(a, bgreen, green);
564     int b = cinterp(a, bblue, blue);
565
566     xcl.red = (unsigned short) ((r << 8) | r);
567     xcl.green = (unsigned short) ((g << 8) | g);
568     xcl.blue = (unsigned short) ((b << 8) | b);
569     xcl.flags = DoRed | DoGreen | DoBlue;
570
571     XAllocColor(display, colormap, &xcl);
572
573     ctab[j++] = (int) xcl.pixel;
574   }
575 }
576
577
578 static void
579 init_oily_colors(void)
580 {
581   XColor *colors = NULL;
582
583   if (ncolors < 2 || mono_p)
584     ncolors = 2;
585   if (ncolors <= 2)
586     mono_p = True;
587   colors = 0;
588
589   if (!mono_p) {
590     colors = (XColor *)malloc(sizeof(*colors) * (ncolors+1));
591     make_smooth_colormap(display, visual, colormap, colors, &ncolors,
592                          True, /* allocate */
593                          False, /* not writable */
594                          True); /* verbose (complain about failure) */
595     if (ncolors <= 2) {
596       if (colors)
597         free (colors);
598       colors = 0;
599       mono_p = True;
600     }
601   }
602   if (!mono_p) {
603     int i, j = 0;
604     for (i = 0; i < ncolors+1; i++) {
605       XAllocColor(display, colormap, colors+i);
606       ctab[j++] = (int) colors[i].pixel;
607     }
608     free (colors);
609   } else {
610     ncolors = 2;
611     ctab[1] = get_pixel_resource("foreground", "Foreground", display, colormap);
612     ctab[0] = get_pixel_resource("background", "Background", display, colormap);
613   }
614 }
615
616
617 /*      -------------------------------------------             */
618
619
620 static void
621 init_cos_tab(void)
622 {
623   int i;
624   for (i = 0; i < TABLE; i++)
625     cos_tab[i] = cos(i * M_PI/2 / TABLE);
626 }
627
628
629 /* Shape of drop to add */
630 static double
631 sinc(double x)
632 {
633 #if 1
634   /* cosine hump */
635   int i;
636   i = (int)(x * TABLE + 0.5);
637   if (i >= TABLE) i = (TABLE-1) - (i-(TABLE-1));
638   return cos_tab[i];
639 #elif 0
640   return cos(x * M_PI/2);
641 #else
642   if (fabs(x) < 0.1)
643     return 1 - x*x;
644   else
645     return sin(x) / x;
646 #endif
647 }
648
649
650 static void
651 add_circle_drop(int x, int y, int radius, int dheight)
652 {
653   int i, r2, cx, cy;
654   short *buf = (random()&1) ? bufferA : bufferB;
655
656   i = y * width + x;
657   r2 = radius * radius;
658
659   for (cy = -radius; cy <= radius; cy++)
660     for (cx = -radius; cx <= radius; cx++) {
661       int r = cx*cx + cy*cy;
662       if (r <= r2) {
663         buf[i + cx + cy*width] =
664           (short)(dheight * sinc(sqrt(r)/radius));
665       }
666     }
667 }
668
669
670 static void
671 add_drop(ripple_mode mode, int drop)
672 {
673   int newx, newy, dheight;
674   int radius = MIN(width, height) / 50;
675   /* Don't put drops too near the edge of the screen or they get stuck */
676   int border = 8;
677
678   switch (mode) {
679   default:
680   case ripple_drop: {
681     int x;
682
683     dheight = 1 + (random() % drop);
684     newx = border + (random() % (width - 2*border));
685     newy = border + (random() % (height - 2*border));
686     x = newy * width + newx;
687     bufferA[x + 1] = bufferA[x] = bufferA[x + width] = bufferA[x + width + 1] =
688       bufferB[x + 1] = bufferB[x] = bufferB[x + width] = bufferB[x + width + 1] =
689       dheight;
690   }
691   break;
692   case ripple_blob: {
693     double power;
694
695     power = drop_dist[random() % (sizeof(drop_dist)/sizeof(drop_dist[0]))]; /* clumsy */
696     dheight = (int)(drop * (power + 0.01));
697     newx = radius + border + (random() % (int)(width - 2*border - 2*radius*power));
698     newy = radius + border + (random() % (int)(height - 2*border - 2*radius*power));
699     add_circle_drop(newx, newy, radius, dheight);
700   }
701   break;
702   /* Adding too many boxes too quickly (-box 1) doesn't give the waves time
703      to disperse and the waves build up (and overflow) */
704   case ripple_box: {
705     int x;
706     int cx, cy;
707     short *buf = (random()&1) ? bufferA : bufferB;
708
709     radius = (1 + (random() % 5)) * (1 + (random() % 5));
710     dheight = drop / 128;
711     if (random() & 1) dheight = -dheight;
712     newx = radius + border + (random() % (width - 2*border - 2*radius));
713     newy = radius + border + (random() % (height - 2*border - 2*radius));
714     x = newy * width + newx;
715     for (cy = -radius; cy <= radius; cy++)
716       for (cx = -radius; cx <= radius; cx++)
717         buf[x + cx + cy*width] = (short)(dheight);
718   }
719   break;
720   case ripple_stir: {
721     static double ang = 0;
722     border += radius;
723     newx = border + (int)((width-2*border) * (1+cos(3*ang)) / 2);
724     newy = border + (int)((height-2*border) * (1+sin(2*ang)) / 2);
725     add_circle_drop(newx, newy, radius, drop / 10);
726     ang += 0.02;
727     if (ang > 12*M_PI) ang = 0;
728   }
729   break;
730   }
731 }
732
733
734 static void
735 init_ripples(int ndrops, int splash)
736 {
737   int i;
738
739   bufferA = (short *)calloc(width * height, sizeof(*bufferA));
740   bufferB = (short *)calloc(width * height, sizeof(*bufferB));
741   temp = (short *)calloc(width * height, sizeof(*temp));
742
743   dirty_buffer = (char *)calloc(width * height, sizeof(*dirty_buffer));
744
745   for (i = 0; i < ndrops; i++)
746     add_drop(ripple_blob, splash);
747
748   if (transparent) {
749     /* There's got to be a better way of doing this  XCopyArea? */
750     memcpy(buffer_map->data, orig_map->data,
751            bigheight * buffer_map->bytes_per_line);
752   } else {
753     int across, down, color;
754
755     color = map_color(0); /* background colour */
756     for (down = 0; down < bigheight; down++)
757       for (across = 0; across < bigwidth; across++)
758         XPutPixel(buffer_map,across,  down,  color);
759   }
760
761   DisplayImage();
762 }
763
764
765 /*
766  Explanation from hq_water.zip (Arturo R Montesinos (ARM) arami@fi.upm.es)
767
768  Differential equation is:  u  = a ( u  + u  )
769                              tt       xx   yy
770
771  Where a = tension * gravity / surface_density.
772
773  Approximating second derivatives by central differences:
774
775   [ u(t+1)-2u(t)+u(t-1) ] / dt = a [ u(x+1)+u(x-1)+u(y+1)+u(y-1)-4u ] / h
776
777  where dt = time step squared, h = dx*dy = mesh resolution squared.
778
779  From where u(t+1) may be calculated as:
780
781             dt  |   1   |                   dt
782  u(t+1) = a --  | 1 0 1 |u - u(t-1) + (2-4a --)u
783             h   |   1   |                    h
784
785  When a*dt/h = 1/2 last term vanishes, giving:
786
787                  1 |   1   |
788         u(t+1) = - | 1 0 1 |u - u(t-1)
789                  2 |   1   |
790
791  (note that u(t-1,x,y) is only used to calculate u(t+1,x,y) so
792   we can use the same array for both t-1 and t+1, needing only
793   two arrays, U[0] and U[1])
794
795  Dampening is simulated by subtracting 1/2^n of result.
796  n=4 gives best-looking result
797  n<4 (eg 2 or 3) thicker consistency, waves die out immediately
798  n>4 (eg 8 or 12) more fluid, waves die out slowly
799  */
800
801 static void
802 ripple(int fluidity)
803 {
804   int across, down, pixel;
805   static int toggle;
806   static int count;
807   short *src, *dest;
808
809   if (toggle == 0) {
810     src = bufferA;
811     dest = bufferB;
812     toggle = 1;
813   } else {
814     src = bufferB;
815     dest = bufferA;
816     toggle = 0;
817   }
818
819   switch (count) {
820   case 0: case 1:
821     pixel = 1 * width + 1;
822     for (down = 1; down < height - 1; down++, pixel += 2 * 1)
823       for (across = 1; across < width - 1; across++, pixel++) {
824         temp[pixel] =
825           (((src[pixel - 1] + src[pixel + 1] +
826              src[pixel - width] + src[pixel + width]) / 2)) - dest[pixel];
827       }
828
829     /* Smooth the output */
830     pixel = 1 * width + 1;
831     for (down = 1; down < height - 1; down++, pixel += 2 * 1)
832       for (across = 1; across < width - 1; across++, pixel++) {
833         if (temp[pixel] != 0) { /* Close enough for government work */
834           int damp =
835             (temp[pixel - 1] + temp[pixel + 1] +
836              temp[pixel - width] + temp[pixel + width] +
837              temp[pixel - width - 1] + temp[pixel - width + 1] +
838              temp[pixel + width - 1] + temp[pixel + width + 1] +
839              temp[pixel]) / 9;
840           dest[pixel] = damp - (damp >> fluidity);
841         } else
842           dest[pixel] = 0;
843       }
844     break;
845   case 2: case 3:
846     pixel = 1 * width + 1;
847     for (down = 1; down < height - 1; down++, pixel += 2 * 1)
848       for (across = 1; across < width - 1; across++, pixel++) {
849         int damp =
850           (((src[pixel - 1] + src[pixel + 1] +
851              src[pixel - width] + src[pixel + width]) / 2)) - dest[pixel];
852         dest[pixel] = damp - (damp >> fluidity);
853       }
854     break;
855   }
856   if (++count > 3) count = 0;
857
858   if (transparent)
859     draw_transparent(dest);
860   else
861     draw_ripple(dest);
862 }
863
864
865 /*      -------------------------------------------             */
866
867
868 char *progclass = "Ripples";
869
870 char *defaults[] =
871 {
872   ".background:         black",
873   ".foreground:         #FFAF5F",
874   "*colors:             200",
875   "*dontClearRoot:      True",
876   "*delay:              50000",
877   "*rate:               5",
878   "*box:                0",
879   "*water:              False",
880   "*oily:               False",
881   "*stir:               False",
882   "*fluidity:           6",
883   "*light:              0",
884 #ifdef HAVE_XSHM_EXTENSION
885   "*useSHM: True",
886 #endif                          /* HAVE_XSHM_EXTENSION */
887   0
888 };
889
890 XrmOptionDescRec options[] =
891 {
892   { "-colors",  ".colors",      XrmoptionSepArg, 0},
893   { "-colours", ".colors",      XrmoptionSepArg, 0},
894   {"-delay",    ".delay",       XrmoptionSepArg, 0},
895   {"-rate",     ".rate",        XrmoptionSepArg, 0},
896   {"-box",      ".box",         XrmoptionSepArg, 0},
897   {"-water",    ".water",       XrmoptionNoArg, "True"},
898   {"-oily",     ".oily",        XrmoptionNoArg, "True"},
899   {"-stir",     ".stir",        XrmoptionNoArg, "True"},
900   {"-fluidity", ".fluidity",    XrmoptionSepArg, 0},
901   {"-light",    ".light",       XrmoptionSepArg, 0},
902 #ifdef HAVE_XSHM_EXTENSION
903   {"-shm",      ".useSHM",      XrmoptionNoArg, "True"},
904   {"-no-shm",   ".useSHM",      XrmoptionNoArg, "False"},
905 #endif                          /* HAVE_XSHM_EXTENSION */
906   {0, 0, 0, 0}
907 };
908
909
910 void screenhack(Display *disp, Window win)
911 {
912   int iterations = 0;
913   int delay = get_integer_resource("delay", "Integer");
914   int rate = get_integer_resource("rate", "Integer");
915   int box = get_integer_resource("box", "Integer");
916   int oily = get_boolean_resource("oily", "Boolean");
917   int stir = get_boolean_resource("stir", "Boolean");
918   int fluidity = get_integer_resource("fluidity", "Integer");
919   transparent = get_boolean_resource("water", "Boolean");
920 #ifdef HAVE_XSHM_EXTENSION
921   use_shm = get_boolean_resource("useSHM", "Boolean");
922 #endif /* HAVE_XSHM_EXTENSION */
923   light = get_integer_resource("light", "Integer");
924
925   if (fluidity <= 1) fluidity = 1;
926   if (fluidity > 16) fluidity = 16; /* 16 = sizeof(short) */
927   if (light < 0) light = 0;
928
929   init_cos_tab();
930   setup_X(disp, win);
931
932   ncolors = get_integer_resource ("colors", "Colors");
933   if (0 == ncolors)             /* English spelling? */
934     ncolors = get_integer_resource ("colours", "Colors");
935
936   if (ncolors > sizeof(ctab)/sizeof(*ctab))
937     ncolors = sizeof(ctab)/sizeof(*ctab);
938
939   if (oily)
940     init_oily_colors();
941   else
942     init_linear_colors();
943
944   if (transparent && light > 0) {
945     int maxbits;
946     draw_transparent = draw_transparent_light;
947     set_mask(visual->red_mask,   &rmask, &rshift);
948     set_mask(visual->green_mask, &gmask, &gshift);
949     set_mask(visual->blue_mask,  &bmask, &bshift);
950     if (rmask == 0) draw_transparent = draw_transparent_vanilla;
951
952     /* Adjust the shift value "light" when we don't have 8 bits per colour */
953     maxbits = MIN(MIN(BITCOUNT(rmask), BITCOUNT(gmask)), BITCOUNT(bmask));
954     light -= 8-maxbits;
955     if (light < 0) light = 0;
956   } else
957     draw_transparent = draw_transparent_vanilla;
958
959   init_ripples(0, -SPLASH); /* Start off without any drops */
960
961   /* Main drawing loop */
962   while (1) {
963     if (rate > 0 && (iterations % rate) == 0)
964       add_drop(ripple_blob, -SPLASH);
965     if (stir)
966       add_drop(ripple_stir, -SPLASH);
967     if (box > 0 && (random() % box) == 0)
968       add_drop(ripple_box, -SPLASH);
969
970     ripple(fluidity);
971     DisplayImage();
972
973     if (delay)
974       usleep(delay);
975
976     iterations++;
977   }
978 }