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