/*****************************************************************************
* Windows.m: MacOS X interface module
*****************************************************************************
- * Copyright (C) 2012 VLC authors and VideoLAN
+ * Copyright (C) 2012-2013 VLC authors and VideoLAN
* $Id$
*
* Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
*****************************************************************************/
@implementation VLCWindow
+
+@synthesize hasActiveVideo=b_has_active_video;
+@synthesize fullscreen=b_fullscreen;
+
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
backing:(NSBackingStoreType)backingType defer:(BOOL)flag
{
return;
}
- invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
+ // TODO this callback stuff does not work and is not needed
+ invoc = [[[NSInvocation alloc] init] autorelease];
+ [invoc setSelector:@selector(close)];
[invoc setTarget: self];
if (![self isVisible] || [self alphaValue] == 0.0) {
- (void)orderOut: (id)sender animate: (BOOL)animate
{
- NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
+ NSInvocation *invoc = [[[NSInvocation alloc] init] autorelease];
+ [invoc setSelector:@selector(orderOut:)];
[invoc setTarget: self];
- [invoc setArgument: sender atIndex: 0];
+ [invoc setArgument: sender atIndex: 2];
[self orderOut: sender animate: animate callback: invoc];
}
[dict setObject:self forKey:NSViewAnimationTargetKey];
[dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
- anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
+ anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
[dict release];
[anim setAnimationBlockingMode:NSAnimationNonblocking];
[anim setDuration:0.9];
[anim setFrameRate:30];
- [anim setUserInfo: callback];
+ [anim setUserInfo:callback];
+ [anim setDelegate:self];
@synchronized(self) {
current_anim = self->o_current_animation;
[dict setObject:self forKey:NSViewAnimationTargetKey];
[dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
- anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
+ anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
[dict release];
[anim setAnimationBlockingMode:NSAnimationNonblocking];
[anim setDuration:0.5];
[anim setFrameRate:30];
+ [anim setDelegate:self];
@synchronized(self) {
current_anim = self->o_current_animation;
NSInvocation * invoc;
[super orderOut: nil];
[self setAlphaValue: 1.0];
- if ((invoc = [anim userInfo]))
+ if ((invoc = [anim userInfo])) {
[invoc invoke];
+ }
}
}
* Common code for main window, detached window and extra video window
*****************************************************************************/
+@interface VLCVideoWindowCommon (Internal)
+- (void)customZoom:(id)sender;
+- (void)hasBecomeFullscreen;
+- (void)leaveFullscreenAndFadeOut:(BOOL)fadeout;
+- (void)hasEndedFullscreen;
+@end
+
@implementation VLCVideoWindowCommon
@synthesize videoView=o_video_view;
@synthesize controlsBar=o_controls_bar;
+@synthesize enteringFullscreenTransition=b_entering_fullscreen_transition;
#pragma mark -
#pragma mark Init
[super dealloc];
}
+- (void)awakeFromNib
+{
+ BOOL b_nativeFullscreenMode = NO;
+#ifdef MAC_OS_X_VERSION_10_7
+ if (!OSX_SNOW_LEOPARD)
+ b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
+#endif
+
+ if (b_nativeFullscreenMode) {
+ [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
+ } else {
+ [o_titlebar_view setFullscreenButtonHidden: YES];
+ }
+
+ [super awakeFromNib];
+}
+
- (void)setTitle:(NSString *)title
{
if (!title || [title length] < 1)
if (!([self styleMask] & NSTitledWindowMask)) {
[[NSNotificationCenter defaultCenter] postNotificationName:NSWindowWillCloseNotification object:self];
- [self orderOut: sender];
+ [self close];
} else
[super performClose: sender];
}
#pragma mark -
#pragma mark Video window resizing logic
+- (void)setWindowLevel:(NSInteger)i_state
+{
+ if (var_InheritBool(VLCIntf, "video-wallpaper") || [self level] < NSNormalWindowLevel)
+ return;
+
+ if (!b_fullscreen && !b_entering_fullscreen_transition)
+ [self setLevel: i_state];
+ else {
+ // only save it for restore
+ i_originalLevel = i_state;
+ }
+}
+
- (NSRect)getWindowRectForProposedVideoViewSize:(NSSize)size
{
NSSize windowMinSize = [self minSize];
- (void)resizeWindow
{
- if ([[VLCMainWindow sharedInstance] fullscreen])
+ if ([self fullscreen])
return;
NSRect window_rect = [self getWindowRectForProposedVideoViewSize:nativeVideoSize];
return proposedFrameSize;
// needed when entering lion fullscreen mode
- if ([[VLCMainWindow sharedInstance] fullscreen])
+ if (b_entering_fullscreen_transition || [self fullscreen])
return proposedFrameSize;
if ([[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]) {
return proposedFrameSize;
}
+
#pragma mark -
-#pragma mark Fullscreen Logic
+#pragma mark Mouse cursor handling
-- (void)lockFullscreenAnimation
+// NSTimer selectors require this function signature as per Apple's docs
+- (void)hideMouseCursor:(NSTimer *)timer
{
- [o_animation_lock lock];
+ [NSCursor setHiddenUntilMouseMoves: YES];
}
-- (void)unlockFullscreenAnimation
+- (void)recreateHideMouseTimer
{
- [o_animation_lock unlock];
+ if (t_hide_mouse_timer != nil) {
+ [t_hide_mouse_timer invalidate];
+ [t_hide_mouse_timer release];
+ }
+
+ t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
+ target:self
+ selector:@selector(hideMouseCursor:)
+ userInfo:nil
+ repeats:NO];
+ [t_hide_mouse_timer retain];
}
+// Called automatically if window's acceptsMouseMovedEvents property is true
+- (void)mouseMoved:(NSEvent *)theEvent
+{
+ if (b_fullscreen)
+ [self recreateHideMouseTimer];
+
+ [super mouseMoved: theEvent];
+}
+
+#pragma mark -
+#pragma mark Lion native fullscreen handling
+
+- (void)becomeKeyWindow
+{
+ [super becomeKeyWindow];
+
+ // change fspanel state for the case when multiple windows are in fullscreen
+ if ([self hasActiveVideo] && [self fullscreen])
+ [[[VLCMainWindow sharedInstance] fsPanel] setActive:nil];
+ else
+ [[[VLCMainWindow sharedInstance] fsPanel] setNonActive:nil];
+}
+
+- (void)resignKeyWindow
+{
+ [super resignKeyWindow];
+
+ [[[VLCMainWindow sharedInstance] fsPanel] setNonActive:nil];
+}
+
+- (void)windowWillEnterFullScreen:(NSNotification *)notification
+{
+ // workaround, see #6668
+ [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
+
+ i_originalLevel = [self level];
+ // b_fullscreen and b_entering_fullscreen_transition must not be true yet
+ [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
+ [self setLevel:NSNormalWindowLevel];
+
+ b_entering_fullscreen_transition = YES;
+
+ var_SetBool(pl_Get(VLCIntf), "fullscreen", true);
+
+ if ([self hasActiveVideo]) {
+ vout_thread_t *p_vout = getVoutForActiveWindow();
+ if (p_vout) {
+ var_SetBool(p_vout, "fullscreen", true);
+ vlc_object_release(p_vout);
+ }
+ }
+
+ if ([self hasActiveVideo])
+ [[VLCMainWindow sharedInstance] recreateHideMouseTimer];
+
+ if (b_dark_interface) {
+ [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
+
+ NSRect winrect;
+ CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
+ winrect = [self frame];
+
+ winrect.size.height = winrect.size.height - f_titleBarHeight;
+ [self setFrame: winrect display:NO animate:NO];
+ }
+
+ [o_video_view setFrame: [[self contentView] frame]];
+ if (![o_video_view isHidden]) {
+ [[o_controls_bar bottomBarView] setHidden: YES];
+ }
+
+
+ [self setMovableByWindowBackground: NO];
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification
+{
+ // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
+ // But this creates some problems when leaving fs over remote intfs, so activate app here.
+ [NSApp activateIgnoringOtherApps:YES];
+
+ [self setFullscreen: YES];
+ b_entering_fullscreen_transition = NO;
+
+ if ([self hasActiveVideo]) {
+ [[[VLCMainWindow sharedInstance] fsPanel] setVoutWasUpdated: self];
+ if (![o_video_view isHidden])
+ [[[VLCMainWindow sharedInstance] fsPanel] setActive: nil];
+ }
+
+ NSArray *subviews = [[self videoView] subviews];
+ NSUInteger count = [subviews count];
+
+ for (NSUInteger x = 0; x < count; x++) {
+ if ([[subviews objectAtIndex:x] respondsToSelector:@selector(reshape)])
+ [[subviews objectAtIndex:x] reshape];
+ }
+
+}
+
+- (void)windowWillExitFullScreen:(NSNotification *)notification
+{
+ [self setFullscreen: NO];
+
+ var_SetBool(pl_Get(VLCIntf), "fullscreen", false);
+
+ if ([self hasActiveVideo]) {
+ vout_thread_t *p_vout = getVoutForActiveWindow();
+ if (p_vout) {
+ var_SetBool(p_vout, "fullscreen", false);
+ vlc_object_release(p_vout);
+ }
+ }
+
+ [NSCursor setHiddenUntilMouseMoves: NO];
+ [[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
+
+
+ if (b_dark_interface) {
+ NSRect winrect;
+ CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
+
+ winrect = [o_video_view frame];
+ winrect.size.height -= f_titleBarHeight;
+ [o_video_view setFrame: winrect];
+
+ winrect = [self frame];
+ [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
+ winrect.size.width, f_titleBarHeight)];
+ [[self contentView] addSubview: o_titlebar_view];
+
+ winrect.size.height = winrect.size.height + f_titleBarHeight;
+ [self setFrame: winrect display:NO animate:NO];
+ }
+
+ NSRect videoViewFrame = [o_video_view frame];
+ videoViewFrame.origin.y += [o_controls_bar height];
+ videoViewFrame.size.height -= [o_controls_bar height];
+ [o_video_view setFrame: videoViewFrame];
+
+ if (![o_video_view isHidden]) {
+ [[o_controls_bar bottomBarView] setHidden: NO];
+ }
+
+ [self setMovableByWindowBackground: YES];
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)notification
+{
+ [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: i_originalLevel];
+ [self setLevel:i_originalLevel];
+}
+
+#pragma mark -
+#pragma mark Fullscreen Logic
+
- (void)enterFullscreen
{
NSMutableDictionary *dict1, *dict2;
BOOL blackout_other_displays = var_InheritBool(VLCIntf, "macosx-black");
screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger(VLCIntf, "macosx-vdev")];
- [self lockFullscreenAnimation];
if (!screen) {
msg_Dbg(VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode");
/* Make sure we don't see the window flashes in float-on-top mode */
i_originalLevel = [self level];
+ // b_fullscreen must not be true yet
+ [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
[self setLevel:NSNormalWindowLevel];
/* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
[o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
[o_fullscreen_window setCanBecomeKeyWindow: YES];
[o_fullscreen_window setCanBecomeMainWindow: YES];
+ [o_fullscreen_window setHasActiveVideo: YES];
+ [o_fullscreen_window setFullscreen: YES];
if (![self isVisible] || [self alphaValue] == 0.0) {
/* We don't animate if we are not visible, instead we
CGDisplayFade(token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES);
}
- if ([screen mainScreen])
- [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
+ [screen setFullscreenPresentationOptions];
+ [o_video_view retain];
[[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
[o_temp_view setFrame:[o_video_view frame]];
[o_fullscreen_window setContentView:o_video_view];
+ [o_video_view release];
[o_fullscreen_window makeKeyAndOrderFront:self];
[o_fullscreen_window orderFront:self animate:YES];
return;
}
+ /* Make sure video view gets visible in case the playlist was visible before */
+ b_video_view_was_hidden = [o_video_view isHidden];
+ [o_video_view setHidden: NO];
+
/* Make sure we don't see the o_video_view disappearing of the screen during this operation */
NSDisableScreenUpdates();
+ [o_video_view retain];
[[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
[o_temp_view setFrame:[o_video_view frame]];
[o_fullscreen_window setContentView:o_video_view];
+ [o_video_view release];
[o_fullscreen_window makeKeyAndOrderFront:self];
NSEnableScreenUpdates();
}
/* We are in fullscreen (and no animation is running) */
- if ([[VLCMainWindow sharedInstance] fullscreen]) {
+ if ([self fullscreen]) {
/* Make sure we are hidden */
[self orderOut: self];
- [self unlockFullscreenAnimation];
return;
}
[o_fullscreen_anim2 release];
}
- if ([screen mainScreen])
- [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
+ [screen setFullscreenPresentationOptions];
dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
[o_fullscreen_anim1 startAnimation];
/* fullscreenAnimation will be unlocked when animation ends */
+
+ b_entering_fullscreen_transition = YES;
}
- (void)hasBecomeFullscreen
if ([self isVisible])
[self orderOut: self];
- [[VLCMainWindow sharedInstance] setFullscreen:YES];
- [self unlockFullscreenAnimation];
+ b_entering_fullscreen_transition = NO;
+ [self setFullscreen:YES];
}
- (void)leaveFullscreen
NSRect frame;
BOOL blackout_other_displays = var_InheritBool(VLCIntf, "macosx-black");
- [self lockFullscreenAnimation];
-
if (o_controls_bar)
[o_controls_bar setFullscreenState:NO];
[[[VLCMainWindow sharedInstance] controlsBar] setFullscreenState:NO];
/* We always try to do so */
[NSScreen unblackoutScreens];
- vout_thread_t *p_vout = getVoutForActiveWindow();
- if (p_vout) {
- if (var_GetBool(p_vout, "video-on-top"))
- [[o_video_view window] setLevel: NSStatusWindowLevel];
- else
- [[o_video_view window] setLevel: NSNormalWindowLevel];
- vlc_object_release(p_vout);
- }
[[o_video_view window] makeKeyAndOrderFront: nil];
/* Don't do anything if o_fullscreen_window is already closed */
if (!o_fullscreen_window) {
- [self unlockFullscreenAnimation];
return;
}
}
[[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
- [NSApp setPresentationOptions: NSApplicationPresentationDefault];
+ [[o_fullscreen_window screen] setNonFullscreenPresentationOptions];
/* Will release the lock */
[self hasEndedFullscreen];
[[o_video_view window] orderFront: self];
[[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
- [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
+ [[o_fullscreen_window screen] setNonFullscreenPresentationOptions];
if (o_fullscreen_anim1) {
[o_fullscreen_anim1 stopAnimation];
[dict2 setObject:self forKey:NSViewAnimationTargetKey];
[dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
- o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
+ o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
[dict2 release];
[o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
[dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
[dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
- o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
+ o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
[dict1 release];
[o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
- (void)hasEndedFullscreen
{
- [[VLCMainWindow sharedInstance] setFullscreen:NO];
-
+ [self setFullscreen:NO];
+
/* This function is private and should be only triggered at the end of the fullscreen change animation */
/* Make sure we don't see the o_video_view disappearing of the screen during this operation */
NSDisableScreenUpdates();
if ([[o_video_view subviews] count] > 0)
[self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
+ [o_video_view setHidden: b_video_view_was_hidden];
+
[super makeKeyAndOrderFront:self]; /* our version (in main window) contains a workaround */
[o_fullscreen_window orderOut: self];
[o_fullscreen_window release];
o_fullscreen_window = nil;
+
+ [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: i_originalLevel];
[self setLevel:i_originalLevel];
+
[self setAlphaValue: config_GetFloat(VLCIntf, "macosx-opaqueness")];
// if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
if (![[VLCMain sharedInstance] activeVideoPlayback] && [self class] != [VLCMainWindow class])
[self orderOut: self];
-
- [self unlockFullscreenAnimation];
}
- (void)animationDidEnd:(NSAnimation*)animation
/* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
viewAnimations = [o_fullscreen_anim2 viewAnimations];
if ([viewAnimations count] >=1 &&
- [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect]) {
+ [[[viewAnimations objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect]) {
/* Fullscreen ended */
[self hasEndedFullscreen];
} else
[self hasBecomeFullscreen];
}
+- (void)orderOut:(id)sender
+{
+ [super orderOut:sender];
+
+ /*
+ * TODO reimplement leaveFullscreenAndFadeOut:YES, or remove code
+ * and the hack below
+
+ if (![NSStringFromClass([self class]) isEqualToString:@"VLCMainWindow"]) {
+ [self leaveFullscreenAndFadeOut:YES];
+ }
+ */
+}
+
+- (void)makeKeyAndOrderFront: (id)sender
+{
+ /* Hack
+ * when we exit fullscreen and fade out, we may endup in
+ * having a window that is faded. We can't have it fade in unless we
+ * animate again. */
+
+ if (!b_window_is_invisible) {
+ /* Make sure we don't do it too much */
+ [super makeKeyAndOrderFront: sender];
+ return;
+ }
+
+ [super setAlphaValue:0.0f];
+ [super makeKeyAndOrderFront: sender];
+
+ NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
+ [dict setObject:self forKey:NSViewAnimationTargetKey];
+ [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
+
+ o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
+ [dict release];
+
+ [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
+ [o_makekey_anim setDuration: 0.1];
+ [o_makekey_anim setFrameRate: 30];
+ [o_makekey_anim setDelegate: self];
+
+ [o_makekey_anim startAnimation];
+ b_window_is_invisible = NO;
+
+ /* fullscreenAnimation will be unlocked when animation ends */
+}
+
+
#pragma mark -
#pragma mark Accessibility stuff
static NSMutableArray *attributes = nil;
if (attributes == nil) {
attributes = [[super accessibilityAttributeNames] mutableCopy];
- NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
+ NSArray *appendAttributes = [NSArray arrayWithObjects:NSAccessibilitySubroleAttribute,
NSAccessibilityCloseButtonAttribute,
NSAccessibilityMinimizeButtonAttribute,
- NSAccessibilityZoomButtonAttribute,
- nil];
+ NSAccessibilityZoomButtonAttribute, nil];
for(NSString *attribute in appendAttributes) {
if (![attributes containsObject:attribute])