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