http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / OSX / osxgrabscreen.m
index c636754d88560e1c8bf0904c33d75f7fb38cdbfc..0138fcf41682682d60c9e153ef0e15f4cff5eb14 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1992-2009 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1992-2011 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
  */
 
 /* This is the OSX implementation of desktop-grabbing and image-loading.
+   This code is invoked by "utils/grabclient.c", which is linked directly
+   in to each screen saver bundle.
+
+   X11-based builds of the savers do not use this code (even on MacOS).
+   This is used only by the Cocoa build of the savers.
  */
 
 #import <stdlib.h>
 #import "usleep.h"
 
 
+#ifndef  MAC_OS_X_VERSION_10_6
+# define MAC_OS_X_VERSION_10_6 1060  /* undefined in 10.4 SDK, grr */
+#endif
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
+
+     /* 10.4 code.
+
+        This version of the code works on 10.4, but is flaky.  There is
+        a better way to do it on 10.5 and newer, but taking this path,
+        then we are being compiled against the 10.4 SDK instead of the
+        10.5 SDK, and the newer API is not available to us.
+      */
+
 static void
 copy_framebuffer_to_ximage (CGDirectDisplayID cgdpy, XImage *xim,
                             int window_x, int window_y)
@@ -117,18 +136,18 @@ void
 osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable)
 {
   Display *dpy = DisplayOfScreen (screen);
-  XWindowAttributes xgwa;
   NSView *nsview = jwxyz_window_view (xwindow);
   NSWindow *nswindow = [nsview window];
+  XWindowAttributes xgwa;
   int window_x, window_y;
   Window unused;
 
-  // figure out where this window is on the screen
+  // Figure out where this window is on the screen.
   //
   XGetWindowAttributes (dpy, xwindow, &xgwa);
   XTranslateCoordinates (dpy, xwindow, RootWindowOfScreen (screen), 0, 0, 
                          &window_x, &window_y, &unused);
-  
+
   // Use the size of the Drawable, not the Window.
   {
     Window r;
@@ -202,17 +221,119 @@ osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable)
   // Splat the XImage onto the target drawable (probably the window)
   // and free the bits.
   //
-  GC gc = 0;
+  XGCValues gcv;
+  GC gc = XCreateGC (dpy, drawable, 0, &gcv);
   XPutImage (dpy, drawable, gc, xim, 0, 0, 0, 0, xim->width, xim->height);
+  XFreeGC (dpy, gc);
   XDestroyImage (xim);
 }
 
 
+#else /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+
+         10.5+ code.
+
+         This version of the code is simpler and more reliable, but
+         uses an API that only exist on 10.5 and newer, so we can only
+         use it if when being compiled against the 10.5 SDK or later.
+       */
+
+/* Loads an image into the Drawable, returning once the image is loaded.
+ */
+void
+osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable)
+{
+  Display *dpy = DisplayOfScreen (screen);
+  NSView *nsview = jwxyz_window_view (xwindow);
+  XWindowAttributes xgwa;
+  int window_x, window_y;
+  Window unused;
+
+  // Figure out where this window is on the screen.
+  //
+  XGetWindowAttributes (dpy, xwindow, &xgwa);
+  XTranslateCoordinates (dpy, xwindow, RootWindowOfScreen (screen), 0, 0, 
+                         &window_x, &window_y, &unused);
+
+  // Grab only the rectangle of the screen underlying this window.
+  //
+  CGRect cgrect;
+  cgrect.origin.x    = window_x;
+  cgrect.origin.y    = window_y;
+  cgrect.size.width  = xgwa.width;
+  cgrect.size.height = xgwa.height;
+
+  /* If a password is required to unlock the screen, a large black
+     window will be on top of all of the desktop windows by the time
+     we reach here, making the screen-grab rather uninteresting.  If
+     we move ourselves temporarily below the login-window windows
+     before capturing the image, we capture the real desktop as
+     intended.
+   */
+
+  // save our current level so we can restore it later
+  int oldLevel = [[nsview window] level]; 
+
+  [[nsview window] setLevel:CGWindowLevelForKey(kCGPopUpMenuWindowLevelKey)];
+
+  // Grab a screen shot of those windows below this one
+  // (hey, X11 can't do that!)
+  //
+  CGImageRef img = 
+    CGWindowListCreateImage (cgrect,
+                             kCGWindowListOptionOnScreenBelowWindow,
+                             [[nsview window] windowNumber],
+                             kCGWindowImageDefault);
+
+  // put us back above the login windows so the screensaver is visible.
+  [[nsview window] setLevel:oldLevel];
+
+  // Render the grabbed CGImage into the Drawable.
+  if (img) {
+    jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (screen), drawable, 
+                                   False, img, NULL, 0);
+    CGImageRelease (img);
+  }
+}
+
+#endif /* 10.5+ code */
+
+
 /* Returns the EXIF rotation property of the image, if any.
  */
 static int
 exif_rotation (const char *filename)
 {
+  /* As of 10.6, NSImage rotates according to EXIF by default:
+     http://developer.apple.com/mac/library/releasenotes/cocoa/appkit.html
+     So this function should return -1 when *running* on 10.6 systems.
+     But when running against older systems, we need to examine the image
+     to figure out its rotation.
+   */
+
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6  /* 10.6 SDK */
+
+  /* When we have compiled against the 10.6 SDK, we know that we are 
+     running on a 10.6 or later system.
+   */
+  return -1;
+
+# else /* Compiled against 10.5 SDK or earlier */
+
+  /* If this selector exists, then we are running against a 10.6 runtime
+     that does automatic EXIF rotation (despite the fact that we were
+     compiled against the 10.5 or earlier SDK).  So in that case, this
+     function should no-op.
+   */
+  if ([NSImage instancesRespondToSelector:
+                 @selector(initWithDataIgnoringOrientation:)])
+    return -1;
+
+  /* Otherwise, go ahead and figure out what the rotational characteristics
+     of this image are. */
+
+
+
   /* This is a ridiculous amount of rigamarole to go through, but for some
      reason the "Orientation" tag does not exist in the "NSImageEXIFData"
      dictionary inside the NSBitmapImageRep of the NSImage.  Several other
@@ -232,6 +353,8 @@ exif_rotation (const char *filename)
   CFRelease (url);
   CFRelease (s);
   return rot;
+
+# endif /* 10.5 */
 }
 
 
@@ -250,8 +373,9 @@ osx_load_image_file (Screen *screen, Window xwindow, Drawable drawable,
   if (!img)
     return False;
 
-  jwxyz_draw_NSImage (DisplayOfScreen (screen), drawable, img, geom_ret,
-                      exif_rotation (filename));
+  jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (screen), drawable, 
+                                 True, img, geom_ret, 
+                                 exif_rotation (filename));
   [img release];
   return True;
 }