X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=OSX%2FXScreenSaverView.m;h=ba87a89d633a1f3ec45dbcfb1335228f935d179f;hp=d773fc84d048ab6b75c4015befb677e5431867b5;hb=2762a7d7cf8d83e68b8f635941f6609119d630ae;hpb=4ade52359b6eba3621566dac79793a33aa4c915f diff --git a/OSX/XScreenSaverView.m b/OSX/XScreenSaverView.m index d773fc84..ba87a89d 100644 --- a/OSX/XScreenSaverView.m +++ b/OSX/XScreenSaverView.m @@ -1,13 +1,13 @@ /* xscreensaver, Copyright (c) 2006-2013 Jamie Zawinski -* -* Permission to use, copy, modify, distribute, and sell this software and its -* documentation for any purpose is hereby granted without fee, provided that -* the above copyright notice appear in all copies and that both that -* copyright notice and this permission notice appear in supporting -* documentation. No representations are made about the suitability of this -* software for any purpose. It is provided "as is" without express or -* implied warranty. -*/ + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ /* This is a subclass of Apple's ScreenSaverView that knows how to run xscreensaver programs without X11 via the dark magic of the "jwxyz" @@ -16,6 +16,7 @@ */ #import +#import #import "XScreenSaverView.h" #import "XScreenSaverConfigSheet.h" #import "screenhackI.h" @@ -220,6 +221,10 @@ add_default_options (const XrmOptionDescRec *opts, { "-image-directory", ".imageDirectory", XrmoptionSepArg, 0 }, { "-fps", ".doFPS", XrmoptionNoArg, "True" }, { "-no-fps", ".doFPS", XrmoptionNoArg, "False"}, + { "-foreground", ".foreground", XrmoptionSepArg, 0 }, + { "-fg", ".foreground", XrmoptionSepArg, 0 }, + { "-background", ".background", XrmoptionSepArg, 0 }, + { "-bg", ".background", XrmoptionSepArg, 0 }, { 0, 0, 0, 0 } }; static const char *default_defaults [] = { @@ -320,6 +325,7 @@ double_time (void) isPreview:(BOOL)isPreview { # ifdef USE_IPHONE + initial_bounds = frame.size; rot_current_size = frame.size; // needs to be early, because rot_from = rot_current_size; // [self setFrame] is called by rot_to = rot_current_size; // [super initWithFrame]. @@ -385,10 +391,12 @@ double_time (void) - (void) initLayer { -# ifndef USE_IPHONE +# if !defined(USE_IPHONE) && defined(USE_CALAYER) [self setLayer: [CALayer layer]]; + self.layer.delegate = self; + self.layer.opaque = YES; [self setWantsLayer: YES]; -# endif +# endif // !USE_IPHONE && USE_CALAYER } @@ -408,7 +416,16 @@ double_time (void) # ifdef USE_BACKBUFFER if (backbuffer) CGContextRelease (backbuffer); -# endif + + if (colorspace) + CGColorSpaceRelease (colorspace); + +# ifndef USE_CALAYER + if (window_ctx) + CGContextRelease (window_ctx); +# endif // !USE_CALAYER + +# endif // USE_BACKBUFFER [prefsReader release]; @@ -568,9 +585,8 @@ screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure) - (CGFloat) hackedContentScaleFactor { GLfloat s = [self contentScaleFactor]; - CGRect frame = [self bounds]; - if (frame.size.width >= 1024 || - frame.size.height >= 1024) + if (initial_bounds.width >= 1024 || + initial_bounds.height >= 1024) s = 1; return s; } @@ -677,12 +693,81 @@ double current_device_rotation (void) { # ifdef USE_IPHONE double s = [self hackedContentScaleFactor]; - int new_w = s * rot_current_size.width; - int new_h = s * rot_current_size.height; + CGSize rotsize = ignore_rotation_p ? initial_bounds : rot_current_size; + int new_w = s * rotsize.width; + int new_h = s * rotsize.height; # else int new_w = [self bounds].size.width; int new_h = [self bounds].size.height; # endif + + // Colorspaces and CGContexts only happen with non-GL hacks. + if (colorspace) + CGColorSpaceRelease (colorspace); +# ifndef USE_CALAYER + if (window_ctx) + CGContextRelease (window_ctx); +# endif + + NSWindow *window = [self window]; + + if (window && xdpy) { + [self lockFocus]; + +# ifndef USE_CALAYER + // TODO: This was borrowed from jwxyz_window_resized, and should + // probably be refactored. + + // Figure out which screen the window is currently on. + CGDirectDisplayID cgdpy = 0; + + { +// int wx, wy; +// TODO: XTranslateCoordinates is returning (0,1200) on my system. +// Is this right? +// In any case, those weren't valid coordinates for CGGetDisplaysWithPoint. +// XTranslateCoordinates (xdpy, xwindow, NULL, 0, 0, &wx, &wy, NULL); +// p.x = wx; +// p.y = wy; + + NSPoint p0 = {0, 0}; + p0 = [window convertBaseToScreen:p0]; + CGPoint p = {p0.x, p0.y}; + CGDisplayCount n; + CGGetDisplaysWithPoint (p, 1, &cgdpy, &n); + NSAssert (cgdpy, @"unable to find CGDisplay"); + } + + { + // Figure out this screen's colorspace, and use that for every CGImage. + // + CMProfileRef profile = 0; + + // CMGetProfileByAVID is deprecated as of OS X 10.6, but there's no + // documented replacement as of OS X 10.9. + // http://lists.apple.com/archives/colorsync-dev/2012/Nov/msg00001.html + CMGetProfileByAVID ((CMDisplayIDType) cgdpy, &profile); + NSAssert (profile, @"unable to find colorspace profile"); + colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile); + NSAssert (colorspace, @"unable to find colorspace"); + } +# else // USE_CALAYER + // Was apparently faster until 10.9. + colorspace = CGColorSpaceCreateDeviceRGB (); +# endif // USE_CALAYER + +# ifndef USE_CALAYER + window_ctx = [[window graphicsContext] graphicsPort]; + CGContextRetain (window_ctx); +# endif // !USE_CALAYER + + [self unlockFocus]; + } else { +# ifndef USE_CALAYER + window_ctx = NULL; +# endif // !USE_CALAYER + colorspace = CGColorSpaceCreateDeviceRGB(); + } if (backbuffer && backbuffer_size.width == new_w && @@ -695,15 +780,16 @@ double current_device_rotation (void) backbuffer_size.width = new_w; backbuffer_size.height = new_h; - CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); backbuffer = CGBitmapContextCreate (NULL, backbuffer_size.width, backbuffer_size.height, 8, backbuffer_size.width * 4, - cs, - kCGImageAlphaPremultipliedLast); - CGColorSpaceRelease (cs); + colorspace, + // kCGImageAlphaPremultipliedLast + (kCGImageAlphaNoneSkipFirst | + kCGBitmapByteOrder32Host) + ); NSAssert (backbuffer, @"unable to allocate back buffer"); // Clear it. @@ -775,6 +861,13 @@ double current_device_rotation (void) xdpy = jwxyz_make_display (self, 0); # endif xwindow = XRootWindow (xdpy, 0); + +# ifdef USE_IPHONE + /* Some X11 hacks (fluidballs) want to ignore all rotation events. */ + ignore_rotation_p = + get_boolean_resource (xdpy, "ignoreRotation", "IgnoreRotation"); +# endif // USE_IPHONE + [self resize_x11]; } @@ -786,12 +879,6 @@ double current_device_rotation (void) initted_p = YES; resized_p = NO; NSAssert(!xdata, @"xdata already initialized"); - -# ifdef USE_IPHONE - /* Some X11 hacks (fluidballs) want to ignore all rotation events. */ - ignore_rotation_p = - get_boolean_resource (xdpy, "ignoreRotation", "IgnoreRotation"); -# endif // USE_IPHONE # undef ya_rand_init @@ -982,6 +1069,7 @@ double current_device_rotation (void) - (void) animateOneFrame { [self render_x11]; + jwxyz_flush_context(xdpy); } #else // USE_BACKBUFFER @@ -1004,31 +1092,117 @@ double current_device_rotation (void) # ifdef USE_IPHONE // Then compute the transformations for rotation. + double hs = [self hackedContentScaleFactor]; + double s = [self contentScaleFactor]; + + // The rotation origin for layer.affineTransform is in the center already. + CGAffineTransform t = ignore_rotation_p ? + CGAffineTransformIdentity : + CGAffineTransformMakeRotation (rot_current_angle / (180.0 / M_PI)); + + CGFloat f = s / hs; + self.layer.affineTransform = CGAffineTransformScale(t, f, f); + + CGRect bounds; + bounds.origin.x = 0; + bounds.origin.y = 0; + bounds.size.width = backbuffer_size.width / s; + bounds.size.height = backbuffer_size.height / s; + self.layer.bounds = bounds; +# endif // USE_IPHONE + +# ifdef USE_CALAYER + [self.layer setNeedsDisplay]; +# else // !USE_CALAYER + size_t + w = CGBitmapContextGetWidth (backbuffer), + h = CGBitmapContextGetHeight (backbuffer); + + size_t bpl = CGBitmapContextGetBytesPerRow (backbuffer); + CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, + CGBitmapContextGetData(backbuffer), + bpl * h, + NULL); + + + CGImageRef img = CGImageCreate (w, h, + 8, 32, + CGBitmapContextGetBytesPerRow(backbuffer), + colorspace, + CGBitmapContextGetBitmapInfo(backbuffer), + prov, NULL, NO, + kCGRenderingIntentDefault); + + CGDataProviderRelease (prov); + + CGRect rect; + rect.origin.x = 0; + rect.origin.y = 0; + rect.size = backbuffer_size; + CGContextDrawImage (window_ctx, rect, img); + + CGImageRelease (img); - if (!ignore_rotation_p) { - // The rotation origin for layer.affineTransform is in the center already. - CGAffineTransform t = - CGAffineTransformMakeRotation (rot_current_angle / (180.0 / M_PI)); + CGContextFlush (window_ctx); +# endif // !USE_CALAYER +} - // Correct the aspect ratio. - CGRect frame = [self bounds]; - double s = [self hackedContentScaleFactor]; - t = CGAffineTransformScale(t, - backbuffer_size.width / (s * frame.size.width), - backbuffer_size.height / (s * frame.size.height)); - self.layer.affineTransform = t; - } -# endif // USE_IPHONE +# ifdef USE_CALAYER - // Then copy that bitmap to the screen, by just stuffing it into - // the layer. The superclass drawRect method will handle the rest. +- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx +{ + // This "isn't safe" if NULL is passed to CGBitmapCreateContext before iOS 4. + char *dest_data = (char *)CGBitmapContextGetData (ctx); - CGImageRef img = CGBitmapContextCreateImage (backbuffer); - self.layer.contents = (id)img; - CGImageRelease (img); + // The CGContext here is normally upside-down on iOS. + if (dest_data && + CGBitmapContextGetBitmapInfo (ctx) == + (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) +# ifdef USE_IPHONE + && CGContextGetCTM (ctx).d < 0 +# endif // USE_IPHONE + ) + { + size_t dest_height = CGBitmapContextGetHeight (ctx); + size_t dest_bpr = CGBitmapContextGetBytesPerRow (ctx); + size_t src_height = CGBitmapContextGetHeight (backbuffer); + size_t src_bpr = CGBitmapContextGetBytesPerRow (backbuffer); + char *src_data = (char *)CGBitmapContextGetData (backbuffer); + + size_t height = src_height < dest_height ? src_height : dest_height; + + if (src_bpr == dest_bpr) { + // iPad 1: 4.0 ms, iPad 2: 6.7 ms + memcpy (dest_data, src_data, src_bpr * height); + } else { + // iPad 1: 4.6 ms, iPad 2: 7.2 ms + size_t bpr = src_bpr < dest_bpr ? src_bpr : dest_bpr; + while (height) { + memcpy (dest_data, src_data, bpr); + --height; + src_data += src_bpr; + dest_data += dest_bpr; + } + } + } else { + + // iPad 1: 9.6 ms, iPad 2: 12.1 ms + +# ifdef USE_IPHONE + CGContextScaleCTM (ctx, 1, -1); + CGFloat s = [self contentScaleFactor]; + CGFloat hs = [self hackedContentScaleFactor]; + CGContextTranslateCTM (ctx, 0, -backbuffer_size.height * hs / s); +# endif // USE_IPHONE + + CGImageRef img = CGBitmapContextCreateImage (backbuffer); + CGContextDrawImage (ctx, self.layer.bounds, img); + CGImageRelease (img); + } } +# endif // USE_CALAYER -#endif // !USE_BACKBUFFER +#endif // USE_BACKBUFFER @@ -1061,6 +1235,40 @@ double current_device_rotation (void) return YES; } ++ (NSString *) decompressXML: (NSData *)data +{ + if (! data) return 0; + BOOL compressed_p = !!strncmp ((const char *) data.bytes, "")); + } + + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; +} + + #ifndef USE_IPHONE - (NSWindow *) configureSheet #else @@ -1084,11 +1292,13 @@ double current_device_rotation (void) NSWindow *sheet; # endif // !USE_IPHONE + NSData *xmld = [NSData dataWithContentsOfFile:path]; + NSString *xml = [[self class] decompressXML: xmld]; sheet = [[XScreenSaverConfigSheet alloc] - initWithXMLFile:path - options:xsft->options - controller:[prefsReader userDefaultsController] - defaults:[prefsReader defaultOptions]]; + initWithXML:[xml dataUsingEncoding:NSUTF8StringEncoding] + options:xsft->options + controller:[prefsReader userDefaultsController] + defaults:[prefsReader defaultOptions]]; // #### am I expected to retain this, or not? wtf. // I thought not, but if I don't do this, we (sometimes) crash. @@ -1418,29 +1628,27 @@ double current_device_rotation (void) default: angle_to = 0; break; } - NSRect ff = [self bounds]; - switch (orientation) { case UIDeviceOrientationLandscapeRight: // from landscape case UIDeviceOrientationLandscapeLeft: - rot_from.width = ff.size.height; - rot_from.height = ff.size.width; + rot_from.width = initial_bounds.height; + rot_from.height = initial_bounds.width; break; default: // from portrait - rot_from.width = ff.size.width; - rot_from.height = ff.size.height; + rot_from.width = initial_bounds.width; + rot_from.height = initial_bounds.height; break; } switch (new_orientation) { case UIDeviceOrientationLandscapeRight: // to landscape case UIDeviceOrientationLandscapeLeft: - rot_to.width = ff.size.height; - rot_to.height = ff.size.width; + rot_to.width = initial_bounds.height; + rot_to.height = initial_bounds.width; break; default: // to portrait - rot_to.width = ff.size.width; - rot_to.height = ff.size.height; + rot_to.width = initial_bounds.width; + rot_to.height = initial_bounds.height; break; } @@ -1486,7 +1694,9 @@ double current_device_rotation (void) - (void) rotateMouse:(int)rot x:(int*)x y:(int *)y w:(int)w h:(int)h { - CGRect frame = [self bounds]; // Correct aspect ratio and scale. + // This is a no-op unless contentScaleFactor != hackedContentScaleFactor. + // Currently, this is the iPad Retina only. + CGRect frame = [self bounds]; // Scale. double s = [self hackedContentScaleFactor]; *x *= (backbuffer_size.width / frame.size.width) / s; *y *= (backbuffer_size.height / frame.size.height) / s;