1 /* xscreensaver, Copyright (c) 1992-2009 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* This is the OSX implementation of desktop-grabbing and image-loading.
17 #import <Cocoa/Cocoa.h>
19 #import "grabscreen.h"
25 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
29 This version of the code works on 10.4, but is flaky. There is
30 a better way to do it on 10.5 and newer, but taking this path,
31 then we are being compiled against the 10.4 SDK instead of the
32 10.5 SDK, and the newer API is not available to us.
36 copy_framebuffer_to_ximage (CGDirectDisplayID cgdpy, XImage *xim,
37 int window_x, int window_y)
39 unsigned char *data = (unsigned char *)
40 CGDisplayAddressForPosition (cgdpy, window_x, window_y);
41 int bpp = CGDisplayBitsPerPixel (cgdpy);
42 int spp = CGDisplaySamplesPerPixel (cgdpy);
43 int bps = CGDisplayBitsPerSample (cgdpy);
44 int bpr = CGDisplayBytesPerRow (cgdpy);
47 int ximw = xim->width;
48 int ximh = xim->height;
50 uint32_t *odata = (uint32_t *) xim->data;
54 if (spp != 3) abort();
55 if (bps != 8) abort();
56 int xwpl = xim->bytes_per_line/4;
57 for (y = 0; y < ximh; y++) {
58 // We can do this because the frame buffer and XImage are both ARGB 32.
59 // Both PPC and Intel use ARGB, viewed in word order (not byte-order).
60 memcpy (odata, data, ximw * 4);
67 if (spp != 3) abort();
68 if (bps != 5) abort();
69 for (y = 0; y < ximh; y++) {
70 uint16_t *ip = (uint16_t *) data;
72 for (x = 0; x < ximw; x++) {
74 // This should be ok on both PPC and Intel (ARGB, word order)
75 unsigned char r = (p >> 10) & 0x1F;
76 unsigned char g = (p >> 5) & 0x1F;
77 unsigned char b = (p ) & 0x1F;
78 r = (r << 3) | (r >> 2);
79 g = (g << 3) | (g >> 2);
80 b = (b << 3) | (b >> 2);
81 uint32_t pixel = 0xFF000000 | (r << 16) | (g << 8) | b;
82 // XPutPixel (xim, x, y, pixel);
91 /* Get the current palette of the display. */
92 CGDirectPaletteRef pal = CGPaletteCreateWithDisplay (cgdpy);
94 /* Map it to 32bpp pixels */
96 for (y = 0; y < 256; y++) {
97 CGDeviceColor c = CGPaletteGetColorAtIndex (pal, y);
98 unsigned char r = c.red * 255.0;
99 unsigned char g = c.green * 255.0;
100 unsigned char b = c.blue * 255.0;
101 uint32_t pixel = 0xFF000000 | (r << 16) | (g << 8) | b;
105 for (y = 0; y < ximh; y++) {
106 unsigned char *ip = data;
108 for (x = 0; x < ximw; x++) {
109 *odata++ = map[*ip++];
113 CGPaletteRelease (pal);
124 /* Loads an image into the Drawable, returning once the image is loaded.
127 osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable)
129 Display *dpy = DisplayOfScreen (screen);
130 NSView *nsview = jwxyz_window_view (xwindow);
131 NSWindow *nswindow = [nsview window];
132 XWindowAttributes xgwa;
133 int window_x, window_y;
136 // Figure out where this window is on the screen.
138 XGetWindowAttributes (dpy, xwindow, &xgwa);
139 XTranslateCoordinates (dpy, xwindow, RootWindowOfScreen (screen), 0, 0,
140 &window_x, &window_y, &unused);
142 // Use the size of the Drawable, not the Window.
146 unsigned int w, h, bbw, d;
147 XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
152 // Create a tmp ximage to hold the screen data.
154 XImage *xim = XCreateImage (dpy, xgwa.visual, 32, ZPixmap, 0, 0,
155 xgwa.width, xgwa.height, 8, 0);
156 xim->data = (char *) malloc (xim->height * xim->bytes_per_line);
159 // Find the address in the frame buffer of the top left of this window.
161 CGDirectDisplayID cgdpy = 0;
164 // #### this isn't quite right for screen 2: it's offset slightly.
168 CGGetDisplaysWithPoint (p, 1, &cgdpy, &n);
172 // Paint a transparent "hole" in this window.
174 BOOL oopaque = [nswindow isOpaque];
175 [nswindow setOpaque:NO];
177 [[NSColor clearColor] set];
178 NSRectFill ([nsview frame]);
179 [[nswindow graphicsContext] flushGraphics];
182 // Without this, we get a dozen black scanlines at the top.
183 // #### But with this, the screen saver loops, because calling this
184 // seems to implicitly mark the display as non-idle!
185 // CGDisplayCaptureWithOptions (cgdpy, kCGCaptureNoFill);
187 // #### So let's try waiting for the vertical blank instead...
188 // Nope, that doesn't work.
190 // CGDisplayWaitForBeamPositionOutsideLines (cgdpy, 0,
191 // window_y + [nswindow frame].size.height);
193 // #### Ok, try a busy-wait?
197 // #### Ok, just fuckin' sleep!
202 // Pull the bits out of the frame buffer.
204 copy_framebuffer_to_ximage (cgdpy, xim, window_x, window_y);
206 // CGDisplayRelease (cgdpy);
208 // Make the window visible again.
210 [nswindow setOpaque:oopaque];
212 // Splat the XImage onto the target drawable (probably the window)
213 // and free the bits.
216 XPutImage (dpy, drawable, gc, xim, 0, 0, 0, 0, xim->width, xim->height);
221 #else /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
225 This version of the code is simpler and more reliable, but
226 uses an API that only exist on 10.5 and newer, so we can only
227 use it if when being compiled against the 10.5 SDK or later.
230 /* Loads an image into the Drawable, returning once the image is loaded.
233 osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable)
235 Display *dpy = DisplayOfScreen (screen);
236 NSView *nsview = jwxyz_window_view (xwindow);
237 XWindowAttributes xgwa;
238 int window_x, window_y;
241 // Figure out where this window is on the screen.
243 XGetWindowAttributes (dpy, xwindow, &xgwa);
244 XTranslateCoordinates (dpy, xwindow, RootWindowOfScreen (screen), 0, 0,
245 &window_x, &window_y, &unused);
247 // Grab only the rectangle of the screen underlying this window.
250 cgrect.origin.x = window_x;
251 cgrect.origin.y = window_y;
252 cgrect.size.width = xgwa.width;
253 cgrect.size.height = xgwa.height;
255 // Grab a screen shot of those windows below this one
256 // (hey, X11 can't do that!)
259 CGWindowListCreateImage (cgrect,
260 kCGWindowListOptionOnScreenBelowWindow,
261 [[nsview window] windowNumber],
262 kCGWindowImageDefault);
264 // Render the grabbed CGImage into the Drawable.
266 jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (screen), drawable,
267 False, img, NULL, 0);
268 CGImageRelease (img);
272 #endif /* 10.5+ code */
275 /* Returns the EXIF rotation property of the image, if any.
278 exif_rotation (const char *filename)
280 /* This is a ridiculous amount of rigamarole to go through, but for some
281 reason the "Orientation" tag does not exist in the "NSImageEXIFData"
282 dictionary inside the NSBitmapImageRep of the NSImage. Several other
283 EXIF tags are there (e.g., shutter speed) but not orientation. WTF?
285 CFStringRef s = CFStringCreateWithCString (NULL, filename,
286 kCFStringEncodingUTF8);
287 CFURLRef url = CFURLCreateWithFileSystemPath (NULL, s,
288 kCFURLPOSIXPathStyle, 0);
289 CGImageSourceRef cgimg = CGImageSourceCreateWithURL (url, NULL);
290 if (! cgimg) return -1;
292 NSDictionary *props = (NSDictionary *)
293 CGImageSourceCopyPropertiesAtIndex (cgimg, 0, NULL);
294 int rot = [[props objectForKey:@"Orientation"] intValue];
302 /* Loads an image file and splats it onto the drawable.
303 The image is drawn as large as possible while preserving its aspect ratio.
304 If geom_ret is provided, the actual rectangle the rendered image takes
305 up will be returned there.
308 osx_load_image_file (Screen *screen, Window xwindow, Drawable drawable,
309 const char *filename, XRectangle *geom_ret)
311 NSImage *img = [[NSImage alloc] initWithContentsOfFile:
312 [NSString stringWithCString:filename
313 encoding:NSUTF8StringEncoding]];
317 jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (screen), drawable,
319 exif_rotation (filename));