http://ftp.x.org/contrib/applications/xscreensaver-3.10.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 #ifdef HAVE_XSHM_EXTENSION
129   shared = True;
130   xim = create_xshm_image (display, visual, depth, ZPixmap, NULL,
131                            &shminfo, width, height);
132 #else  /* !HAVE_XSHM_EXTENSION */
133   xim = 0;
134 #endif /* !HAVE_XSHM_EXTENSION */
135
136   if (!xim)
137     {
138       shared = False;
139       xim = XCreateImage (display, visual, depth, ZPixmap, 0, NULL,
140                           width, height, 32, 0);
141       if (xim)
142         xim->data = (char *) calloc(xim->height, xim->bytes_per_line);
143       if (!xim || !xim->data)
144         {
145           fprintf(stderr,"%s: out of memory.\n", progname);
146           exit(1);
147         }
148     }
149
150   gc = XCreateGC(display,window,0,&gcv);
151   if (!gc) exit (1);
152 }
153
154
155 static void
156 InitColors(void)
157 {
158   int i = 0, j = 0;
159   for (i = 0; i < 256 * 2; i += 2)
160     {
161       XColor xcl;
162       int r = (i -   0) * 3;
163       int g = (i -  80) * 3;
164       int b = (i - 160) * 3;
165     
166       if (r < 0)   r = 0;
167       if (r > 255) r = 255;
168       if (g < 0)   g = 0;
169       if (g > 255) g = 255;
170       if (b < 0)   b = 0;
171       if (b > 255) b = 255;
172
173       xcl.red   = (unsigned short)((r << 8) | r);
174       xcl.green = (unsigned short)((g << 8) | g);
175       xcl.blue  = (unsigned short)((b << 8) | b);
176       xcl.flags = DoRed | DoGreen | DoBlue;
177
178       XAllocColor(display,colormap,&xcl);
179
180       ctab[j++] = (int)xcl.pixel;
181     }
182 }
183
184
185 static void
186 DisplayImage(void)
187 {
188 #ifdef HAVE_XSHM_EXTENSION
189   if (shared)
190     XShmPutImage(display, window, gc, xim, 0,(top - 1) << 1, 0,
191                  (top - 1) << 1, width, height - ((top - 1) << 1), False);
192   else
193 #endif /* HAVE_XSHM_EXTENSION */
194     XPutImage(display, window, gc, xim, 0, (top - 1) << 1, 0,
195               (top - 1) << 1, width, height - ((top - 1) << 1));
196 }
197
198
199 static void
200 InitFlame(void)
201 {
202   fwidth  = width / 2;
203   fheight = height / 2;
204   flame   = (unsigned char *) malloc((fwidth + 2) * (fheight + 2)
205                                      * sizeof(unsigned char));
206
207   if (!flame)
208     {
209       fprintf(stderr,"%s: out of memory\n", progname);
210       exit(1);
211     }
212
213   top      = 1;
214   ihspread  = get_integer_resource("hspread", "Integer");
215   ivspread  = get_integer_resource("vspread", "Integer");
216   iresidual = get_integer_resource("residual", "Integer");
217   variance  = get_integer_resource("variance", "Integer");
218   vartrend  = get_integer_resource("vartrend", "Integer");
219
220 # define THROTTLE(VAR,NAME) \
221   if (VAR < 0 || VAR > 255) { \
222     fprintf(stderr, "%s: %s must be in the range 0-255 (not %d).\n", \
223             progname, NAME, VAR); \
224     exit(1); }
225   THROTTLE (ihspread, "hspread");
226   THROTTLE (ivspread, "vspread");
227   THROTTLE (iresidual,"residual");
228   THROTTLE (variance, "variance");
229   THROTTLE (vartrend, "vartrend");
230 # undef THROTTLE
231
232
233
234   hspread = ihspread;
235   vspread = ivspread;
236   residual = iresidual;
237 }
238
239
240 static void
241 Flame2Image16(void)
242 {
243   int x,y;
244   unsigned short *ptr;
245   unsigned char *ptr1;
246   int v1,v2,v3,v4;
247
248   ptr  = (unsigned short *)xim->data;
249   ptr += (top << 1) * width;
250   ptr1 = flame + 1 + (top * (fwidth + 2));
251
252   for(y = top; y < fheight; y++)
253     {
254       for( x = 0; x < fwidth; x++)
255         {
256           v1 = (int)*ptr1;
257           v2 = (int)*(ptr1 + 1);
258           v3 = (int)*(ptr1 + fwidth + 2);
259           v4 = (int)*(ptr1 + fwidth + 2 + 1);
260           ptr1++;
261           *ptr++ = (unsigned short)ctab[v1];
262           *ptr   = (unsigned short)ctab[(v1 + v2) >> 1];
263           ptr   += width - 1;
264           *ptr++ = (unsigned short)ctab[(v1 + v3) >> 1];
265           *ptr   = (unsigned short)ctab[(v1 + v4) >> 1];
266           ptr   -= width - 1;
267         }
268       ptr  += width;
269       ptr1 += 2;
270     }
271 }
272
273 static void
274 Flame2Image32(void)
275 {
276   int x,y;
277   unsigned int *ptr;
278   unsigned char *ptr1;
279   int v1,v2,v3,v4;
280
281   ptr  = (unsigned int *)xim->data;
282   ptr += (top << 1) * width;
283   ptr1 = flame + 1 + (top * (fwidth + 2));
284
285   for( y = top; y < fheight; y++)
286     {
287       for( x = 0; x < fwidth; x++)
288         {
289           v1 = (int)*ptr1;
290           v2 = (int)*(ptr1 + 1);
291           v3 = (int)*(ptr1 + fwidth + 2);
292           v4 = (int)*(ptr1 + fwidth + 2 + 1);
293           ptr1++;
294           *ptr++ = (unsigned int)ctab[v1];
295           *ptr   = (unsigned int)ctab[(v1 + v2) >> 1];
296           ptr   += width - 1;
297           *ptr++ = (unsigned int)ctab[(v1 + v3) >> 1];
298           *ptr   = (unsigned int)ctab[(v1 + v4) >> 1];
299           ptr   -= width - 1;
300         }
301       ptr  += width;
302       ptr1 += 2;
303     }
304 }
305
306 static void
307 Flame2Image8(void)
308 {
309   int x,y;
310   unsigned char *ptr;
311   unsigned char *ptr1;
312   int v1,v2,v3,v4;
313
314   ptr  = (unsigned char *)xim->data;
315   ptr += (top << 1) * width;
316   ptr1 = flame + 1 + (top * (fwidth + 2));
317
318   for(y=top;y<fheight;y++)
319     {
320       for(x=0;x<fwidth;x++)
321         {
322           v1 = (int)*ptr1;
323           v2 = (int)*(ptr1 + 1);
324           v3 = (int)*(ptr1 + fwidth + 2);
325           v4 = (int)*(ptr1 + fwidth + 2 + 1);
326           ptr1++;
327           *ptr++ = (unsigned char)ctab[v1];
328           *ptr   = (unsigned char)ctab[(v1 + v2) >> 1];
329           ptr   += width - 1;
330           *ptr++ = (unsigned char)ctab[(v1 + v3) >> 1];
331           *ptr   = (unsigned char)ctab[(v1 + v4) >> 1];
332           ptr   -= width - 1;
333         }
334       ptr  += width;
335       ptr1 += 2;
336     }
337 }
338
339 static void
340 Flame2Image1234567(void)
341 {
342   int x,y;
343   unsigned char *ptr1;
344   int v1,v2,v3,v4;
345
346   ptr1 = flame + 1 + (top * (fwidth + 2));
347
348   for( y = top; y < fheight; y++)
349     {
350       for( x = 0; x < fwidth; x++)
351         {
352           v1 = (int)*ptr1;
353           v2 = (int)*(ptr1 + 1);
354           v3 = (int)*(ptr1 + fwidth + 2);
355           v4 = (int)*(ptr1 + fwidth + 2 + 1);
356           ptr1++;
357           XPutPixel(xim,(x << 1),    (y << 1),    ctab[v1]);
358           XPutPixel(xim,(x << 1) + 1,(y << 1),    ctab[(v1 + v2) >> 1]);
359           XPutPixel(xim,(x << 1),    (y << 1) + 1,ctab[(v1 + v3) >> 1]);
360           XPutPixel(xim,(x << 1) + 1,(y << 1) + 1,ctab[(v1 + v4) >> 1]);
361         }
362     }
363 }
364
365 static void
366 Flame2Image(void)
367 {
368   if (depth >= 24)      Flame2Image32();
369   else if (depth == 16) Flame2Image16();
370   else if (depth == 8)  Flame2Image8();
371   else if (depth == 15) Flame2Image16(); 
372   else if (depth <  8)  Flame2Image1234567();
373   else if (depth == 12) Flame2Image16();
374 }
375
376 static void
377 FlameActive(void)
378 {
379   int x,v1;
380   unsigned char *ptr1;
381    
382   ptr1 = flame + ((fheight + 1) * (fwidth + 2));
383
384   for (x = 0; x < fwidth + 2; x++)
385     {
386       v1      = *ptr1;
387       v1     += ((random() % variance) - vartrend);
388       *ptr1++ = v1 % 255;
389     }
390
391   v1= (random() % 100);
392   if (v1 == 10)
393     residual += (random()%10);
394   else if (v1 == 20)
395     hspread += (random()%15);
396   else if (v1 == 30)
397     vspread += (random()%20);
398
399   residual = ((iresidual* 10) + (residual *90)) / 100;
400   hspread  = ((ihspread * 10) + (hspread  *90)) / 100;
401   vspread  = ((ivspread * 10) + (vspread  *90)) / 100;
402 }
403
404
405 static void
406 FlameAdvance(void)
407 {
408   int x,y;
409   unsigned char *ptr2;
410   int newtop = top;
411
412   for (y = fheight + 1; y >= top; y--)
413     {
414       int used = 0;
415       unsigned char *ptr1 = flame + 1 + (y * (fwidth + 2));
416       for (x = 0; x < fwidth; x++)
417         {
418           int v1 = (int)*ptr1;
419           int v2, v3;
420           if (v1 > 0)
421             {
422               used = 1;
423               ptr2 = ptr1 - fwidth - 2;
424               v3   = (v1 * vspread) >> 8;
425               v2   = (int)*(ptr2);
426               v2  += v3;
427               if (v2 > MAX) 
428                 v2 = MAX;
429
430               *(ptr2) = (unsigned char)v2;
431               v3  = (v1 * hspread) >> 8;
432               v2  = (int)*(ptr2 + 1);
433               v2 += v3;
434               if (v2 > MAX) 
435                 v2 = MAX;
436           
437               *(ptr2 + 1) = (unsigned char)v2;
438               v2          = (int)*(ptr2 - 1);
439               v2         += v3;
440               if (v2 > MAX) 
441                 v2 = MAX;
442           
443               *(ptr2 - 1) = (unsigned char)v2;
444         
445               if (y < fheight + 1)
446                 {
447                   v1    = (v1 * residual) >> 8;
448                   *ptr1 = (unsigned char)v1;
449                 }
450             }
451           ptr1++;
452           if (used) 
453             newtop = y - 1;
454         }
455     }
456
457   top = newtop - 1;
458
459   if (top < 1)
460     top = 1;
461 }
462
463
464 static void
465 FlameFill(int val)
466 {
467   int x, y;
468   for (y = 0; y < fheight + 1; y++)
469     {
470       unsigned char *ptr1 = flame + 1 + (y * (fwidth + 2));
471       for (x = 0; x < fwidth; x++)
472         {
473           *ptr1 = val;
474           ptr1++;
475         }
476     }
477 }
478
479
480 static void
481 FlamePasteData(unsigned char *d, int xx, int yy, int w, int h)
482 {
483   unsigned char *ptr1,*ptr2;
484   ptr2 = d;
485
486   if (xx < 0) xx = 0;
487   if (yy < 0) yy = 0;
488
489   if ((xx >= 0) &&
490       (yy >= 0) &&
491       (xx + w <= fwidth) &&
492       (yy + h <= fheight))
493     {
494       int x, y;
495       for (y = 0; y < h; y++)
496         {
497           ptr1 = flame + 1 + xx + ((yy + y) * (fwidth + 2));
498           for (x = 0; x < w; x++)
499             {
500               if (*ptr2 / 24)
501                 *ptr1 += random() % (*ptr2 / 24);
502
503               ptr1++;
504               ptr2++;
505             }
506         }
507     }
508   else
509     {
510       static Bool warned = False;
511       if (!warned)
512         {
513           fprintf (stderr, "%s: window is %dx%d; image must be "
514                    "smaller than %dx%d (not %dx%d).\n",
515                    progname, width, height, fwidth, fheight, w, h);
516           warned = True;
517         }
518     }
519 }
520
521
522 static unsigned char *
523 loadBitmap(int *w, int *h)
524 {
525   char *bitmap_name = get_string_resource ("bitmap", "Bitmap");
526
527   if (bitmap_name &&
528       *bitmap_name &&
529       !!strcmp(bitmap_name, "none"))
530     {
531 #ifdef HAVE_XPM
532       XpmInfo xpm_info = { 0, };
533       XpmImage xpm_image = { 0, };
534
535       int result = XpmReadFileToXpmImage (bitmap_name, &xpm_image, &xpm_info);
536       if (result == XpmSuccess)
537         {
538           int x, y;
539           unsigned char *result, *o;
540           unsigned char *grays;
541           XWindowAttributes xgwa;
542
543           *w = xpm_image.width;
544           *h = xpm_image.height;
545           result = (unsigned char *) malloc ((*w) * (*h));
546           if (!result)
547             {
548               fprintf(stderr, "%s: out of memory loading %s\n",
549                       progname, bitmap_name);
550               exit (1);
551             }
552
553           XGetWindowAttributes (display, window, &xgwa);
554
555           grays = (unsigned char *) calloc (xpm_image.ncolors+1, 1);
556           for (x = 0; x < xpm_image.ncolors; x++)
557             {
558               XColor xc;
559               XpmColor *xpmc = &xpm_image.colorTable[x];
560               char *cstring = 0;
561               if (xpmc->g_color && *xpmc->g_color)
562                 cstring = xpmc->g_color;
563               else if (xpmc->g4_color && *xpmc->g4_color)
564                 cstring = xpmc->g4_color;
565               else if (xpmc->c_color && *xpmc->c_color)
566                 cstring = xpmc->c_color;
567               else
568                 cstring = xpmc->m_color;
569
570               memset (&xc, 0, sizeof(xc));
571               if (!cstring ||
572                   !*cstring ||
573                   !XParseColor (display, xgwa.colormap, cstring, &xc))
574                 grays[x] = 0;
575               else
576                 grays[x] = (int) (((xc.red   * 0.299) +
577                                    (xc.green * 0.587) +
578                                    (xc.blue  * 0.114))
579                                   / 255);
580             }
581
582           o = result;
583           for (y = 0; y < *h; y++)
584             for (x = 0; x < *w; x++)
585               {
586                 int color = xpm_image.data[(y * (*w)) + x];
587                 if (color < 0 || color > xpm_image.ncolors) abort();
588                 *o++ = grays[color];
589               }
590           return result;
591         }
592       else      /* failed to read XPM -- fall through and try XBM */
593 #endif /* HAVE_XPM */
594         {
595 #ifdef HAVE_XMU
596           XImage *ximage = 0;
597           int width, height, xh, yh;
598           int x, y;
599           unsigned char *result, *o;
600           Pixmap bitmap =
601             XmuLocateBitmapFile (DefaultScreenOfDisplay (display),
602                                  bitmap_name, 0, 0, &width, &height, &xh, &yh);
603           if (!bitmap)
604             {
605               fprintf(stderr, "%s: unable to load bitmap file %s\n",
606                       progname, bitmap_name);
607               exit (1);
608             }
609           ximage = XGetImage (display, bitmap, 0, 0, width, height,
610                               1L, XYPixmap);
611           XFreePixmap (display, bitmap);
612
613           if (ximage->depth != 1) abort();
614
615           *w = ximage->width;
616           *h = ximage->height;
617           result = (unsigned char *) malloc ((*w) * (*h));
618           if (!result)
619             {
620               fprintf(stderr, "%s: out of memory loading %s\n",
621                       progname, bitmap_name);
622               exit (1);
623             }
624
625           o = result;
626           for (y = 0; y < *h; y++)
627             for (x = 0; x < *w; x++)
628               *o++ = (XGetPixel(ximage, x, y) ? 255 : 0);
629
630           return result;
631
632 #else  /* !XMU */
633           fprintf (stderr,
634                    "%s: your vendor doesn't ship the standard Xmu library.\n",
635                    progname);
636           fprintf (stderr, "\tWe can't load XBM files without it.\n");
637           exit (1);
638 #endif /* !XMU */
639         }
640     }
641
642   *w = 0;
643   *h = 0;
644   return 0;
645
646 }
647
648
649 \f
650 char *progclass = "XFlame";
651
652 char *defaults [] = {
653   ".background:     black",
654   ".foreground:     red",
655   "*bitmap:         none",
656   "*bitmapBaseline: 20",
657   "*delay:          10000",
658   "*hspread:        30",
659   "*vspread:        97",
660   "*residual:       99",
661   "*variance:       50",
662   "*vartrend:       20",
663
664 #ifdef HAVE_XSHM_EXTENSION
665   "*useSHM: False",   /* xshm turns out not to help. */
666 #endif /* HAVE_XSHM_EXTENSION */
667    0
668 };
669
670 XrmOptionDescRec options [] = {
671   { "-delay",     ".delay",          XrmoptionSepArg, 0 },
672   { "-bitmap",    ".bitmap",         XrmoptionSepArg, 0 },
673   { "-baseline",  ".bitmapBaseline", XrmoptionSepArg, 0 },
674   { "-hspread",   ".hspread",        XrmoptionSepArg, 0 },
675   { "-vspread",   ".vspread",        XrmoptionSepArg, 0 },
676   { "-residual",  ".residual",       XrmoptionSepArg, 0 },
677   { "-variance",  ".variance",       XrmoptionSepArg, 0 },
678   { "-vartrend",  ".vartrend",       XrmoptionSepArg, 0 },
679 #ifdef HAVE_XSHM_EXTENSION
680   { "-shm",       ".useSHM",         XrmoptionNoArg, "True" },
681   { "-no-shm",    ".useSHM",         XrmoptionNoArg, "False" },
682 #endif /* HAVE_XSHM_EXTENSION */
683   { 0, 0, 0, 0 }
684 };
685
686 void
687 screenhack (Display *disp, Window win)
688 {
689   int theimx = 0, theimy = 0;
690   int baseline = get_integer_resource ("bitmapBaseline", "Integer");
691   int delay = get_integer_resource ("delay", "Integer");
692   xim      = NULL;
693   top      = 1;
694   flame    = NULL;
695
696   GetXInfo(disp,win);
697   InitColors();
698   theim = loadBitmap(&theimx, &theimy);
699
700   /* utils/xshm.c doesn't provide a way to free the shared-memory image, which
701      makes it hard for us to react to window resizing.  So, punt for now.  The
702      size of the window at startup is the size it will stay.
703   */
704   GetXInfo(disp,win);
705
706   MakeImage();
707   InitFlame();
708   FlameFill(0);
709
710   while (1)
711     {
712       FlameActive();
713
714       if (theim)
715         FlamePasteData(theim, (fwidth - theimx) / 2,
716                        fheight - theimy - baseline, theimx, theimy);
717
718       FlameAdvance();
719       Flame2Image();
720       DisplayImage();
721
722       XSync(display,False);
723       screenhack_handle_events(display);
724       if (delay)
725         usleep (delay);
726     }
727 }