http://slackware.bholcomb.com/slackware/slackware-11.0/source/xap/xscreensaver/xscree...
[xscreensaver] / hacks / rd-bomb.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998, 1999, 2003, 2006
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
25 #ifdef HAVE_XSHM_EXTENSION
26 # include "xshm.h"
27 #endif /* HAVE_XSHM_EXTENSION */
28
29 /* costs ~6% speed */
30 #define dither_when_mapped 1
31
32 struct state {
33   Display *dpy;
34   Window window;
35
36   int verbose;
37   int ncolors;
38   XColor *colors;
39   Visual *visual;
40 #if dither_when_mapped
41   unsigned char *mc;
42 #endif
43   Colormap cmap;
44   int mapped;
45   int pdepth;
46
47   int frame, epoch_time;
48   unsigned short *r1, *r2, *r1b, *r2b;
49   int width, height, npix;
50   int radius;
51   int reaction;
52   int diffusion;
53
54   char *pd;
55   int array_width, array_height;
56
57 #ifdef HAVE_XSHM_EXTENSION
58   Bool use_shm;
59   XShmSegmentInfo shm_info;
60 #endif
61
62   GC gc;
63   XImage *image;
64   double array_x, array_y;
65   double array_dx, array_dy;
66   XWindowAttributes xgwa;
67   int delay;
68 };
69
70 static void random_colors(struct state *st);
71
72 /* -----------------------------------------------------------
73    pixel hack, 8-bit pixel grid, first/next frame interface
74
75    pixack_init(int *size_h, int *size_v)
76    pixack_frame(char *pix_buf)
77    */
78
79
80 #define bps 16
81 #define mx ((1<<16)-1)
82
83 /* you can replace integer mults wish shift/adds with these,
84    but it doesn't help on my 586 */
85 #define x5(n) ((n<<2)+n)
86 #define x7(n) ((n<<3)-n)
87
88 /* why strip bit? */
89 #define R (random()&((1<<30)-1))
90
91 /* returns number of pixels that the pixack produces.  called once. */
92 static void
93 pixack_init(struct state *st, int *size_h, int *size_v) 
94 {
95   int sz_base;
96   st->width = get_integer_resource (st->dpy, "width", "Integer");
97   st->height = get_integer_resource (st->dpy, "height", "Integer");
98   sz_base = 80 + (R%40);
99   if (st->width <= 0) st->width = (R%20) ? sz_base : (28 + R%10);
100   if (st->height <= 0) st->height = (R%20) ? sz_base : (28 + R%10);
101
102   /* jwz: when (and only when) XSHM is in use on an SGI 8-bit visual,
103      we get shear unless st->width is a multiple of 4.  I don't understand
104      why.  This is undoubtedly the wrong fix... */
105   st->width &= ~0x7;
106
107   /* don't go there */
108   if (st->width < 10) st->width = 10;
109   if (st->height < 10) st->height = 10;
110   st->epoch_time = get_integer_resource (st->dpy, "epoch", "Integer");
111   st->npix = (st->width + 2) * (st->height + 2);
112   st->r1 = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
113   st->r2 = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
114   st->r1b = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
115   st->r2b = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
116
117   if (!st->r1 || !st->r2 || !st->r1b || !st->r2b) {
118     fprintf(stderr, "not enough memory for %d pixels.\n", st->npix);
119     exit(1);
120   }
121
122   *size_h = st->width;
123   *size_v = st->height;
124 }
125
126 #define test_pattern_hyper 0
127
128
129 /* returns the pixels.  called many times. */
130 static void
131 pixack_frame(struct state *st, char *pix_buf) 
132 {
133   int i, j;
134   int w2 = st->width + 2;
135   unsigned short *t;
136 #if test_pattern_hyper
137   if (st->frame&0x100)
138     sleep(1);
139 #endif
140   if (st->verbose) {
141     double tm = 0;
142     struct timeval tp;
143     if (!(st->frame%100)) {
144       double tm2;
145 #ifdef GETTIMEOFDAY_TWO_ARGS
146       struct timezone tzp;
147       gettimeofday(&tp, &tzp);
148 #else
149       gettimeofday(&tp);
150 #endif
151       tm2 = tp.tv_sec + tp.tv_usec * 1e-6;
152       if (st->frame > 0)
153         printf("fps = %2.4g\n", 100.0 / (tm2 - tm));
154       tm = tm2;
155     }
156   }
157   if (!(st->frame%st->epoch_time)) {
158     int s;
159     if (0 != st->frame) {
160       int tt = st->epoch_time / 500;
161       if (tt > 15)
162         tt = 15;
163       /*sleep(tt);*/
164     }
165           
166     for (i = 0; i < st->npix; i++) {
167       /* equilibrium */
168       st->r1[i] = 65500;
169       st->r2[i] = 11;
170     }
171
172     random_colors(st);
173
174     XSetWindowBackground(st->dpy, st->window, st->colors[255 % st->ncolors].pixel);
175     XClearWindow(st->dpy, st->window);
176
177     s = w2 * (st->height/2) + st->width/2;
178     st->radius = get_integer_resource (st->dpy, "radius", "Integer");
179     {
180       int maxr = st->width/2-2;
181       int maxr2 = st->height/2-2;
182       if (maxr2 < maxr) maxr = maxr2;
183
184       if (st->radius < 0)
185         st->radius = 1 + ((R%10) ? (R%5) : (R % maxr));
186       if (st->radius > maxr) st->radius = maxr;
187     }
188     for (i = -st->radius; i < (st->radius+1); i++)
189       for (j = -st->radius; j < (st->radius+1); j++)
190         st->r2[s + i + j*w2] = mx - (R&63);
191     st->reaction = get_integer_resource (st->dpy, "reaction", "Integer");
192     if (st->reaction < 0 || st->reaction > 2) st->reaction = R&1;
193     st->diffusion = get_integer_resource (st->dpy, "diffusion", "Integer");
194     if (st->diffusion < 0 || st->diffusion > 2)
195       st->diffusion = (R%5) ? ((R%3)?0:1) : 2;
196     if (2 == st->reaction && 2 == st->diffusion)
197       st->reaction = st->diffusion = 0;
198       
199     if (st->verbose)
200       printf("reaction = %d\ndiffusion = %d\nradius = %d\n",
201              st->reaction, st->diffusion, st->radius);
202   }
203   for (i = 0; i <= st->width+1; i++) {
204     st->r1[i] = st->r1[i + w2 * st->height];
205     st->r2[i] = st->r2[i + w2 * st->height];
206     st->r1[i + w2 * (st->height + 1)] = st->r1[i + w2];
207     st->r2[i + w2 * (st->height + 1)] = st->r2[i + w2];
208   }
209   for (i = 0; i <= st->height+1; i++) {
210     st->r1[w2 * i] = st->r1[st->width + w2 * i];
211     st->r2[w2 * i] = st->r2[st->width + w2 * i];
212     st->r1[w2 * i + st->width + 1] = st->r1[w2 * i + 1];
213     st->r2[w2 * i + st->width + 1] = st->r2[w2 * i + 1];
214   }
215   for (i = 0; i < st->height; i++) {
216     int ii = i + 1;
217     char *q = pix_buf + st->width * i;
218     short *qq = ((short *) pix_buf) + st->width * i;
219 /*  long  *qqq = ((long *) pix_buf) + st->width * i;  -- crashes on Alpha */
220     int   *qqq = ((int  *) pix_buf) + st->width * i;
221     unsigned short *i1 = st->r1 + 1 + w2 * ii;
222     unsigned short *i2 = st->r2 + 1 + w2 * ii;
223     unsigned short *o1 = st->r1b + 1 + w2 * ii;
224     unsigned short *o2 = st->r2b + 1 + w2 * ii;
225     for (j = 0; j < st->width; j++) {
226 #if test_pattern_hyper
227       int r1 = (i * j + (st->frame&127)*frame)&65535;
228 #else
229       int uvv, r1 = 0, r2 = 0;
230       switch (st->diffusion) {
231       case 0:
232         r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
233         r1 = r1 / 5;
234         r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
235         r2 = r2 / 12;
236         break;
237       case 1:
238         r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
239         r1 = r1 >> 2;
240         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
241         r2 = r2 >> 3;
242         break;
243       case 2:
244         r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
245         r1 = r1 >> 3;
246         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
247         r2 = r2 >> 3;
248         break;
249       }
250
251       /* John E. Pearson "Complex Patterns in a Simple System"
252          Science, July 1993 */
253
254       uvv = (((r1 * r2) >> bps) * r2) >> bps;
255       switch (st->reaction) {  /* costs 4% */
256       case 0:
257         r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
258         r2 += 4 * (uvv - ((80 * r2) >> 10));
259         break;
260       case 1:
261         r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
262         r2 += 3 * (uvv - ((80 * r2) >> 10));
263         break;
264       case 2:
265         r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
266         r2 += 3 * (uvv - ((80 * r2) >> 10));
267         break;
268       }
269       if (r1 > mx) r1 = mx;
270       if (r2 > mx) r2 = mx;
271       if (r1 < 0) r1 = 0;
272       if (r2 < 0) r2 = 0;
273       o1[j] = r1;
274       o2[j] = r2;
275 #endif
276
277       /* this is terrible.  here i want to assume ncolors = 256.
278          should lose double indirection */
279
280       if (st->mapped)
281 #if dither_when_mapped
282         q[j] = st->colors[st->mc[r1]  % st->ncolors].pixel;
283 #else
284       q[j] = st->colors[(r1>>8) % st->ncolors].pixel;
285 #endif
286       else if (st->pdepth == 8)
287         q[j] = st->colors[(r1>>8) % st->ncolors].pixel;
288       else if (st->pdepth == 16)
289 #if dither_when_mapped
290         qq[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
291 #else
292         qq[j] = st->colors[(r1>>8) % st->ncolors].pixel;
293 #endif
294       else if (st->pdepth == 32)
295 #if dither_when_mapped
296         qqq[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
297 #else
298         qqq[j] = st->colors[(r1>>8) % st->ncolors].pixel;
299 #endif
300       else
301         abort();
302     }
303   }
304   t = st->r1; st->r1 = st->r1b; st->r1b = t;
305   t = st->r2; st->r2 = st->r2b; st->r2b = t;  
306 }
307
308
309 /* ------------- xscreensaver rendering -------------- */
310
311 static const char *rd_defaults [] = {
312   ".background: black",
313   ".foreground: white",
314   "*width:      0",                     /* tried to use -1 but it complained */
315   "*height:     0",
316   "*epoch:      40000",
317   "*reaction:   -1",
318   "*diffusion:  -1",
319   "*verbose:    off",
320   "*radius:     -1",
321   "*speed:      0.0",
322   "*size:       1.0",
323   "*delay:      20000",
324   "*colors:     -1",
325 #ifdef HAVE_XSHM_EXTENSION
326   "*useSHM:     True",
327 #else
328   "*useSHM:     False",
329 #endif
330   0
331 };
332
333 static XrmOptionDescRec rd_options [] = {
334   { "-width",           ".width",       XrmoptionSepArg, 0 },
335   { "-height",          ".height",      XrmoptionSepArg, 0 },
336   { "-epoch",           ".epoch",       XrmoptionSepArg, 0 },
337   { "-reaction",        ".reaction",    XrmoptionSepArg, 0 },
338   { "-diffusion",       ".diffusion",   XrmoptionSepArg, 0 },
339   { "-verbose",         ".verbose",     XrmoptionNoArg, "True" },
340   { "-radius",          ".radius",      XrmoptionSepArg, 0 },
341   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
342   { "-size",            ".size",        XrmoptionSepArg, 0 },
343   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
344   { "-ncolors",         ".colors",      XrmoptionSepArg, 0 },
345   { "-shm",             ".useSHM",      XrmoptionNoArg, "True" },
346   { "-no-shm",          ".useSHM",      XrmoptionNoArg, "False" },
347   { 0, 0, 0, 0 }
348 };
349
350
351 static void
352 random_colors(struct state *st)
353 {
354   memset(st->colors, 0, st->ncolors*sizeof(*st->colors));
355   make_smooth_colormap (st->dpy, st->visual, st->cmap, st->colors, &st->ncolors,
356                         True, 0, True);
357   if (st->ncolors <= 2) {
358     mono_p = True;
359     st->ncolors = 2;
360     st->colors[0].flags = DoRed|DoGreen|DoBlue;
361     st->colors[0].red = st->colors[0].green = st->colors[0].blue = 0;
362     XAllocColor(st->dpy, st->cmap, &st->colors[0]);
363     st->colors[1].flags = DoRed|DoGreen|DoBlue;
364     st->colors[1].red = st->colors[1].green = st->colors[1].blue = 0xFFFF;
365     XAllocColor(st->dpy, st->cmap, &st->colors[1]);
366   }
367
368   /* Scale it up so that there are exactly 255 colors -- that keeps the
369      animation speed consistent, even when there aren't many allocatable
370      colors, and prevents the -mono mode from looking like static. */
371   if (st->ncolors != 255) {
372     int i, n = 255;
373     double scale = (double) st->ncolors / (double) (n+1);
374     XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
375     for (i = 0; i < n; i++)
376       c2[i] = st->colors[(int) (i * scale)];
377     free(st->colors);
378     st->colors = c2;
379     st->ncolors = n;
380   }
381
382 }
383
384
385 /* should factor into RD-specfic and compute-every-pixel general */
386 static void *
387 rd_init (Display *dpy, Window win)
388 {
389   struct state *st = (struct state *) calloc (1, sizeof(*st));
390   XGCValues gcv;
391   int w2;
392   int vdepth;
393
394   st->dpy = dpy;
395   st->window = win;
396
397   st->delay = get_integer_resource (st->dpy, "delay", "Float");
398
399 #ifdef HAVE_XSHM_EXTENSION
400   st->use_shm = get_boolean_resource(st->dpy, "useSHM", "Boolean");
401 #endif
402
403   XGetWindowAttributes (st->dpy, win, &st->xgwa);
404   st->visual = st->xgwa.visual;
405   pixack_init(st, &st->width, &st->height);
406   {
407     double s = get_float_resource (st->dpy, "size", "Float");
408     double p = get_float_resource (st->dpy, "speed", "Float");
409     if (s < 0.0 || s > 1.0)
410       s = 1.0;
411     s = sqrt(s);
412     st->array_width = st->xgwa.width * s;
413     st->array_height = st->xgwa.height * s;
414     if (s < 0.99) {
415       st->array_width = (st->array_width / st->width) * st->width;
416       st->array_height = (st->array_height / st->height) * st->height;
417     }
418     if (st->array_width < st->width) st->array_width = st->width;
419     if (st->array_height < st->height) st->array_height = st->height;
420     st->array_x = (st->xgwa.width - st->array_width)/2;
421     st->array_y = (st->xgwa.height - st->array_height)/2;
422     st->array_dx = p;
423     st->array_dy = .31415926 * p;
424
425     /* start in a random direction */
426     if (random() & 1) st->array_dx = -st->array_dx;
427     if (random() & 1) st->array_dy = -st->array_dy;
428
429   }
430   st->verbose = get_boolean_resource (st->dpy, "verbose", "Boolean");
431   st->npix = (st->width + 2) * (st->height + 2);
432   w2 = st->width + 2;
433 /*  gcv.function = GXcopy;*/
434   st->gc = XCreateGC(st->dpy, win, 0 /*GCFunction*/, &gcv);
435   vdepth = visual_depth(DefaultScreenOfDisplay(st->dpy), st->xgwa.visual);
436
437   /* This code only deals with pixmap depths of 1, 8, 16, and 32.
438      Therefore, we assume that those depths will be supported by the
439      coresponding visual depths (that depth-24 dpys accept depth-32
440      pixmaps, and that depth-12 dpys accept depth-16 pixmaps.) */
441   st->pdepth = (vdepth == 1 ? 1 :
442             vdepth <= 8 ? 8 :
443             vdepth <= 16 ? 16 :
444             32);
445
446   /* Ok, this like, sucks and stuff.  There are some XFree86 systems
447      that have depth-24 visuals, that do not accept depth-32 XImages!
448      Which if you ask me is just absurd, since all it would take is
449      for the server to truncate the bits in that case.  So, this crap
450      here detects the specific case of: we have chosen depth 32;
451      and the server does not support depth 32.  In that case, we
452      try and use depth 16 instead.
453
454      The real fix would be to rewrite this program to deal with
455      depth 24 directly (or even better, arbitrary depths, but that
456      would mean going through the XImage routines instead of messing
457      with the XImage->data directly.)
458
459      jwz, 18-Mar-99: well, the X servers I have access to these days do
460      support 32-deep images on deep visuals, so I no longer have the
461      ability to test this code -- but it was causing problems on the
462      visuals that I do have, and I think that's because I mistakenly
463      wrote `pfv[i].depth' when I meant to write `pfv[i].bits_per_pixel'.
464      The symptom I was seeing was that the grid was 64x64, but the
465      images were being drawn 32x32 -- so there was a black stripe on
466      every other row.  Wow, this code sucks so much.
467   */
468   if (st->pdepth == 32)
469     {
470       int i, pfvc = 0;
471       Bool ok = False;
472       XPixmapFormatValues *pfv = XListPixmapFormats (st->dpy, &pfvc);
473       for (i = 0; i < pfvc; i++)
474         if (pfv[i].bits_per_pixel == st->pdepth)
475           ok = True;
476       if (!ok)
477         st->pdepth = 16;
478     }
479
480   st->cmap = st->xgwa.colormap;
481   st->ncolors = get_integer_resource (st->dpy, "colors", "Integer");
482
483   if (st->ncolors <= 0) {
484     if (vdepth > 8)
485       st->ncolors = 2047;
486     else
487       st->ncolors = 255;
488   }
489
490   if (mono_p || st->ncolors < 2) st->ncolors = 2;
491   if (st->ncolors <= 2) mono_p = True;
492   st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
493
494   st->mapped = (vdepth <= 8 &&
495                 has_writable_cells(st->xgwa.screen, st->xgwa.visual));
496
497   {
498     int i, di;
499     st->mc = (unsigned char *) malloc(1<<16);
500     for (i = 0; i < (1<<16); i++) {
501       di = (i + (random()&255))>>8;
502       if (di > 255) di = 255;
503       st->mc[i] = di;
504     }
505   }
506
507   st->pd = malloc(st->npix * (st->pdepth == 1 ? 1 : (st->pdepth / 8)));
508   if (!st->pd) {
509     fprintf(stderr, "not enough memory for %d pixels.\n", st->npix);
510     exit(1);
511   }
512
513   st->image = 0;
514
515 #ifdef HAVE_XSHM_EXTENSION
516   if (st->use_shm)
517     {
518       st->image = create_xshm_image(st->dpy, st->xgwa.visual, vdepth,
519                                 ZPixmap, 0, &st->shm_info, st->width, st->height);
520       if (!st->image)
521         st->use_shm = False;
522       else
523         {
524           free(st->pd);
525           st->pd = st->image->data;
526         }
527     }
528 #endif /* HAVE_XSHM_EXTENSION */
529
530   if (!st->image)
531     {
532       st->image = XCreateImage(st->dpy, st->xgwa.visual, vdepth,
533                            ZPixmap, 0, st->pd,
534                            st->width, st->height, 8, 0);
535     }
536
537   return st;
538 }
539
540 static unsigned long
541 rd_draw (Display *dpy, Window win, void *closure)
542 {
543   struct state *st = (struct state *) closure;
544   Bool bump = False;
545
546   int i, j;
547   pixack_frame(st, st->pd);
548   for (i = 0; i < st->array_width; i += st->width)
549     for (j = 0; j < st->array_height; j += st->height)
550 #ifdef HAVE_XSHM_EXTENSION
551       if (st->use_shm)
552         XShmPutImage(st->dpy, st->window, st->gc, st->image, 0, 0, i+st->array_x, j+st->array_y,
553                      st->width, st->height, False);
554       else
555 #endif
556         XPutImage(st->dpy, win, st->gc, st->image, 0, 0, i+st->array_x, j+st->array_y,
557                   st->width, st->height);
558
559   st->array_x += st->array_dx;
560   st->array_y += st->array_dy;
561   if (st->array_x < 0) {
562     st->array_x = 0;
563     st->array_dx = -st->array_dx;
564     bump = True;
565   } else if (st->array_x > (st->xgwa.width - st->array_width)) {
566     st->array_x = (st->xgwa.width - st->array_width);
567     st->array_dx = -st->array_dx;
568     bump = True;
569   }
570   if (st->array_y < 0) {
571     st->array_y = 0;
572     st->array_dy = -st->array_dy;
573     bump = True;
574   } else if (st->array_y > (st->xgwa.height - st->array_height)) {
575     st->array_y = (st->xgwa.height - st->array_height);
576     st->array_dy = -st->array_dy;
577     bump = True;
578   }
579
580   if (bump) {
581     if (random() & 1) {
582       double swap = st->array_dx;
583       st->array_dx = st->array_dy;
584       st->array_dy = swap;
585     }
586   }
587
588   st->frame++;
589
590   return st->delay;
591 }
592
593 static void
594 rd_reshape (Display *dpy, Window window, void *closure, 
595                  unsigned int w, unsigned int h)
596 {
597 }
598
599 static Bool
600 rd_event (Display *dpy, Window window, void *closure, XEvent *event)
601 {
602   return False;
603 }
604
605 static void
606 rd_free (Display *dpy, Window window, void *closure)
607 {
608 }
609
610 XSCREENSAVER_MODULE_2 ("RDbomb", rdbomb, rd)