From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[xscreensaver] / driver / xscreensaver-getimage.c
index 68637a7be4e2fb8662d035a293247ea48ef11993..092540d478695bc297a1ffd5b209ef33ea7a87ec 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 2001-2006 by Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2001-2018 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
 
 
 #ifdef __APPLE__
-  /* On MacOSX / XDarwin, the usual X11 mechanism of getting a screen shot
-     doesn't work, and we need to use an external program. */
+  /* On MacOS under X11, the usual X11 mechanism of getting a screen shot
+     doesn't work, and we need to use an external program.  This is only
+     used when running under X11 on MacOS.  If it's a Cocoa build, this
+     path is not taken, and OSX/grabclient-osx.m is used instead.
+   */
 # define USE_EXTERNAL_SCREEN_GRABBER
 #endif
 
@@ -253,9 +256,24 @@ compute_image_scaling (int src_w, int src_h,
       float rw = (float) dest_w  / src_w;
       float rh = (float) dest_h / src_h;
       float r = (rw < rh ? rw : rh);
-      int tw = src_w * r;
-      int th = src_h * r;
-      int pct = (r * 100);
+      int tw, th, pct;
+
+      /* If the window is a goofy aspect ratio, take a middle slice of
+         the image instead. */
+      if (dest_w > dest_h * 5 || dest_h > dest_w * 5)
+        {
+          double r2 = (dest_w > dest_h
+                       ? dest_w / (double) dest_h
+                       : dest_h / (double) dest_w);
+          r *= r2;
+          if (verbose_p)
+            fprintf (stderr, "%s: weird aspect: scaling by %.1f\n",
+                     progname, r2);
+        }
+
+      tw = src_w * r;
+      th = src_h * r;
+      pct = (r * 100);
 
 #if 0
       /* this optimization breaks things */
@@ -278,8 +296,8 @@ compute_image_scaling (int src_w, int src_h,
   if (destx < 0) srcx = -destx, destx = 0;
   if (desty < 0) srcy = -desty, desty = 0;
 
-  if (dest_w < src_w) src_w = dest_w;
-  if (dest_h < src_h) src_h = dest_h;
+  /* if (dest_w < src_w) src_w = dest_w;
+     if (dest_h < src_h) src_h = dest_h; */
 
   *scaled_w_ret = src_w;
   *scaled_h_ret = src_h;
@@ -289,8 +307,23 @@ 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 in %dx%d.\n",
-             progname, src_w, src_h, destx, desty, dest_w, dest_h);
+    fprintf (stderr, "%s: displaying %dx%d+%d+%d image at %d,%d in %dx%d.\n",
+             progname, src_w, src_h, srcx, srcy, destx, desty, dest_w, dest_h);
+}
+
+
+static void
+colorbars (Screen *screen, Visual *visual, Drawable drawable, Colormap cmap)
+{
+  Pixmap mask = 0;
+  unsigned long *pixels; /* ignored - unfreed */
+  int npixels;
+  Pixmap logo = xscreensaver_logo (screen, visual, drawable, cmap,
+                                   BlackPixelOfScreen (screen),
+                                   &pixels, &npixels, &mask, True);
+  draw_colorbars (screen, visual, drawable, cmap, 0, 0, 0, 0, logo, mask);
+  XFreePixmap (DisplayOfScreen (screen), logo);
+  XFreePixmap (DisplayOfScreen (screen), mask);
 }
 
 
@@ -376,9 +409,11 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable,
                   &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
   }
 
-  gdk_pixbuf_xlib_init (dpy, screen_number (screen));
+  gdk_pixbuf_xlib_init_with_depth (dpy, screen_number (screen), win_depth);
 # ifdef HAVE_GTK2
+# if !GLIB_CHECK_VERSION(2, 36 ,0)
   g_type_init();
+# endif
 # else  /* !HAVE_GTK2 */
   xlib_rgb_init (dpy, screen);
 # endif /* !HAVE_GTK2 */
@@ -405,6 +440,20 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable,
       int srcx, srcy, destx, desty, w2, h2;
       Bool bg_p = False;
 
+# ifdef HAVE_GDK_PIXBUF_APPLY_EMBEDDED_ORIENTATION
+      {
+        int ow = w, oh = h;
+        GdkPixbuf *opb = pb;
+        pb = gdk_pixbuf_apply_embedded_orientation (opb);
+        g_object_unref (opb);
+        w = gdk_pixbuf_get_width (pb);
+        h = gdk_pixbuf_get_height (pb);
+        if (verbose_p && (w != ow || h != oh))
+          fprintf (stderr, "%s: rotated %dx%d to %dx%d\n",
+                   progname, ow, oh, w, h);
+      }
+# endif
+
       compute_image_scaling (w, h, win_width, win_height, verbose_p,
                              &srcx, &srcy, &destx, &desty, &w2, &h2);
       if (w != w2 || h != h2)
@@ -413,7 +462,7 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable,
                                                     GDK_INTERP_BILINEAR);
           if (pb2)
             {
-              gdk_pixbuf_unref (pb);
+              g_object_unref (pb);
               pb = pb2;
               w = w2;
               h = h2;
@@ -447,6 +496,8 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable,
          gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
          But I haven't tried.
        */
+      if (srcx > 0) w -= srcx;
+      if (srcy > 0) h -= srcy;
       gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
                                                 srcx, srcy, destx, desty,
                                                 w, h,
@@ -480,6 +531,8 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable,
 
 /* 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,
@@ -546,6 +599,8 @@ allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
 
 /* 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,
@@ -586,6 +641,8 @@ find_closest_pixel (XColor *colors, int ncolors,
    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)
@@ -815,8 +872,7 @@ 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);
+  colorbars (err->screen, err->visual, err->drawable, err->cmap);
   XSync (DisplayOfScreen (err->screen), False);
   exit (1);
 }
@@ -1105,7 +1161,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.  'grab_type' controls which program
-   to run.
+   to run.  Returned pathname may be relative to 'directory', or absolute.
  */
 static char *
 get_filename_1 (Screen *screen, const char *directory, grab_type type,
@@ -1115,7 +1171,7 @@ get_filename_1 (Screen *screen, const char *directory, grab_type type,
   pid_t forked;
   int fds [2];
   int in, out;
-  char buf[1024];
+  char buf[10240];
   char *av[20];
   int ac = 0;
 
@@ -1200,6 +1256,7 @@ get_filename_1 (Screen *screen, const char *directory, grab_type type,
         int wait_status = 0;
         FILE *f = fdopen (in, "r");
         int L;
+        char *ret = 0;
 
         close (out);  /* don't need this one */
         *buf = 0;
@@ -1216,14 +1273,28 @@ get_filename_1 (Screen *screen, const char *directory, grab_type type,
           
         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 strdup (buf);
+          return ret;
       }
     }
 
@@ -1451,7 +1522,7 @@ drawable_miniscule_p (Display *dpy, Drawable drawable)
   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);
+  return (w < 32 || h < 30);
 }
 
 
@@ -1473,6 +1544,7 @@ get_image (Screen *screen,
   grab_type which = GRAB_BARS;
   struct stat st;
   const char *file_prop = 0;
+  char *absfile = 0;
   XRectangle geom = { 0, 0, 0, 0 };
 
   if (! drawable_window_p (dpy, window))
@@ -1558,7 +1630,8 @@ get_image (Screen *screen,
      We cannot grab desktop images that way if:
        - the window is a non-top-level window.
 
-     Using the MacOS X way, desktops are just like loaded image files.
+     Under X11 on MacOS, desktops are just like loaded image files.
+     Under Cocoa on MacOS, this code is not used at all.
    */
 # ifndef USE_EXTERNAL_SCREEN_GRABBER
   if (desk_p)
@@ -1623,9 +1696,10 @@ get_image (Screen *screen,
         if (verbose_p)
           fprintf (stderr, "%s: drawing colorbars.\n", progname);
         XGetWindowAttributes (dpy, window, &xgwa);
-        draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
-                        0, 0, 0, 0);
+        colorbars (screen, xgwa.visual, drawable, xgwa.colormap);
         XSync (dpy, False);
+        if (! file_prop) file_prop = "";
+
       }
       break;
 
@@ -1636,7 +1710,18 @@ get_image (Screen *screen,
       break;
 
     case GRAB_FILE:
-      if (! display_file (screen, window, drawable, file, verbose_p, &geom))
+      if (*file && *file != '/')       /* pathname is relative to dir. */
+        {
+          if (absfile) free (absfile);
+          absfile = malloc (strlen(dir) + strlen(file) + 10);
+          strcpy (absfile, dir);
+          if (dir[strlen(dir)-1] != '/')
+            strcat (absfile, "/");
+          strcat (absfile, file);
+        }
+      if (! display_file (screen, window, drawable, 
+                          (absfile ? absfile : file),
+                          verbose_p, &geom))
         goto COLORBARS;
       file_prop = file;
       break;
@@ -1655,8 +1740,23 @@ get_image (Screen *screen,
   {
     Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
     if (file_prop && *file_prop)
-      XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace, 
-                       (unsigned char *) file_prop, strlen(file_prop));
+      {
+        char *f2 = strdup (file_prop);
+
+        /* Take the extension off of the file name. */
+        /* Duplicated in utils/grabclient.c. */
+        char *slash = strrchr (f2, '/');
+        char *dot = strrchr ((slash ? slash : f2), '.');
+        if (dot) *dot = 0;
+        /* Replace slashes with newlines */
+        /* while ((dot = strchr(f2, '/'))) *dot = '\n'; */
+        /* Replace slashes with spaces */
+        /* while ((dot = strchr(f2, '/'))) *dot = ' '; */
+
+        XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace, 
+                         (unsigned char *) f2, strlen(f2));
+        free (f2);
+      }
     else
       XDeleteProperty (dpy, window, a);
 
@@ -1672,6 +1772,7 @@ get_image (Screen *screen,
       XDeleteProperty (dpy, window, a);
   }
 
+  if (absfile) free (absfile);
   XSync (dpy, False);
 }