+ /* 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 = (unsigned 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.num_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 == 16 || depth == 15)
+ /* Gah! I don't understand why these are in the other
+ order. */
+ pixel = (((r >> 3) << 10) | ((g >> 3) << 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;
+}
+
+
+/* Scales an XImage, modifying it in place.
+ If out of memory, returns False, and the XImage will have been
+ destroyed and freed.
+ */
+static Bool
+scale_ximage (Screen *screen, Visual *visual,
+ XImage *ximage, int new_width, int new_height)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int depth = visual_depth (screen, visual);
+ int x, y;
+ double xscale, yscale;
+
+ XImage *ximage2 = XCreateImage (dpy, visual, depth,
+ ZPixmap, 0, 0,
+ new_width, new_height, 8, 0);
+ ximage2->data = (unsigned char *)
+ calloc (ximage2->height, ximage2->bytes_per_line);
+
+ if (!ximage2->data)
+ {
+ fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
+ progname,
+ ximage->width, ximage->height,
+ ximage2->width, ximage2->height);
+ if (ximage->data) free (ximage->data);
+ if (ximage2->data) free (ximage2->data);
+ ximage->data = 0;
+ ximage2->data = 0;
+ XDestroyImage (ximage);
+ XDestroyImage (ximage2);
+ return False;
+ }
+
+ /* Brute force scaling... */
+ xscale = (double) ximage->width / ximage2->width;
+ yscale = (double) ximage->height / ximage2->height;
+ for (y = 0; y < ximage2->height; y++)
+ for (x = 0; x < ximage2->width; x++)
+ XPutPixel (ximage2, x, y,
+ XGetPixel (ximage, x * xscale, y * yscale));
+
+ free (ximage->data);
+ ximage->data = 0;
+
+ (*ximage) = (*ximage2);
+
+ ximage2->data = 0;
+ XDestroyImage (ximage2);
+
+ return True;
+}
+
+
+/* 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)
+{
+ 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;
+
+ {
+ Window root;
+ int x, y;
+ unsigned int bw;
+ XWindowAttributes xgwa;
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ screen = xgwa.screen;
+ 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.
+ */
+ clear_drawable (screen, drawable);
+ {
+ GC gc;
+ XGCValues gcv;
+ gc = XCreateGC (dpy, drawable, 0, &gcv);
+ XPutImage (dpy, drawable, gc, ximage,
+ srcx, srcy, destx, desty, ximage->width, ximage->height);
+ XFreeGC (dpy, gc);
+ }
+
+ 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)
+{
+ 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))
+ return True;
+# elif defined(HAVE_JPEGLIB)
+ if (read_file_jpeglib (screen, window, drawable, filename, verbose_p))
+ 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. video_p controls which program
+ to run.
+ */
+static char *
+get_filename_1 (Screen *screen, const char *directory, Bool video_p,
+ Bool verbose_p)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ pid_t forked;
+ int fds [2];
+ int in, out;
+ char buf[1024];
+ char *av[20];
+ int ac = 0;
+
+ if (!video_p)
+ {
+ av[ac++] = GETIMAGE_FILE_PROGRAM;
+ if (verbose_p)
+ av[ac++] = "--verbose";
+ av[ac++] = "--name";
+ av[ac++] = (char *) directory;
+ }
+ else
+ {
+ av[ac++] = GETIMAGE_VIDEO_PROGRAM;
+ if (verbose_p)
+ av[ac++] = "--verbose";
+ av[ac++] = "--name";
+ }
+ 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;
+
+ close (out); /* don't need this one */
+ *buf = 0;
+ fgets (buf, sizeof(buf)-1, f);
+ 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;
+ if (stat(buf, &st))
+ {
+ fprintf (stderr, "%s: file does not exist: \"%s\"\n",
+ progname, buf);
+ return 0;
+ }
+ else
+ return strdup (buf);
+ }
+ }
+
+ 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, False, 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, True, verbose_p);
+}
+
+
+/* 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)
+{
+ 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);
+
+ 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 an image (from a file, video, or the desktop) and renders it on
+ the Drawable. If `file' is specified, always use that file. Otherwise,
+ select randomly, based on the other arguments.
+ */
+static void
+get_image (Screen *screen,
+ Window window, Drawable drawable,
+ Bool verbose_p,
+ Bool desk_p,
+ Bool video_p,
+ Bool image_p,
+ const char *dir,
+ const char *file)
+{
+ Display *dpy = DisplayOfScreen (screen);