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