http://ftp.aanet.ru/pub/Linux/X11/apps/xscreensaver-2.31.tar.gz
[xscreensaver] / hacks / rd-bomb.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998
2  *  Jamie Zawinski <jwz@jwz.org>
3  *
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
8  *
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 
15  * implied warranty.
16  *
17  * And remember: X Windows is to graphics hacking as roman numerals are to
18  * the square root of pi.
19  */
20
21 #include <math.h>
22
23 #include "screenhack.h"
24 #include <X11/Xutil.h>
25
26 #ifdef HAVE_XSHM_EXTENSION
27 # include "xshm.h"
28 #endif /* HAVE_XSHM_EXTENSION */
29
30 /* costs ~6% speed */
31 #define dither_when_mapped 1
32
33 static int verbose;
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;
40 #endif
41 static Colormap cmap = 0;
42 static Window window;
43 static int mapped;
44 static int pdepth;
45 static void random_colors(void);
46
47 /* -----------------------------------------------------------
48    pixel hack, 8-bit pixel grid, first/next frame interface
49
50    pixack_init(int *size_h, int *size_v)
51    pixack_frame(char *pix_buf)
52    */
53
54
55 #define bps 16
56 #define mx ((1<<16)-1)
57
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)
62
63 /* why strip bit? */
64 #define R (ya_random()&((1<<30)-1))
65
66 static int frame = 0, epoch_time;
67 static ushort *r1, *r2, *r1b, *r2b;
68 static int width, height, npix;
69 static int radius;
70 static int reaction = 0;
71 static int diffusion = 0;
72
73 /* returns number of pixels that the pixack produces.  called once. */
74 static void
75 pixack_init(int *size_h, int *size_v) 
76 {
77   int sz_base;
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);
83
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... */
87   width &= ~0x7;
88
89   /* don't go there */
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);
98
99   if (!r1 || !r2 || !r1b || !r2b) {
100     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
101     exit(1);
102   }
103
104   *size_h = width;
105   *size_v = height;
106 }
107
108 #define test_pattern_hyper 0
109
110
111 /* returns the pixels.  called many times. */
112 static void
113 pixack_frame(char *pix_buf) 
114 {
115   int i, j;
116   int w2 = width + 2;
117   ushort *t;
118 #if test_pattern_hyper
119   if (frame&0x100)
120     sleep(1);
121 #endif
122   if (verbose) {
123     double tm = 0;
124     struct timeval tp;
125     if (!(frame%100)) {
126       double tm2;
127 #ifdef GETTIMEOFDAY_TWO_ARGS
128       struct timezone tzp;
129       gettimeofday(&tp, &tzp);
130 #else
131       gettimeofday(&tp);
132 #endif
133       tm2 = tp.tv_sec + tp.tv_usec * 1e-6;
134       if (frame > 0)
135         printf("fps = %2.4g\n", 100.0 / (tm2 - tm));
136       tm = tm2;
137     }
138   }
139   if (!(frame%epoch_time)) {
140     int s;
141     if (0 != frame) {
142       int t = epoch_time / 500;
143       if (t > 15)
144         t = 15;
145       sleep(t);
146     }
147           
148     for (i = 0; i < npix; i++) {
149       /* equilibrium */
150       r1[i] = 65500;
151       r2[i] = 11;
152     }
153
154     random_colors();
155
156     XSetWindowBackground(display, window, colors[255 % ncolors].pixel);
157     XClearWindow(display, window);
158
159     s = w2 * (height/2) + width/2;
160     radius = get_integer_resource ("radius", "Integer");
161     {
162       int maxr = width/2-2;
163       int maxr2 = height/2-2;
164       if (maxr2 < maxr) maxr = maxr2;
165
166       if (radius < 0)
167         radius = 1 + ((R%10) ? (R%5) : (R % maxr));
168       if (radius > maxr) radius = maxr;
169     }
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;
180       
181     if (verbose)
182       printf("reaction = %d\ndiffusion = %d\nradius = %d\n",
183              reaction, diffusion, radius);
184   }
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];
190   }
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];
196   }
197   for (i = 0; i < height; i++) {
198     int ii = i + 1;
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;
209 #else
210       int uvv, r1 = 0, r2 = 0;
211       switch (diffusion) {
212       case 0:
213         r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
214         r1 = r1 / 5;
215         r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
216         r2 = r2 / 12;
217         break;
218       case 1:
219         r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
220         r1 = r1 >> 2;
221         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
222         r2 = r2 >> 3;
223         break;
224       case 2:
225         r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
226         r1 = r1 >> 3;
227         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
228         r2 = r2 >> 3;
229         break;
230       }
231
232       /* John E. Pearson "Complex Patterns in a Simple System"
233          Science, July 1993 */
234
235       uvv = (((r1 * r2) >> bps) * r2) >> bps;
236       switch (reaction) {  /* costs 4% */
237       case 0:
238         r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
239         r2 += 4 * (uvv - ((80 * r2) >> 10));
240         break;
241       case 1:
242         r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
243         r2 += 3 * (uvv - ((80 * r2) >> 10));
244         break;
245       case 2:
246         r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
247         r2 += 3 * (uvv - ((80 * r2) >> 10));
248         break;
249       }
250       if (r1 > mx) r1 = mx;
251       if (r2 > mx) r2 = mx;
252       if (r1 < 0) r1 = 0;
253       if (r2 < 0) r2 = 0;
254       o1[j] = r1;
255       o2[j] = r2;
256 #endif
257
258       /* this is terrible.  here i want to assume ncolors = 256.
259          should lose double indirection */
260
261       if (mapped)
262 #if dither_when_mapped
263         q[j] = colors[mc[r1]  % ncolors].pixel;
264 #else
265       q[j] = colors[(r1>>8) % ncolors].pixel;
266 #endif
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;
272 #else
273         qq[j] = colors[(r1>>8) % ncolors].pixel;
274 #endif
275       else if (pdepth == 32)
276 #if dither_when_mapped
277         qqq[j] = colors[mc[r1] % ncolors].pixel;
278 #else
279         qqq[j] = colors[(r1>>8) % ncolors].pixel;
280 #endif
281       else
282         abort();
283     }
284   }
285   t = r1; r1 = r1b; r1b = t;
286   t = r2; r2 = r2b; r2b = t;  
287 }
288
289
290 /* ------------- xscreensaver rendering -------------- */
291
292
293
294 char *progclass = "RD";
295
296
297 char *defaults [] = {
298   ".background: black",
299   ".foreground: white",
300   "*width:      0",                     /* tried to use -1 but it complained */
301   "*height:     0",
302   "*epoch:      40000",
303   "*reaction:   -1",
304   "*diffusion:  -1",
305   "*verbose:    off",
306   "*radius:     -1",
307   "*speed:      0.0",
308   "*size:       0.66",
309   "*delay:      1",
310   "*colors:     -1",
311 #ifdef HAVE_XSHM_EXTENSION
312   "*useSHM:     True",
313 #endif /* HAVE_XSHM_EXTENSION */
314   0
315 };
316
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 */
333   { 0, 0, 0, 0 }
334 };
335
336
337 static void
338 random_colors(void)
339 {
340   memset(colors, 0, ncolors*sizeof(*colors));
341   make_smooth_colormap (display, visual, cmap, colors, &ncolors,
342                         True, 0, True);
343   if (ncolors <= 2) {
344     mono_p = True;
345     ncolors = 2;
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]);
352   }
353
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) {
358     int i, n = 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)];
363     free(colors);
364     colors = c2;
365     ncolors = n;
366   }
367
368 }
369
370 /* should factor into RD-specfic and compute-every-pixel general */
371 void
372 screenhack (Display *dpy, Window win)
373 {
374   GC gc;
375   XGCValues gcv;
376   XWindowAttributes xgwa;
377   XImage *image;
378   int array_width, array_height;
379   double array_x, array_y;
380   double array_dx, array_dy;
381   int w2;
382   char *p;
383   int vdepth;
384   int npix;
385 #ifdef HAVE_XSHM_EXTENSION
386   Bool use_shm = get_boolean_resource("useSHM", "Boolean");
387   XShmSegmentInfo shm_info;
388 #endif
389
390   double delay = get_float_resource ("delay", "Float");
391
392   display = dpy;
393   window = win;
394
395
396   XGetWindowAttributes (dpy, win, &xgwa);
397   visual = xgwa.visual;
398   pixack_init(&width, &height);
399   {
400     double s = get_float_resource ("size", "Float");
401     double p = get_float_resource ("speed", "Float");
402     if (s < 0.0 || s > 1.0)
403       s = 1.0;
404     s = sqrt(s);
405     array_width = xgwa.width * s;
406     array_height = xgwa.height * s;
407     if (s < 0.99) {
408       array_width = (array_width / width) * width;
409       array_height = (array_height / height) * height;
410     }
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;
415     array_dx = p;
416     array_dy = .31415926 * p;
417
418     /* start in a random direction */
419     if (random() & 1) array_dx = -array_dx;
420     if (random() & 1) array_dy = -array_dy;
421
422   }
423   verbose = get_boolean_resource ("verbose", "Boolean");
424   npix = (width + 2) * (height + 2);
425   w2 = width + 2;
426   gcv.function = GXcopy;
427   gc = XCreateGC(dpy, win, GCFunction, &gcv);
428   vdepth = visual_depth(DefaultScreenOfDisplay(dpy), xgwa.visual);
429
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 :
435             vdepth <= 8 ? 8 :
436             vdepth <= 16 ? 16 :
437             32);
438
439   cmap = xgwa.colormap;
440   ncolors = get_integer_resource ("colors", "Integer");
441
442   if (ncolors <= 0) {
443     if (vdepth > 8)
444       ncolors = 2047;
445     else
446       ncolors = 255;
447   }
448
449   if (mono_p || ncolors < 2) ncolors = 2;
450   if (ncolors <= 2) mono_p = True;
451   colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
452
453   mapped = (vdepth <= 8 &&
454             has_writable_cells(xgwa.screen, xgwa.visual));
455
456   {
457     int i, di;
458     mc = (unsigned char *) malloc(1<<16);
459     for (i = 0; i < (1<<16); i++) {
460       di = (i + (random()&255))>>8;
461       if (di > 255) di = 255;
462       mc[i] = di;
463     }
464   }
465
466   p = malloc(npix * (pdepth == 1 ? 1 : (pdepth / 8)));
467   if (!p) {
468     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
469     exit(1);
470   }
471
472   image = 0;
473
474 #ifdef HAVE_XSHM_EXTENSION
475   if (use_shm)
476     {
477       image = create_xshm_image(dpy, xgwa.visual, vdepth,
478                                 ZPixmap, 0, &shm_info, width, height);
479       if (!image)
480         use_shm = False;
481       else
482         {
483           free(p);
484           p = image->data;
485         }
486     }
487 #endif /* HAVE_XSHM_EXTENSION */
488
489   if (!image)
490     {
491       image = XCreateImage(dpy, xgwa.visual, vdepth,
492                            ZPixmap, 0, p,
493                            width, height, 8, 0);
494     }
495
496   while (1) {
497     Bool bump = False;
498
499     int i, j;
500     pixack_frame(p);
501     for (i = 0; i < array_width; i += width)
502       for (j = 0; j < array_height; j += height)
503 #ifdef HAVE_XSHM_EXTENSION
504         if (use_shm)
505           XShmPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
506                        width, height, False);
507         else
508 #endif
509           XPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
510                     width, height);
511
512     array_x += array_dx;
513     array_y += array_dy;
514     if (array_x < 0) {
515       array_x = 0;
516       array_dx = -array_dx;
517       bump = True;
518     } else if (array_x > (xgwa.width - array_width)) {
519       array_x = (xgwa.width - array_width);
520       array_dx = -array_dx;
521       bump = True;
522     }
523     if (array_y < 0) {
524       array_y = 0;
525       array_dy = -array_dy;
526       bump = True;
527     } else if (array_y > (xgwa.height - array_height)) {
528       array_y = (xgwa.height - array_height);
529       array_dy = -array_dy;
530       bump = True;
531     }
532
533     if (bump) {
534       if (random() & 1) {
535         double swap = array_dx;
536         array_dx = array_dy;
537         array_dy = swap;
538       }
539     }
540
541     frame++;
542
543     XSync(dpy, False);
544     if (delay > 0)
545       usleep(1000 * delay);
546   }
547 }