1 /* ripples, Copyright (c) 1999 Ian McConnell <ian@emit.demon.co.uk>
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
13 * "Water" ripples that can cross and interfere with each other.
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
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
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)
30 * Code mainly hacked from xflame and decayscreen.
34 * Speeded up graphics with dirty buffer. Returned to using putpixel for
36 * Added a variety of methods for splashing screen.
41 #include "screenhack.h"
42 #include <X11/Xutil.h>
44 typedef enum {ripple_drop, ripple_blob, ripple_box, ripple_stir} ripple_mode;
46 #ifdef HAVE_XSHM_EXTENSION
49 static XShmSegmentInfo shm_info;
50 #endif /* HAVE_XSHM_EXTENSION */
53 static Display *display;
55 static Visual *visual;
57 static XImage *orig_map, *buffer_map;
59 static Colormap colormap;
63 static int width, height; /* ripple size */
64 static int bigwidth, bigheight; /* screen size */
66 static Bool transparent;
67 static short *bufferA, *bufferB, *temp;
68 static char *dirty_buffer;
71 static double cos_tab[TABLE];
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};
76 /* How hard to hit the water */
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 */
83 /* ------------------------------------------- */
90 grey = ncolors * abs(grey) / (SPLASH/4);
100 draw_ripple(short *src)
103 char *dirty = dirty_buffer;
105 for (down = 0; down < height - 1; down++, src += 1, dirty += 1)
106 for (across = 0; across < width - 1; across++, src++, dirty++) {
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)) {
121 /* dx = ((v2 - v1) + (v4 - v3)) << light; */ /* light from left */
122 dx = ((v3 - v1) + (v4 - v2)) << light; /* light from top */
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)));
134 /* ------------------------------------------- */
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;
147 set_mask(unsigned long color, unsigned long *mask, int *shift)
150 while (color != 0 && (color & 1) == 0) {
159 cadd(unsigned long color, int dx, unsigned long mask, int shift)
166 else if (x > (int)mask) x = mask;
168 return color << shift;
173 dobright(int dx, unsigned long color)
175 return (cadd(color, dx, rmask, rshift) |
176 cadd(color, dx, gmask, gshift) |
177 cadd(color, dx, bmask, bshift));
182 nobright(int dx, unsigned long color)
189 draw_transparent(short *src)
191 int across, down, pixel;
192 char *dirty = dirty_buffer;
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;
203 y1 = src[pixel + width];
204 y2 = src[pixel + 2*width];
210 gradx1 = 1 + (gradx + gradx1) / 2;
211 grady1 = 1 + (grady + grady1) / 2;
213 if ((2*across+MIN(gradx,gradx1) < 0) ||
214 (2*across+MAX(gradx,gradx1) >= bigwidth)) {
218 if ((2*down+MIN(grady,grady1) < 0) ||
219 (2*down+MAX(grady,grady1) >= bigheight)) {
224 if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
225 if (dirty[pixel] > 0)
228 dirty[pixel] = DIRTY;
230 if (dirty[pixel] > 0) {
234 dx = (grady + (src[pixel+width+1]-x1)) >> (4-light); /* light from top */
236 dx = (grady + (src[pixel+width+1]-x1)) << (light-4); /* light from top */
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)));
252 /* ------------------------------------------- */
257 draw_ripple16(short *src)
260 unsigned short *dest;
262 dest = (unsigned short *) buffer_map->data;
264 for (down = 0; down < height - 1; down++, src += 1, dest += bigwidth+2)
265 for (across = 0; across < width - 1; across++, 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;
285 /* ------------------------------------------- */
289 setup_X(Display * disp, Window win)
291 XWindowAttributes xwa;
294 XGetWindowAttributes(disp, win, &xwa);
298 colormap = xwa.colormap;
299 bigwidth = xwa.width;
300 bigheight = xwa.height;
303 #if 1 /* I'm not entirely sure if I need this */
309 width = bigwidth / 2;
310 height = bigheight / 2;
316 gcv.function = GXcopy;
317 gcv.subwindow_mode = IncludeInferiors;
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);
324 grab_screen_image(xwa.screen, window);
326 orig_map = XGetImage(display, window, 0, 0, xwa.width, xwa.height,
331 gc = XCreateGC(display, window, 0, &gcv);
333 fprintf(stderr, "XCreateGC failed\n");
342 #ifdef HAVE_XSHM_EXTENSION
344 buffer_map = create_xshm_image(display, xwa.visual, depth,
345 ZPixmap, 0, &shm_info, bigwidth, bigheight);
348 fprintf(stderr, "create_xshm_image failed\n");
351 #endif /* HAVE_XSHM_EXTENSION */
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);
366 #ifdef HAVE_XSHM_EXTENSION
368 XShmPutImage(display, window, gc, buffer_map, 0, 0, 0, 0,
369 bigwidth, bigheight, False);
371 #endif /* HAVE_XSHM_EXTENSION */
372 XPutImage(display, window, gc, buffer_map, 0, 0, 0, 0,
373 bigwidth, bigheight);
377 /* ------------------------------------------- */
381 cinterp(double a, int bg, int fg)
384 result = (int)((1-a) * bg + a * fg + 0.5);
385 if (result < 0) result = 0;
386 if (result > 255) result = 255;
391 /* Interpolate the ripple colours between the background colour and
394 init_linear_colors(void)
396 int i, j, red, green, blue, bred, bgreen, bblue;
399 if (ncolors < 2 || mono_p)
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.
407 fg.pixel = get_pixel_resource("foreground", "Foreground",
409 XQueryColor(display, colormap, &fg);
411 green = (fg.green >> 8);
412 blue = (fg.blue >> 8);
414 bg.pixel = get_pixel_resource("background", "Background",
416 XQueryColor(display, colormap, &bg);
417 bred = (bg.red >> 8);
418 bgreen = (bg.green >> 8);
419 bblue = (bg.blue >> 8);
422 for (i = 0; i < ncolors+1; i++) {
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);
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;
434 XAllocColor(display, colormap, &xcl);
436 ctab[j++] = (int) xcl.pixel;
442 init_oily_colors(void)
444 XColor *colors = NULL;
446 if (ncolors < 2 || mono_p)
453 colors = (XColor *)malloc(sizeof(*colors) * (ncolors+1));
454 make_smooth_colormap(display, visual, colormap, colors, &ncolors,
456 False, /* not writable */
457 True); /* verbose (complain about failure) */
467 for (i = 0; i < ncolors+1; i++) {
468 XAllocColor(display, colormap, colors+i);
469 ctab[j++] = (int) colors[i].pixel;
474 ctab[1] = get_pixel_resource("foreground", "Foreground", display, colormap);
475 ctab[0] = get_pixel_resource("background", "Background", display, colormap);
480 /* ------------------------------------------- */
483 /* Shape of drop to add */
489 i = (int)(x * TABLE + 0.5);
490 if (i >= TABLE) i = (TABLE-1) - (i-(TABLE-1));
493 return cos(x * M_PI/2);
504 add_circle_drop(int x, int y, int radius, int dheight)
507 short *buf = (random()&1) ? bufferA : bufferB;
510 r2 = radius * radius;
512 for (cy = -radius; cy <= radius; cy++)
513 for (cx = -radius; cx <= radius; cx++) {
514 int r = cx*cx + cy*cy;
516 buf[i + cx + cy*width] =
517 (short)(dheight * sinc(sqrt(r)/radius));
524 add_drop(ripple_mode mode, int drop)
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 */
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] =
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);
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) */
560 short *buf = (random()&1) ? bufferA : bufferB;
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);
574 static double ang = 0;
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);
580 if (ang > 12*M_PI) ang = 0;
588 init_ripples(int ndrops, int splash)
592 bufferA = (short *)calloc(width * height, sizeof(*bufferA));
593 bufferB = (short *)calloc(width * height, sizeof(*bufferB));
594 temp = (short *)calloc(width * height, sizeof(*temp));
596 dirty_buffer = (char *)malloc(width * height);
597 memset(dirty_buffer, DIRTY, width * height);
599 for (i = 0; i < ndrops; i++)
600 add_drop(ripple_blob, splash);
603 /* There's got to be a better way of doing this XCopyArea? */
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));
614 int across, down, color;
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);
627 Explanation from hq_water.zip (Arturo R Montesinos (ARM) arami@fi.upm.es)
629 Differential equation is: u = a ( u + u )
632 Where a = tension * gravity / surface_density.
634 Approximating second derivatives by central differences:
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
638 where dt = time step squared, h= dx*dy = mesh resolution squared.
640 From where u(t+1) may be calculated as:
643 u(t+1) = a -- | 1 0 1 |u - u(t-1) + (2-4a --)u
646 When a*dt/h = 1/2 last term vanishes, giving:
649 u(t+1) = - | 1 0 1 |u - u(t-1)
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])
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
665 int across, down, pixel;
681 /* Traditional, squarer ripples */
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++) {
688 (((src[pixel - 1] + src[pixel + 1] +
689 src[pixel - width] + src[pixel + width]) / 2)) - dest[pixel];
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++) {
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] +
702 dest[pixel] = damp - (damp >> fluidity);
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++) {
710 (((src[pixel - 1] + src[pixel + 1] +
711 src[pixel - width] + src[pixel + width]) / 2)) - dest[pixel];
712 dest[pixel] = damp - (damp >> fluidity);
716 if (++count > 3) count = 0;
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++) {
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);
734 draw_transparent(dest);
740 /* ------------------------------------------- */
743 char *progclass = "Ripples";
747 ".background: black",
748 ".foreground: #FFAF5F",
750 "*dontClearRoot: True",
759 #ifdef HAVE_XSHM_EXTENSION
761 #endif /* HAVE_XSHM_EXTENSION */
765 XrmOptionDescRec options[] =
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 */
785 void screenhack(Display *disp, Window win)
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");
798 if (fluidity <= 1) fluidity = 1;
799 if (fluidity > 16) fluidity = 16; /* 16 = sizeof(short) */
800 if (light < 0) light = 0;
803 for (i = 0; i < TABLE; i++)
804 cos_tab[i] = cos(i * M_PI/2 / TABLE);
809 ncolors = get_integer_resource ("colors", "Colors");
810 if (0 == ncolors) /* English spelling? */
811 ncolors = get_integer_resource ("colours", "Colors");
815 init_linear_colors();
817 if (transparent && light > 0) {
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;
826 init_ripples(0, -SPLASH); /* Start off without any drops */
829 if (rate > 0 && (iterations % rate) == 0)
830 add_drop(ripple_blob, -SPLASH);
832 add_drop(ripple_stir, -SPLASH);
833 if (box > 0 && (random() % box) == 0)
834 add_drop(ripple_box, -SPLASH);
839 XSync(display,False);
840 screenhack_handle_events(display);