1/* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#if ENABLE(VIDEO) 27 28#import "WebVideoFullscreenController.h" 29 30#import "WebTypesInternal.h" 31#import "WebVideoFullscreenHUDWindowController.h" 32#import "WebWindowAnimation.h" 33#import <IOKit/pwr_mgt/IOPMLib.h> 34#import <OSServices/Power.h> 35#import <QTKit/QTKit.h> 36#import <WebCore/HTMLMediaElement.h> 37#import <WebCore/SoftLinking.h> 38#import <objc/objc-runtime.h> 39#import <wtf/UnusedParam.h> 40 41#if USE(GSTREAMER) 42#import <WebCore/GStreamerGWorld.h> 43#endif 44 45SOFT_LINK_FRAMEWORK(QTKit) 46SOFT_LINK_CLASS(QTKit, QTMovieLayer) 47 48SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *) 49 50#define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification() 51static const NSTimeInterval tickleTimerInterval = 1.0; 52 53@interface WebVideoFullscreenWindow : NSWindow 54#if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_TIGER) 55<NSAnimationDelegate> 56#endif 57{ 58 SEL _controllerActionOnAnimationEnd; 59 WebWindowScaleAnimation *_fullscreenAnimation; // (retain) 60} 61- (void)animateFromRect:(NSRect)startRect toRect:(NSRect)endRect withSubAnimation:(NSAnimation *)subAnimation controllerAction:(SEL)controllerAction; 62@end 63 64@interface WebVideoFullscreenController(HUDWindowControllerDelegate) <WebVideoFullscreenHUDWindowControllerDelegate> 65- (void)requestExitFullscreenWithAnimation:(BOOL)animation; 66- (void)updateMenuAndDockForFullscreen; 67- (void)updatePowerAssertions; 68@end 69 70@interface NSWindow(IsOnActiveSpaceAdditionForTigerAndLeopard) 71- (BOOL)isOnActiveSpace; 72@end 73 74@implementation WebVideoFullscreenController 75- (id)init 76{ 77 // Do not defer window creation, to make sure -windowNumber is created (needed by WebWindowScaleAnimation). 78 NSWindow *window = [[WebVideoFullscreenWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; 79 self = [super initWithWindow:window]; 80 [window release]; 81 if (!self) 82 return nil; 83 [self windowDidLoad]; 84 return self; 85 86} 87- (void)dealloc 88{ 89 ASSERT(!_backgroundFullscreenWindow); 90 ASSERT(!_fadeAnimation); 91 [_tickleTimer invalidate]; 92 [_tickleTimer release]; 93 _tickleTimer = nil; 94 [[NSNotificationCenter defaultCenter] removeObserver:self]; 95 [super dealloc]; 96} 97 98- (WebVideoFullscreenWindow *)fullscreenWindow 99{ 100 return (WebVideoFullscreenWindow *)[super window]; 101} 102 103- (void)setupVideoOverlay:(QTMovieLayer*)layer 104{ 105 WebVideoFullscreenWindow *window = [self fullscreenWindow]; 106#if USE(GSTREAMER) 107 if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::GStreamerGWorldType) { 108 WebCore::GStreamerGWorld* gstGworld = _mediaElement->platformMedia().media.gstreamerGWorld; 109 if (gstGworld->enterFullscreen()) 110 [window setContentView:gstGworld->platformVideoWindow()->window()]; 111 } 112#else 113 [[window contentView] setLayer:layer]; 114 [[window contentView] setWantsLayer:YES]; 115 if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType) 116 [layer setMovie:_mediaElement->platformMedia().media.qtMovie]; 117#endif 118} 119 120- (void)windowDidLoad 121{ 122#ifdef BUILDING_ON_TIGER 123 // WebVideoFullscreenController is not supported on Tiger: 124 ASSERT_NOT_REACHED(); 125#else 126 WebVideoFullscreenWindow *window = [self fullscreenWindow]; 127 [window setHasShadow:YES]; // This is nicer with a shadow. 128 [window setLevel:NSPopUpMenuWindowLevel-1]; 129 130 QTMovieLayer *layer = [[getQTMovieLayerClass() alloc] init]; 131 [self setupVideoOverlay:layer]; 132 [layer release]; 133 134#if !USE(GSTREAMER) 135 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:NSApp]; 136 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp]; 137#endif 138#endif 139} 140 141- (WebCore::HTMLMediaElement*)mediaElement 142{ 143 return _mediaElement.get(); 144} 145 146- (void)setMediaElement:(WebCore::HTMLMediaElement*)mediaElement 147{ 148#ifdef BUILDING_ON_TIGER 149 // WebVideoFullscreenController is not supported on Tiger: 150 ASSERT_NOT_REACHED(); 151#else 152 _mediaElement = mediaElement; 153 if ([self isWindowLoaded]) { 154 QTMovieLayer *movieLayer = (QTMovieLayer *)[[[self fullscreenWindow] contentView] layer]; 155 156 ASSERT(movieLayer && [movieLayer isKindOfClass:[getQTMovieLayerClass() class]]); 157 [self setupVideoOverlay:movieLayer]; 158#if !USE(GSTREAMER) 159 ASSERT([movieLayer movie]); 160 [[NSNotificationCenter defaultCenter] addObserver:self 161 selector:@selector(rateChanged:) 162 name:QTMovieRateDidChangeNotification 163 object:[movieLayer movie]]; 164#endif 165 } 166#endif 167} 168 169- (id <WebVideoFullscreenControllerDelegate>)delegate 170{ 171 return _delegate; 172} 173 174- (void)setDelegate:(id <WebVideoFullscreenControllerDelegate>)delegate 175{ 176 _delegate = delegate; 177} 178 179- (CGFloat)clearFadeAnimation 180{ 181 [_fadeAnimation stopAnimation]; 182 CGFloat previousAlpha = [_fadeAnimation currentAlpha]; 183 [_fadeAnimation setWindow:nil]; 184 [_fadeAnimation release]; 185 _fadeAnimation = nil; 186 return previousAlpha; 187} 188 189- (void)windowDidExitFullscreen 190{ 191#if USE(GSTREAMER) 192 if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::GStreamerGWorldType) 193 _mediaElement->platformMedia().media.gstreamerGWorld->exitFullscreen(); 194#endif 195 [self clearFadeAnimation]; 196 [[self window] close]; 197 [self setWindow:nil]; 198 [self updateMenuAndDockForFullscreen]; 199 [self updatePowerAssertions]; 200 [_hudController setDelegate:nil]; 201 [_hudController release]; 202 _hudController = nil; 203 [_backgroundFullscreenWindow close]; 204 [_backgroundFullscreenWindow release]; 205 _backgroundFullscreenWindow = nil; 206 207 [self autorelease]; // Associated -retain is in -exitFullscreen. 208 _isEndingFullscreen = NO; 209} 210 211- (void)windowDidEnterFullscreen 212{ 213 [self clearFadeAnimation]; 214 215 ASSERT(!_hudController); 216 _hudController = [[WebVideoFullscreenHUDWindowController alloc] init]; 217 [_hudController setDelegate:self]; 218 219 [self updateMenuAndDockForFullscreen]; 220 [self updatePowerAssertions]; 221 [NSCursor setHiddenUntilMouseMoves:YES]; 222 223 // Give the HUD keyboard focus initially 224 [_hudController fadeWindowIn]; 225} 226 227- (NSRect)mediaElementRect 228{ 229 return _mediaElement->screenRect(); 230} 231 232- (void)applicationDidResignActive:(NSNotification*)notification 233{ 234 // Check to see if the fullscreenWindow is on the active space; this function is available 235 // on 10.6 and later, so default to YES if the function is not available: 236 NSWindow* fullscreenWindow = [self fullscreenWindow]; 237 BOOL isOnActiveSpace = ([fullscreenWindow respondsToSelector:@selector(isOnActiveSpace)] ? [fullscreenWindow isOnActiveSpace] : YES); 238 239 // Replicate the QuickTime Player (X) behavior when losing active application status: 240 // Is the fullscreen screen the main screen? (Note: this covers the case where only a 241 // single screen is available.) Is the fullscreen screen on the current space? IFF so, 242 // then exit fullscreen mode. 243 if ([fullscreenWindow screen] == [[NSScreen screens] objectAtIndex:0] && isOnActiveSpace) 244 [self requestExitFullscreenWithAnimation:NO]; 245} 246 247 248// MARK: - 249// MARK: Exposed Interface 250 251static void constrainFrameToRatioOfFrame(NSRect *frameToConstrain, const NSRect *frame) 252{ 253 // Keep a constrained aspect ratio for the destination window 254 double originalRatio = frame->size.width / frame->size.height; 255 double newRatio = frameToConstrain->size.width / frameToConstrain->size.height; 256 if (newRatio > originalRatio) { 257 double newWidth = originalRatio * frameToConstrain->size.height; 258 double diff = frameToConstrain->size.width - newWidth; 259 frameToConstrain->size.width = newWidth; 260 frameToConstrain->origin.x += diff / 2; 261 } else { 262 double newHeight = frameToConstrain->size.width / originalRatio; 263 double diff = frameToConstrain->size.height - newHeight; 264 frameToConstrain->size.height = newHeight; 265 frameToConstrain->origin.y += diff / 2; 266 } 267} 268 269static NSWindow *createBackgroundFullscreenWindow(NSRect frame, int level) 270{ 271 NSWindow *window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; 272 [window setOpaque:YES]; 273 [window setBackgroundColor:[NSColor blackColor]]; 274 [window setLevel:level]; 275 [window setReleasedWhenClosed:NO]; 276 return window; 277} 278 279- (void)setupFadeAnimationIfNeededAndFadeIn:(BOOL)fadeIn 280{ 281 CGFloat initialAlpha = fadeIn ? 0 : 1; 282 if (_fadeAnimation) { 283 // Make sure we support queuing animation if the previous one isn't over yet 284 initialAlpha = [self clearFadeAnimation]; 285 } 286 if (!_forceDisableAnimation) 287 _fadeAnimation = [[WebWindowFadeAnimation alloc] initWithDuration:0.2 window:_backgroundFullscreenWindow initialAlpha:initialAlpha finalAlpha:fadeIn ? 1 : 0]; 288} 289 290- (void)enterFullscreen:(NSScreen *)screen 291{ 292 if (!screen) 293 screen = [NSScreen mainScreen]; 294 295 NSRect frame = [self mediaElementRect]; 296 NSRect endFrame = [screen frame]; 297 constrainFrameToRatioOfFrame(&endFrame, &frame); 298 299 // Create a black window if needed 300 if (!_backgroundFullscreenWindow) 301 _backgroundFullscreenWindow = createBackgroundFullscreenWindow([screen frame], [[self window] level]-1); 302 else 303 [_backgroundFullscreenWindow setFrame:[screen frame] display:NO]; 304 305 [self setupFadeAnimationIfNeededAndFadeIn:YES]; 306 if (_forceDisableAnimation) { 307 // This will disable scale animation 308 frame = NSZeroRect; 309 } 310 [[self fullscreenWindow] animateFromRect:frame toRect:endFrame withSubAnimation:_fadeAnimation controllerAction:@selector(windowDidEnterFullscreen)]; 311 312 [_backgroundFullscreenWindow orderWindow:NSWindowBelow relativeTo:[[self fullscreenWindow] windowNumber]]; 313} 314 315- (void)exitFullscreen 316{ 317 if (_isEndingFullscreen) 318 return; 319 _isEndingFullscreen = YES; 320 [_hudController closeWindow]; 321 322 NSRect endFrame = [self mediaElementRect]; 323 324 [self setupFadeAnimationIfNeededAndFadeIn:NO]; 325 if (_forceDisableAnimation) { 326 // This will disable scale animation 327 endFrame = NSZeroRect; 328 } 329 330 // We have to retain ourselves because we want to be alive for the end of the animation. 331 // If our owner releases us we could crash if this is not the case. 332 // Balanced in windowDidExitFullscreen 333 [self retain]; 334 335 [[self fullscreenWindow] animateFromRect:[[self window] frame] toRect:endFrame withSubAnimation:_fadeAnimation controllerAction:@selector(windowDidExitFullscreen)]; 336} 337 338- (void)applicationDidChangeScreenParameters:(NSNotification*)notification 339{ 340 // The user may have changed the main screen by moving the menu bar, or they may have changed 341 // the Dock's size or location, or they may have changed the fullscreen screen's dimensions. 342 // Update our presentation parameters, and ensure that the full screen window occupies the 343 // entire screen: 344 [self updateMenuAndDockForFullscreen]; 345 [[self window] setFrame:[[[self window] screen] frame] display:YES]; 346} 347 348- (void)updateMenuAndDockForFullscreen 349{ 350 // NSApplicationPresentationOptions is available on > 10.6 only: 351#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 352 NSApplicationPresentationOptions options = NSApplicationPresentationDefault; 353 NSScreen* fullscreenScreen = [[self window] screen]; 354 355 if (!_isEndingFullscreen) { 356 // Auto-hide the menu bar if the fullscreenScreen contains the menu bar: 357 // NOTE: if the fullscreenScreen contains the menu bar but not the dock, we must still 358 // auto-hide the dock, or an exception will be thrown. 359 if ([[NSScreen screens] objectAtIndex:0] == fullscreenScreen) 360 options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock); 361 // Check if the current screen contains the dock by comparing the screen's frame to its 362 // visibleFrame; if a dock is present, the visibleFrame will differ. If the current screen 363 // contains the dock, hide it. 364 else if (!NSEqualRects([fullscreenScreen frame], [fullscreenScreen visibleFrame])) 365 options |= NSApplicationPresentationAutoHideDock; 366 } 367 368 if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) 369 [NSApp setPresentationOptions:options]; 370 else 371#endif 372 SetSystemUIMode(_isEndingFullscreen ? kUIModeNormal : kUIModeAllHidden, 0); 373} 374 375#if !defined(BUILDING_ON_TIGER) // IOPMAssertionCreateWithName not defined on < 10.5 376- (void)_disableIdleDisplaySleep 377{ 378 if (_idleDisplaySleepAssertion == kIOPMNullAssertionID) 379#if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK 380 IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &_idleDisplaySleepAssertion); 381#else // IOPMAssertionCreate is depreciated in > 10.5 382 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleDisplaySleepAssertion); 383#endif 384} 385 386- (void)_enableIdleDisplaySleep 387{ 388 if (_idleDisplaySleepAssertion != kIOPMNullAssertionID) { 389 IOPMAssertionRelease(_idleDisplaySleepAssertion); 390 _idleDisplaySleepAssertion = kIOPMNullAssertionID; 391 } 392} 393 394- (void)_disableIdleSystemSleep 395{ 396 if (_idleSystemSleepAssertion == kIOPMNullAssertionID) 397#if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK 398 IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_idleSystemSleepAssertion); 399#else // IOPMAssertionCreate is depreciated in > 10.5 400 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleSystemSleepAssertion); 401#endif 402} 403 404- (void)_enableIdleSystemSleep 405{ 406 if (_idleSystemSleepAssertion != kIOPMNullAssertionID) { 407 IOPMAssertionRelease(_idleSystemSleepAssertion); 408 _idleSystemSleepAssertion = kIOPMNullAssertionID; 409 } 410} 411 412- (void)_enableTickleTimer 413{ 414 [_tickleTimer invalidate]; 415 [_tickleTimer release]; 416 _tickleTimer = [[NSTimer scheduledTimerWithTimeInterval:tickleTimerInterval target:self selector:@selector(_tickleTimerFired) userInfo:nil repeats:YES] retain]; 417} 418 419- (void)_disableTickleTimer 420{ 421 [_tickleTimer invalidate]; 422 [_tickleTimer release]; 423 _tickleTimer = nil; 424} 425 426- (void)_tickleTimerFired 427{ 428 UpdateSystemActivity(OverallAct); 429} 430#endif 431 432- (void)updatePowerAssertions 433{ 434#if !defined(BUILDING_ON_TIGER) 435 float rate = 0; 436 if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType) 437 rate = [_mediaElement->platformMedia().media.qtMovie rate]; 438 439 if (rate && !_isEndingFullscreen) { 440 [self _disableIdleSystemSleep]; 441 [self _disableIdleDisplaySleep]; 442 [self _enableTickleTimer]; 443 } else { 444 [self _enableIdleSystemSleep]; 445 [self _enableIdleDisplaySleep]; 446 [self _disableTickleTimer]; 447 } 448#endif 449} 450 451// MARK: - 452// MARK: Window callback 453 454- (void)_requestExit 455{ 456 if (_mediaElement) 457 _mediaElement->exitFullscreen(); 458 _forceDisableAnimation = NO; 459} 460 461- (void)requestExitFullscreenWithAnimation:(BOOL)animation 462{ 463 if (_isEndingFullscreen) 464 return; 465 466 _forceDisableAnimation = !animation; 467 [self performSelector:@selector(_requestExit) withObject:nil afterDelay:0]; 468 469} 470 471- (void)requestExitFullscreen 472{ 473 [self requestExitFullscreenWithAnimation:YES]; 474} 475 476- (void)fadeHUDIn 477{ 478 [_hudController fadeWindowIn]; 479} 480 481// MARK: - 482// MARK: QTMovie callbacks 483 484- (void)rateChanged:(NSNotification *)unusedNotification 485{ 486 UNUSED_PARAM(unusedNotification); 487 [_hudController updateRate]; 488 [self updatePowerAssertions]; 489} 490 491@end 492 493@implementation WebVideoFullscreenWindow 494 495- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag 496{ 497 UNUSED_PARAM(aStyle); 498 self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag]; 499 if (!self) 500 return nil; 501 [self setOpaque:NO]; 502 [self setBackgroundColor:[NSColor clearColor]]; 503 [self setIgnoresMouseEvents:NO]; 504 [self setAcceptsMouseMovedEvents:YES]; 505 return self; 506} 507 508- (void)dealloc 509{ 510 ASSERT(!_fullscreenAnimation); 511 [super dealloc]; 512} 513 514- (BOOL)resignFirstResponder 515{ 516 return NO; 517} 518 519- (BOOL)canBecomeKeyWindow 520{ 521 return NO; 522} 523 524- (void)mouseDown:(NSEvent *)theEvent 525{ 526 UNUSED_PARAM(theEvent); 527} 528 529- (void)cancelOperation:(id)sender 530{ 531 UNUSED_PARAM(sender); 532 [[self windowController] requestExitFullscreen]; 533} 534 535- (void)animatedResizeDidEnd 536{ 537 // Call our windowController. 538 if (_controllerActionOnAnimationEnd) 539 [[self windowController] performSelector:_controllerActionOnAnimationEnd]; 540 _controllerActionOnAnimationEnd = NULL; 541} 542 543// 544// This function will animate a change of frame rectangle 545// We support queuing animation, that means that we'll correctly 546// interrupt the running animation, and queue the next one. 547// 548- (void)animateFromRect:(NSRect)startRect toRect:(NSRect)endRect withSubAnimation:(NSAnimation *)subAnimation controllerAction:(SEL)controllerAction 549{ 550 _controllerActionOnAnimationEnd = controllerAction; 551 552 BOOL wasAnimating = NO; 553 if (_fullscreenAnimation) { 554 wasAnimating = YES; 555 556 // Interrupt any running animation. 557 [_fullscreenAnimation stopAnimation]; 558 559 // Save the current rect to ensure a smooth transition. 560 startRect = [_fullscreenAnimation currentFrame]; 561 [_fullscreenAnimation release]; 562 _fullscreenAnimation = nil; 563 } 564 565 if (NSIsEmptyRect(startRect) || NSIsEmptyRect(endRect)) { 566 // Fakely end the subanimation. 567 [subAnimation setCurrentProgress:1.0]; 568 // And remove the weak link to the window. 569 [subAnimation stopAnimation]; 570 571 [self setFrame:endRect display:NO]; 572 [self makeKeyAndOrderFront:self]; 573 [self animatedResizeDidEnd]; 574 return; 575 } 576 577 if (!wasAnimating) { 578 // We'll downscale the window during the animation based on the higher resolution rect 579 BOOL higherResolutionIsEndRect = startRect.size.width < endRect.size.width && startRect.size.height < endRect.size.height; 580 [self setFrame:higherResolutionIsEndRect ? endRect : startRect display:NO]; 581 } 582 583 ASSERT(!_fullscreenAnimation); 584 _fullscreenAnimation = [[WebWindowScaleAnimation alloc] initWithHintedDuration:0.2 window:self initalFrame:startRect finalFrame:endRect]; 585 [_fullscreenAnimation setSubAnimation:subAnimation]; 586 [_fullscreenAnimation setDelegate:self]; 587 588 // Make sure the animation has scaled the window before showing it. 589 [_fullscreenAnimation setCurrentProgress:0]; 590 [self makeKeyAndOrderFront:self]; 591 592 [_fullscreenAnimation startAnimation]; 593} 594 595- (void)animationDidEnd:(NSAnimation *)animation 596{ 597#if !defined(BUILDING_ON_TIGER) // Animations are never threaded on Tiger. 598 if (![NSThread isMainThread]) { 599 [self performSelectorOnMainThread:@selector(animationDidEnd:) withObject:animation waitUntilDone:NO]; 600 return; 601 } 602#endif 603 if (animation != _fullscreenAnimation) 604 return; 605 606 // The animation is not really over and was interrupted 607 // Don't send completion events. 608 if ([animation currentProgress] < 1.0) 609 return; 610 611 // Ensure that animation (and subanimation) don't keep 612 // the weak reference to the window ivar that may be destroyed from 613 // now on. 614 [_fullscreenAnimation setWindow:nil]; 615 616 [_fullscreenAnimation autorelease]; 617 _fullscreenAnimation = nil; 618 619 [self animatedResizeDidEnd]; 620} 621 622- (void)mouseMoved:(NSEvent *)theEvent 623{ 624 [[self windowController] fadeHUDIn]; 625} 626 627@end 628 629#endif /* ENABLE(VIDEO) */ 630