| /* |
| * Copyright (C) 2009, 2010, 2011 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "config.h" |
| |
| #if ENABLE(FULLSCREEN_API) |
| |
| #import "WKFullScreenWindowController.h" |
| |
| #import "LayerTreeContext.h" |
| #import "WKAPICast.h" |
| #import "WKViewInternal.h" |
| #import "WebFullScreenManagerProxy.h" |
| #import "WebPageProxy.h" |
| #import <Carbon/Carbon.h> // For SetSystemUIMode() |
| #import <IOKit/pwr_mgt/IOPMLib.h> // For IOPMAssertionCreate() |
| #import <QuartzCore/QuartzCore.h> |
| #import <WebCore/FloatRect.h> |
| #import <WebCore/IntRect.h> |
| #import <WebKitSystemInterface.h> |
| |
| static const NSTimeInterval tickleTimerInterval = 1.0; |
| |
| using namespace WebKit; |
| using namespace WebCore; |
| |
| #if defined(BUILDING_ON_LEOPARD) |
| @interface CATransaction(SnowLeopardConvenienceFunctions) |
| + (void)setDisableActions:(BOOL)flag; |
| + (void)setAnimationDuration:(CFTimeInterval)dur; |
| @end |
| |
| @implementation CATransaction(SnowLeopardConvenienceFunctions) |
| + (void)setDisableActions:(BOOL)flag |
| { |
| [self setValue:[NSNumber numberWithBool:flag] forKey:kCATransactionDisableActions]; |
| } |
| |
| + (void)setAnimationDuration:(CFTimeInterval)dur |
| { |
| [self setValue:[NSNumber numberWithDouble:dur] forKey:kCATransactionAnimationDuration]; |
| } |
| @end |
| |
| #endif |
| |
| @interface WKFullScreenWindow : NSWindow |
| { |
| NSView* _animationView; |
| CALayer* _backgroundLayer; |
| } |
| - (CALayer*)backgroundLayer; |
| - (NSView*)animationView; |
| @end |
| |
| @interface WKFullScreenWindowController(Private) |
| - (void)_requestExitFullScreenWithAnimation:(BOOL)animation; |
| - (void)_updateMenuAndDockForFullScreen; |
| - (void)_updatePowerAssertions; |
| - (WKFullScreenWindow *)_fullScreenWindow; |
| - (CFTimeInterval)_animationDuration; |
| - (void)_swapView:(NSView*)view with:(NSView*)otherView; |
| - (WebFullScreenManagerProxy*)_manager; |
| @end |
| |
| @interface NSWindow(IsOnActiveSpaceAdditionForTigerAndLeopard) |
| - (BOOL)isOnActiveSpace; |
| @end |
| |
| @implementation WKFullScreenWindowController |
| |
| #pragma mark - |
| #pragma mark Initialization |
| - (id)init |
| { |
| NSWindow *window = [[WKFullScreenWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; |
| self = [super initWithWindow:window]; |
| [window release]; |
| if (!self) |
| return nil; |
| [self windowDidLoad]; |
| |
| return self; |
| } |
| |
| - (void)dealloc |
| { |
| [self setWebView:nil]; |
| |
| [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| |
| [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| [super dealloc]; |
| } |
| |
| - (void)windowDidLoad |
| { |
| [super windowDidLoad]; |
| |
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:NSApp]; |
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp]; |
| } |
| |
| #pragma mark - |
| #pragma mark Accessors |
| |
| - (WKView*)webView |
| { |
| return _webView; |
| } |
| |
| - (void)setWebView:(WKView *)webView |
| { |
| [webView retain]; |
| [_webView release]; |
| _webView = webView; |
| } |
| |
| #pragma mark - |
| #pragma mark Notifications |
| |
| - (void)applicationDidResignActive:(NSNotification*)notification |
| { |
| // Check to see if the fullScreenWindow is on the active space; this function is available |
| // on 10.6 and later, so default to YES if the function is not available: |
| NSWindow* fullScreenWindow = [self _fullScreenWindow]; |
| BOOL isOnActiveSpace = ([fullScreenWindow respondsToSelector:@selector(isOnActiveSpace)] ? [fullScreenWindow isOnActiveSpace] : YES); |
| |
| // Replicate the QuickTime Player (X) behavior when losing active application status: |
| // Is the fullScreen screen the main screen? (Note: this covers the case where only a |
| // single screen is available.) Is the fullScreen screen on the current space? IFF so, |
| // then exit fullScreen mode. |
| if ([fullScreenWindow screen] == [[NSScreen screens] objectAtIndex:0] && isOnActiveSpace) |
| [self _requestExitFullScreenWithAnimation:NO]; |
| } |
| |
| - (void)applicationDidChangeScreenParameters:(NSNotification*)notification |
| { |
| // The user may have changed the main screen by moving the menu bar, or they may have changed |
| // the Dock's size or location, or they may have changed the fullScreen screen's dimensions. |
| // Update our presentation parameters, and ensure that the full screen window occupies the |
| // entire screen: |
| [self _updateMenuAndDockForFullScreen]; |
| NSWindow* window = [self window]; |
| [window setFrame:[[window screen] frame] display:YES]; |
| } |
| |
| #pragma mark - |
| #pragma mark Exposed Interface |
| |
| - (void)enterFullScreen:(NSScreen *)screen |
| { |
| if (_isFullScreen) |
| return; |
| |
| _isFullScreen = YES; |
| _isAnimating = YES; |
| |
| NSDisableScreenUpdates(); |
| |
| if (!screen) |
| screen = [NSScreen mainScreen]; |
| NSRect screenFrame = [screen frame]; |
| |
| NSRect webViewFrame = [_webView convertRectToBase:[_webView frame]]; |
| webViewFrame.origin = [[_webView window] convertBaseToScreen:webViewFrame.origin]; |
| |
| // In the case of a multi-monitor setup where the webView straddles two |
| // monitors, we must create a window large enough to contain the destination |
| // frame and the initial frame. |
| NSRect windowFrame = NSUnionRect(screenFrame, webViewFrame); |
| [[self window] setFrame:windowFrame display:YES]; |
| |
| CALayer* backgroundLayer = [[self _fullScreenWindow] backgroundLayer]; |
| NSRect backgroundFrame = {[[self window] convertScreenToBase:screenFrame.origin], screenFrame.size}; |
| backgroundFrame = [[[self window] contentView] convertRectFromBase:backgroundFrame]; |
| |
| [CATransaction begin]; |
| [CATransaction setDisableActions:YES]; |
| [backgroundLayer setFrame:NSRectToCGRect(backgroundFrame)]; |
| [CATransaction commit]; |
| |
| CFTimeInterval duration = [self _animationDuration]; |
| [self _manager]->willEnterFullScreen(); |
| [self _manager]->beginEnterFullScreenAnimation(duration); |
| } |
| |
| - (void)beganEnterFullScreenAnimation |
| { |
| [self _updateMenuAndDockForFullScreen]; |
| [self _updatePowerAssertions]; |
| |
| // In a previous incarnation, the NSWindow attached to this controller may have |
| // been on a different screen. Temporarily change the collectionBehavior of the window: |
| NSWindow* fullScreenWindow = [self window]; |
| NSWindowCollectionBehavior behavior = [fullScreenWindow collectionBehavior]; |
| [fullScreenWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]; |
| [fullScreenWindow makeKeyAndOrderFront:self]; |
| [fullScreenWindow setCollectionBehavior:behavior]; |
| |
| // Start the opacity animation. We can use implicit animations here because we don't care when |
| // the animation finishes. |
| [CATransaction begin]; |
| [CATransaction setAnimationDuration:[self _animationDuration]]; |
| [[[self _fullScreenWindow] backgroundLayer] setOpacity:1]; |
| [CATransaction commit]; |
| |
| NSEnableScreenUpdates(); |
| _isAnimating = YES; |
| } |
| |
| - (void)finishedEnterFullScreenAnimation:(bool)completed |
| { |
| NSDisableScreenUpdates(); |
| |
| if (completed) { |
| // Swap the webView placeholder into place. |
| if (!_webViewPlaceholder) |
| _webViewPlaceholder.adoptNS([[NSView alloc] init]); |
| [self _swapView:_webView with:_webViewPlaceholder.get()]; |
| |
| // Then insert the WebView into the full screen window |
| NSView* animationView = [[self _fullScreenWindow] animationView]; |
| [animationView addSubview:_webView positioned:NSWindowBelow relativeTo:_layerHostingView.get()]; |
| [_webView setFrame:[animationView bounds]]; |
| |
| // FIXME: In Barolo, orderIn will animate, which is not what we want. Find a way |
| // to work around this behavior. |
| //[[_webViewPlaceholder.get() window] orderOut:self]; |
| [[self window] makeKeyAndOrderFront:self]; |
| } |
| |
| [self _manager]->didEnterFullScreen(); |
| NSEnableScreenUpdates(); |
| |
| _isAnimating = NO; |
| } |
| |
| - (void)exitFullScreen |
| { |
| if (!_isFullScreen) |
| return; |
| |
| _isFullScreen = NO; |
| _isAnimating = YES; |
| |
| NSDisableScreenUpdates(); |
| |
| [self _manager]->willExitFullScreen(); |
| [self _manager]->beginExitFullScreenAnimation([self _animationDuration]); |
| } |
| |
| - (void)beganExitFullScreenAnimation |
| { |
| [self _updateMenuAndDockForFullScreen]; |
| [self _updatePowerAssertions]; |
| |
| // The user may have moved the fullScreen window in Spaces, so temporarily change |
| // the collectionBehavior of the webView's window: |
| NSWindow* webWindow = [[self webView] window]; |
| NSWindowCollectionBehavior behavior = [webWindow collectionBehavior]; |
| [webWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]; |
| [webWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]]; |
| [webWindow setCollectionBehavior:behavior]; |
| |
| // Swap the webView back into its original position: |
| if ([_webView window] == [self window]) |
| [self _swapView:_webViewPlaceholder.get() with:_webView]; |
| |
| [CATransaction begin]; |
| [CATransaction setAnimationDuration:[self _animationDuration]]; |
| [[[self _fullScreenWindow] backgroundLayer] setOpacity:0]; |
| [CATransaction commit]; |
| |
| NSEnableScreenUpdates(); |
| _isAnimating = YES; |
| } |
| |
| - (void)finishedExitFullScreenAnimation:(bool)completed |
| { |
| NSDisableScreenUpdates(); |
| |
| if (completed) { |
| [self _updateMenuAndDockForFullScreen]; |
| [self _updatePowerAssertions]; |
| [NSCursor setHiddenUntilMouseMoves:YES]; |
| |
| [[self window] orderOut:self]; |
| [[_webView window] makeKeyAndOrderFront:self]; |
| } |
| |
| [self _manager]->didExitFullScreen(); |
| NSEnableScreenUpdates(); |
| |
| _isAnimating = NO; |
| } |
| |
| - (void)enterAcceleratedCompositingMode:(const WebKit::LayerTreeContext&)layerTreeContext |
| { |
| if (_layerHostingView) |
| return; |
| |
| ASSERT(!layerTreeContext.isEmpty()); |
| |
| // Create an NSView that will host our layer tree. |
| _layerHostingView.adoptNS([[NSView alloc] initWithFrame:[[self window] frame]]); |
| [_layerHostingView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; |
| |
| [CATransaction begin]; |
| [CATransaction setDisableActions:YES]; |
| WKFullScreenWindow* window = [self _fullScreenWindow]; |
| [[window animationView] addSubview:_layerHostingView.get()]; |
| |
| // Create a root layer that will back the NSView. |
| RetainPtr<CALayer> rootLayer(AdoptNS, [[CALayer alloc] init]); |
| #ifndef NDEBUG |
| [rootLayer.get() setName:@"Hosting root layer"]; |
| #endif |
| |
| CALayer *renderLayer = WKMakeRenderLayer(layerTreeContext.contextID); |
| [rootLayer.get() addSublayer:renderLayer]; |
| |
| [_layerHostingView.get() setLayer:rootLayer.get()]; |
| [_layerHostingView.get() setWantsLayer:YES]; |
| [[window backgroundLayer] setHidden:NO]; |
| [CATransaction commit]; |
| } |
| |
| - (void)exitAcceleratedCompositingMode |
| { |
| if (!_layerHostingView) |
| return; |
| |
| [CATransaction begin]; |
| [CATransaction setDisableActions:YES]; |
| [_layerHostingView.get() removeFromSuperview]; |
| [_layerHostingView.get() setLayer:nil]; |
| [_layerHostingView.get() setWantsLayer:NO]; |
| [[[self _fullScreenWindow] backgroundLayer] setHidden:YES]; |
| [CATransaction commit]; |
| |
| _layerHostingView = 0; |
| } |
| |
| - (WebCore::IntRect)getFullScreenRect |
| { |
| return enclosingIntRect([[self window] frame]); |
| } |
| |
| #pragma mark - |
| #pragma mark Internal Interface |
| |
| - (void)_updateMenuAndDockForFullScreen |
| { |
| // NSApplicationPresentationOptions is available on > 10.6 only: |
| #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
| NSApplicationPresentationOptions options = NSApplicationPresentationDefault; |
| NSScreen* fullScreenScreen = [[self window] screen]; |
| |
| if (_isFullScreen) { |
| // Auto-hide the menu bar if the fullScreenScreen contains the menu bar: |
| // NOTE: if the fullScreenScreen contains the menu bar but not the dock, we must still |
| // auto-hide the dock, or an exception will be thrown. |
| if ([[NSScreen screens] objectAtIndex:0] == fullScreenScreen) |
| options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock); |
| // Check if the current screen contains the dock by comparing the screen's frame to its |
| // visibleFrame; if a dock is present, the visibleFrame will differ. If the current screen |
| // contains the dock, hide it. |
| else if (!NSEqualRects([fullScreenScreen frame], [fullScreenScreen visibleFrame])) |
| options |= NSApplicationPresentationAutoHideDock; |
| } |
| |
| if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) |
| [NSApp setPresentationOptions:options]; |
| else |
| #endif |
| SetSystemUIMode(_isFullScreen ? kUIModeNormal : kUIModeAllHidden, 0); |
| } |
| |
| #if !defined(BUILDING_ON_TIGER) // IOPMAssertionCreateWithName not defined on < 10.5 |
| - (void)_disableIdleDisplaySleep |
| { |
| if (_idleDisplaySleepAssertion == kIOPMNullAssertionID) |
| #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK |
| IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &_idleDisplaySleepAssertion); |
| #else // IOPMAssertionCreate is depreciated in > 10.5 |
| IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullScreen."), &_idleDisplaySleepAssertion); |
| #endif |
| } |
| |
| - (void)_enableIdleDisplaySleep |
| { |
| if (_idleDisplaySleepAssertion != kIOPMNullAssertionID) { |
| IOPMAssertionRelease(_idleDisplaySleepAssertion); |
| _idleDisplaySleepAssertion = kIOPMNullAssertionID; |
| } |
| } |
| |
| - (void)_disableIdleSystemSleep |
| { |
| if (_idleSystemSleepAssertion == kIOPMNullAssertionID) |
| #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK |
| IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_idleSystemSleepAssertion); |
| #else // IOPMAssertionCreate is depreciated in > 10.5 |
| IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullScreen."), &_idleSystemSleepAssertion); |
| #endif |
| } |
| |
| - (void)_enableIdleSystemSleep |
| { |
| if (_idleSystemSleepAssertion != kIOPMNullAssertionID) { |
| IOPMAssertionRelease(_idleSystemSleepAssertion); |
| _idleSystemSleepAssertion = kIOPMNullAssertionID; |
| } |
| } |
| |
| - (void)_enableTickleTimer |
| { |
| [_tickleTimer invalidate]; |
| [_tickleTimer release]; |
| _tickleTimer = [[NSTimer scheduledTimerWithTimeInterval:tickleTimerInterval target:self selector:@selector(_tickleTimerFired) userInfo:nil repeats:YES] retain]; |
| } |
| |
| - (void)_disableTickleTimer |
| { |
| [_tickleTimer invalidate]; |
| [_tickleTimer release]; |
| _tickleTimer = nil; |
| } |
| |
| - (void)_tickleTimerFired |
| { |
| UpdateSystemActivity(OverallAct); |
| } |
| #endif |
| |
| - (void)_updatePowerAssertions |
| { |
| #if !defined(BUILDING_ON_TIGER) |
| if (_isPlaying && _isFullScreen) { |
| [self _disableIdleSystemSleep]; |
| [self _disableIdleDisplaySleep]; |
| [self _enableTickleTimer]; |
| } else { |
| [self _enableIdleSystemSleep]; |
| [self _enableIdleDisplaySleep]; |
| [self _disableTickleTimer]; |
| } |
| #endif |
| } |
| |
| - (WebFullScreenManagerProxy*)_manager |
| { |
| WebPageProxy* webPage = toImpl([_webView pageRef]); |
| if (!webPage) |
| return 0; |
| return webPage->fullScreenManager(); |
| } |
| |
| - (void)_requestExit |
| { |
| [self exitFullScreen]; |
| _forceDisableAnimation = NO; |
| } |
| |
| - (void)_requestExitFullScreenWithAnimation:(BOOL)animation |
| { |
| _forceDisableAnimation = !animation; |
| [self performSelector:@selector(_requestExit) withObject:nil afterDelay:0]; |
| |
| } |
| |
| - (void)_swapView:(NSView*)view with:(NSView*)otherView |
| { |
| [otherView setFrame:[view frame]]; |
| [otherView setAutoresizingMask:[view autoresizingMask]]; |
| [otherView removeFromSuperview]; |
| [[view superview] replaceSubview:view with:otherView]; |
| } |
| |
| #pragma mark - |
| #pragma mark Utility Functions |
| |
| - (WKFullScreenWindow *)_fullScreenWindow |
| { |
| ASSERT([[self window] isKindOfClass:[WKFullScreenWindow class]]); |
| return (WKFullScreenWindow *)[self window]; |
| } |
| |
| - (CFTimeInterval)_animationDuration |
| { |
| static const CFTimeInterval defaultDuration = 0.5; |
| CFTimeInterval duration = defaultDuration; |
| #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
| NSUInteger modifierFlags = [NSEvent modifierFlags]; |
| #else |
| NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags]; |
| #endif |
| if ((modifierFlags & NSControlKeyMask) == NSControlKeyMask) |
| duration *= 2; |
| if ((modifierFlags & NSShiftKeyMask) == NSShiftKeyMask) |
| duration *= 10; |
| if (_forceDisableAnimation) { |
| // This will disable scale animation |
| duration = 0; |
| } |
| return duration; |
| } |
| |
| @end |
| |
| #pragma mark - |
| @implementation WKFullScreenWindow |
| |
| - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag |
| { |
| UNUSED_PARAM(aStyle); |
| self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag]; |
| if (!self) |
| return nil; |
| [self setOpaque:NO]; |
| [self setBackgroundColor:[NSColor clearColor]]; |
| [self setIgnoresMouseEvents:NO]; |
| [self setAcceptsMouseMovedEvents:YES]; |
| [self setReleasedWhenClosed:NO]; |
| [self setHasShadow:YES]; |
| #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
| [self setMovable:NO]; |
| #else |
| [self setMovableByWindowBackground:NO]; |
| #endif |
| |
| NSView* contentView = [self contentView]; |
| _animationView = [[NSView alloc] initWithFrame:[contentView bounds]]; |
| |
| CALayer* contentLayer = [[CALayer alloc] init]; |
| [_animationView setLayer:contentLayer]; |
| [_animationView setWantsLayer:YES]; |
| [_animationView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; |
| [contentView addSubview:_animationView]; |
| |
| _backgroundLayer = [[CALayer alloc] init]; |
| [contentLayer addSublayer:_backgroundLayer]; |
| |
| [_backgroundLayer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)]; |
| [_backgroundLayer setOpacity:0]; |
| return self; |
| } |
| |
| - (void)dealloc |
| { |
| [_animationView release]; |
| [_backgroundLayer release]; |
| [super dealloc]; |
| } |
| |
| - (BOOL)canBecomeKeyWindow |
| { |
| return YES; |
| } |
| |
| - (void)keyDown:(NSEvent *)theEvent |
| { |
| if ([[theEvent charactersIgnoringModifiers] isEqual:@"\e"]) // Esacpe key-code |
| [self cancelOperation:self]; |
| else [super keyDown:theEvent]; |
| } |
| |
| - (void)cancelOperation:(id)sender |
| { |
| UNUSED_PARAM(sender); |
| [[self windowController] _requestExitFullScreenWithAnimation:YES]; |
| } |
| |
| - (CALayer*)backgroundLayer |
| { |
| return _backgroundLayer; |
| } |
| |
| - (NSView*)animationView |
| { |
| return _animationView; |
| } |
| @end |
| |
| #endif |