1 /* xflame, Copyright (c) 1996-2018 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.
44 * 9-Oct-2016, Dave Odell <dmo2118@gmail.com>: Updated for new xshm.c.
48 /* portions by Daniel Zahn <stumpy@religions.com> */
51 #include "screenhack.h"
52 #include "ximage-loader.h"
56 #define countof(x) (sizeof((x))/sizeof((*x)))
60 #include "images/gen/bob_png.h"
75 XShmSegmentInfo shminfo;
100 GetXInfo(struct state *st)
102 XWindowAttributes xwa;
104 XGetWindowAttributes(st->dpy,st->window,&xwa);
106 st->colormap = xwa.colormap;
107 st->depth = xwa.depth;
108 st->visual = xwa.visual;
109 st->screen = xwa.screen;
110 st->width = xwa.width;
111 st->height = xwa.height;
120 MakeImage(struct state *st)
125 destroy_xshm_image (st->dpy, st->xim, &st->shminfo);
127 st->xim = create_xshm_image (st->dpy, st->visual, st->depth, ZPixmap,
128 &st->shminfo, st->width, st->height);
131 fprintf(stderr,"%s: out of memory.\n", progname);
136 st->gc = XCreateGC(st->dpy,st->window,0,&gcv);
141 InitColors(struct state *st)
143 int i = 0, j = 0, red = 0, green = 0, blue = 0;
146 /* Make it possible to set the color of the flames,
147 by Raymond Medeiros <ray@stommel.marine.usf.edu> and jwz.
149 fg.pixel = get_pixel_resource (st->dpy, st->colormap,
150 "foreground", "Foreground");
151 XQueryColor (st->dpy, st->colormap, &fg);
153 red = 255 - (fg.red >> 8);
154 green = 255 - (fg.green >> 8);
155 blue = 255 - (fg.blue >> 8);
158 for (i = 0; i < 256 * 2; i += 2)
161 int r = (i - red) * 3;
162 int g = (i - green) * 3;
163 int b = (i - blue) * 3;
166 if (r > 255) r = 255;
168 if (g > 255) g = 255;
170 if (b > 255) b = 255;
172 xcl.red = (unsigned short)((r << 8) | r);
173 xcl.green = (unsigned short)((g << 8) | g);
174 xcl.blue = (unsigned short)((b << 8) | b);
175 xcl.flags = DoRed | DoGreen | DoBlue;
177 XAllocColor(st->dpy,st->colormap,&xcl);
179 st->ctab[j++] = (int)xcl.pixel;
185 DisplayImage(struct state *st)
187 put_xshm_image(st->dpy, st->window, st->gc, st->xim, 0,(st->top - 1) << 1, 0,
188 (st->top - 1) << 1, st->width, st->height - ((st->top - 1) << 1),
194 InitFlame(struct state *st)
196 st->fwidth = st->width / 2;
197 st->fheight = st->height / 2;
199 if (st->flame) free (st->flame);
200 st->flame = (unsigned char *) malloc((st->fwidth + 2) * (st->fheight + 2)
201 * sizeof(unsigned char));
205 fprintf(stderr,"%s: out of memory\n", progname);
210 st->ihspread = get_integer_resource(st->dpy, "hspread", "Integer");
211 st->ivspread = get_integer_resource(st->dpy, "vspread", "Integer");
212 st->iresidual = get_integer_resource(st->dpy, "residual", "Integer");
213 st->variance = get_integer_resource(st->dpy, "variance", "Integer");
214 st->vartrend = get_integer_resource(st->dpy, "vartrend", "Integer");
215 st->bloom = get_boolean_resource(st->dpy, "bloom", "Boolean");
217 # define THROTTLE(VAR,NAME) \
218 if (VAR < 0 || VAR > 255) { \
219 fprintf(stderr, "%s: %s must be in the range 0-255 (not %d).\n", \
220 progname, NAME, VAR); \
222 THROTTLE (st->ihspread, "hspread");
223 THROTTLE (st->ivspread, "vspread");
224 THROTTLE (st->iresidual,"residual");
225 THROTTLE (st->variance, "variance");
226 THROTTLE (st->vartrend, "vartrend");
231 st->hspread = st->ihspread;
232 st->vspread = st->ivspread;
233 st->residual = st->iresidual;
238 Flame2Image16(struct state *st)
245 ptr = (unsigned short *)st->xim->data;
246 ptr += (st->top << 1) * st->width;
247 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
249 for(y = st->top; y < st->fheight; y++)
251 for( x = 0; x < st->fwidth; x++)
254 v2 = (int)*(ptr1 + 1);
255 v3 = (int)*(ptr1 + st->fwidth + 2);
256 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
258 *ptr++ = (unsigned short)st->ctab[v1];
259 *ptr = (unsigned short)st->ctab[(v1 + v2) >> 1];
260 ptr += st->width - 1;
261 *ptr++ = (unsigned short)st->ctab[(v1 + v3) >> 1];
262 *ptr = (unsigned short)st->ctab[(v1 + v4) >> 1];
263 ptr -= st->width - 1;
271 Flame2Image32(struct state *st)
278 ptr = (unsigned int *)st->xim->data;
279 ptr += (st->top << 1) * st->width;
280 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
282 for( y = st->top; y < st->fheight; y++)
284 for( x = 0; x < st->fwidth; x++)
287 v2 = (int)*(ptr1 + 1);
288 v3 = (int)*(ptr1 + st->fwidth + 2);
289 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
291 *ptr++ = (unsigned int)st->ctab[v1];
292 *ptr = (unsigned int)st->ctab[(v1 + v2) >> 1];
293 ptr += st->width - 1;
294 *ptr++ = (unsigned int)st->ctab[(v1 + v3) >> 1];
295 *ptr = (unsigned int)st->ctab[(v1 + v4) >> 1];
296 ptr -= st->width - 1;
304 Flame2Image24(struct state *st)
311 ptr = (unsigned char *)st->xim->data;
312 ptr += (st->top << 1) * st->xim->bytes_per_line;
313 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
315 for( y = st->top; y < st->fheight; y++)
317 unsigned char *last_ptr = ptr;
318 for( x = 0; x < st->fwidth; x++)
321 v2 = (int)*(ptr1 + 1);
322 v3 = (int)*(ptr1 + st->fwidth + 2);
323 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
326 ptr[2] = ((unsigned int)st->ctab[v1] & 0x00FF0000) >> 16;
327 ptr[1] = ((unsigned int)st->ctab[v1] & 0x0000FF00) >> 8;
328 ptr[0] = ((unsigned int)st->ctab[v1] & 0x000000FF);
331 ptr[2] = ((unsigned int)st->ctab[(v1 + v2) >> 1] & 0x00FF0000) >> 16;
332 ptr[1] = ((unsigned int)st->ctab[(v1 + v2) >> 1] & 0x0000FF00) >> 8;
333 ptr[0] = ((unsigned int)st->ctab[(v1 + v2) >> 1] & 0x000000FF);
334 ptr += ((st->width - 1) * 3);
336 ptr[2] = ((unsigned int)st->ctab[(v1 + v3) >> 1] & 0x00FF0000) >> 16;
337 ptr[1] = ((unsigned int)st->ctab[(v1 + v3) >> 1] & 0x0000FF00) >> 8;
338 ptr[0] = ((unsigned int)st->ctab[(v1 + v3) >> 1] & 0x000000FF);
341 ptr[2] = ((unsigned int)st->ctab[(v1 + v4) >> 1] & 0x00FF0000) >> 16;
342 ptr[1] = ((unsigned int)st->ctab[(v1 + v4) >> 1] & 0x0000FF00) >> 8;
343 ptr[0] = ((unsigned int)st->ctab[(v1 + v4) >> 1] & 0x000000FF);
344 ptr -= ((st->width - 1) * 3);
347 ptr = last_ptr + (st->xim->bytes_per_line << 1);
353 Flame2Image8(struct state *st)
360 ptr = (unsigned char *)st->xim->data;
361 ptr += (st->top << 1) * st->width;
362 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
364 for(y=st->top;y<st->fheight;y++)
366 for(x=0;x<st->fwidth;x++)
369 v2 = (int)*(ptr1 + 1);
370 v3 = (int)*(ptr1 + st->fwidth + 2);
371 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
373 *ptr++ = (unsigned char)st->ctab[v1];
374 *ptr = (unsigned char)st->ctab[(v1 + v2) >> 1];
375 ptr += st->width - 1;
376 *ptr++ = (unsigned char)st->ctab[(v1 + v3) >> 1];
377 *ptr = (unsigned char)st->ctab[(v1 + v4) >> 1];
378 ptr -= st->width - 1;
386 Flame2Image1234567(struct state *st)
392 ptr1 = st->flame + 1 + (st->top * (st->fwidth + 2));
394 for( y = st->top; y < st->fheight; y++)
396 for( x = 0; x < st->fwidth; x++)
399 v2 = (int)*(ptr1 + 1);
400 v3 = (int)*(ptr1 + st->fwidth + 2);
401 v4 = (int)*(ptr1 + st->fwidth + 2 + 1);
403 XPutPixel(st->xim,(x << 1), (y << 1), st->ctab[v1]);
404 XPutPixel(st->xim,(x << 1) + 1,(y << 1), st->ctab[(v1 + v2) >> 1]);
405 XPutPixel(st->xim,(x << 1), (y << 1) + 1,st->ctab[(v1 + v3) >> 1]);
406 XPutPixel(st->xim,(x << 1) + 1,(y << 1) + 1,st->ctab[(v1 + v4) >> 1]);
412 Flame2Image(struct state *st)
414 switch (st->xim->bits_per_pixel)
416 case 32: Flame2Image32(st); break;
417 case 24: Flame2Image24(st); break;
418 case 16: Flame2Image16(st); break;
419 case 8: Flame2Image8(st); break;
421 if (st->xim->bits_per_pixel <= 7)
422 Flame2Image1234567(st);
431 FlameActive(struct state *st)
436 ptr1 = st->flame + ((st->fheight + 1) * (st->fwidth + 2));
438 for (x = 0; x < st->fwidth + 2; x++)
441 v1 += ((random() % st->variance) - st->vartrend);
447 v1= (random() % 100);
449 st->residual += (random()%10);
451 st->hspread += (random()%15);
453 st->vspread += (random()%20);
456 st->residual = ((st->iresidual* 10) + (st->residual *90)) / 100;
457 st->hspread = ((st->ihspread * 10) + (st->hspread *90)) / 100;
458 st->vspread = ((st->ivspread * 10) + (st->vspread *90)) / 100;
463 FlameAdvance(struct state *st)
467 int newtop = st->top;
469 for (y = st->fheight + 1; y >= st->top; y--)
472 unsigned char *ptr1 = st->flame + 1 + (y * (st->fwidth + 2));
473 for (x = 0; x < st->fwidth; x++)
480 ptr2 = ptr1 - st->fwidth - 2;
481 v3 = (v1 * st->vspread) >> 8;
487 *(ptr2) = (unsigned char)v2;
488 v3 = (v1 * st->hspread) >> 8;
489 v2 = (int)*(ptr2 + 1);
494 *(ptr2 + 1) = (unsigned char)v2;
495 v2 = (int)*(ptr2 - 1);
500 *(ptr2 - 1) = (unsigned char)v2;
502 if (y < st->fheight + 1)
504 v1 = (v1 * st->residual) >> 8;
505 *ptr1 = (unsigned char)v1;
513 /* clean up the right gutter */
516 v1 = (v1 * st->residual) >> 8;
517 *ptr1 = (unsigned char)v1;
521 st->top = newtop - 1;
529 FlameFill(struct state *st, int val)
532 for (y = 0; y < st->fheight + 1; y++)
534 unsigned char *ptr1 = st->flame + 1 + (y * (st->fwidth + 2));
535 for (x = 0; x < st->fwidth; x++)
545 FlamePasteData(struct state *st,
546 unsigned char *d, int xx, int yy, int w, int h)
548 unsigned char *ptr1,*ptr2;
556 (xx + w <= st->fwidth) &&
557 (yy + h <= st->fheight))
560 for (y = 0; y < h; y++)
562 ptr1 = st->flame + 1 + xx + ((yy + y) * (st->fwidth + 2));
563 for (x = 0; x < w; x++)
566 *ptr1 += random() % (*ptr2 / 24);
575 static Bool warned = False;
578 fprintf (stderr, "%s: st->window is %dx%d; image must be "
579 "smaller than %dx%d (not %dx%d).\n",
580 progname, st->width, st->height, st->fwidth, st->fheight, w, h);
588 double_pixmap (Display *dpy, Visual *visual, int depth, Pixmap pixmap,
589 int pix_w, int pix_h)
592 Pixmap p2 = XCreatePixmap(dpy, pixmap, pix_w*2, pix_h*2, depth);
593 XImage *i1 = XGetImage (dpy, pixmap, 0, 0, pix_w, pix_h, ~0L,
594 (depth == 1 ? XYPixmap : ZPixmap));
595 XImage *i2 = XCreateImage (dpy, visual, depth,
596 (depth == 1 ? XYPixmap : ZPixmap), 0, 0,
597 pix_w*2, pix_h*2, 8, 0);
599 GC gc = XCreateGC (dpy, p2, 0, &gcv);
600 i2->data = (char *) calloc(i2->height, i2->bytes_per_line);
601 for (y = 0; y < pix_h; y++)
602 for (x = 0; x < pix_w; x++)
604 unsigned long p = XGetPixel(i1, x, y);
605 XPutPixel(i2, x*2, y*2, p);
606 XPutPixel(i2, x*2+1, y*2, p);
607 XPutPixel(i2, x*2, y*2+1, p);
608 XPutPixel(i2, x*2+1, y*2+1, p);
610 free(i1->data); i1->data = 0;
612 XPutImage(dpy, p2, gc, i2, 0, 0, 0, 0, i2->width, i2->height);
614 free(i2->data); i2->data = 0;
616 XFreePixmap(dpy, pixmap);
621 static unsigned char *
622 reformat_pixmap (struct state *st, Pixmap pixmap, Pixmap mask, int *w, int *h)
624 XImage *image = 0, *mimage = 0;
626 unsigned char *result, *o;
628 Bool cmap_p = has_writable_cells (st->screen, st->visual);
630 while (*w < st->width / 10 &&
631 *h < st->height / 10)
633 pixmap = double_pixmap (st->dpy, st->visual, st->depth, pixmap, *w, *h);
635 mask = double_pixmap (st->dpy, st->visual, st->depth, mask, *w, *h);
643 for (i = 0; i < countof (colors); i++)
645 XQueryColors (st->dpy, st->colormap, colors, countof (colors));
648 image = XGetImage (st->dpy, pixmap, 0, 0, *w, *h, ~0L, ZPixmap);
649 XFreePixmap(st->dpy, pixmap);
653 mimage = XGetImage (st->dpy, mask, 0, 0, *w, *h, ~0L, ZPixmap);
654 XFreePixmap(st->dpy, mask);
657 result = (unsigned char *) malloc (image->width * image->height);
659 for (y = 0; y < image->height; y++)
660 for (x = 0; x < image->width; x++)
662 unsigned long rgb = XGetPixel (image, x, y);
663 unsigned long a = mimage ? XGetPixel (mimage, x, y) : 1;
668 gray = ((200 - ((((colors[rgb].red >> 8) & 0xFF) +
669 ((colors[rgb].green >> 8) & 0xFF) +
670 ((colors[rgb].blue >> 8) & 0xFF))
674 /* This is *so* not handling all the cases... */
675 gray = (image->depth > 16
676 ? ((((rgb >> 24) & 0xFF) +
677 ((rgb >> 16) & 0xFF) +
678 ((rgb >> 8) & 0xFF) +
679 ((rgb ) & 0xFF)) >> 2)
680 : ((((rgb >> 12) & 0x0F) +
681 ((rgb >> 8) & 0x0F) +
682 ((rgb >> 4) & 0x0F) +
683 ((rgb ) & 0x0F)) >> 1));
690 XDestroyImage (image);
692 XDestroyImage (mimage);
699 static unsigned char *
700 loadBitmap(struct state *st, int *w, int *h)
703 const char *bitmap_name = "(default)"; /* #### always use builtin */
705 char *bitmap_name = get_string_resource (st->dpy, "bitmap", "Bitmap");
707 Pixmap p = 0, mask = 0;
710 !strcmp(bitmap_name, "none"))
712 else if (!strcmp(bitmap_name, "(default)")) /* use the builtin */
713 p = image_data_to_pixmap (st->dpy, st->window,
714 bob_png, sizeof(bob_png),
716 else /* load a bitmap file */
717 p = file_to_pixmap (st->dpy, st->window, bitmap_name,
718 &st->width, &st->height, 0);
721 return reformat_pixmap (st, p, mask, w, h);
725 xflame_init (Display *dpy, Window win)
727 struct state *st = (struct state *) calloc (1, sizeof(*st));
730 st->baseline = get_integer_resource (dpy, "bitmapBaseline", "Integer");
731 st->delay = get_integer_resource (dpy, "delay", "Integer");
738 st->theim = loadBitmap(st, &st->theimx, &st->theimy);
748 xflame_draw (Display *dpy, Window win, void *closure)
750 struct state *st = (struct state *) closure;
754 FlamePasteData(st, st->theim, (st->fwidth - st->theimx) / 2,
755 st->fheight - st->theimy - st->baseline, st->theimx, st->theimy);
765 xflame_reshape (Display *dpy, Window window, void *closure,
766 unsigned int w, unsigned int h)
768 struct state *st = (struct state *) closure;
773 XClearWindow (dpy, window);
777 xflame_event (Display *dpy, Window window, void *closure, XEvent *event)
783 xflame_free (Display *dpy, Window window, void *closure)
790 static const char *xflame_defaults [] = {
791 ".background: black",
792 ".foreground: #FFAF5F",
795 "*bitmap: (default)",
796 "*bitmapBaseline: 20",
805 #ifdef HAVE_XSHM_EXTENSION
806 "*useSHM: False", /* xshm turns out not to help. */
807 #endif /* HAVE_XSHM_EXTENSION */
811 static XrmOptionDescRec xflame_options [] = {
812 { "-foreground",".foreground", XrmoptionSepArg, 0 },
813 { "-fg", ".foreground", XrmoptionSepArg, 0 },
814 { "-delay", ".delay", XrmoptionSepArg, 0 },
815 { "-bitmap", ".bitmap", XrmoptionSepArg, 0 },
816 { "-baseline", ".bitmapBaseline", XrmoptionSepArg, 0 },
817 { "-hspread", ".hspread", XrmoptionSepArg, 0 },
818 { "-vspread", ".vspread", XrmoptionSepArg, 0 },
819 { "-residual", ".residual", XrmoptionSepArg, 0 },
820 { "-variance", ".variance", XrmoptionSepArg, 0 },
821 { "-vartrend", ".vartrend", XrmoptionSepArg, 0 },
822 { "-bloom", ".bloom", XrmoptionNoArg, "True" },
823 { "-no-bloom", ".bloom", XrmoptionNoArg, "False" },
824 #ifdef HAVE_XSHM_EXTENSION
825 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
826 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
827 #endif /* HAVE_XSHM_EXTENSION */
832 XSCREENSAVER_MODULE ("XFlame", xflame)