1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998, 1999
2 * Jamie Zawinski <jwz@jwz.org>
4 * reaction/diffusion textures
5 * Copyright (c) 1997 Scott Draves spot@transmeta.com
6 * this code is derived from Bomb
7 * see http://www.cs.cmu.edu/~spot/bomb.html
9 * Permission to use, copy, modify, distribute, and sell this software and its
10 * documentation for any purpose is hereby granted without fee, provided that
11 * the above copyright notice appear in all copies and that both that
12 * copyright notice and this permission notice appear in supporting
13 * documentation. No representations are made about the suitability of this
14 * software for any purpose. It is provided "as is" without express or
17 * And remember: X Windows is to graphics hacking as roman numerals are to
18 * the square root of pi.
23 #include "screenhack.h"
24 #include <X11/Xutil.h>
26 #ifdef HAVE_XSHM_EXTENSION
28 #endif /* HAVE_XSHM_EXTENSION */
31 #define dither_when_mapped 1
34 static int ncolors = 0;
35 static XColor *colors = 0;
36 static Display *display;
37 static Visual *visual;
38 #if dither_when_mapped
39 static unsigned char *mc = 0;
41 static Colormap cmap = 0;
45 static void random_colors(void);
47 /* -----------------------------------------------------------
48 pixel hack, 8-bit pixel grid, first/next frame interface
50 pixack_init(int *size_h, int *size_v)
51 pixack_frame(char *pix_buf)
56 #define mx ((1<<16)-1)
58 /* you can replace integer mults wish shift/adds with these,
59 but it doesn't help on my 586 */
60 #define x5(n) ((n<<2)+n)
61 #define x7(n) ((n<<3)-n)
64 #define R (ya_random()&((1<<30)-1))
66 static int frame = 0, epoch_time;
67 static ushort *r1, *r2, *r1b, *r2b;
68 static int width, height, npix;
70 static int reaction = 0;
71 static int diffusion = 0;
73 /* returns number of pixels that the pixack produces. called once. */
75 pixack_init(int *size_h, int *size_v)
78 width = get_integer_resource ("width", "Integer");
79 height = get_integer_resource ("height", "Integer");
80 sz_base = 80 + (R%40);
81 if (width <= 0) width = (R%20) ? sz_base : (28 + R%10);
82 if (height <= 0) height = (R%20) ? sz_base : (28 + R%10);
84 /* jwz: when (and only when) XSHM is in use on an SGI 8-bit visual,
85 we get shear unless width is a multiple of 4. I don't understand
86 why. This is undoubtedly the wrong fix... */
90 if (width < 10) width = 10;
91 if (height < 10) height = 10;
92 epoch_time = get_integer_resource ("epoch", "Integer");
93 npix = (width + 2) * (height + 2);
94 r1 = (ushort *) malloc(sizeof(ushort) * npix);
95 r2 = (ushort *) malloc(sizeof(ushort) * npix);
96 r1b = (ushort *) malloc(sizeof(ushort) * npix);
97 r2b = (ushort *) malloc(sizeof(ushort) * npix);
99 if (!r1 || !r2 || !r1b || !r2b) {
100 fprintf(stderr, "not enough memory for %d pixels.\n", npix);
108 #define test_pattern_hyper 0
111 /* returns the pixels. called many times. */
113 pixack_frame(char *pix_buf)
118 #if test_pattern_hyper
127 #ifdef GETTIMEOFDAY_TWO_ARGS
129 gettimeofday(&tp, &tzp);
133 tm2 = tp.tv_sec + tp.tv_usec * 1e-6;
135 printf("fps = %2.4g\n", 100.0 / (tm2 - tm));
139 if (!(frame%epoch_time)) {
142 int t = epoch_time / 500;
148 for (i = 0; i < npix; i++) {
156 XSetWindowBackground(display, window, colors[255 % ncolors].pixel);
157 XClearWindow(display, window);
159 s = w2 * (height/2) + width/2;
160 radius = get_integer_resource ("radius", "Integer");
162 int maxr = width/2-2;
163 int maxr2 = height/2-2;
164 if (maxr2 < maxr) maxr = maxr2;
167 radius = 1 + ((R%10) ? (R%5) : (R % maxr));
168 if (radius > maxr) radius = maxr;
170 for (i = -radius; i < (radius+1); i++)
171 for (j = -radius; j < (radius+1); j++)
172 r2[s + i + j*w2] = mx - (R&63);
173 reaction = get_integer_resource ("reaction", "Integer");
174 if (reaction < 0 || reaction > 2) reaction = R&1;
175 diffusion = get_integer_resource ("diffusion", "Integer");
176 if (diffusion < 0 || diffusion > 2)
177 diffusion = (R%5) ? ((R%3)?0:1) : 2;
178 if (2 == reaction && 2 == diffusion)
179 reaction = diffusion = 0;
182 printf("reaction = %d\ndiffusion = %d\nradius = %d\n",
183 reaction, diffusion, radius);
185 for (i = 0; i <= width+1; i++) {
186 r1[i] = r1[i + w2 * height];
187 r2[i] = r2[i + w2 * height];
188 r1[i + w2 * (height + 1)] = r1[i + w2];
189 r2[i + w2 * (height + 1)] = r2[i + w2];
191 for (i = 0; i <= height+1; i++) {
192 r1[w2 * i] = r1[width + w2 * i];
193 r2[w2 * i] = r2[width + w2 * i];
194 r1[w2 * i + width + 1] = r1[w2 * i + 1];
195 r2[w2 * i + width + 1] = r2[w2 * i + 1];
197 for (i = 0; i < height; i++) {
199 char *q = pix_buf + width * i;
200 short *qq = ((short *) pix_buf) + width * i;
201 long *qqq = ((long *) pix_buf) + width * i;
202 ushort *i1 = r1 + 1 + w2 * ii;
203 ushort *i2 = r2 + 1 + w2 * ii;
204 ushort *o1 = r1b + 1 + w2 * ii;
205 ushort *o2 = r2b + 1 + w2 * ii;
206 for (j = 0; j < width; j++) {
207 #if test_pattern_hyper
208 int r1 = (i * j + (frame&127)*frame)&65535;
210 int uvv, r1 = 0, r2 = 0;
213 r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
215 r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
219 r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
221 r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
225 r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
227 r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
232 /* John E. Pearson "Complex Patterns in a Simple System"
233 Science, July 1993 */
235 uvv = (((r1 * r2) >> bps) * r2) >> bps;
236 switch (reaction) { /* costs 4% */
238 r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
239 r2 += 4 * (uvv - ((80 * r2) >> 10));
242 r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
243 r2 += 3 * (uvv - ((80 * r2) >> 10));
246 r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
247 r2 += 3 * (uvv - ((80 * r2) >> 10));
250 if (r1 > mx) r1 = mx;
251 if (r2 > mx) r2 = mx;
258 /* this is terrible. here i want to assume ncolors = 256.
259 should lose double indirection */
262 #if dither_when_mapped
263 q[j] = colors[mc[r1] % ncolors].pixel;
265 q[j] = colors[(r1>>8) % ncolors].pixel;
267 else if (pdepth == 8)
268 q[j] = colors[(r1>>8) % ncolors].pixel;
269 else if (pdepth == 16)
270 #if dither_when_mapped
271 qq[j] = colors[mc[r1] % ncolors].pixel;
273 qq[j] = colors[(r1>>8) % ncolors].pixel;
275 else if (pdepth == 32)
276 #if dither_when_mapped
277 qqq[j] = colors[mc[r1] % ncolors].pixel;
279 qqq[j] = colors[(r1>>8) % ncolors].pixel;
285 t = r1; r1 = r1b; r1b = t;
286 t = r2; r2 = r2b; r2b = t;
290 /* ------------- xscreensaver rendering -------------- */
294 char *progclass = "RD";
297 char *defaults [] = {
298 ".background: black",
299 ".foreground: white",
300 "*width: 0", /* tried to use -1 but it complained */
311 #ifdef HAVE_XSHM_EXTENSION
313 #endif /* HAVE_XSHM_EXTENSION */
317 XrmOptionDescRec options [] = {
318 { "-width", ".width", XrmoptionSepArg, 0 },
319 { "-height", ".height", XrmoptionSepArg, 0 },
320 { "-epoch", ".epoch", XrmoptionSepArg, 0 },
321 { "-reaction", ".reaction", XrmoptionSepArg, 0 },
322 { "-diffusion", ".diffusion", XrmoptionSepArg, 0 },
323 { "-verbose", ".verbose", XrmoptionNoArg, "True" },
324 { "-radius", ".radius", XrmoptionSepArg, 0 },
325 { "-speed", ".speed", XrmoptionSepArg, 0 },
326 { "-size", ".size", XrmoptionSepArg, 0 },
327 { "-delay", ".delay", XrmoptionSepArg, 0 },
328 { "-ncolors", ".colors", XrmoptionSepArg, 0 },
329 #ifdef HAVE_XSHM_EXTENSION
330 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
331 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
332 #endif /* HAVE_XSHM_EXTENSION */
340 memset(colors, 0, ncolors*sizeof(*colors));
341 make_smooth_colormap (display, visual, cmap, colors, &ncolors,
346 colors[0].flags = DoRed|DoGreen|DoBlue;
347 colors[0].red = colors[0].green = colors[0].blue = 0;
348 XAllocColor(display, cmap, &colors[0]);
349 colors[1].flags = DoRed|DoGreen|DoBlue;
350 colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
351 XAllocColor(display, cmap, &colors[1]);
354 /* Scale it up so that there are exactly 255 colors -- that keeps the
355 animation speed consistent, even when there aren't many allocatable
356 colors, and prevents the -mono mode from looking like static. */
357 if (ncolors != 255) {
359 double scale = (double) ncolors / (double) (n+1);
360 XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
361 for (i = 0; i < n; i++)
362 c2[i] = colors[(int) (i * scale)];
370 /* should factor into RD-specfic and compute-every-pixel general */
372 screenhack (Display *dpy, Window win)
376 XWindowAttributes xgwa;
378 int array_width, array_height;
379 double array_x, array_y;
380 double array_dx, array_dy;
385 #ifdef HAVE_XSHM_EXTENSION
386 Bool use_shm = get_boolean_resource("useSHM", "Boolean");
387 XShmSegmentInfo shm_info;
390 double delay = get_float_resource ("delay", "Float");
396 XGetWindowAttributes (dpy, win, &xgwa);
397 visual = xgwa.visual;
398 pixack_init(&width, &height);
400 double s = get_float_resource ("size", "Float");
401 double p = get_float_resource ("speed", "Float");
402 if (s < 0.0 || s > 1.0)
405 array_width = xgwa.width * s;
406 array_height = xgwa.height * s;
408 array_width = (array_width / width) * width;
409 array_height = (array_height / height) * height;
411 if (array_width < width) array_width = width;
412 if (array_height < height) array_height = height;
413 array_x = (xgwa.width - array_width)/2;
414 array_y = (xgwa.height - array_height)/2;
416 array_dy = .31415926 * p;
418 /* start in a random direction */
419 if (random() & 1) array_dx = -array_dx;
420 if (random() & 1) array_dy = -array_dy;
423 verbose = get_boolean_resource ("verbose", "Boolean");
424 npix = (width + 2) * (height + 2);
426 gcv.function = GXcopy;
427 gc = XCreateGC(dpy, win, GCFunction, &gcv);
428 vdepth = visual_depth(DefaultScreenOfDisplay(dpy), xgwa.visual);
430 /* This code only deals with pixmap depths of 1, 8, 16, and 32.
431 Therefore, we assume that those depths will be supported by the
432 coresponding visual depths (that depth-24 displays accept depth-32
433 pixmaps, and that depth-12 displays accept depth-16 pixmaps.) */
434 pdepth = (vdepth == 1 ? 1 :
439 /* Ok, this like, sucks and stuff. There are some XFree86 systems
440 that have depth-24 visuals, that do not accept depth-32 XImages!
441 Which if you ask me is just absurd, since all it would take is
442 for the server to truncate the bits in that case. So, this crap
443 here detects the specific case of: we have chosen depth 32;
444 and the server does not support depth 32. In that case, we
445 try and use depth 16 instead.
447 The real fix would be to rewrite this program to deal with
448 depth 24 directly (or even better, arbitrary depths, but that
449 would mean going through the XImage routines instead of messing
450 with the XImage->data directly.)
456 XPixmapFormatValues *pfv = XListPixmapFormats (dpy, &pfvc);
457 for (i = 0; i < pfvc; i++)
458 if (pfv[i].depth == pdepth)
464 cmap = xgwa.colormap;
465 ncolors = get_integer_resource ("colors", "Integer");
474 if (mono_p || ncolors < 2) ncolors = 2;
475 if (ncolors <= 2) mono_p = True;
476 colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
478 mapped = (vdepth <= 8 &&
479 has_writable_cells(xgwa.screen, xgwa.visual));
483 mc = (unsigned char *) malloc(1<<16);
484 for (i = 0; i < (1<<16); i++) {
485 di = (i + (random()&255))>>8;
486 if (di > 255) di = 255;
491 p = malloc(npix * (pdepth == 1 ? 1 : (pdepth / 8)));
493 fprintf(stderr, "not enough memory for %d pixels.\n", npix);
499 #ifdef HAVE_XSHM_EXTENSION
502 image = create_xshm_image(dpy, xgwa.visual, vdepth,
503 ZPixmap, 0, &shm_info, width, height);
512 #endif /* HAVE_XSHM_EXTENSION */
516 image = XCreateImage(dpy, xgwa.visual, vdepth,
518 width, height, 8, 0);
526 for (i = 0; i < array_width; i += width)
527 for (j = 0; j < array_height; j += height)
528 #ifdef HAVE_XSHM_EXTENSION
530 XShmPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
531 width, height, False);
534 XPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
541 array_dx = -array_dx;
543 } else if (array_x > (xgwa.width - array_width)) {
544 array_x = (xgwa.width - array_width);
545 array_dx = -array_dx;
550 array_dy = -array_dy;
552 } else if (array_y > (xgwa.height - array_height)) {
553 array_y = (xgwa.height - array_height);
554 array_dy = -array_dy;
560 double swap = array_dx;
569 screenhack_handle_events (dpy);
571 usleep(1000 * delay);