ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-5.01.tar.gz
[xscreensaver] / driver / xscreensaver-getimage.c
index e46bc5c97262a6e9d55ce566c0142ec8c4b25228..68637a7be4e2fb8662d035a293247ea48ef11993 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 2001, 2002, 2003 by Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2001-2006 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
@@ -101,6 +101,8 @@ typedef enum {
 #define GETIMAGE_FILE_PROGRAM    "xscreensaver-getimage-file"
 #define GETIMAGE_SCREEN_PROGRAM  "xscreensaver-getimage-desktop"
 
+extern const char *blurb (void);
+
 const char *
 blurb (void)
 {
@@ -136,6 +138,17 @@ ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
   return 0;
 }
 
+#ifndef USE_EXTERNAL_SCREEN_GRABBER
+static int
+ignore_badmatch_ehandler (Display *dpy, XErrorEvent *error)
+{
+  if (error->error_code == BadMatch)
+    return ignore_all_errors_ehandler (dpy, error);
+  else
+    return x_ehandler (dpy, error);
+}
+#endif /* ! USE_EXTERNAL_SCREEN_GRABBER */
+
 
 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
  */
@@ -171,7 +184,7 @@ root_window_p (Screen *screen, Window window)
   Atom type;
   int format;
   unsigned long nitems, bytesafter;
-  char *version;
+  unsigned char *version;
 
   if (window != RootWindowOfScreen (screen))
     return False;
@@ -180,7 +193,7 @@ root_window_p (Screen *screen, Window window)
                          XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
                          0, 1, False, XA_STRING,
                          &type, &format, &nitems, &bytesafter,
-                         (unsigned char **) &version)
+                         &version)
       == Success
       && type != None)
     return False;
@@ -276,8 +289,8 @@ compute_image_scaling (int src_w, int src_h,
   *scaled_to_y_ret = desty;
 
   if (verbose_p)
-    fprintf (stderr, "%s: displaying %dx%d image at %d,%d.\n",
-             progname, src_w, src_h, destx, desty);
+    fprintf (stderr, "%s: displaying %dx%d image at %d,%d in %dx%d.\n",
+             progname, src_w, src_h, destx, desty, dest_w, dest_h);
 }
 
 
@@ -286,6 +299,7 @@ compute_image_scaling (int src_w, int src_h,
    If out of memory, returns False, and the XImage will have been
    destroyed and freed.
  */
+#if !defined(USE_EXTERNAL_SCREEN_GRABBER) || defined(HAVE_JPEGLIB)
 static Bool
 scale_ximage (Screen *screen, Visual *visual,
               XImage *ximage, int new_width, int new_height)
@@ -333,6 +347,7 @@ scale_ximage (Screen *screen, Visual *visual,
 
   return True;
 }
+#endif /* !USE_EXTERNAL_SCREEN_GRABBER || HAVE_JPEGLIB */
 
 
 #ifdef HAVE_GDK_PIXBUF
@@ -342,7 +357,8 @@ scale_ximage (Screen *screen, Visual *visual,
  */
 static Bool
 read_file_gdk (Screen *screen, Window window, Drawable drawable,
-               const char *filename, Bool verbose_p)
+               const char *filename, Bool verbose_p,
+               XRectangle *geom_ret)
 {
   GdkPixbuf *pb;
   Display *dpy = DisplayOfScreen (screen);
@@ -442,6 +458,14 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable,
           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);
@@ -885,7 +909,7 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
           int x;
           for (x = 0; x < ximage->width; x++)
             {
-              int j = x * cinfo.num_components;
+              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];
@@ -897,10 +921,12 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
                 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)
+              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();
 
@@ -940,7 +966,8 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
  */
 static Bool
 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
-                   const char *filename, Bool verbose_p)
+                   const char *filename, Bool verbose_p,
+                   XRectangle *geom_ret)
 {
   Display *dpy = DisplayOfScreen (screen);
   XImage *ximage;
@@ -1031,6 +1058,14 @@ read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
     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);
@@ -1046,16 +1081,18 @@ read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
  */
 static Bool
 display_file (Screen *screen, Window window, Drawable drawable,
-              const char *filename, Bool verbose_p)
+              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))
+  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))
+  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. */
@@ -1067,7 +1104,7 @@ display_file (Screen *screen, Window window, Drawable drawable,
 
 
 /* Invokes a sub-process and returns its output (presumably, a file to
-   load.)  Free the string when done.  video_p controls which program
+   load.)  Free the string when done.  'grab_type' controls which program
    to run.
  */
 static char *
@@ -1166,7 +1203,8 @@ get_filename_1 (Screen *screen, const char *directory, grab_type type,
 
         close (out);  /* don't need this one */
         *buf = 0;
-        fgets (buf, sizeof(buf)-1, f);
+        if (! fgets (buf, sizeof(buf)-1, f))
+          *buf = 0;
         fclose (f);
 
         /* Wait for the child to die. */
@@ -1228,7 +1266,7 @@ get_desktop_filename (Screen *screen, Bool verbose_p)
  */
 static Bool
 display_video (Screen *screen, Window window, Drawable drawable,
-               Bool verbose_p)
+               Bool verbose_p, XRectangle *geom_ret)
 {
   char *filename = get_video_filename (screen, verbose_p);
   Bool status;
@@ -1240,7 +1278,8 @@ display_video (Screen *screen, Window window, Drawable drawable,
       return False;
     }
 
-  status = display_file (screen, window, drawable, filename, verbose_p);
+  status = display_file (screen, window, drawable, filename, verbose_p,
+                         geom_ret);
 
   if (unlink (filename))
     {
@@ -1263,7 +1302,7 @@ display_video (Screen *screen, Window window, Drawable drawable,
  */
 static Bool
 display_desktop (Screen *screen, Window window, Drawable drawable,
-                 Bool verbose_p)
+                 Bool verbose_p, XRectangle *geom_ret)
 {
 # ifdef USE_EXTERNAL_SCREEN_GRABBER
 
@@ -1299,7 +1338,8 @@ display_desktop (Screen *screen, Window window, Drawable drawable,
       return False;
     }
 
-  status = display_file (screen, window, drawable, filename, verbose_p);
+  status = display_file (screen, window, drawable, filename, verbose_p,
+                         geom_ret);
 
   if (unlink (filename))
     {
@@ -1347,12 +1387,38 @@ display_desktop (Screen *screen, Window window, Drawable drawable,
     }
   else  /* size mismatch -- must scale client-side images to fit drawable */
     {
-      XImage *ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
-                                  ~0L, ZPixmap);
       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, 
@@ -1361,6 +1427,14 @@ display_desktop (Screen *screen, Window window, Drawable drawable,
       XFreeGC (dpy, gc);
     }
 
+  if (geom_ret)
+    {
+      geom_ret->x = destx;
+      geom_ret->y = desty;
+      geom_ret->width  = w2;
+      geom_ret->height = h2;
+    }
+
   XSync (dpy, False);
   return True;
 
@@ -1368,6 +1442,19 @@ display_desktop (Screen *screen, Window window, Drawable drawable,
 }
 
 
+/* Whether the given Drawable is unreasonably small.
+ */
+static Bool
+drawable_miniscule_p (Display *dpy, Drawable drawable)
+{
+  Window root;
+  int xx, yy;
+  unsigned int bw, d, w = 0, h = 0;
+  XGetGeometry (dpy, drawable, &root, &xx, &yy, &w, &h, &bw, &d);
+  return (w < 32 || h < 32);
+}
+
+
 /* 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.
@@ -1384,9 +1471,9 @@ get_image (Screen *screen,
 {
   Display *dpy = DisplayOfScreen (screen);
   grab_type which = GRAB_BARS;
-  int count = 0;
   struct stat st;
   const char *file_prop = 0;
+  XRectangle geom = { 0, 0, 0, 0 };
 
   if (! drawable_window_p (dpy, window))
     {
@@ -1442,8 +1529,7 @@ get_image (Screen *screen,
       video_p = False;
       image_p = True;
     }
-
-  if (!dir || !*dir)
+  else if (!dir || !*dir)
     {
       if (verbose_p && image_p)
         fprintf (stderr,
@@ -1452,6 +1538,15 @@ get_image (Screen *screen,
       image_p = False;
     }
 
+  /* If the target drawable is really small, no good can come of that.
+     Always do colorbars in that case.
+   */
+  if (drawable_miniscule_p (dpy, drawable))
+    {
+      desk_p  = False;
+      video_p = False;
+      image_p = False;
+    }
 
 # ifndef _VROOT_H_
 #  error Error!  This file definitely needs vroot.h!
@@ -1479,24 +1574,28 @@ get_image (Screen *screen,
     }
 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
 
-  count = 0;
-  if (desk_p)  count++;
-  if (video_p) count++;
-  if (image_p) count++;
-
-  if (count == 0)
+  if (! (desk_p || video_p || image_p))
     which = GRAB_BARS;
   else
     {
       int i = 0;
-      while (1)  /* loop until we get one that's permitted */
-        {
-          which = (random() % 3);
-          if (which == GRAB_DESK  && desk_p)  break;
-          if (which == GRAB_VIDEO && video_p) break;
-          if (which == GRAB_FILE  && image_p) break;
-          if (++i > 200) abort();
-        }
+      int n;
+      /* Loop until we get one that's permitted.
+         If files or video are permitted, do them more often
+         than desktop.
+
+             D+V+I: 10% + 45% + 45%.
+             V+I:   50% + 50%
+             D+V:   18% + 82%
+             D+I:   18% + 82%
+       */
+    AGAIN:
+      n = (random() % 100);
+      if (++i > 300) abort();
+      else if (desk_p  && n < 10) which = GRAB_DESK;   /* 10% */
+      else if (video_p && n < 55) which = GRAB_VIDEO;  /* 45% */
+      else if (image_p)           which = GRAB_FILE;   /* 45% */
+      else goto AGAIN;
     }
 
 
@@ -1531,19 +1630,19 @@ get_image (Screen *screen,
       break;
 
     case GRAB_DESK:
-      if (! display_desktop (screen, window, drawable, verbose_p))
+      if (! display_desktop (screen, window, drawable, verbose_p, &geom))
         goto COLORBARS;
       file_prop = "desktop";
       break;
 
     case GRAB_FILE:
-      if (! display_file (screen, window, drawable, file, verbose_p))
+      if (! display_file (screen, window, drawable, file, verbose_p, &geom))
         goto COLORBARS;
       file_prop = file;
       break;
 
     case GRAB_VIDEO:
-      if (! display_video (screen, window, drawable, verbose_p))
+      if (! display_video (screen, window, drawable, verbose_p, &geom))
         goto COLORBARS;
       file_prop = "video";
       break;
@@ -1560,6 +1659,17 @@ get_image (Screen *screen,
                        (unsigned char *) file_prop, strlen(file_prop));
     else
       XDeleteProperty (dpy, window, a);
+
+    a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
+    if (geom.width > 0)
+      {
+        char gstr[30];
+        sprintf (gstr, "%dx%d+%d+%d", geom.width, geom.height, geom.x, geom.y);
+        XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace, 
+                         (unsigned char *) gstr, strlen (gstr));
+      }
+    else
+      XDeleteProperty (dpy, window, a);
   }
 
   XSync (dpy, False);
@@ -1687,7 +1797,7 @@ main (int argc, char **argv)
 
   memset (&P, 0, sizeof(P));
   P.db = db;
-  load_init_file (&P);
+  load_init_file (dpy, &P);
 
   progname = argv[0] = oprogname;