ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.22.tar.gz
[xscreensaver] / hacks / rd-bomb.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998, 1999, 2003
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 tt = epoch_time / 500;
143       if (tt > 15)
144         tt = 15;
145       sleep(tt);
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;  -- crashes on Alpha */
202     int   *qqq = ((int  *) pix_buf) + width * i;
203     ushort *i1 = r1 + 1 + w2 * ii;
204     ushort *i2 = r2 + 1 + w2 * ii;
205     ushort *o1 = r1b + 1 + w2 * ii;
206     ushort *o2 = r2b + 1 + w2 * ii;
207     for (j = 0; j < width; j++) {
208 #if test_pattern_hyper
209       int r1 = (i * j + (frame&127)*frame)&65535;
210 #else
211       int uvv, r1 = 0, r2 = 0;
212       switch (diffusion) {
213       case 0:
214         r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
215         r1 = r1 / 5;
216         r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
217         r2 = r2 / 12;
218         break;
219       case 1:
220         r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
221         r1 = r1 >> 2;
222         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
223         r2 = r2 >> 3;
224         break;
225       case 2:
226         r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
227         r1 = r1 >> 3;
228         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
229         r2 = r2 >> 3;
230         break;
231       }
232
233       /* John E. Pearson "Complex Patterns in a Simple System"
234          Science, July 1993 */
235
236       uvv = (((r1 * r2) >> bps) * r2) >> bps;
237       switch (reaction) {  /* costs 4% */
238       case 0:
239         r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
240         r2 += 4 * (uvv - ((80 * r2) >> 10));
241         break;
242       case 1:
243         r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
244         r2 += 3 * (uvv - ((80 * r2) >> 10));
245         break;
246       case 2:
247         r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
248         r2 += 3 * (uvv - ((80 * r2) >> 10));
249         break;
250       }
251       if (r1 > mx) r1 = mx;
252       if (r2 > mx) r2 = mx;
253       if (r1 < 0) r1 = 0;
254       if (r2 < 0) r2 = 0;
255       o1[j] = r1;
256       o2[j] = r2;
257 #endif
258
259       /* this is terrible.  here i want to assume ncolors = 256.
260          should lose double indirection */
261
262       if (mapped)
263 #if dither_when_mapped
264         q[j] = colors[mc[r1]  % ncolors].pixel;
265 #else
266       q[j] = colors[(r1>>8) % ncolors].pixel;
267 #endif
268       else if (pdepth == 8)
269         q[j] = colors[(r1>>8) % ncolors].pixel;
270       else if (pdepth == 16)
271 #if dither_when_mapped
272         qq[j] = colors[mc[r1] % ncolors].pixel;
273 #else
274         qq[j] = colors[(r1>>8) % ncolors].pixel;
275 #endif
276       else if (pdepth == 32)
277 #if dither_when_mapped
278         qqq[j] = colors[mc[r1] % ncolors].pixel;
279 #else
280         qqq[j] = colors[(r1>>8) % ncolors].pixel;
281 #endif
282       else
283         abort();
284     }
285   }
286   t = r1; r1 = r1b; r1b = t;
287   t = r2; r2 = r2b; r2b = t;  
288 }
289
290
291 /* ------------- xscreensaver rendering -------------- */
292
293
294
295 char *progclass = "RD";
296
297
298 char *defaults [] = {
299   ".background: black",
300   ".foreground: white",
301   "*width:      0",                     /* tried to use -1 but it complained */
302   "*height:     0",
303   "*epoch:      40000",
304   "*reaction:   -1",
305   "*diffusion:  -1",
306   "*verbose:    off",
307   "*radius:     -1",
308   "*speed:      0.0",
309   "*size:       1.0",
310   "*delay:      1",
311   "*colors:     -1",
312 #ifdef HAVE_XSHM_EXTENSION
313   "*useSHM:     True",
314 #endif /* HAVE_XSHM_EXTENSION */
315   0
316 };
317
318 XrmOptionDescRec options [] = {
319   { "-width",           ".width",       XrmoptionSepArg, 0 },
320   { "-height",          ".height",      XrmoptionSepArg, 0 },
321   { "-epoch",           ".epoch",       XrmoptionSepArg, 0 },
322   { "-reaction",        ".reaction",    XrmoptionSepArg, 0 },
323   { "-diffusion",       ".diffusion",   XrmoptionSepArg, 0 },
324   { "-verbose",         ".verbose",     XrmoptionNoArg, "True" },
325   { "-radius",          ".radius",      XrmoptionSepArg, 0 },
326   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
327   { "-size",            ".size",        XrmoptionSepArg, 0 },
328   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
329   { "-ncolors",         ".colors",      XrmoptionSepArg, 0 },
330 #ifdef HAVE_XSHM_EXTENSION
331   { "-shm",             ".useSHM",      XrmoptionNoArg, "True" },
332   { "-no-shm",          ".useSHM",      XrmoptionNoArg, "False" },
333 #endif /* HAVE_XSHM_EXTENSION */
334   { 0, 0, 0, 0 }
335 };
336
337
338 static void
339 random_colors(void)
340 {
341   memset(colors, 0, ncolors*sizeof(*colors));
342   make_smooth_colormap (display, visual, cmap, colors, &ncolors,
343                         True, 0, True);
344   if (ncolors <= 2) {
345     mono_p = True;
346     ncolors = 2;
347     colors[0].flags = DoRed|DoGreen|DoBlue;
348     colors[0].red = colors[0].green = colors[0].blue = 0;
349     XAllocColor(display, cmap, &colors[0]);
350     colors[1].flags = DoRed|DoGreen|DoBlue;
351     colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
352     XAllocColor(display, cmap, &colors[1]);
353   }
354
355   /* Scale it up so that there are exactly 255 colors -- that keeps the
356      animation speed consistent, even when there aren't many allocatable
357      colors, and prevents the -mono mode from looking like static. */
358   if (ncolors != 255) {
359     int i, n = 255;
360     double scale = (double) ncolors / (double) (n+1);
361     XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
362     for (i = 0; i < n; i++)
363       c2[i] = colors[(int) (i * scale)];
364     free(colors);
365     colors = c2;
366     ncolors = n;
367   }
368
369 }
370
371 /* should factor into RD-specfic and compute-every-pixel general */
372 void
373 screenhack (Display *dpy, Window win)
374 {
375   GC gc;
376   XGCValues gcv;
377   XWindowAttributes xgwa;
378   XImage *image;
379   int array_width, array_height;
380   double array_x, array_y;
381   double array_dx, array_dy;
382   int w2;
383   char *pd;
384   int vdepth;
385   int npix;
386 #ifdef HAVE_XSHM_EXTENSION
387   Bool use_shm = get_boolean_resource("useSHM", "Boolean");
388   XShmSegmentInfo shm_info;
389 #endif
390
391   double delay = get_float_resource ("delay", "Float");
392
393   display = dpy;
394   window = win;
395
396
397   XGetWindowAttributes (dpy, win, &xgwa);
398   visual = xgwa.visual;
399   pixack_init(&width, &height);
400   {
401     double s = get_float_resource ("size", "Float");
402     double p = get_float_resource ("speed", "Float");
403     if (s < 0.0 || s > 1.0)
404       s = 1.0;
405     s = sqrt(s);
406     array_width = xgwa.width * s;
407     array_height = xgwa.height * s;
408     if (s < 0.99) {
409       array_width = (array_width / width) * width;
410       array_height = (array_height / height) * height;
411     }
412     if (array_width < width) array_width = width;
413     if (array_height < height) array_height = height;
414     array_x = (xgwa.width - array_width)/2;
415     array_y = (xgwa.height - array_height)/2;
416     array_dx = p;
417     array_dy = .31415926 * p;
418
419     /* start in a random direction */
420     if (random() & 1) array_dx = -array_dx;
421     if (random() & 1) array_dy = -array_dy;
422
423   }
424   verbose = get_boolean_resource ("verbose", "Boolean");
425   npix = (width + 2) * (height + 2);
426   w2 = width + 2;
427   gcv.function = GXcopy;
428   gc = XCreateGC(dpy, win, GCFunction, &gcv);
429   vdepth = visual_depth(DefaultScreenOfDisplay(dpy), xgwa.visual);
430
431   /* This code only deals with pixmap depths of 1, 8, 16, and 32.
432      Therefore, we assume that those depths will be supported by the
433      coresponding visual depths (that depth-24 displays accept depth-32
434      pixmaps, and that depth-12 displays accept depth-16 pixmaps.) */
435   pdepth = (vdepth == 1 ? 1 :
436             vdepth <= 8 ? 8 :
437             vdepth <= 16 ? 16 :
438             32);
439
440   /* Ok, this like, sucks and stuff.  There are some XFree86 systems
441      that have depth-24 visuals, that do not accept depth-32 XImages!
442      Which if you ask me is just absurd, since all it would take is
443      for the server to truncate the bits in that case.  So, this crap
444      here detects the specific case of: we have chosen depth 32;
445      and the server does not support depth 32.  In that case, we
446      try and use depth 16 instead.
447
448      The real fix would be to rewrite this program to deal with
449      depth 24 directly (or even better, arbitrary depths, but that
450      would mean going through the XImage routines instead of messing
451      with the XImage->data directly.)
452
453      jwz, 18-Mar-99: well, the X servers I have access to these days do
454      support 32-deep images on deep visuals, so I no longer have the
455      ability to test this code -- but it was causing problems on the
456      visuals that I do have, and I think that's because I mistakenly
457      wrote `pfv[i].depth' when I meant to write `pfv[i].bits_per_pixel'.
458      The symptom I was seeing was that the grid was 64x64, but the
459      images were being drawn 32x32 -- so there was a black stripe on
460      every other row.  Wow, this code sucks so much.
461    */
462   if (pdepth == 32)
463     {
464       int i, pfvc = 0;
465       Bool ok = False;
466       XPixmapFormatValues *pfv = XListPixmapFormats (dpy, &pfvc);
467       for (i = 0; i < pfvc; i++)
468         if (pfv[i].bits_per_pixel == pdepth)
469           ok = True;
470       if (!ok)
471         pdepth = 16;
472     }
473
474   cmap = xgwa.colormap;
475   ncolors = get_integer_resource ("colors", "Integer");
476
477   if (ncolors <= 0) {
478     if (vdepth > 8)
479       ncolors = 2047;
480     else
481       ncolors = 255;
482   }
483
484   if (mono_p || ncolors < 2) ncolors = 2;
485   if (ncolors <= 2) mono_p = True;
486   colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
487
488   mapped = (vdepth <= 8 &&
489             has_writable_cells(xgwa.screen, xgwa.visual));
490
491   {
492     int i, di;
493     mc = (unsigned char *) malloc(1<<16);
494     for (i = 0; i < (1<<16); i++) {
495       di = (i + (random()&255))>>8;
496       if (di > 255) di = 255;
497       mc[i] = di;
498     }
499   }
500
501   pd = malloc(npix * (pdepth == 1 ? 1 : (pdepth / 8)));
502   if (!pd) {
503     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
504     exit(1);
505   }
506
507   image = 0;
508
509 #ifdef HAVE_XSHM_EXTENSION
510   if (use_shm)
511     {
512       image = create_xshm_image(dpy, xgwa.visual, vdepth,
513                                 ZPixmap, 0, &shm_info, width, height);
514       if (!image)
515         use_shm = False;
516       else
517         {
518           free(pd);
519           pd = image->data;
520         }
521     }
522 #endif /* HAVE_XSHM_EXTENSION */
523
524   if (!image)
525     {
526       image = XCreateImage(dpy, xgwa.visual, vdepth,
527                            ZPixmap, 0, pd,
528                            width, height, 8, 0);
529     }
530
531   while (1) {
532     Bool bump = False;
533
534     int i, j;
535     pixack_frame(pd);
536     for (i = 0; i < array_width; i += width)
537       for (j = 0; j < array_height; j += height)
538 #ifdef HAVE_XSHM_EXTENSION
539         if (use_shm)
540           XShmPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
541                        width, height, False);
542         else
543 #endif
544           XPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
545                     width, height);
546
547     array_x += array_dx;
548     array_y += array_dy;
549     if (array_x < 0) {
550       array_x = 0;
551       array_dx = -array_dx;
552       bump = True;
553     } else if (array_x > (xgwa.width - array_width)) {
554       array_x = (xgwa.width - array_width);
555       array_dx = -array_dx;
556       bump = True;
557     }
558     if (array_y < 0) {
559       array_y = 0;
560       array_dy = -array_dy;
561       bump = True;
562     } else if (array_y > (xgwa.height - array_height)) {
563       array_y = (xgwa.height - array_height);
564       array_dy = -array_dy;
565       bump = True;
566     }
567
568     if (bump) {
569       if (random() & 1) {
570         double swap = array_dx;
571         array_dx = array_dy;
572         array_dy = swap;
573       }
574     }
575
576     frame++;
577
578     XSync(dpy, False);
579     screenhack_handle_events (dpy);
580     if (delay > 0)
581       usleep(1000 * delay);
582   }
583 }