-/* xscreensaver, Copyright (c) 2001 by Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2001, 2002 by Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#include "utils.h"
#include <X11/Intrinsic.h>
+#include <ctype.h>
#include <errno.h>
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h> /* for waitpid() and associated macros */
+#endif
+
#ifdef HAVE_XMU
# ifndef VMS
# include <X11/Xmu/Error.h>
#include "grabscreen.h"
#include "resources.h"
#include "colorbars.h"
+#include "visual.h"
#include "prefs.h"
#include "vroot.h"
+#ifdef HAVE_GDK_PIXBUF
+
+# ifdef HAVE_GTK2
+# include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
+# else /* !HAVE_GTK2 */
+# include <gdk-pixbuf/gdk-pixbuf-xlib.h>
+# endif /* !HAVE_GTK2 */
+
+# define HAVE_BUILTIN_IMAGE_LOADER
+#endif /* HAVE_GDK_PIXBUF */
+
static char *defaults[] = {
#include "../driver/XScreenSaver_ad.h"
fprintf (stderr, "\n");
}
- exit (1);
+ exit (-1);
}
static int
x_ehandler (Display *dpy, XErrorEvent *error)
{
fprintf (stderr, "\nX error in %s:\n", progname);
- if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
- exit (-1);
- else
- fprintf (stderr, " (nonfatal.)\n");
+ XmuPrintDefaultErrorMessage (dpy, error, stderr);
+ exit (-1);
return 0;
}
+#ifdef HAVE_BUILTIN_IMAGE_LOADER
+static void load_image_internal (Screen *screen, Window window,
+ int win_width, int win_height,
+ Bool verbose_p,
+ int ac, char **av);
+#endif /* HAVE_BUILTIN_IMAGE_LOADER */
+
+
static void
get_image (Screen *screen, Window window, Bool verbose_p)
{
enum { do_desk, do_video, do_image, do_bars } which = do_bars;
int count = 0;
+ XWindowAttributes xgwa;
+ XGetWindowAttributes (dpy, window, &xgwa);
+ screen = xgwa.screen;
+
if (verbose_p)
{
fprintf (stderr, "%s: grabDesktopImages: %s\n",
if ((desk_p || video_p || image_p) &&
!top_level_window_p (screen, window))
{
- desk_p = False;
- video_p = False;
- image_p = False;
- if (verbose_p)
+ Bool changed_p = False;
+ if (desk_p) desk_p = False, changed_p = True;
+ if (video_p) video_p = False, changed_p = True;
+# ifndef HAVE_BUILTIN_IMAGE_LOADER
+ if (image_p) image_p = False, changed_p = True;
+ if (changed_p && verbose_p)
fprintf (stderr, "%s: not a top-level window: using colorbars.\n",
progname);
+# endif /* !HAVE_BUILTIN_IMAGE_LOADER */
}
else if (window != VirtualRootWindowOfScreen (screen))
{
Bool changed_p = False;
- if (!desk_p) desk_p = True, changed_p = True;
if (video_p) video_p = False, changed_p = True;
+# ifndef HAVE_BUILTIN_IMAGE_LOADER
+ if (!desk_p) desk_p = True, changed_p = True;
if (image_p) image_p = False, changed_p = True;
if (changed_p && verbose_p)
fprintf (stderr,
"%s: not running on root window: grabbing desktop.\n",
progname);
+# endif /* !HAVE_BUILTIN_IMAGE_LOADER */
}
count = 0;
if (count == 0)
which = do_bars;
else
- while (1) /* loop until we get one that's permitted */
- {
- which = (random() % 3);
- if (which == do_desk && desk_p) break;
- if (which == do_video && video_p) break;
- if (which == do_image && image_p) break;
- }
+ {
+ int i = 0;
+ while (1) /* loop until we get one that's permitted */
+ {
+ which = (random() % 3);
+ if (which == do_desk && desk_p) break;
+ if (which == do_video && video_p) break;
+ if (which == do_image && image_p) break;
+ if (++i > 200) abort();
+ }
+ }
if (which == do_desk)
{
}
else if (which == do_bars)
{
- XWindowAttributes xgwa;
- XGetWindowAttributes (dpy, window, &xgwa);
if (verbose_p)
fprintf (stderr, "%s: drawing colorbars\n", progname);
draw_colorbars (dpy, window, 0, 0, xgwa.width, xgwa.height);
else
{
char *av[10];
+ int ac = 0;
memset (av, 0, sizeof(av));
switch (which)
{
case do_video:
if (verbose_p)
fprintf (stderr, "%s: grabbing video\n", progname);
- av[0] = GETIMAGE_VIDEO_PROGRAM;
+ av[ac++] = GETIMAGE_VIDEO_PROGRAM;
break;
case do_image:
if (verbose_p)
fprintf (stderr, "%s: loading random image file\n", progname);
- av[0] = GETIMAGE_FILE_PROGRAM;
- av[1] = dir;
+ av[ac++] = GETIMAGE_FILE_PROGRAM;
+
+# ifdef HAVE_BUILTIN_IMAGE_LOADER
+ av[ac++] = "--name";
+# endif /* !HAVE_BUILTIN_IMAGE_LOADER */
+ av[ac++] = dir;
break;
default:
abort();
if (verbose_p)
{
int i;
- for (i = (sizeof(av)/sizeof(*av))-1; i > 1; i--)
+ for (i = ac; i > 1; i--)
av[i] = av[i-1];
av[1] = strdup ("--verbose");
+ ac++;
}
if (verbose_p)
{
const char *odpy = DisplayString (dpy);
char *ndpy = (char *) malloc(strlen(odpy) + 20);
+ char *s;
+ int screen_no = screen_number (screen); /* might not be default now */
+
strcpy (ndpy, "DISPLAY=");
- strcat (ndpy, odpy);
+ s = ndpy + strlen(ndpy);
+ strcpy (s, odpy);
+
+ while (*s && *s != ':') s++; /* skip to colon */
+ while (*s == ':') s++; /* skip over colons */
+ while (isdigit(*s)) s++; /* skip over dpy number */
+ while (*s == '.') s++; /* skip over dot */
+ if (s[-1] != '.') *s++ = '.'; /* put on a dot */
+ sprintf(s, "%d", screen_no); /* put on screen number */
+
if (putenv (ndpy))
abort ();
+
+ /* don't free (ndpy) -- some implementations of putenv (BSD
+ 4.4, glibc 2.0) copy the argument, but some (libc4,5, glibc
+ 2.1.2) do not. So we must leak it (and/or the previous
+ setting). Yay.
+ */
}
# endif /* HAVE_PUTENV */
+# ifdef HAVE_BUILTIN_IMAGE_LOADER
+ if (which == do_image)
+ {
+ load_image_internal (screen, window, xgwa.width, xgwa.height,
+ verbose_p, ac, av);
+ return;
+ }
+# endif /* HAVE_BUILTIN_IMAGE_LOADER */
+
+
close (ConnectionNumber (dpy)); /* close display fd */
execvp (av[0], av); /* shouldn't return */
}
+#ifdef HAVE_BUILTIN_IMAGE_LOADER
+
+/* Reads a filename from "GETIMAGE_FILE_PROGRAM --name /DIR"
+ */
+static char *
+get_filename (Display *dpy, int ac, char **av)
+{
+ pid_t forked;
+ int fds [2];
+ int in, out;
+ char buf[1024];
+
+ 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:
+ {
+ 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;
+
+ return strdup (buf);
+ }
+ }
+
+ abort();
+}
+
+
+
+static void
+load_image_internal (Screen *screen, Window window,
+ int win_width, int win_height,
+ Bool verbose_p,
+ int ac, char **av)
+{
+ GdkPixbuf *pb;
+ Display *dpy = DisplayOfScreen (screen);
+ char *filename = get_filename (dpy, ac, av);
+#ifdef HAVE_GTK2
+ GError *gerr = 0;
+#endif /* HAVE_GTK2 */
+
+ if (!filename)
+ {
+ fprintf (stderr, "%s: no file name returned by %s\n",
+ progname, av[0]);
+ goto FAIL;
+ }
+ else if (verbose_p)
+ fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
+
+ gdk_pixbuf_xlib_init (dpy, screen_number (screen));
+#ifdef HAVE_GTK2
+ g_type_init();
+#else /* !HAVE_GTK2 */
+ xlib_rgb_init (dpy, screen);
+#endif /* !HAVE_GTK2 */
+
+ pb = gdk_pixbuf_new_from_file (filename
+#ifdef HAVE_GTK2
+ , &gerr
+#endif /* HAVE_GTK2 */
+ );
+
+ if (pb)
+ {
+ int w = gdk_pixbuf_get_width (pb);
+ int h = gdk_pixbuf_get_height (pb);
+ int srcx, srcy, destx, desty;
+
+ Bool exact_fit_p = ((w == win_width && h <= win_height) ||
+ (h == win_height && w <= win_width));
+
+ if (!exact_fit_p) /* scale the image up or down */
+ {
+ float rw = (float) win_width / w;
+ float rh = (float) win_height / h;
+ float r = (rw < rh ? rw : rh);
+ int tw = w * r;
+ int th = h * r;
+ int pct = (r * 100);
+
+ if (pct < 95 || pct > 105) /* don't scale if it's close */
+ {
+ GdkPixbuf *pb2;
+ if (verbose_p)
+ fprintf (stderr,
+ "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
+ progname, pct, w, h, tw, th);
+
+ pb2 = gdk_pixbuf_scale_simple (pb, tw, th, GDK_INTERP_BILINEAR);
+ if (pb2)
+ {
+ gdk_pixbuf_unref (pb);
+ pb = pb2;
+ w = tw;
+ h = th;
+ }
+ else
+ fprintf (stderr, "%s: out of memory when scaling?\n",
+ progname);
+ }
+ }
+
+ /* Center the image on the window. */
+ srcx = 0;
+ srcy = 0;
+ destx = (win_width - w) / 2;
+ desty = (win_height - h) / 2;
+ if (destx < 0) srcx = -destx, destx = 0;
+ if (desty < 0) srcy = -desty, desty = 0;
+
+ if (win_width < w) w = win_width;
+ if (win_height < h) h = win_height;
+
+ /* The window might have no-op background of None, so to clear it,
+ draw a black rectangle first, then do XClearWindow (in case the
+ actual background color is non-black...) */
+ {
+ XGCValues gcv;
+ GC gc;
+ /* #### really we should allocate "black" instead, but I'm lazy... */
+ gcv.foreground = BlackPixelOfScreen (screen);
+ gc = XCreateGC (dpy, window, GCForeground, &gcv);
+ XFillRectangle (dpy, window, gc, 0, 0, win_width, win_height);
+ XFreeGC (dpy, gc);
+ XClearWindow (dpy, window);
+ XFlush (dpy);
+ }
+
+ /* #### 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 don't have Gnome 2.0 yet.
+ */
+ gdk_pixbuf_xlib_render_to_drawable_alpha (pb, window,
+ srcx, srcy, destx, desty, w, h,
+ GDK_PIXBUF_ALPHA_FULL, 127,
+ XLIB_RGB_DITHER_NORMAL, 0, 0);
+ XSync (dpy, False);
+
+ if (verbose_p)
+ fprintf (stderr, "%s: displayed %dx%d image at %d,%d.\n",
+ progname, w, h, destx, desty);
+ }
+ else if (filename)
+ {
+ fprintf (stderr, "%s: unable to load %s\n", progname, filename);
+#ifdef HAVE_GTK2
+ if (gerr && gerr->message && *gerr->message)
+ fprintf (stderr, "%s: reason %s\n", progname, gerr->message);
+#endif /* HAVE_GTK2 */
+
+ goto FAIL;
+ }
+ else
+ {
+ fprintf (stderr, "%s: unable to initialize built-in images\n", progname);
+ goto FAIL;
+ }
+
+ return;
+
+ FAIL:
+ if (verbose_p)
+ fprintf (stderr, "%s: drawing colorbars\n", progname);
+ draw_colorbars (dpy, window, 0, 0, win_width, win_height);
+ XSync (dpy, False);
+}
+
+#endif /* HAVE_BUILTIN_IMAGE_LOADER */
+
+
+
#if 0
static Bool
mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
!strcmp (argv[i], "--root"))
window = RootWindowOfScreen (screen);
- else if ((1 == sscanf (argv[i], " 0x%x %c", &w, &dummy) ||
- 1 == sscanf (argv[i], " %d %c", &w, &dummy)) &&
+ else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
+ 1 == sscanf (argv[i], " %ld %c", &w, &dummy)) &&
w != 0)
window = (Window) w;
else