http://ftp.x.org/contrib/applications/xscreensaver-3.09.tar.gz
[xscreensaver] / hacks / xflame.c
1 /* xflame, Copyright (c) 1996-1999 Carsten Haitzler <raster@redhat.com>
2  *
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 
9  * implied warranty.
10  */
11
12 /* Version history as near as I (jwz) can piece together:
13
14    * Carsten Haitzler <raster@redhat.com> wrote the first version in 1996.
15
16    * Rahul Jain <rahul@rice.edu> added support for TrueColor displays.
17
18    * Someone did a rough port of it to use the xscreensaver utility routines
19      instead of creating its own window by hand.
20
21    * Someone (probably Raster) came up with a subsequent version that had
22      a Red Hat logo hardcoded into it.
23
24    * Daniel Zahn <stumpy@religions.com> found that version in 1998, and 
25      hacked it to be able to load a different logo from a PGM (P5) file, 
26      with a single hardcoded pathname.
27
28    * Jamie Zawinski <jwz@jwz.org> found several versions of xflame in
29      March 1999, and pieced them together.  Changes:
30
31        - Correct and fault-tolerant use of the Shared Memory extension;
32          previous versions of xflame did not work when $DISPLAY was remote.
33
34        - Replaced PGM-reading code with code that can read arbitrary XBM
35          and XPM files (color ones will be converted to grayscale.)
36
37        - Command-line options all around -- no hardcoded pathnames or
38          behavioral constants.
39
40        - General cleanup and portability tweaks.
41  */
42
43
44 /* portions by Daniel Zahn <stumpy@religions.com> */
45
46
47 #include "screenhack.h"
48 #include <X11/Xutil.h>
49 #include <limits.h>
50
51 #ifdef HAVE_XSHM_EXTENSION
52 # include "xshm.h"
53 #endif /* HAVE_XSHM_EXTENSION */
54
55 #ifdef HAVE_XPM
56 # include <X11/xpm.h>
57 # ifndef PIXEL_ALREADY_TYPEDEFED
58 #  define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
59 # endif
60 #endif
61
62 #ifdef HAVE_XMU
63 # ifndef VMS
64 #  include <X11/Xmu/Drawing.h>
65 # else  /* VMS */
66 #  include <Xmu/Drawing.h>
67 # endif /* VMS */
68 #endif /* HAVE_XMU */
69
70 #define MAX             255
71
72 static Display         *display;
73 static Window          window;
74 static int             depth;
75 static int             width;
76 static int             height;
77 static Colormap        colormap;
78 static Visual          *visual;
79 static Bool            shared;
80 static XImage          *xim;
81 #ifdef HAVE_XSHM_EXTENSION
82 static XShmSegmentInfo shminfo;
83 #endif /* HAVE_XSHM_EXTENSION */
84 static GC              gc;
85 static int             ctab[256];
86
87 static unsigned char  *flame;
88 static unsigned char  *theim;
89 static int             fwidth;
90 static int             fheight;
91 static int             top;
92 static int             hspread;
93 static int             vspread;
94 static int             residual;
95
96 static int ihspread;
97 static int ivspread;
98 static int iresidual;
99 static int variance;
100 static int vartrend;
101
102 static void
103 GetXInfo(Display *disp, Window win)
104 {
105   XWindowAttributes xwa;
106    
107   XGetWindowAttributes(disp,win,&xwa);
108
109   window   = win;
110   display  = disp;
111   colormap = xwa.colormap;
112   depth    = xwa.depth;
113   visual   = xwa.visual;
114   width    = xwa.width;
115   height   = xwa.height;
116
117   if (width%2)
118     width++;
119   if (height%2)
120     height++;
121 }
122
123 static void
124 MakeImage(void)
125 {
126   XGCValues gcv;
127
128   shared = True;
129   xim = create_xshm_image (display, visual, depth, ZPixmap, NULL,
130                            &shminfo, width, height);
131   if (!xim)
132     {
133       shared = False;
134       xim = XCreateImage (display, visual, depth, ZPixmap, 0, NULL,
135                           width, height, 32, 0);
136       if (xim)
137         xim->data = (char *) calloc(xim->height, xim->bytes_per_line);
138       if (!xim || !xim->data)
139         {
140           fprintf(stderr,"%s: out of memory.\n", progname);
141           exit(1);
142         }
143     }
144
145   gc = XCreateGC(display,window,0,&gcv);
146   if (!gc) exit (1);
147 }
148
149
150 static void
151 InitColors(void)
152 {
153   int i = 0, j = 0;
154   for (i = 0; i < 256 * 2; i += 2)
155     {
156       XColor xcl;
157       int r = (i -   0) * 3;
158       int g = (i -  80) * 3;
159       int b = (i - 160) * 3;
160     
161       if (r < 0)   r = 0;
162       if (r > 255) r = 255;
163       if (g < 0)   g = 0;
164       if (g > 255) g = 255;
165       if (b < 0)   b = 0;
166       if (b > 255) b = 255;
167
168       xcl.red   = (unsigned short)((r << 8) | r);
169       xcl.green = (unsigned short)((g << 8) | g);
170       xcl.blue  = (unsigned short)((b << 8) | b);
171       xcl.flags = DoRed | DoGreen | DoBlue;
172
173       XAllocColor(display,colormap,&xcl);
174
175       ctab[j++] = (int)xcl.pixel;
176     }
177 }
178
179
180 static void
181 DisplayImage(void)
182 {
183   if (shared)
184     XShmPutImage(display, window, gc, xim, 0,(top - 1) << 1, 0,
185                  (top - 1) << 1, width, height - ((top - 1) << 1), False);
186   else
187     XPutImage(display, window, gc, xim, 0, (top - 1) << 1, 0,
188               (top - 1) << 1, width, height - ((top - 1) << 1));
189 }
190
191
192 static void
193 InitFlame(void)
194 {
195   fwidth  = width / 2;
196   fheight = height / 2;
197   flame   = (unsigned char *) malloc((fwidth + 2) * (fheight + 2)
198                                      * sizeof(unsigned char));
199
200   if (!flame)
201     {
202       fprintf(stderr,"%s: out of memory\n", progname);
203       exit(1);
204     }
205
206   top      = 1;
207   ihspread  = get_integer_resource("hspread", "Integer");
208   ivspread  = get_integer_resource("vspread", "Integer");
209   iresidual = get_integer_resource("residual", "Integer");
210   variance  = get_integer_resource("variance", "Integer");
211   vartrend  = get_integer_resource("vartrend", "Integer");
212
213 # define THROTTLE(VAR,NAME) \
214   if (VAR < 0 || VAR > 255) { \
215     fprintf(stderr, "%s: %s must be in the range 0-255 (not %d).\n", \
216             progname, NAME, VAR); \
217     exit(1); }
218   THROTTLE (ihspread, "hspread");
219   THROTTLE (ivspread, "vspread");
220   THROTTLE (iresidual,"residual");
221   THROTTLE (variance, "variance");
222   THROTTLE (vartrend, "vartrend");
223 # undef THROTTLE
224
225
226
227   hspread = ihspread;
228   vspread = ivspread;
229   residual = iresidual;
230 }
231
232
233 static void
234 Flame2Image16(void)
235 {
236   int x,y;
237   unsigned short *ptr;
238   unsigned char *ptr1;
239   int v1,v2,v3,v4;
240
241   ptr  = (unsigned short *)xim->data;
242   ptr += (top << 1) * width;
243   ptr1 = flame + 1 + (top * (fwidth + 2));
244
245   for(y = top; y < fheight; y++)
246     {
247       for( x = 0; x < fwidth; x++)
248         {
249           v1 = (int)*ptr1;
250           v2 = (int)*(ptr1 + 1);
251           v3 = (int)*(ptr1 + fwidth + 2);
252           v4 = (int)*(ptr1 + fwidth + 2 + 1);
253           ptr1++;
254           *ptr++ = (unsigned short)ctab[v1];
255           *ptr   = (unsigned short)ctab[(v1 + v2) >> 1];
256           ptr   += width - 1;
257           *ptr++ = (unsigned short)ctab[(v1 + v3) >> 1];
258           *ptr   = (unsigned short)ctab[(v1 + v4) >> 1];
259           ptr   -= width - 1;
260         }
261       ptr  += width;
262       ptr1 += 2;
263     }
264 }
265
266 static void
267 Flame2Image32(void)
268 {
269   int x,y;
270   unsigned int *ptr;
271   unsigned char *ptr1;
272   int v1,v2,v3,v4;
273
274   ptr  = (unsigned int *)xim->data;
275   ptr += (top << 1) * width;
276   ptr1 = flame + 1 + (top * (fwidth + 2));
277
278   for( y = top; y < fheight; y++)
279     {
280       for( x = 0; x < fwidth; x++)
281         {
282           v1 = (int)*ptr1;
283           v2 = (int)*(ptr1 + 1);
284           v3 = (int)*(ptr1 + fwidth + 2);
285           v4 = (int)*(ptr1 + fwidth + 2 + 1);
286           ptr1++;
287           *ptr++ = (unsigned int)ctab[v1];
288           *ptr   = (unsigned int)ctab[(v1 + v2) >> 1];
289           ptr   += width - 1;
290           *ptr++ = (unsigned int)ctab[(v1 + v3) >> 1];
291           *ptr   = (unsigned int)ctab[(v1 + v4) >> 1];
292           ptr   -= width - 1;
293         }
294       ptr  += width;
295       ptr1 += 2;
296     }
297 }
298
299 static void
300 Flame2Image8(void)
301 {
302   int x,y;
303   unsigned char *ptr;
304   unsigned char *ptr1;
305   int v1,v2,v3,v4;
306
307   ptr  = (unsigned char *)xim->data;
308   ptr += (top << 1) * width;
309   ptr1 = flame + 1 + (top * (fwidth + 2));
310
311   for(y=top;y<fheight;y++)
312     {
313       for(x=0;x<fwidth;x++)
314         {
315           v1 = (int)*ptr1;
316           v2 = (int)*(ptr1 + 1);
317           v3 = (int)*(ptr1 + fwidth + 2);
318           v4 = (int)*(ptr1 + fwidth + 2 + 1);
319           ptr1++;
320           *ptr++ = (unsigned char)ctab[v1];
321           *ptr   = (unsigned char)ctab[(v1 + v2) >> 1];
322           ptr   += width - 1;
323           *ptr++ = (unsigned char)ctab[(v1 + v3) >> 1];
324           *ptr   = (unsigned char)ctab[(v1 + v4) >> 1];
325           ptr   -= width - 1;
326         }
327       ptr  += width;
328       ptr1 += 2;
329     }
330 }
331
332 static void
333 Flame2Image1234567(void)
334 {
335   int x,y;
336   unsigned char *ptr1;
337   int v1,v2,v3,v4;
338
339   ptr1 = flame + 1 + (top * (fwidth + 2));
340
341   for( y = top; y < fheight; y++)
342     {
343       for( x = 0; x < fwidth; x++)
344         {
345           v1 = (int)*ptr1;
346           v2 = (int)*(ptr1 + 1);
347           v3 = (int)*(ptr1 + fwidth + 2);
348           v4 = (int)*(ptr1 + fwidth + 2 + 1);
349           ptr1++;
350           XPutPixel(xim,(x << 1),    (y << 1),    ctab[v1]);
351           XPutPixel(xim,(x << 1) + 1,(y << 1),    ctab[(v1 + v2) >> 1]);
352           XPutPixel(xim,(x << 1),    (y << 1) + 1,ctab[(v1 + v3) >> 1]);
353           XPutPixel(xim,(x << 1) + 1,(y << 1) + 1,ctab[(v1 + v4) >> 1]);
354         }
355     }
356 }
357
358 static void
359 Flame2Image(void)
360 {
361   if (depth >= 24)      Flame2Image32();
362   else if (depth == 16) Flame2Image16();
363   else if (depth == 8)  Flame2Image8();
364   else if (depth == 15) Flame2Image16(); 
365   else if (depth <  8)  Flame2Image1234567();
366   else if (depth == 12) Flame2Image16();
367 }
368
369 static void
370 FlameActive(void)
371 {
372   int x,v1;
373   unsigned char *ptr1;
374    
375   ptr1 = flame + ((fheight + 1) * (fwidth + 2));
376
377   for (x = 0; x < fwidth + 2; x++)
378     {
379       v1      = *ptr1;
380       v1     += ((random() % variance) - vartrend);
381       *ptr1++ = v1 % 255;
382     }
383
384   v1= (random() % 100);
385   if (v1 == 10)
386     residual += (random()%10);
387   else if (v1 == 20)
388     hspread += (random()%15);
389   else if (v1 == 30)
390     vspread += (random()%20);
391
392   residual = ((iresidual* 10) + (residual *90)) / 100;
393   hspread  = ((ihspread * 10) + (hspread  *90)) / 100;
394   vspread  = ((ivspread * 10) + (vspread  *90)) / 100;
395 }
396
397
398 static void
399 FlameAdvance(void)
400 {
401   int x,y;
402   unsigned char *ptr2;
403   int newtop = top;
404
405   for (y = fheight + 1; y >= top; y--)
406     {
407       int used = 0;
408       unsigned char *ptr1 = flame + 1 + (y * (fwidth + 2));
409       for (x = 0; x < fwidth; x++)
410         {
411           int v1 = (int)*ptr1;
412           int v2, v3;
413           if (v1 > 0)
414             {
415               used = 1;
416               ptr2 = ptr1 - fwidth - 2;
417               v3   = (v1 * vspread) >> 8;
418               v2   = (int)*(ptr2);
419               v2  += v3;
420               if (v2 > MAX) 
421                 v2 = MAX;
422
423               *(ptr2) = (unsigned char)v2;
424               v3  = (v1 * hspread) >> 8;
425               v2  = (int)*(ptr2 + 1);
426               v2 += v3;
427               if (v2 > MAX) 
428                 v2 = MAX;
429           
430               *(ptr2 + 1) = (unsigned char)v2;
431               v2          = (int)*(ptr2 - 1);
432               v2         += v3;
433               if (v2 > MAX) 
434                 v2 = MAX;
435           
436               *(ptr2 - 1) = (unsigned char)v2;
437         
438               if (y < fheight + 1)
439                 {
440                   v1    = (v1 * residual) >> 8;
441                   *ptr1 = (unsigned char)v1;
442                 }
443             }
444           ptr1++;
445           if (used) 
446             newtop = y - 1;
447         }
448     }
449
450   top = newtop - 1;
451
452   if (top < 1)
453     top = 1;
454 }
455
456
457 static void
458 FlameFill(int val)
459 {
460   int x, y;
461   for (y = 0; y < fheight + 1; y++)
462     {
463       unsigned char *ptr1 = flame + 1 + (y * (fwidth + 2));
464       for (x = 0; x < fwidth; x++)
465         {
466           *ptr1 = val;
467           ptr1++;
468         }
469     }
470 }
471
472
473 static void
474 FlamePasteData(unsigned char *d, int xx, int yy, int w, int h)
475 {
476   unsigned char *ptr1,*ptr2;
477   ptr2 = d;
478
479   if (xx < 0) xx = 0;
480   if (yy < 0) yy = 0;
481
482   if ((xx >= 0) &&
483       (yy >= 0) &&
484       (xx + w <= fwidth) &&
485       (yy + h <= fheight))
486     {
487       int x, y;
488       for (y = 0; y < h; y++)
489         {
490           ptr1 = flame + 1 + xx + ((yy + y) * (fwidth + 2));
491           for (x = 0; x < w; x++)
492             {
493               if (*ptr2 / 24)
494                 *ptr1 += random() % (*ptr2 / 24);
495
496               ptr1++;
497               ptr2++;
498             }
499         }
500     }
501   else
502     {
503       static Bool warned = False;
504       if (!warned)
505         {
506           fprintf (stderr, "%s: window is %dx%d; image must be "
507                    "smaller than %dx%d (not %dx%d).\n",
508                    progname, width, height, fwidth, fheight, w, h);
509           warned = True;
510         }
511     }
512 }
513
514
515 static unsigned char *
516 loadBitmap(int *w, int *h)
517 {
518   char *bitmap_name = get_string_resource ("bitmap", "Bitmap");
519
520   if (bitmap_name &&
521       *bitmap_name &&
522       !!strcmp(bitmap_name, "none"))
523     {
524       XpmInfo xpm_info = { 0, };
525       XpmImage xpm_image = { 0, };
526
527       int result = XpmReadFileToXpmImage (bitmap_name, &xpm_image, &xpm_info);
528       if (result == XpmSuccess)
529         {
530           int x, y;
531           unsigned char *result, *o;
532           unsigned char *grays;
533           XWindowAttributes xgwa;
534
535           *w = xpm_image.width;
536           *h = xpm_image.height;
537           result = (unsigned char *) malloc ((*w) * (*h));
538           if (!result)
539             {
540               fprintf(stderr, "%s: out of memory loading %s\n",
541                       progname, bitmap_name);
542               exit (1);
543             }
544
545           XGetWindowAttributes (display, window, &xgwa);
546
547           grays = (unsigned char *) calloc (xpm_image.ncolors+1, 1);
548           for (x = 0; x < xpm_image.ncolors; x++)
549             {
550               XColor xc;
551               XpmColor *xpmc = &xpm_image.colorTable[x];
552               char *cstring = 0;
553               if (xpmc->g_color && *xpmc->g_color)
554                 cstring = xpmc->g_color;
555               else if (xpmc->g4_color && *xpmc->g4_color)
556                 cstring = xpmc->g4_color;
557               else if (xpmc->c_color && *xpmc->c_color)
558                 cstring = xpmc->c_color;
559               else
560                 cstring = xpmc->m_color;
561
562               memset (&xc, 0, sizeof(xc));
563               if (!cstring ||
564                   !*cstring ||
565                   !XParseColor (display, xgwa.colormap, cstring, &xc))
566                 grays[x] = 0;
567               else
568                 grays[x] = (int) (((xc.red   * 0.299) +
569                                    (xc.green * 0.587) +
570                                    (xc.blue  * 0.114))
571                                   / 255);
572             }
573
574           o = result;
575           for (y = 0; y < *h; y++)
576             for (x = 0; x < *w; x++)
577               {
578                 int color = xpm_image.data[(y * (*w)) + x];
579                 if (color < 0 || color > xpm_image.ncolors) abort();
580                 *o++ = grays[color];
581               }
582           return result;
583         }
584       else      /* failed to read XPM -- fall through and try XBM */
585         {
586 #ifdef HAVE_XMU
587           XImage *ximage = 0;
588           int width, height, xh, yh;
589           int x, y;
590           unsigned char *result, *o;
591           Pixmap bitmap =
592             XmuLocateBitmapFile (DefaultScreenOfDisplay (display),
593                                  bitmap_name, 0, 0, &width, &height, &xh, &yh);
594           if (!bitmap)
595             {
596               fprintf(stderr, "%s: unable to load bitmap file %s\n",
597                       progname, bitmap_name);
598               exit (1);
599             }
600           ximage = XGetImage (display, bitmap, 0, 0, width, height,
601                               1L, XYPixmap);
602           XFreePixmap (display, bitmap);
603
604           if (ximage->depth != 1) abort();
605
606           *w = ximage->width;
607           *h = ximage->height;
608           result = (unsigned char *) malloc ((*w) * (*h));
609           if (!result)
610             {
611               fprintf(stderr, "%s: out of memory loading %s\n",
612                       progname, bitmap_name);
613               exit (1);
614             }
615
616           o = result;
617           for (y = 0; y < *h; y++)
618             for (x = 0; x < *w; x++)
619               *o++ = (XGetPixel(ximage, x, y) ? 255 : 0);
620
621           return result;
622
623 #else  /* !XMU */
624           fprintf (stderr,
625                    "%s: your vendor doesn't ship the standard Xmu library.\n",
626                    progname);
627           fprintf (stderr, "\tWe can't load XBM files without it.\n");
628           exit (1);
629 #endif /* !XMU */
630         }
631     }
632
633   *w = 0;
634   *h = 0;
635   return 0;
636
637 }
638
639
640 \f
641 char *progclass = "XFlame";
642
643 char *defaults [] = {
644   ".background:     black",
645   ".foreground:     red",
646   "*bitmap:         none",
647   "*bitmapBaseline: 20",
648   "*delay:          10000",
649   "*hspread:        30",
650   "*vspread:        97",
651   "*residual:       99",
652   "*variance:       50",
653   "*vartrend:       20",
654
655 #ifdef HAVE_XSHM_EXTENSION
656   "*useSHM: False",   /* xshm turns out not to help. */
657 #endif /* HAVE_XSHM_EXTENSION */
658    0
659 };
660
661 XrmOptionDescRec options [] = {
662   { "-delay",     ".delay",          XrmoptionSepArg, 0 },
663   { "-bitmap",    ".bitmap",         XrmoptionSepArg, 0 },
664   { "-baseline",  ".bitmapBaseline", XrmoptionSepArg, 0 },
665   { "-hspread",   ".hspread",        XrmoptionSepArg, 0 },
666   { "-vspread",   ".vspread",        XrmoptionSepArg, 0 },
667   { "-residual",  ".residual",       XrmoptionSepArg, 0 },
668   { "-variance",  ".variance",       XrmoptionSepArg, 0 },
669   { "-vartrend",  ".vartrend",       XrmoptionSepArg, 0 },
670 #ifdef HAVE_XSHM_EXTENSION
671   { "-shm",       ".useSHM",         XrmoptionNoArg, "True" },
672   { "-no-shm",    ".useSHM",         XrmoptionNoArg, "False" },
673 #endif /* HAVE_XSHM_EXTENSION */
674   { 0, 0, 0, 0 }
675 };
676
677 void
678 screenhack (Display *disp, Window win)
679 {
680   int theimx = 0, theimy = 0;
681   int baseline = get_integer_resource ("bitmapBaseline", "Integer");
682   int delay = get_integer_resource ("delay", "Integer");
683   xim      = NULL;
684   top      = 1;
685   flame    = NULL;
686
687   GetXInfo(disp,win);
688   InitColors();
689   theim = loadBitmap(&theimx, &theimy);
690
691   /* utils/xshm.c doesn't provide a way to free the shared-memory image, which
692      makes it hard for us to react to window resizing.  So, punt for now.  The
693      size of the window at startup is the size it will stay.
694   */
695   GetXInfo(disp,win);
696
697   MakeImage();
698   InitFlame();
699   FlameFill(0);
700
701   while (1)
702     {
703       FlameActive();
704
705       if (theim)
706         FlamePasteData(theim, (fwidth - theimx) / 2,
707                        fheight - theimy - baseline, theimx, theimy);
708
709       FlameAdvance();
710       Flame2Image();
711       DisplayImage();
712
713       XSync(display,False);
714       screenhack_handle_events(display);
715       if (delay)
716         usleep (delay);
717     }
718 }