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