1 /* xflame, Copyright (c) 1996-2002 Carsten Haitzler <raster@redhat.com>
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
12 /* Version history as near as I (jwz) can piece together:
14 * Carsten Haitzler <raster@redhat.com> wrote the first version in 1996.
16 * Rahul Jain <rahul@rice.edu> added support for TrueColor displays.
18 * Someone did a rough port of it to use the xscreensaver utility routines
19 instead of creating its own window by hand.
21 * Someone (probably Raster) came up with a subsequent version that had
22 a Red Hat logo hardcoded into it.
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.
28 * Jamie Zawinski <jwz@jwz.org> found several versions of xflame in
29 March 1999, and pieced them together. Changes:
31 - Correct and fault-tolerant use of the Shared Memory extension;
32 previous versions of xflame did not work when $DISPLAY was remote.
34 - Replaced PGM-reading code with code that can read arbitrary XBM
35 and XPM files (color ones will be converted to grayscale.)
37 - Command-line options all around -- no hardcoded pathnames or
40 - General cleanup and portability tweaks.
42 * 4-Oct-99, jwz: added support for packed-24bpp (versus 32bpp.)
43 * 16-Jan-2002, jwz: added gdk_pixbuf support.
47 /* portions by Daniel Zahn <stumpy@religions.com> */
50 #include "screenhack.h"
51 #include "xpm-pixmap.h"
55 #define countof(x) (sizeof((x))/sizeof((*x)))
57 #ifdef HAVE_XSHM_EXTENSION
59 #endif /* HAVE_XSHM_EXTENSION */
61 #include "images/bob.xbm"
77 #ifdef HAVE_XSHM_EXTENSION
78 XShmSegmentInfo shminfo;
79 #endif /* HAVE_XSHM_EXTENSION */
104 GetXInfo(struct state *st)
106 XWindowAttributes xwa;
108 XGetWindowAttributes(st->dpy,st->window,&xwa);
110 st->colormap = xwa.colormap;
111 st->depth = xwa.depth;
112 st->visual = xwa.visual;
113 st->screen = xwa.screen;
114 st->width = xwa.width;
115 st->height = xwa.height;
124 MakeImage(struct state *st)
128 #ifdef HAVE_XSHM_EXTENSION
130 st->xim = create_xshm_image (st->dpy, st->visual, st->depth, ZPixmap, NULL,
131 &st->shminfo, st->width, st->height);
132 #else /* !HAVE_XSHM_EXTENSION */
134 #endif /* !HAVE_XSHM_EXTENSION */
139 st->xim = XCreateImage (st->dpy, st->visual, st->depth, ZPixmap, 0, NULL,
140 st->width, st->height, 32, 0);
142 st->xim->data = (char *) calloc(st->xim->height, st->xim->bytes_per_line);
143 if (!st->xim || !st->xim->data)
145 fprintf(stderr,"%s: out of memory.\n", progname);
150 st->gc = XCreateGC(st->dpy,st->window,0,&gcv);
151 if (!st->gc) exit (1);
156 InitColors(struct state *st)
158 int i = 0, j = 0, red = 0, green = 0, blue = 0;
161 /* Make it possible to set the color of the flames,
162 by Raymond Medeiros <ray@stommel.marine.usf.edu> and jwz.
164 fg.pixel = get_pixel_resource (st->dpy, st->colormap,
165 "foreground", "Foreground");
166 XQueryColor (st->dpy, st->colormap, &fg);
168 red = 255 - (fg.red >> 8);
169 green = 255 - (fg.green >> 8);
170 blue = 255 - (fg.blue >> 8);
173 for (i = 0; i < 256 * 2; i += 2)
176 int r = (i - red) * 3;
177 int g = (i - green) * 3;
178 int b = (i - blue) * 3;
181 if (r > 255) r = 255;
183 if (g > 255) g = 255;
185 if (b > 255) b = 255;
187 xcl.red = (unsigned short)((r << 8) | r);
188 xcl.green = (unsigned short)((g << 8) | g);
189 xcl.blue = (unsigned short)((b << 8) | b);
190 xcl.flags = DoRed | DoGreen | DoBlue;
192 XAllocColor(st->dpy,st->colormap,&xcl);
194 st->ctab[j++] = (int)xcl.pixel;
200 DisplayImage(struct state *st)
202 #ifdef HAVE_XSHM_EXTENSION
204 XShmPutImage(st->dpy, st->window, st->gc, st->xim, 0,(st->top - 1) << 1, 0,
205 (st->top - 1) << 1, st->width, st->height - ((st->top - 1) << 1), False);
207 #endif /* HAVE_XSHM_EXTENSION */
208 XPutImage(st->dpy, st->window, st->gc, st->xim, 0, (st->top - 1) << 1, 0,
209 (st->top - 1) << 1, st->width, st->height - ((st->top - 1) << 1));
214 InitFlame(struct state *st)
216 st->fwidth = st->width / 2;
217 st->fheight = st->height / 2;
218 st->flame = (unsigned char *) malloc((st->fwidth + 2) * (st->fheight + 2)
219 * sizeof(unsigned char));
223 fprintf(stderr,"%s: out of memory\n", progname);
228 st->ihspread = get_integer_resource(st->dpy, "hspread", "Integer");
229 st->ivspread = get_integer_resource(st->dpy, "vspread", "Integer");
230 st->iresidual = get_integer_resource(st->dpy, "residual", "Integer");
231 st->variance = get_integer_resource(st->dpy, "variance", "Integer");
232 st->vartrend = get_integer_resource(st->dpy, "vartrend", "Integer");
233 st->bloom = get_boolean_resource(st->dpy, "bloom", "Boolean");
235 # define THROTTLE(VAR,NAME) \
236 if (VAR < 0 || VAR > 255) { \
237 fprintf(stderr, "%s: %s must be in the range 0-255 (not %d).\n", \
238 progname, NAME, VAR); \
240 THROTTLE (st->ihspread, "hspread");
241 THROTTLE (st->ivspread, "vspread");
242 THROTTLE (st->iresidual,"residual");
243 THROTTLE (st->variance, "variance");
244 THROTTLE (st->vartrend, "vartrend");
249 st->hspread = st->ihspread;
250 st->vspread = st->ivspread;
251 st->residual = st->iresidual;
256 Flame2Image16(struct state *st)
263 ptr = (unsigned short *)st->xim->data;
264 ptr += (st->top << 1) * st->width;
265 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
267 for(y = st->top; y < st->fheight; y++)
269 for( x = 0; x < st->fwidth; x++)
272 v2 = (int)*(ptr1 + 1);
273 v3 = (int)*(ptr1 + st->fwidth + 2);
274 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
276 *ptr++ = (unsigned short)st->ctab[v1];
277 *ptr = (unsigned short)st->ctab[(v1 + v2) >> 1];
278 ptr += st->width - 1;
279 *ptr++ = (unsigned short)st->ctab[(v1 + v3) >> 1];
280 *ptr = (unsigned short)st->ctab[(v1 + v4) >> 1];
281 ptr -= st->width - 1;
289 Flame2Image32(struct state *st)
296 ptr = (unsigned int *)st->xim->data;
297 ptr += (st->top << 1) * st->width;
298 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
300 for( y = st->top; y < st->fheight; y++)
302 for( x = 0; x < st->fwidth; x++)
305 v2 = (int)*(ptr1 + 1);
306 v3 = (int)*(ptr1 + st->fwidth + 2);
307 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
309 *ptr++ = (unsigned int)st->ctab[v1];
310 *ptr = (unsigned int)st->ctab[(v1 + v2) >> 1];
311 ptr += st->width - 1;
312 *ptr++ = (unsigned int)st->ctab[(v1 + v3) >> 1];
313 *ptr = (unsigned int)st->ctab[(v1 + v4) >> 1];
314 ptr -= st->width - 1;
322 Flame2Image24(struct state *st)
329 ptr = (unsigned char *)st->xim->data;
330 ptr += (st->top << 1) * st->xim->bytes_per_line;
331 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
333 for( y = st->top; y < st->fheight; y++)
335 unsigned char *last_ptr = ptr;
336 for( x = 0; x < st->fwidth; x++)
339 v2 = (int)*(ptr1 + 1);
340 v3 = (int)*(ptr1 + st->fwidth + 2);
341 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
344 ptr[2] = ((unsigned int)st->ctab[v1] & 0x00FF0000) >> 16;
345 ptr[1] = ((unsigned int)st->ctab[v1] & 0x0000FF00) >> 8;
346 ptr[0] = ((unsigned int)st->ctab[v1] & 0x000000FF);
349 ptr[2] = ((unsigned int)st->ctab[(v1 + v2) >> 1] & 0x00FF0000) >> 16;
350 ptr[1] = ((unsigned int)st->ctab[(v1 + v2) >> 1] & 0x0000FF00) >> 8;
351 ptr[0] = ((unsigned int)st->ctab[(v1 + v2) >> 1] & 0x000000FF);
352 ptr += ((st->width - 1) * 3);
354 ptr[2] = ((unsigned int)st->ctab[(v1 + v3) >> 1] & 0x00FF0000) >> 16;
355 ptr[1] = ((unsigned int)st->ctab[(v1 + v3) >> 1] & 0x0000FF00) >> 8;
356 ptr[0] = ((unsigned int)st->ctab[(v1 + v3) >> 1] & 0x000000FF);
359 ptr[2] = ((unsigned int)st->ctab[(v1 + v4) >> 1] & 0x00FF0000) >> 16;
360 ptr[1] = ((unsigned int)st->ctab[(v1 + v4) >> 1] & 0x0000FF00) >> 8;
361 ptr[0] = ((unsigned int)st->ctab[(v1 + v4) >> 1] & 0x000000FF);
362 ptr -= ((st->width - 1) * 3);
365 ptr = last_ptr + (st->xim->bytes_per_line << 1);
371 Flame2Image8(struct state *st)
378 ptr = (unsigned char *)st->xim->data;
379 ptr += (st->top << 1) * st->width;
380 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
382 for(y=st->top;y<st->fheight;y++)
384 for(x=0;x<st->fwidth;x++)
387 v2 = (int)*(ptr1 + 1);
388 v3 = (int)*(ptr1 + st->fwidth + 2);
389 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
391 *ptr++ = (unsigned char)st->ctab[v1];
392 *ptr = (unsigned char)st->ctab[(v1 + v2) >> 1];
393 ptr += st->width - 1;
394 *ptr++ = (unsigned char)st->ctab[(v1 + v3) >> 1];
395 *ptr = (unsigned char)st->ctab[(v1 + v4) >> 1];
396 ptr -= st->width - 1;
404 Flame2Image1234567(struct state *st)
410 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
412 for( y = st->top; y < st->fheight; y++)
414 for( x = 0; x < st->fwidth; x++)
417 v2 = (int)*(ptr1 + 1);
418 v3 = (int)*(ptr1 + st->fwidth + 2);
419 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
421 XPutPixel(st->xim,(x << 1), (y << 1), st->ctab[v1]);
422 XPutPixel(st->xim,(x << 1) + 1,(y << 1), st->ctab[(v1 + v2) >> 1]);
423 XPutPixel(st->xim,(x << 1), (y << 1) + 1,st->ctab[(v1 + v3) >> 1]);
424 XPutPixel(st->xim,(x << 1) + 1,(y << 1) + 1,st->ctab[(v1 + v4) >> 1]);
430 Flame2Image(struct state *st)
432 switch (st->xim->bits_per_pixel)
434 case 32: Flame2Image32(st); break;
435 case 24: Flame2Image24(st); break;
436 case 16: Flame2Image16(st); break;
437 case 8: Flame2Image8(st); break;
439 if (st->xim->bits_per_pixel <= 7)
440 Flame2Image1234567(st);
449 FlameActive(struct state *st)
454 ptr1 = st->flame + ((st->fheight + 1) * (st->fwidth + 2));
456 for (x = 0; x < st->fwidth + 2; x++)
459 v1 += ((random() % st->variance) - st->vartrend);
465 v1= (random() % 100);
467 st->residual += (random()%10);
469 st->hspread += (random()%15);
471 st->vspread += (random()%20);
474 st->residual = ((st->iresidual* 10) + (st->residual *90)) / 100;
475 st->hspread = ((st->ihspread * 10) + (st->hspread *90)) / 100;
476 st->vspread = ((st->ivspread * 10) + (st->vspread *90)) / 100;
481 FlameAdvance(struct state *st)
485 int newtop = st->top;
487 for (y = st->fheight + 1; y >= st->top; y--)
490 unsigned char *ptr1 = st->flame + 1 + (y * (st->fwidth + 2));
491 for (x = 0; x < st->fwidth; x++)
498 ptr2 = ptr1 - st->fwidth - 2;
499 v3 = (v1 * st->vspread) >> 8;
505 *(ptr2) = (unsigned char)v2;
506 v3 = (v1 * st->hspread) >> 8;
507 v2 = (int)*(ptr2 + 1);
512 *(ptr2 + 1) = (unsigned char)v2;
513 v2 = (int)*(ptr2 - 1);
518 *(ptr2 - 1) = (unsigned char)v2;
520 if (y < st->fheight + 1)
522 v1 = (v1 * st->residual) >> 8;
523 *ptr1 = (unsigned char)v1;
531 /* clean up the right gutter */
534 v1 = (v1 * st->residual) >> 8;
535 *ptr1 = (unsigned char)v1;
539 st->top = newtop - 1;
547 FlameFill(struct state *st, int val)
550 for (y = 0; y < st->fheight + 1; y++)
552 unsigned char *ptr1 = st->flame + 1 + (y * (st->fwidth + 2));
553 for (x = 0; x < st->fwidth; x++)
563 FlamePasteData(struct state *st,
564 unsigned char *d, int xx, int yy, int w, int h)
566 unsigned char *ptr1,*ptr2;
574 (xx + w <= st->fwidth) &&
575 (yy + h <= st->fheight))
578 for (y = 0; y < h; y++)
580 ptr1 = st->flame + 1 + xx + ((yy + y) * (st->fwidth + 2));
581 for (x = 0; x < w; x++)
584 *ptr1 += random() % (*ptr2 / 24);
593 static Bool warned = False;
596 fprintf (stderr, "%s: st->window is %dx%d; image must be "
597 "smaller than %dx%d (not %dx%d).\n",
598 progname, st->width, st->height, st->fwidth, st->fheight, w, h);
605 static unsigned char *
606 loadBitmap(struct state *st, int *w, int *h)
608 char *bitmap_name = get_string_resource (st->dpy, "bitmap", "Bitmap");
611 bitmap_name = "(default)"; /* #### always use builtin */
612 #endif /* HAVE_COCOA */
616 !strcmp(bitmap_name, "none"))
618 else if (!strcmp(bitmap_name, "(default)")) /* use the builtin */
621 unsigned char *result, *o;
622 char *bits = (char *) malloc (sizeof(bob_bits));
624 int scale = ((st->width > bob_width * 11) ? 2 : 1);
626 memcpy (bits, bob_bits, sizeof(bob_bits));
627 ximage = XCreateImage (st->dpy, st->visual, 1, XYBitmap, 0, bits,
628 bob_width, bob_height, 8, 0);
629 ximage->byte_order = LSBFirst;
630 ximage->bitmap_bit_order = LSBFirst;
631 *w = ximage->width * scale;
632 *h = ximage->height * scale;
633 o = result = (unsigned char *) malloc ((*w * scale) * (*h * scale));
634 for (y = 0; y < *h; y++)
635 for (x = 0; x < *w; x++)
636 *o++ = (XGetPixel(ximage, x/scale, y/scale) ? 255 : 0);
640 else /* load a bitmap file */
642 abort(); /* #### fix me */
646 xpm_file_to_pixmap (st->dpy, st->window, bitmap_name, &st->width, &st->height, 0);
649 unsigned char *result, *o;
651 Bool cmap_p = has_writable_cells (st->screen, st->visual);
656 for (i = 0; i < countof (colors); i++)
658 XQueryColors (st->dpy, st->colormap, colors, countof (colors));
661 image = XGetImage (st->dpy, pixmap, 0, 0, st->width, st->height, ~0L, ZPixmap);
662 XFreePixmap(st->dpy, pixmap);
664 result = (unsigned char *) malloc (st->width * st->height);
666 for (y = 0; y < st->height; y++)
667 for (x = 0; x < st->width; x++)
669 int rgba = XGetPixel (image, x, y);
672 gray = ((200 - ((((colors[rgba].red >> 8) & 0xFF) +
673 ((colors[rgba].green >> 8) & 0xFF) +
674 ((colors[rgba].blue >> 8) & 0xFF))
678 /* This is *so* not handling all the cases... */
679 gray = (image->depth > 16
680 ? ((((rgba >> 24) & 0xFF) +
681 ((rgba >> 16) & 0xFF) +
682 ((rgba >> 8) & 0xFF) +
683 ((rgba ) & 0xFF)) >> 2)
684 : ((((rgba >> 12) & 0x0F) +
685 ((rgba >> 8) & 0x0F) +
686 ((rgba >> 4) & 0x0F) +
687 ((rgba ) & 0x0F)) >> 1));
694 XDestroyImage (image);
700 #endif /* !HAVE_COCOA */
709 xflame_init (Display *dpy, Window win)
711 struct state *st = (struct state *) calloc (1, sizeof(*st));
714 st->baseline = get_integer_resource (dpy, "bitmapBaseline", "Integer");
715 st->delay = get_integer_resource (dpy, "delay", "Integer");
722 st->theim = loadBitmap(st, &st->theimx, &st->theimy);
724 /* utils/xshm.c doesn't provide a way to free the shared-memory image, which
725 makes it hard for us to react to window resizing. So, punt for now. The
726 size of the window at startup is the size it will stay.
738 xflame_draw (Display *dpy, Window win, void *closure)
740 struct state *st = (struct state *) closure;
744 FlamePasteData(st, st->theim, (st->fwidth - st->theimx) / 2,
745 st->fheight - st->theimy - st->baseline, st->theimx, st->theimy);
755 xflame_reshape (Display *dpy, Window window, void *closure,
756 unsigned int w, unsigned int h)
761 xflame_event (Display *dpy, Window window, void *closure, XEvent *event)
767 xflame_free (Display *dpy, Window window, void *closure)
774 static const char *xflame_defaults [] = {
775 ".background: black",
776 ".foreground: #FFAF5F",
779 "*bitmap: (default)",
780 "*bitmapBaseline: 20",
789 #ifdef HAVE_XSHM_EXTENSION
790 "*useSHM: False", /* xshm turns out not to help. */
791 #endif /* HAVE_XSHM_EXTENSION */
795 static XrmOptionDescRec xflame_options [] = {
796 { "-foreground",".foreground", XrmoptionSepArg, 0 },
797 { "-fg", ".foreground", XrmoptionSepArg, 0 },
798 { "-delay", ".delay", XrmoptionSepArg, 0 },
799 { "-bitmap", ".bitmap", XrmoptionSepArg, 0 },
800 { "-baseline", ".bitmapBaseline", XrmoptionSepArg, 0 },
801 { "-hspread", ".hspread", XrmoptionSepArg, 0 },
802 { "-vspread", ".vspread", XrmoptionSepArg, 0 },
803 { "-residual", ".residual", XrmoptionSepArg, 0 },
804 { "-variance", ".variance", XrmoptionSepArg, 0 },
805 { "-vartrend", ".vartrend", XrmoptionSepArg, 0 },
806 { "-bloom", ".bloom", XrmoptionNoArg, "True" },
807 { "-no-bloom", ".bloom", XrmoptionNoArg, "False" },
808 #ifdef HAVE_XSHM_EXTENSION
809 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
810 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
811 #endif /* HAVE_XSHM_EXTENSION */
816 XSCREENSAVER_MODULE ("XFlame", xflame)