X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fxscreensaver-getimage.c;h=2fe88afc0fde6bf2a3604f520ae7567677e5457c;hb=4cecfc89e5e889c7232693897c06168fb378bd5c;hp=c8e8228b6820af6711b371f9ab2a80637c378deb;hpb=585e1a6717d1dd9b90fbb53acaaae82106354d33;p=xscreensaver diff --git a/driver/xscreensaver-getimage.c b/driver/xscreensaver-getimage.c index c8e8228b..2fe88afc 100644 --- a/driver/xscreensaver-getimage.c +++ b/driver/xscreensaver-getimage.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2001 by Jamie Zawinski +/* xscreensaver, Copyright (c) 2001, 2002, 2003 by Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -16,15 +16,42 @@ #include "utils.h" #include +#include #include +#ifdef HAVE_SYS_WAIT_H +# include /* for waitpid() and associated macros */ +#endif + +#ifdef HAVE_XMU +# ifndef VMS +# include +# else /* VMS */ +# include +# endif +#else +# include "xmu.h" +#endif + #include "yarandom.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 +# else /* !HAVE_GTK2 */ +# include +# endif /* !HAVE_GTK2 */ + +# define HAVE_BUILTIN_IMAGE_LOADER +#endif /* HAVE_GDK_PIXBUF */ + static char *defaults[] = { #include "../driver/XScreenSaver_ad.h" @@ -91,24 +118,43 @@ exec_error (char **av) fprintf (stderr, "\n"); } - exit (1); + exit (-1); } +static int +x_ehandler (Display *dpy, XErrorEvent *error) +{ + fprintf (stderr, "\nX error in %s:\n", progname); + 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) +get_image (Screen *screen, Window window, + Bool verbose_p, + Bool desk_p, + Bool video_p, + Bool image_p, + char *dir) { Display *dpy = DisplayOfScreen (screen); - Bool desk_p = get_boolean_resource ("grabDesktopImages", "Boolean"); - Bool video_p = get_boolean_resource ("grabVideoFrames", "Boolean"); - Bool image_p = get_boolean_resource ("chooseRandomImages", "Boolean"); - char *dir = get_string_resource ("imageDirectory", "ImageDirectory"); - enum { do_desk, do_video, do_image, do_bars } which = do_bars; int count = 0; - if (desk_p) count++; - if (video_p) count++; - if (image_p) count++; + + XWindowAttributes xgwa; + XGetWindowAttributes (dpy, window, &xgwa); + screen = xgwa.screen; if (verbose_p) { @@ -146,28 +192,61 @@ get_image (Screen *screen, Window window, Bool verbose_p) know it's not a security problem to expose desktop bits.) */ - if (window != VirtualRootWindowOfScreen (screen)) + if ((desk_p || video_p || image_p) && + !top_level_window_p (screen, window)) { Bool changed_p = False; - if (!desk_p) desk_p = True, changed_p = True; + if (desk_p) desk_p = False, changed_p = True; if (video_p) video_p = False, changed_p = True; +# ifndef HAVE_BUILTIN_IMAGE_LOADER + /* We can display images on non-top-level windows with the builtin + loader, but not if we're using the external (chbg-based) loader. */ if (image_p) image_p = False, changed_p = True; +# endif /* !HAVE_BUILTIN_IMAGE_LOADER */ + + if (changed_p && verbose_p) + fprintf (stderr, "%s: not a top-level window: using colorbars.\n", + progname); + } + else if (window != VirtualRootWindowOfScreen (screen)) + { + /* We can display images on non-root windows with the builtin loader, + but not if we're using the external (chbg-based) loader. + We can never display video on non-root windows (since that always + uses the external image loader.) + */ + Bool changed_p = False; + 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; +# endif /* !HAVE_BUILTIN_IMAGE_LOADER */ + if (changed_p && verbose_p) fprintf (stderr, "%s: not running on root window: grabbing desktop.\n", progname); } + count = 0; + if (desk_p) count++; + if (video_p) count++; + if (image_p) count++; + 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) { @@ -181,8 +260,6 @@ get_image (Screen *screen, Window window, Bool verbose_p) } 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); @@ -191,19 +268,24 @@ get_image (Screen *screen, Window window, Bool verbose_p) 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(); @@ -213,9 +295,10 @@ get_image (Screen *screen, Window window, Bool verbose_p) 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) @@ -237,13 +320,41 @@ get_image (Screen *screen, Window window, Bool 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 */ @@ -252,7 +363,226 @@ get_image (Screen *screen, Window window, Bool verbose_p) } -#if 0 +#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 */ + + + +#ifdef DEBUG static Bool mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks, XrmRepresentation *type, XrmValue *value, XPointer closure) @@ -273,8 +603,31 @@ mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks, return False; } -#endif - +#endif /* DEBUG */ + + +#define USAGE "usage: %s [ -options... ] window-id\n" \ + "\n" \ + " This program puts an image on the given window.\n" \ + "\n" \ + " It is used by those xscreensaver demos that operate on images.\n" \ + " The image may be a file loaded from disk, a frame grabbed from\n" \ + " the system's video camera, or a screenshot of the desktop,\n" \ + " depending on command-line options or the ~/.xscreensaver file.\n" \ + "\n" \ + " Options include:\n" \ + "\n" \ + " -display host:dpy.screen which display to use\n" \ + " -root draw to the root window\n" \ + " -verbose print diagnostics\n" \ + " -images / -no-images whether to allow image file loading\n" \ + " -video / -no-video whether to allow video grabs\n" \ + " -desktop / -no-desktop whether to allow desktop screen grabs\n"\ + " -directory where to find image files to load\n" \ + "\n" \ + " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\ + " defaults for these options in your ~/.xscreensaver file.\n" \ + "\n" int main (int argc, char **argv) @@ -283,95 +636,121 @@ main (int argc, char **argv) Widget toplevel; Display *dpy; Screen *screen; + char *oprogname = progname; + Window window = (Window) 0; - Bool verbose_p = False; char *s; int i; progname = argv[0]; s = strrchr (progname, '/'); if (s) progname = s+1; + oprogname = progname; + + /* half-assed way of avoiding buffer-overrun attacks. */ + if (strlen (progname) >= 100) progname[100] = 0; + /* We must read exactly the same resources as xscreensaver. That means we must have both the same progclass *and* progname, at least as far as the resource database is concerned. So, put "xscreensaver" in argv[0] while initializing Xt. */ - argv[0] = "xscreensaver"; + progname = argv[0] = "xscreensaver"; + + /* allow one dash or two. */ + for (i = 1; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++; + toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, - defaults, 0, 0); - argv[0] = progname; + defaults, 0, 0); dpy = XtDisplay (toplevel); screen = XtScreen (toplevel); db = XtDatabase (dpy); - XtGetApplicationNameAndClass (dpy, &s, &progclass); + XSetErrorHandler (x_ehandler); + XSync (dpy, False); - /* half-assed way of avoiding buffer-overrun attacks. */ - if (strlen (progname) >= 100) progname[100] = 0; + /* Randomize -- only need to do this here because this program + doesn't use the `screenhack.h' or `lockmore.h' APIs. */ +# undef ya_rand_init + ya_rand_init (0); + + memset (&P, 0, sizeof(P)); + P.db = db; + load_init_file (&P); + + progname = argv[0] = oprogname; for (i = 1; i < argc; i++) { - if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++; - if (!strcmp (argv[i], "-v") || - !strcmp (argv[i], "-verbose")) - verbose_p = True; + /* Have to re-process these, or else the .xscreensaver file + has priority over the command line... + */ + if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose")) + P.verbose_p = True; + else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True; + else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False; + else if (!strcmp (argv[i], "-video")) P.grab_video_p = True; + else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False; + else if (!strcmp (argv[i], "-images")) P.random_image_p = True; + else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False; + else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir")) + P.image_directory = argv[++i]; else if (window == 0) { unsigned long w; char dummy; - if (!strcmp (argv[i], "root") || - !strcmp (argv[i], "-root") || - !strcmp (argv[i], "--root")) + if (!strcmp (argv[i], "-root") || + !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 - goto LOSE; + { + if (argv[i][0] == '-') + fprintf (stderr, "\n%s: unknown option \"%s\"\n", + progname, argv[i]); + else + fprintf (stderr, "\n%s: unparsable window ID: \"%s\"\n", + progname, argv[i]); + goto LOSE; + } } else { + fprintf (stderr, "\n%s: unknown option \"%s\"\n", + progname, argv[i]); LOSE: - fprintf (stderr, - "usage: %s [ -display host:dpy.screen ] [ -v ] window-id\n", - progname); - fprintf (stderr, "\n" - "\tThis program puts an image of the desktop on the given window.\n" - "\tIt is used by those xscreensaver demos that operate on images.\n" - "\n"); + fprintf (stderr, USAGE, progname); exit (1); } } - if (window == 0) goto LOSE; - - /* Randomize -- only need to do this here because this program - doesn't use the `screenhack.h' or `lockmore.h' APIs. */ -# undef ya_rand_init - ya_rand_init (0); + if (window == 0) + { + fprintf (stderr, "\n%s: no window specified!\n", progname); + goto LOSE; + } - memset (&P, 0, sizeof(P)); - P.db = db; - load_init_file (&P); - if (P.verbose_p) - verbose_p = True; - -#if 0 - /* Print out all the resources we read. */ - { - XrmName name = { 0 }; - XrmClass class = { 0 }; - int count = 0; - XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper, - (XtPointer) &count); - } -#endif +#ifdef DEBUG + if (P.verbose_p) /* Print out all the resources we can see. */ + { + XrmName name = { 0 }; + XrmClass class = { 0 }; + int count = 0; + XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper, + (XtPointer) &count); + } +#endif /* DEBUG */ - get_image (screen, window, verbose_p); + get_image (screen, window, P.verbose_p, + P.grab_desktop_p, P.grab_video_p, P.random_image_p, + P.image_directory); exit (0); }