+
+ /* If we're rendering onto the root window (and it's not the
+ xscreensaver pseudo-root) then put the image in the window's
+ background. Otherwise, just paint the image onto the window.
+ */
+ bg_p = (window == drawable && root_window_p (screen, window));
+
+ if (bg_p)
+ {
+ XGCValues gcv;
+ GC gc;
+ drawable = XCreatePixmap (dpy, window,
+ win_width, win_height, win_depth);
+ gcv.foreground = BlackPixelOfScreen (screen);
+ gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
+ XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
+ XFreeGC (dpy, gc);
+ }
+ else
+ clear_drawable (screen, drawable);
+
+ /* #### Note that this always uses the default colormap! Morons!
+ Owen says that in Gnome 2.0, I should try using
+ gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
+ But I haven't tried.
+ */
+ gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
+ srcx, srcy, destx, desty,
+ w, h,
+ GDK_PIXBUF_ALPHA_FULL, 127,
+ XLIB_RGB_DITHER_NORMAL,
+ 0, 0);
+ if (bg_p)
+ {
+ XSetWindowBackgroundPixmap (dpy, window, drawable);
+ XClearWindow (dpy, window);
+ }
+
+ if (geom_ret)
+ {
+ geom_ret->x = destx;
+ geom_ret->y = desty;
+ geom_ret->width = w;
+ geom_ret->height = h;
+ }
+ }
+
+ XSync (dpy, False);
+ return True;
+}
+
+#endif /* HAVE_GDK_PIXBUF */
+
+
+
+#ifdef HAVE_JPEGLIB
+
+/* Allocates a colormap that makes a PseudoColor or DirectColor
+ visual behave like a TrueColor visual of the same depth.
+
+ #### Duplicated in utils/grabscreen.c
+ */
+static void
+allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
+ Bool verbose_p)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int nr, ng, nb, cells;
+ int r, g, b;
+ int depth;
+ XColor colors[4097];
+ int i;
+
+ depth = visual_depth (screen, visual);
+
+ switch (depth)
+ {
+ case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
+ case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
+ default: abort(); break;
+ }
+
+ memset(colors, 0, sizeof(colors));
+ for (r = 0; r < (1 << nr); r++)
+ for (g = 0; g < (1 << ng); g++)
+ for (b = 0; b < (1 << nb); b++)
+ {
+ i = (r | (g << nr) | (b << (nr + ng)));
+ colors[i].pixel = i;
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ if (depth == 8)
+ {
+ colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
+ (r << 4) | (r << 1));
+ colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
+ (g << 4) | (g << 1));
+ colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
+ (b << 8) | (b << 6) | (b << 4) |
+ (b << 2) | b);
+ }
+ else
+ {
+ colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
+ colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
+ colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
+ }
+ }
+
+ {
+ int j;
+ int allocated = 0;
+ int interleave = cells / 8; /* skip around, rather than allocating in
+ order, so that we get better coverage if
+ we can't allocated all of them. */
+ for (j = 0; j < interleave; j++)
+ for (i = 0; i < cells; i += interleave)
+ if (XAllocColor (dpy, cmap, &colors[i + j]))
+ allocated++;
+
+ if (verbose_p)
+ fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
+ progname, allocated, cells);
+ }
+}
+
+/* Find the pixel index that is closest to the given color
+ (using linear distance in RGB space -- which is far from the best way.)
+
+ #### Duplicated in utils/grabscreen.c
+ */
+static unsigned long
+find_closest_pixel (XColor *colors, int ncolors,
+ unsigned long r, unsigned long g, unsigned long b)
+{
+ unsigned long distance = ~0;
+ int i, found = 0;
+
+ if (ncolors == 0)
+ abort();
+ for (i = 0; i < ncolors; i++)
+ {
+ unsigned long d;
+ int rd, gd, bd;
+
+ rd = r - colors[i].red;
+ gd = g - colors[i].green;
+ bd = b - colors[i].blue;
+ if (rd < 0) rd = -rd;
+ if (gd < 0) gd = -gd;
+ if (bd < 0) bd = -bd;
+ d = (rd << 1) + (gd << 2) + bd;
+
+ if (d < distance)
+ {
+ distance = d;
+ found = i;
+ if (distance == 0)
+ break;
+ }
+ }
+
+ return found;
+}
+
+
+/* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
+ displayable with the given X colormap. The farther from a perfect
+ color cube the contents of the colormap are, the lossier the
+ transformation will be. No dithering is done.
+
+ #### Duplicated in utils/grabscreen.c
+ */
+static void
+remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ unsigned long map[4097];
+ int x, y, i;
+ int cells;
+ XColor colors[4097];
+
+ if (image->depth == 8)
+ cells = 256;
+ else if (image->depth == 12)
+ cells = 4096;
+ else
+ abort();
+
+ memset(map, -1, sizeof(*map));
+ memset(colors, -1, sizeof(*colors));
+
+ for (i = 0; i < cells; i++)
+ colors[i].pixel = i;
+ XQueryColors (dpy, cmap, colors, cells);
+
+ if (verbose_p)
+ fprintf(stderr, "%s: building color cube for %d bit image\n",
+ progname, image->depth);
+
+ for (i = 0; i < cells; i++)
+ {
+ unsigned short r, g, b;
+
+ if (cells == 256)
+ {
+ /* "RRR GGG BB" In an 8 bit map. Convert that to
+ "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
+ an even spread. */
+ r = (i & 0x07);
+ g = (i & 0x38) >> 3;
+ b = (i & 0xC0) >> 6;
+
+ r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
+ g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
+ b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
+ (b << 6) | (b << 4) | (b << 2) | b);
+ }
+ else
+ {
+ /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
+ "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
+ spread. */
+ r = (i & 0x00F);
+ g = (i & 0x0F0) >> 4;
+ b = (i & 0xF00) >> 8;
+
+ r = (r << 12) | (r << 8) | (r << 4) | r;
+ g = (g << 12) | (g << 8) | (g << 4) | g;
+ b = (b << 12) | (b << 8) | (b << 4) | b;
+ }
+
+ map[i] = find_closest_pixel (colors, cells, r, g, b);
+ }
+
+ if (verbose_p)
+ fprintf(stderr, "%s: remapping colors in %d bit image\n",
+ progname, image->depth);
+
+ for (y = 0; y < image->height; y++)
+ for (x = 0; x < image->width; x++)
+ {
+ unsigned long pixel = XGetPixel(image, x, y);
+ if (pixel >= cells) abort();
+ XPutPixel(image, x, y, map[pixel]);
+ }
+}
+
+
+/* If the file has a PPM (P6) on it, read it and return an XImage.
+ Otherwise, rewind the fd back to the beginning, and return 0.
+ */
+static XImage *
+maybe_read_ppm (Screen *screen, Visual *visual,
+ const char *filename, FILE *in, Bool verbose_p)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int depth = visual_depth (screen, visual);
+ struct stat st;
+ char *buf = 0;
+ int bufsiz = 0;
+ char *s, dummy;
+ int i, j;
+ int x, y, w, h, maxval;
+ XImage *ximage = 0;
+
+ if (fstat (fileno (in), &st))
+ goto FAIL;
+
+ bufsiz = st.st_size;
+ buf = (char *) malloc (bufsiz + 1);
+ if (!buf)
+ {
+ fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
+ progname, bufsiz, filename);
+ goto FAIL;
+ }
+
+ if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
+ goto FAIL;
+
+ if (!strncmp (buf, "\107\111", 2))
+ {
+ fprintf (stderr, "%s: %s: sorry, GIF files not supported"
+ " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
+ progname, filename);
+ goto FAIL;
+ }
+ else if (!strncmp (buf, "\211\120", 2))
+ {
+ fprintf (stderr, "%s: %s: sorry, PNG files not supported"
+ " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
+ progname, filename);
+ goto FAIL;
+ }
+
+ if (strncmp (s, "P6", 2))
+ goto FAIL;
+
+ if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
+ goto FAIL;
+ if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
+ {
+ fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
+ goto FAIL;
+ }
+
+ if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
+ goto FAIL;
+ if (1 != sscanf (s, " %d %c", &maxval, &dummy))
+ {
+ fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
+ goto FAIL;
+ }
+ if (maxval != 255)
+ {
+ fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
+ progname, filename, maxval);
+ goto FAIL;
+ }
+
+ ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
+ w, h, 8, 0);
+ if (ximage)
+ ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
+ if (!ximage || !ximage->data)
+ {
+ fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
+ progname, ximage->width, ximage->height, filename);
+ goto FAIL;
+ }
+
+ s = buf;
+ j = bufsiz;
+ while ((i = fread (s, 1, j, in)) > 0)
+ s += i, j -= i;
+
+ i = 0;
+ for (y = 0; y < ximage->height; y++)
+ for (x = 0; x < ximage->width; x++)
+ {
+ unsigned char r = buf[i++];
+ unsigned char g = buf[i++];
+ unsigned char b = buf[i++];
+ unsigned long pixel;
+
+ if (depth > 16)
+ pixel = (r << 16) | (g << 8) | b;
+ else if (depth == 8)
+ pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
+ else if (depth == 12)
+ pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
+ else if (depth == 16 || depth == 15)
+ pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
+ else
+ abort();
+
+ XPutPixel (ximage, x, y, pixel);
+ }
+
+ free (buf);
+ return ximage;
+
+ FAIL:
+ if (buf) free (buf);
+ if (ximage && ximage->data)
+ {
+ free (ximage->data);
+ ximage->data = 0;
+ }
+ if (ximage) XDestroyImage (ximage);
+ fseek (in, 0, SEEK_SET);
+ return 0;
+}
+
+
+typedef struct {
+ struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
+ const char *filename;
+ Screen *screen;
+ Visual *visual;
+ Drawable drawable;
+ Colormap cmap;
+} getimg_jpg_error_mgr;
+
+
+static void
+jpg_output_message (j_common_ptr cinfo)
+{
+ getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
+ char buf[JMSG_LENGTH_MAX];
+ cinfo->err->format_message (cinfo, buf);
+ fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
+}
+
+
+static void
+jpg_error_exit (j_common_ptr cinfo)
+{
+ getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
+ cinfo->err->output_message (cinfo);
+ draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
+ 0, 0, 0, 0);
+ XSync (DisplayOfScreen (err->screen), False);
+ exit (1);
+}
+
+
+/* Reads a JPEG file, returns an RGB XImage of it.
+ */
+static XImage *
+read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
+ Colormap cmap, const char *filename, Bool verbose_p)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int depth = visual_depth (screen, visual);
+
+ FILE *in = 0;
+ XImage *ximage = 0;
+ struct jpeg_decompress_struct cinfo;
+ getimg_jpg_error_mgr jerr;
+ JSAMPARRAY scanbuf = 0;
+ int y;
+
+ jerr.filename = filename;
+ jerr.screen = screen;
+ jerr.visual = visual;
+ jerr.drawable = drawable;
+ jerr.cmap = cmap;
+
+ if (! (depth >= 15 || depth == 12 || depth == 8))
+ {
+ fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
+ goto FAIL;
+ }
+
+ in = fopen (filename, "rb");
+ if (!in)
+ {
+ fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
+ goto FAIL;
+ }
+
+ /* Check to see if it's a PPM, and if so, read that instead of using
+ the JPEG library. Yeah, this is all modular and stuff.
+ */
+ if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
+ {
+ fclose (in);
+ return ximage;
+ }
+
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.output_message = jpg_output_message;
+ jerr.pub.error_exit = jpg_error_exit;
+
+ jpeg_create_decompress (&cinfo);
+ jpeg_stdio_src (&cinfo, in);
+ jpeg_read_header (&cinfo, TRUE);
+
+ /* set some decode parameters */
+ cinfo.out_color_space = JCS_RGB;
+ cinfo.quantize_colors = FALSE;
+
+ jpeg_start_decompress (&cinfo);
+
+ ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
+ cinfo.output_width, cinfo.output_height,
+ 8, 0);
+ if (ximage)
+ ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
+
+ if (ximage && ximage->data)
+ scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
+ cinfo.rec_outbuf_height *
+ cinfo.output_width *
+ cinfo.output_components,
+ 1);
+ if (!ximage || !ximage->data || !scanbuf)
+ {
+ fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
+ progname, ximage->width, ximage->height, filename);
+ goto FAIL;
+ }
+
+ y = 0;
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
+ int i;
+ for (i = 0; i < n; i++)
+ {
+ int x;
+ for (x = 0; x < ximage->width; x++)
+ {
+ int j = x * cinfo.output_components;
+ unsigned char r = scanbuf[i][j];
+ unsigned char g = scanbuf[i][j+1];
+ unsigned char b = scanbuf[i][j+2];
+ unsigned long pixel;
+
+ if (depth > 16)
+ pixel = (r << 16) | (g << 8) | b;
+ else if (depth == 8)
+ pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
+ else if (depth == 12)
+ pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
+ else if (depth == 15)
+ /* Gah! I don't understand why these are in the other
+ order. */
+ pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
+ else if (depth == 16)
+ pixel = (((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3)));
+ else
+ abort();
+
+ XPutPixel (ximage, x, y, pixel);
+ }
+ y++;
+ }
+ }
+
+ if (cinfo.output_scanline < cinfo.output_height)
+ /* don't goto FAIL -- we might have viewable partial data. */
+ jpeg_abort_decompress (&cinfo);
+ else
+ jpeg_finish_decompress (&cinfo);
+
+ jpeg_destroy_decompress (&cinfo);
+ fclose (in);
+ in = 0;
+
+ return ximage;
+
+ FAIL:
+ if (in) fclose (in);
+ if (ximage && ximage->data)
+ {
+ free (ximage->data);
+ ximage->data = 0;
+ }
+ if (ximage) XDestroyImage (ximage);
+ if (scanbuf) free (scanbuf);
+ return 0;
+}
+
+
+/* Reads the given image file and renders it on the Drawable, using JPEG lib.
+ Returns False if it fails.
+ */
+static Bool
+read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
+ const char *filename, Bool verbose_p,
+ XRectangle *geom_ret)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XImage *ximage;
+ Visual *visual;
+ int class, depth;
+ Colormap cmap;
+ unsigned int win_width, win_height, win_depth;
+ int srcx, srcy, destx, desty, w2, h2;
+
+ /* Find the size of the Drawable, and the Visual/Colormap of the Window. */
+ {
+ Window root;
+ int x, y;
+ unsigned int bw;
+ XWindowAttributes xgwa;
+ XGetWindowAttributes (dpy, window, &xgwa);
+ visual = xgwa.visual;
+ cmap = xgwa.colormap;
+ XGetGeometry (dpy, drawable,
+ &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
+ }
+
+ /* Make sure we're not on some weirdo visual...
+ */
+ class = visual_class (screen, visual);
+ depth = visual_depth (screen, visual);
+ if ((class == PseudoColor || class == DirectColor) &&
+ (depth != 8 && depth != 12))
+ {
+ fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
+ progname, depth);
+ return False;
+ }
+
+ /* Read the file...
+ */
+ ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
+ filename, verbose_p);
+ if (!ximage) return False;
+
+ /* Scale it, if necessary...
+ */
+ compute_image_scaling (ximage->width, ximage->height,
+ win_width, win_height, verbose_p,
+ &srcx, &srcy, &destx, &desty, &w2, &h2);
+ if (ximage->width != w2 || ximage->height != h2)
+ if (! scale_ximage (screen, visual, ximage, w2, h2))
+ return False;
+
+ /* Allocate a colormap, if we need to...
+ */
+ if (class == PseudoColor || class == DirectColor)
+ {
+ allocate_cubic_colormap (screen, visual, cmap, verbose_p);
+ remap_image (screen, cmap, ximage, verbose_p);
+ }
+
+ /* Finally, put the resized image on the window.
+ */
+ {
+ GC gc;
+ XGCValues gcv;
+
+ /* If we're rendering onto the root window (and it's not the xscreensaver
+ pseudo-root) then put the image in the window's background. Otherwise,
+ just paint the image onto the window.
+ */
+ if (window == drawable && root_window_p (screen, window))
+ {
+ Pixmap bg = XCreatePixmap (dpy, window,
+ win_width, win_height, win_depth);
+ gcv.foreground = BlackPixelOfScreen (screen);
+ gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
+ XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height);
+ XPutImage (dpy, bg, gc, ximage,
+ srcx, srcy, destx, desty, ximage->width, ximage->height);
+ XSetWindowBackgroundPixmap (dpy, window, bg);
+ XClearWindow (dpy, window);
+ }
+ else
+ {
+ gc = XCreateGC (dpy, drawable, 0, &gcv);
+ clear_drawable (screen, drawable);
+ XPutImage (dpy, drawable, gc, ximage,
+ srcx, srcy, destx, desty, ximage->width, ximage->height);
+ }
+
+ XFreeGC (dpy, gc);
+ }
+
+ if (geom_ret)
+ {
+ geom_ret->x = destx;
+ geom_ret->y = desty;
+ geom_ret->width = ximage->width;
+ geom_ret->height = ximage->height;
+ }
+
+ free (ximage->data);
+ ximage->data = 0;
+ XDestroyImage (ximage);
+ XSync (dpy, False);
+ return True;
+}
+
+#endif /* HAVE_JPEGLIB */
+
+
+/* Reads the given image file and renders it on the Drawable.
+ Returns False if it fails.
+ */
+static Bool
+display_file (Screen *screen, Window window, Drawable drawable,
+ const char *filename, Bool verbose_p,
+ XRectangle *geom_ret)
+{
+ if (verbose_p)
+ fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
+
+# if defined(HAVE_GDK_PIXBUF)
+ if (read_file_gdk (screen, window, drawable, filename, verbose_p, geom_ret))
+ return True;
+# elif defined(HAVE_JPEGLIB)
+ if (read_file_jpeglib (screen, window, drawable, filename, verbose_p,
+ geom_ret))
+ return True;
+# else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
+ /* shouldn't get here if we have no image-loading methods available. */
+ abort();
+# endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
+
+ return False;
+}
+
+
+/* Invokes a sub-process and returns its output (presumably, a file to
+ load.) Free the string when done. 'grab_type' controls which program
+ to run. Returned pathname may be relative to 'directory', or absolute.
+ */
+static char *
+get_filename_1 (Screen *screen, const char *directory, grab_type type,
+ Bool verbose_p)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ pid_t forked;
+ int fds [2];
+ int in, out;
+ char buf[10240];
+ char *av[20];
+ int ac = 0;
+
+ switch (type)
+ {
+ case GRAB_FILE:
+ av[ac++] = GETIMAGE_FILE_PROGRAM;
+ if (verbose_p)
+ av[ac++] = "--verbose";
+ av[ac++] = "--name";
+ av[ac++] = (char *) directory;
+ break;
+
+ case GRAB_VIDEO:
+ av[ac++] = GETIMAGE_VIDEO_PROGRAM;
+ if (verbose_p)
+ av[ac++] = "--verbose";
+ av[ac++] = "--name";
+ break;
+
+# ifdef USE_EXTERNAL_SCREEN_GRABBER
+ case GRAB_DESK:
+ av[ac++] = GETIMAGE_SCREEN_PROGRAM;
+ if (verbose_p)
+ av[ac++] = "--verbose";
+ av[ac++] = "--name";
+ break;
+# endif
+
+ default:
+ abort();
+ }
+ av[ac] = 0;
+
+ if (verbose_p)
+ {
+ int i;
+ fprintf (stderr, "%s: executing:", progname);
+ for (i = 0; i < ac; i++)
+ fprintf (stderr, " %s", av[i]);
+ fprintf (stderr, "\n");
+ }
+
+ if (pipe (fds))
+ {
+ sprintf (buf, "%s: error creating pipe", progname);
+ perror (buf);
+ return 0;
+ }
+
+ in = fds [0];
+ out = fds [1];
+
+ switch ((int) (forked = fork ()))
+ {
+ case -1:
+ {
+ sprintf (buf, "%s: couldn't fork", progname);
+ perror (buf);
+ return 0;
+ }
+ case 0:
+ {
+ int stdout_fd = 1;
+
+ close (in); /* don't need this one */
+ close (ConnectionNumber (dpy)); /* close display fd */
+
+ if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
+ {
+ sprintf (buf, "%s: could not dup() a new stdout", progname);
+ exit (-1); /* exits fork */
+ }
+
+ execvp (av[0], av); /* shouldn't return. */
+ exit (-1); /* exits fork */
+ break;
+ }
+ default:
+ {
+ struct stat st;
+ int wait_status = 0;
+ FILE *f = fdopen (in, "r");
+ int L;
+ char *ret = 0;
+
+ close (out); /* don't need this one */
+ *buf = 0;
+ if (! fgets (buf, sizeof(buf)-1, f))
+ *buf = 0;
+ fclose (f);
+
+ /* Wait for the child to die. */
+ waitpid (-1, &wait_status, 0);
+
+ L = strlen (buf);
+ while (L && buf[L-1] == '\n')
+ buf[--L] = 0;
+
+ if (!*buf)
+ return 0;
+
+ ret = strdup (buf);
+
+ if (*ret != '/')
+ {
+ /* Program returned path relative to directory. Prepend dir
+ to buf so that we can properly stat it. */
+ strcpy (buf, directory);
+ if (directory[strlen(directory)-1] != '/')
+ strcat (buf, "/");
+ strcat (buf, ret);
+ }
+
+ if (stat(buf, &st))
+ {
+ fprintf (stderr, "%s: file does not exist: \"%s\"\n",
+ progname, buf);
+ free (ret);
+ return 0;
+ }
+ else
+ return ret;
+ }
+ }
+
+ abort();
+}
+
+
+/* Returns a pathname to an image file. Free the string when you're done.
+ */
+static char *
+get_filename (Screen *screen, const char *directory, Bool verbose_p)
+{
+ return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
+}
+
+
+/* Grabs a video frame to a file, and returns a pathname to that file.
+ Delete that file when you are done with it (and free the string.)
+ */
+static char *
+get_video_filename (Screen *screen, Bool verbose_p)
+{
+ return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
+}
+
+/* Grabs a desktop image to a file, and returns a pathname to that file.
+ Delete that file when you are done with it (and free the string.)
+ */
+# ifdef USE_EXTERNAL_SCREEN_GRABBER
+static char *
+get_desktop_filename (Screen *screen, Bool verbose_p)
+{
+ return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
+}
+#endif /* USE_EXTERNAL_SCREEN_GRABBER */
+
+
+/* Grabs a video frame, and renders it on the Drawable.
+ Returns False if it fails;
+ */
+static Bool
+display_video (Screen *screen, Window window, Drawable drawable,
+ Bool verbose_p, XRectangle *geom_ret)
+{
+ char *filename = get_video_filename (screen, verbose_p);
+ Bool status;
+
+ if (!filename)
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: video grab failed.\n", progname);
+ return False;
+ }
+
+ status = display_file (screen, window, drawable, filename, verbose_p,
+ geom_ret);
+
+ if (unlink (filename))
+ {
+ char buf[512];
+ sprintf (buf, "%s: rm %.100s", progname, filename);
+ perror (buf);
+ }
+ else if (verbose_p)
+ fprintf (stderr, "%s: rm %s\n", progname, filename);
+
+ if (filename) free (filename);
+ return status;
+}
+
+
+/* Grabs a desktop screen shot onto the window and the drawable.
+ If the window and drawable are not the same size, the image in
+ the drawable is scaled to fit.
+ Returns False if it fails.
+ */
+static Bool
+display_desktop (Screen *screen, Window window, Drawable drawable,
+ Bool verbose_p, XRectangle *geom_ret)
+{
+# ifdef USE_EXTERNAL_SCREEN_GRABBER
+
+ Display *dpy = DisplayOfScreen (screen);
+ Bool top_p = top_level_window_p (screen, window);
+ char *filename;
+ Bool status;
+
+ if (top_p)
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
+ (unsigned long) window);
+ XUnmapWindow (dpy, window);
+ XSync (dpy, False);
+ }
+
+ filename = get_desktop_filename (screen, verbose_p);
+
+ if (top_p)
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
+ (unsigned long) window);
+ XMapRaised (dpy, window);
+ XSync (dpy, False);
+ }
+
+ if (!filename)
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: desktop grab failed.\n", progname);
+ return False;
+ }
+
+ status = display_file (screen, window, drawable, filename, verbose_p,
+ geom_ret);
+
+ if (unlink (filename))
+ {
+ char buf[512];
+ sprintf (buf, "%s: rm %.100s", progname, filename);
+ perror (buf);
+ }
+ else if (verbose_p)
+ fprintf (stderr, "%s: rm %s\n", progname, filename);
+
+ if (filename) free (filename);
+ return status;
+
+# else /* !USE_EXTERNAL_SCREEN_GRABBER */
+
+ Display *dpy = DisplayOfScreen (screen);
+ XGCValues gcv;
+ XWindowAttributes xgwa;
+ Window root;
+ int px, py;
+ unsigned int pw, ph, pbw, pd;
+ int srcx, srcy, destx, desty, w2, h2;
+
+ if (verbose_p)
+ {
+ fprintf (stderr, "%s: grabbing desktop image\n", progname);
+ grabscreen_verbose();
+ }
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
+
+ grab_screen_image_internal (screen, window);
+
+ compute_image_scaling (xgwa.width, xgwa.height,
+ pw, ph, verbose_p,
+ &srcx, &srcy, &destx, &desty, &w2, &h2);
+
+ if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */
+ {
+ GC gc = XCreateGC (dpy, drawable, 0, &gcv);
+ XCopyArea (dpy, window, drawable, gc,
+ 0, 0, xgwa.width, xgwa.height, 0, 0);
+ XFreeGC (dpy, gc);
+ }
+ else /* size mismatch -- must scale client-side images to fit drawable */
+ {
+ GC gc;
+ XImage *ximage = 0;
+ XErrorHandler old_handler;
+
+ XSync (dpy, False);
+ old_handler = XSetErrorHandler (ignore_badmatch_ehandler);
+ error_handler_hit_p = False;
+
+ /* This can return BadMatch if the window is not fully on screen.
+ Trap that error and return color bars in that case.
+ (Note that this only happens with XGetImage, not with XCopyArea:
+ yet another totally gratuitous inconsistency in X, thanks.)
+ */
+ ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
+ ~0L, ZPixmap);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XSync (dpy, False);
+
+ if (error_handler_hit_p)
+ {
+ ximage = 0;
+ if (verbose_p)
+ fprintf (stderr, "%s: BadMatch reading window 0x%x contents!\n",
+ progname, (unsigned int) window);
+ }
+
+ if (!ximage ||
+ !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
+ return False;
+
+ gc = XCreateGC (dpy, drawable, 0, &gcv);
+ clear_drawable (screen, drawable);
+ XPutImage (dpy, drawable, gc, ximage,
+ srcx, srcy, destx, desty, ximage->width, ximage->height);
+ XDestroyImage (ximage);
+ XFreeGC (dpy, gc);