-/* xscreensaver, Copyright (c) 1992-2009 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1992-2012 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 <stdint.h>
-#import <Cocoa/Cocoa.h>
+#ifndef USE_IPHONE
+# import <Cocoa/Cocoa.h>
+#else
+# import "SaverRunner.h"
+#endif
#import "jwxyz.h"
#import "grabscreen.h"
#import "colorbars.h"
#import "usleep.h"
+#ifdef USE_IPHONE
+# define NSImage UIImage
+#endif
+
+
+#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)
/* Loads an image into the Drawable, returning once the image is loaded.
*/
-void
-osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable)
+Bool
+osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable,
+ XRectangle *geom_ret)
{
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;
// 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);
+
+ if (geom_ret) {
+ geom_ret->x = 0;
+ geom_ret->y = 0;
+ geom_ret->width = xim->width;
+ geom_ret->height = xim->height;
+ }
+
XDestroyImage (xim);
+ return True;
}
+#elif defined(USE_IPHONE)
+
+ /* What a hack!
+
+ On iOS, our application delegate, SaverRunner, grabs an image
+ of itself as a UIImage before creating the XScreenSaverView.
+ In this code, we ask SaverRunner for that UIImage, then copy
+ it to the root window.
+ */
+
+Bool
+osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable,
+ XRectangle *geom_ret)
+{
+
+ /* Just for a little variety, let's return colorbars every other time. */
+ static int counter = 0;
+ if (counter++ & 1)
+ return False;
+
+ SaverRunner *s =
+ (SaverRunner *) [[UIApplication sharedApplication] delegate];
+ if (! s)
+ return False;
+ if (! [s isKindOfClass:[SaverRunner class]])
+ abort();
+ UIImage *img = [s screenshot];
+ if (! img)
+ return False;
+ jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (screen), drawable,
+ True, img, geom_ret, 0);
+ return True;
+}
+
+
+#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.
+ */
+Bool
+osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable,
+ XRectangle *geom_ret)
+{
+ 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];
+
+ if (! img) return False;
+
+ // Render the grabbed CGImage into the Drawable.
+ jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (screen), drawable,
+ False, img, geom_ret, 0);
+ CGImageRelease (img);
+ return True;
+}
+
+#endif /* 10.5+ code */
+
+
+# ifndef USE_IPHONE
+
/* 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
CFRelease (url);
CFRelease (s);
return rot;
+
+# endif /* 10.5 */
}
+# endif /* USE_IPHONE */
+
+
/* Loads an image file and splats it onto the drawable.
The image is drawn as large as possible while preserving its aspect ratio.
osx_load_image_file (Screen *screen, Window xwindow, Drawable drawable,
const char *filename, XRectangle *geom_ret)
{
+# ifndef USE_IPHONE
+
NSImage *img = [[NSImage alloc] initWithContentsOfFile:
[NSString stringWithCString:filename
encoding:NSUTF8StringEncoding]];
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;
-}
+# else /* USE_IPHONE */
+
+ /* It would be nice to select a random image from the Photo Album and
+ load that, but that looks like a gigantic pain in the ass, because
+ it's an asynchronous API, and might require manual authorization
+ by the user. (ALAssetsLibrary, enumerateGroupsWithTypes.)
+
+ Possibly useful sample code to check out:
+ http://www.fiveminutes.eu/accessing-photo-library-using-assets-library-framework-on-iphone/
+
+ So, in the meantime, return False, acquire colorbars.
+ */
+ return False;
+
+# endif /* USE_IPHONE */
+}