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