You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1112 lines
36 KiB
1112 lines
36 KiB
/*****************************************************************************
|
|
* Windows.m: MacOS X interface module
|
|
*****************************************************************************
|
|
* Copyright (C) 2012-2013 VLC authors and VideoLAN
|
|
* $Id$
|
|
*
|
|
* Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
|
|
* David Fuhrmann <david dot fuhrmann at googlemail dot com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#import "Windows.h"
|
|
#import "intf.h"
|
|
#import "CoreInteraction.h"
|
|
#import "ControlsBar.h"
|
|
#import "VideoView.h"
|
|
|
|
/*****************************************************************************
|
|
* VLCWindow
|
|
*
|
|
* Missing extension to NSWindow
|
|
*****************************************************************************/
|
|
|
|
@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
|
|
{
|
|
self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
|
|
if (self) {
|
|
/* we don't want this window to be restored on relaunch */
|
|
if (!OSX_SNOW_LEOPARD)
|
|
[self setRestorable:NO];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
|
|
{
|
|
b_isset_canBecomeKeyWindow = YES;
|
|
b_canBecomeKeyWindow = canBecomeKey;
|
|
}
|
|
|
|
- (BOOL)canBecomeKeyWindow
|
|
{
|
|
if (b_isset_canBecomeKeyWindow)
|
|
return b_canBecomeKeyWindow;
|
|
|
|
return [super canBecomeKeyWindow];
|
|
}
|
|
|
|
- (void)setCanBecomeMainWindow: (BOOL)canBecomeMain
|
|
{
|
|
b_isset_canBecomeMainWindow = YES;
|
|
b_canBecomeMainWindow = canBecomeMain;
|
|
}
|
|
|
|
- (BOOL)canBecomeMainWindow
|
|
{
|
|
if (b_isset_canBecomeMainWindow)
|
|
return b_canBecomeMainWindow;
|
|
|
|
return [super canBecomeMainWindow];
|
|
}
|
|
|
|
- (void)closeAndAnimate: (BOOL)animate
|
|
{
|
|
NSInvocation *invoc;
|
|
|
|
if (!animate) {
|
|
[super close];
|
|
return;
|
|
}
|
|
|
|
invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
|
|
[invoc setTarget: self];
|
|
|
|
if (![self isVisible] || [self alphaValue] == 0.0) {
|
|
[super close];
|
|
return;
|
|
}
|
|
|
|
[self orderOut: self animate: YES callback: invoc];
|
|
}
|
|
|
|
- (void)orderOut: (id)sender animate: (BOOL)animate
|
|
{
|
|
NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
|
|
[invoc setTarget: self];
|
|
[invoc setArgument: sender atIndex: 0];
|
|
[self orderOut: sender animate: animate callback: invoc];
|
|
}
|
|
|
|
- (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
|
|
{
|
|
NSViewAnimation *anim;
|
|
NSViewAnimation *current_anim;
|
|
NSMutableDictionary *dict;
|
|
|
|
if (!animate) {
|
|
[self orderOut: sender];
|
|
return;
|
|
}
|
|
|
|
dict = [[NSMutableDictionary alloc] initWithCapacity:2];
|
|
|
|
[dict setObject:self forKey:NSViewAnimationTargetKey];
|
|
|
|
[dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
|
|
anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
|
|
[dict release];
|
|
|
|
[anim setAnimationBlockingMode:NSAnimationNonblocking];
|
|
[anim setDuration:0.9];
|
|
[anim setFrameRate:30];
|
|
[anim setUserInfo: callback];
|
|
|
|
@synchronized(self) {
|
|
current_anim = self->o_current_animation;
|
|
|
|
if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating]) {
|
|
[anim release];
|
|
} else {
|
|
if (current_anim) {
|
|
[current_anim stopAnimation];
|
|
[anim setCurrentProgress:1.0 - [current_anim currentProgress]];
|
|
[current_anim release];
|
|
}
|
|
else
|
|
[anim setCurrentProgress:1.0 - [self alphaValue]];
|
|
self->o_current_animation = anim;
|
|
[anim startAnimation];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)orderFront: (id)sender animate: (BOOL)animate
|
|
{
|
|
NSViewAnimation *anim;
|
|
NSViewAnimation *current_anim;
|
|
NSMutableDictionary *dict;
|
|
|
|
if (!animate) {
|
|
[super orderFront: sender];
|
|
[self setAlphaValue: 1.0];
|
|
return;
|
|
}
|
|
|
|
if (![self isVisible]) {
|
|
[self setAlphaValue: 0.0];
|
|
[super orderFront: sender];
|
|
}
|
|
else if ([self alphaValue] == 1.0) {
|
|
[super orderFront: self];
|
|
return;
|
|
}
|
|
|
|
dict = [[NSMutableDictionary alloc] initWithCapacity:2];
|
|
|
|
[dict setObject:self forKey:NSViewAnimationTargetKey];
|
|
|
|
[dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
|
|
anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
|
|
[dict release];
|
|
|
|
[anim setAnimationBlockingMode:NSAnimationNonblocking];
|
|
[anim setDuration:0.5];
|
|
[anim setFrameRate:30];
|
|
|
|
@synchronized(self) {
|
|
current_anim = self->o_current_animation;
|
|
|
|
if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating]) {
|
|
[anim release];
|
|
} else {
|
|
if (current_anim) {
|
|
[current_anim stopAnimation];
|
|
[anim setCurrentProgress:1.0 - [current_anim currentProgress]];
|
|
[current_anim release];
|
|
}
|
|
else
|
|
[anim setCurrentProgress:[self alphaValue]];
|
|
self->o_current_animation = anim;
|
|
[self orderFront: sender];
|
|
[anim startAnimation];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)animationDidEnd:(NSAnimation*)anim
|
|
{
|
|
if ([self alphaValue] <= 0.0) {
|
|
NSInvocation * invoc;
|
|
[super orderOut: nil];
|
|
[self setAlphaValue: 1.0];
|
|
if ((invoc = [anim userInfo]))
|
|
[invoc invoke];
|
|
}
|
|
}
|
|
|
|
- (VLCVoutView *)videoView
|
|
{
|
|
if ([[self contentView] class] == [VLCVoutView class])
|
|
return (VLCVoutView *)[self contentView];
|
|
|
|
return nil;
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
|
|
/*****************************************************************************
|
|
* VLCVideoWindowCommon
|
|
*
|
|
* 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
|
|
|
|
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
|
|
backing:(NSBackingStoreType)backingType defer:(BOOL)flag
|
|
{
|
|
b_dark_interface = config_GetInt(VLCIntf, "macosx-interfacestyle");
|
|
|
|
if (b_dark_interface) {
|
|
styleMask = NSBorderlessWindowMask;
|
|
#ifdef MAC_OS_X_VERSION_10_7
|
|
if (!OSX_SNOW_LEOPARD)
|
|
styleMask |= NSResizableWindowMask;
|
|
#endif
|
|
}
|
|
|
|
self = [super initWithContentRect:contentRect styleMask:styleMask
|
|
backing:backingType defer:flag];
|
|
|
|
/* we want to be moveable regardless of our style */
|
|
[self setMovableByWindowBackground: YES];
|
|
[self setCanBecomeKeyWindow:YES];
|
|
|
|
o_temp_view = [[NSView alloc] init];
|
|
[o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[o_temp_view release];
|
|
[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)
|
|
return;
|
|
|
|
if (b_dark_interface && o_titlebar_view)
|
|
[o_titlebar_view setWindowTitle: title];
|
|
|
|
[super setTitle: title];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark zoom / minimize / close
|
|
|
|
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
|
|
{
|
|
SEL s_menuAction = [menuItem action];
|
|
|
|
if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
|
|
return YES;
|
|
|
|
return [super validateMenuItem:menuItem];
|
|
}
|
|
|
|
- (BOOL)windowShouldClose:(id)sender
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (void)performClose:(id)sender
|
|
{
|
|
if (!([self styleMask] & NSTitledWindowMask)) {
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:NSWindowWillCloseNotification object:self];
|
|
|
|
[self orderOut: sender];
|
|
} else
|
|
[super performClose: sender];
|
|
}
|
|
|
|
- (void)performMiniaturize:(id)sender
|
|
{
|
|
if (!([self styleMask] & NSTitledWindowMask))
|
|
[self miniaturize: sender];
|
|
else
|
|
[super performMiniaturize: sender];
|
|
}
|
|
|
|
- (void)performZoom:(id)sender
|
|
{
|
|
if (!([self styleMask] & NSTitledWindowMask))
|
|
[self customZoom: sender];
|
|
else
|
|
[super performZoom: sender];
|
|
}
|
|
|
|
- (void)zoom:(id)sender
|
|
{
|
|
if (!([self styleMask] & NSTitledWindowMask))
|
|
[self customZoom: sender];
|
|
else
|
|
[super zoom: sender];
|
|
}
|
|
|
|
/**
|
|
* Given a proposed frame rectangle, return a modified version
|
|
* which will fit inside the screen.
|
|
*
|
|
* This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
|
|
* Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
|
|
* Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
* Copyright (C) 1996 Free Software Foundation, Inc.
|
|
*/
|
|
- (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
|
|
{
|
|
NSRect screenRect = [screen visibleFrame];
|
|
float difference;
|
|
|
|
/* Move top edge of the window inside the screen */
|
|
difference = NSMaxY (frameRect) - NSMaxY (screenRect);
|
|
if (difference > 0) {
|
|
frameRect.origin.y -= difference;
|
|
}
|
|
|
|
/* If the window is resizable, resize it (if needed) so that the
|
|
bottom edge is on the screen or can be on the screen when the user moves
|
|
the window */
|
|
difference = NSMaxY (screenRect) - NSMaxY (frameRect);
|
|
if (_styleMask & NSResizableWindowMask) {
|
|
float difference2;
|
|
|
|
difference2 = screenRect.origin.y - frameRect.origin.y;
|
|
difference2 -= difference;
|
|
// Take in account the space between the top of window and the top of the
|
|
// screen which can be used to move the bottom of the window on the screen
|
|
if (difference2 > 0) {
|
|
frameRect.size.height -= difference2;
|
|
frameRect.origin.y += difference2;
|
|
}
|
|
|
|
/* Ensure that resizing doesn't makewindow smaller than minimum */
|
|
difference2 = [self minSize].height - frameRect.size.height;
|
|
if (difference2 > 0) {
|
|
frameRect.size.height += difference2;
|
|
frameRect.origin.y -= difference2;
|
|
}
|
|
}
|
|
|
|
return frameRect;
|
|
}
|
|
|
|
#define DIST 3
|
|
|
|
/**
|
|
Zooms the receiver. This method calls the delegate method
|
|
windowShouldZoom:toFrame: to determine if the window should
|
|
be allowed to zoom to full screen.
|
|
*
|
|
* This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
|
|
* Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
|
|
* Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
* Copyright (C) 1996 Free Software Foundation, Inc.
|
|
*/
|
|
- (void) customZoom: (id)sender
|
|
{
|
|
NSRect maxRect = [[self screen] visibleFrame];
|
|
NSRect currentFrame = [self frame];
|
|
|
|
if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)]) {
|
|
maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
|
|
}
|
|
|
|
maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
|
|
|
|
// Compare the new frame with the current one
|
|
if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
|
|
&& (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
|
|
&& (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
|
|
&& (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST)) {
|
|
// Already in zoomed mode, reset user frame, if stored
|
|
if ([self frameAutosaveName] != nil) {
|
|
[self setFrame: previousSavedFrame display: YES animate: YES];
|
|
[self saveFrameUsingName: [self frameAutosaveName]];
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ([self frameAutosaveName] != nil) {
|
|
[self saveFrameUsingName: [self frameAutosaveName]];
|
|
previousSavedFrame = [self frame];
|
|
}
|
|
|
|
[self setFrame: maxRect display: YES animate: YES];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Video window resizing logic
|
|
|
|
- (void)setWindowLevel:(NSInteger)i_state
|
|
{
|
|
if (var_InheritBool(VLCIntf, "video-wallpaper") || [self level] < NSNormalWindowLevel)
|
|
return;
|
|
|
|
[self setLevel: i_state];
|
|
|
|
}
|
|
|
|
- (NSRect)getWindowRectForProposedVideoViewSize:(NSSize)size
|
|
{
|
|
NSSize windowMinSize = [self minSize];
|
|
NSRect screenFrame = [[self screen] visibleFrame];
|
|
|
|
NSPoint topleftbase = NSMakePoint(0, [self frame].size.height);
|
|
NSPoint topleftscreen = [self convertBaseToScreen: topleftbase];
|
|
|
|
unsigned int i_width = size.width;
|
|
unsigned int i_height = size.height;
|
|
if (i_width < windowMinSize.width)
|
|
i_width = windowMinSize.width;
|
|
if (i_height < f_min_video_height)
|
|
i_height = f_min_video_height;
|
|
|
|
/* Calculate the window's new size */
|
|
NSRect new_frame;
|
|
new_frame.size.width = [self frame].size.width - [o_video_view frame].size.width + i_width;
|
|
new_frame.size.height = [self frame].size.height - [o_video_view frame].size.height + i_height;
|
|
new_frame.origin.x = topleftscreen.x;
|
|
new_frame.origin.y = topleftscreen.y - new_frame.size.height;
|
|
|
|
/* make sure the window doesn't exceed the screen size the window is on */
|
|
if (new_frame.size.width > screenFrame.size.width) {
|
|
new_frame.size.width = screenFrame.size.width;
|
|
new_frame.origin.x = screenFrame.origin.x;
|
|
}
|
|
if (new_frame.size.height > screenFrame.size.height) {
|
|
new_frame.size.height = screenFrame.size.height;
|
|
new_frame.origin.y = screenFrame.origin.y;
|
|
}
|
|
if (new_frame.origin.y < screenFrame.origin.y)
|
|
new_frame.origin.y = screenFrame.origin.y;
|
|
|
|
CGFloat right_screen_point = screenFrame.origin.x + screenFrame.size.width;
|
|
CGFloat right_window_point = new_frame.origin.x + new_frame.size.width;
|
|
if (right_window_point > right_screen_point)
|
|
new_frame.origin.x -= (right_window_point - right_screen_point);
|
|
|
|
return new_frame;
|
|
}
|
|
|
|
- (void)resizeWindow
|
|
{
|
|
if ([self fullscreen])
|
|
return;
|
|
|
|
NSRect window_rect = [self getWindowRectForProposedVideoViewSize:nativeVideoSize];
|
|
[[self animator] setFrame:window_rect display:YES];
|
|
}
|
|
|
|
- (void)setNativeVideoSize:(NSSize)size
|
|
{
|
|
nativeVideoSize = size;
|
|
|
|
if (var_InheritBool(VLCIntf, "macosx-video-autoresize") && !var_InheritBool(VLCIntf, "video-wallpaper"))
|
|
[self resizeWindow];
|
|
}
|
|
|
|
- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
|
|
{
|
|
if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != self)
|
|
return proposedFrameSize;
|
|
|
|
// needed when entering lion fullscreen mode
|
|
if (b_entering_fullscreen_transition || [self fullscreen])
|
|
return proposedFrameSize;
|
|
|
|
if ([[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]) {
|
|
NSRect videoWindowFrame = [self frame];
|
|
NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
|
|
NSRect contentRect = [self contentRectForFrameRect:videoWindowFrame];
|
|
float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
|
|
float marginx = contentRect.size.width - viewRect.size.width;
|
|
if (o_titlebar_view && b_dark_interface)
|
|
marginy += [o_titlebar_view frame].size.height;
|
|
|
|
proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
|
|
}
|
|
|
|
return proposedFrameSize;
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Mouse cursor handling
|
|
|
|
// NSTimer selectors require this function signature as per Apple's docs
|
|
- (void)hideMouseCursor:(NSTimer *)timer
|
|
{
|
|
[NSCursor setHiddenUntilMouseMoves: YES];
|
|
}
|
|
|
|
- (void)recreateHideMouseTimer
|
|
{
|
|
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)];
|
|
|
|
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];
|
|
|
|
i_originalLevel = [self level];
|
|
[[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
|
|
[self setLevel:NSNormalWindowLevel];
|
|
|
|
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];
|
|
|
|
[[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: i_originalLevel];
|
|
[self setLevel:i_originalLevel];
|
|
|
|
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];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Fullscreen Logic
|
|
|
|
- (void)lockFullscreenAnimation
|
|
{
|
|
[o_animation_lock lock];
|
|
}
|
|
|
|
- (void)unlockFullscreenAnimation
|
|
{
|
|
[o_animation_lock unlock];
|
|
}
|
|
|
|
- (void)enterFullscreen
|
|
{
|
|
NSMutableDictionary *dict1, *dict2;
|
|
NSScreen *screen;
|
|
NSRect screen_rect;
|
|
NSRect rect;
|
|
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");
|
|
screen = [self screen];
|
|
}
|
|
if (!screen) {
|
|
msg_Dbg(VLCIntf, "Using deepest screen");
|
|
screen = [NSScreen deepestScreen];
|
|
}
|
|
|
|
screen_rect = [screen frame];
|
|
|
|
if (o_controls_bar)
|
|
[o_controls_bar setFullscreenState:YES];
|
|
[[[VLCMainWindow sharedInstance] controlsBar] setFullscreenState:YES];
|
|
|
|
[[VLCMainWindow sharedInstance] recreateHideMouseTimer];
|
|
|
|
if (blackout_other_displays)
|
|
[screen blackoutOtherScreens];
|
|
|
|
/* Make sure we don't see the window flashes in float-on-top mode */
|
|
i_originalLevel = [self level];
|
|
[[[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 */
|
|
if (!o_fullscreen_window) {
|
|
/* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
|
|
|
|
rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
|
|
rect.origin.x += [self frame].origin.x;
|
|
rect.origin.y += [self frame].origin.y;
|
|
o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
|
|
[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
|
|
* simply fade the display */
|
|
CGDisplayFadeReservationToken token;
|
|
|
|
if (blackout_other_displays) {
|
|
CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
|
|
CGDisplayFade(token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES);
|
|
}
|
|
|
|
[screen setFullscreenPresentationOptions];
|
|
|
|
[[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_fullscreen_window makeKeyAndOrderFront:self];
|
|
[o_fullscreen_window orderFront:self animate:YES];
|
|
|
|
[o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
|
|
[o_fullscreen_window setLevel:NSNormalWindowLevel];
|
|
|
|
if (blackout_other_displays) {
|
|
CGDisplayFade(token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO);
|
|
CGReleaseDisplayFadeReservation(token);
|
|
}
|
|
|
|
/* Will release the lock */
|
|
[self hasBecomeFullscreen];
|
|
|
|
return;
|
|
}
|
|
|
|
/* Make sure we don't see the o_video_view disappearing of the screen during this operation */
|
|
NSDisableScreenUpdates();
|
|
[[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_fullscreen_window makeKeyAndOrderFront:self];
|
|
NSEnableScreenUpdates();
|
|
}
|
|
|
|
/* We are in fullscreen (and no animation is running) */
|
|
if ([self fullscreen]) {
|
|
/* Make sure we are hidden */
|
|
[self orderOut: self];
|
|
|
|
[self unlockFullscreenAnimation];
|
|
return;
|
|
}
|
|
|
|
if (o_fullscreen_anim1) {
|
|
[o_fullscreen_anim1 stopAnimation];
|
|
[o_fullscreen_anim1 release];
|
|
}
|
|
if (o_fullscreen_anim2) {
|
|
[o_fullscreen_anim2 stopAnimation];
|
|
[o_fullscreen_anim2 release];
|
|
}
|
|
|
|
[screen setFullscreenPresentationOptions];
|
|
|
|
dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
|
|
dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
|
|
|
|
[dict1 setObject:self forKey:NSViewAnimationTargetKey];
|
|
[dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
|
|
|
|
[dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
|
|
[dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
|
|
[dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
|
|
|
|
/* Strategy with NSAnimation allocation:
|
|
- Keep at most 2 animation at a time
|
|
- leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
|
|
*/
|
|
o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
|
|
o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
|
|
|
|
[dict1 release];
|
|
[dict2 release];
|
|
|
|
[o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
|
|
[o_fullscreen_anim1 setDuration: 0.3];
|
|
[o_fullscreen_anim1 setFrameRate: 30];
|
|
[o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
|
|
[o_fullscreen_anim2 setDuration: 0.2];
|
|
[o_fullscreen_anim2 setFrameRate: 30];
|
|
|
|
[o_fullscreen_anim2 setDelegate: self];
|
|
[o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
|
|
|
|
[o_fullscreen_anim1 startAnimation];
|
|
/* fullscreenAnimation will be unlocked when animation ends */
|
|
}
|
|
|
|
- (void)hasBecomeFullscreen
|
|
{
|
|
if ([[o_video_view subviews] count] > 0)
|
|
[o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
|
|
|
|
[o_fullscreen_window makeKeyWindow];
|
|
[o_fullscreen_window setAcceptsMouseMovedEvents: YES];
|
|
|
|
/* tell the fspanel to move itself to front next time it's triggered */
|
|
[[[VLCMainWindow sharedInstance] fsPanel] setVoutWasUpdated: o_fullscreen_window];
|
|
[[[VLCMainWindow sharedInstance] fsPanel] setActive: nil];
|
|
|
|
if ([self isVisible])
|
|
[self orderOut: self];
|
|
|
|
[self setFullscreen:YES];
|
|
[self unlockFullscreenAnimation];
|
|
}
|
|
|
|
- (void)leaveFullscreen
|
|
{
|
|
[self leaveFullscreenAndFadeOut: NO];
|
|
}
|
|
|
|
- (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
|
|
{
|
|
NSMutableDictionary *dict1, *dict2;
|
|
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];
|
|
|
|
[[o_video_view window] makeKeyAndOrderFront: nil];
|
|
|
|
/* Don't do anything if o_fullscreen_window is already closed */
|
|
if (!o_fullscreen_window) {
|
|
[self unlockFullscreenAnimation];
|
|
return;
|
|
}
|
|
|
|
if (fadeout) {
|
|
/* We don't animate if we are not visible, instead we
|
|
* simply fade the display */
|
|
CGDisplayFadeReservationToken token;
|
|
|
|
if (blackout_other_displays) {
|
|
CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
|
|
CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES);
|
|
}
|
|
|
|
[[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
|
|
[[o_fullscreen_window screen] setNonFullscreenPresentationOptions];
|
|
|
|
/* Will release the lock */
|
|
[self hasEndedFullscreen];
|
|
|
|
/* Our window is hidden, and might be faded. We need to workaround that, so note it
|
|
* here */
|
|
b_window_is_invisible = YES;
|
|
|
|
if (blackout_other_displays) {
|
|
CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO);
|
|
CGReleaseDisplayFadeReservation(token);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
[self setAlphaValue: 0.0];
|
|
[self orderFront: self];
|
|
[[o_video_view window] orderFront: self];
|
|
|
|
[[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
|
|
[[o_fullscreen_window screen] setNonFullscreenPresentationOptions];
|
|
|
|
if (o_fullscreen_anim1) {
|
|
[o_fullscreen_anim1 stopAnimation];
|
|
[o_fullscreen_anim1 release];
|
|
}
|
|
if (o_fullscreen_anim2) {
|
|
[o_fullscreen_anim2 stopAnimation];
|
|
[o_fullscreen_anim2 release];
|
|
}
|
|
|
|
frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
|
|
frame.origin.x += [self frame].origin.x;
|
|
frame.origin.y += [self frame].origin.y;
|
|
|
|
dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
|
|
[dict2 setObject:self forKey:NSViewAnimationTargetKey];
|
|
[dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
|
|
|
|
o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
|
|
[dict2 release];
|
|
|
|
[o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
|
|
[o_fullscreen_anim2 setDuration: 0.3];
|
|
[o_fullscreen_anim2 setFrameRate: 30];
|
|
|
|
[o_fullscreen_anim2 setDelegate: self];
|
|
|
|
dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
|
|
|
|
[dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
|
|
[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]];
|
|
[dict1 release];
|
|
|
|
[o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
|
|
[o_fullscreen_anim1 setDuration: 0.2];
|
|
[o_fullscreen_anim1 setFrameRate: 30];
|
|
[o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
|
|
|
|
/* Make sure o_fullscreen_window is the frontmost window */
|
|
[o_fullscreen_window orderFront: self];
|
|
|
|
[o_fullscreen_anim1 startAnimation];
|
|
/* fullscreenAnimation will be unlocked when animation ends */
|
|
}
|
|
|
|
- (void)hasEndedFullscreen
|
|
{
|
|
[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();
|
|
[o_video_view retain];
|
|
[o_video_view removeFromSuperviewWithoutNeedingDisplay];
|
|
[[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
|
|
[o_video_view release];
|
|
[o_video_view setFrame:[o_temp_view frame]];
|
|
if ([[o_video_view subviews] count] > 0)
|
|
[self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
|
|
|
|
[super makeKeyAndOrderFront:self]; /* our version (in main window) contains a workaround */
|
|
|
|
[o_fullscreen_window orderOut: self];
|
|
NSEnableScreenUpdates();
|
|
|
|
[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
|
|
{
|
|
NSArray *viewAnimations;
|
|
if (o_makekey_anim == animation) {
|
|
[o_makekey_anim release];
|
|
return;
|
|
}
|
|
if ([animation currentValue] < 1.0)
|
|
return;
|
|
|
|
/* 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]) {
|
|
/* Fullscreen ended */
|
|
[self hasEndedFullscreen];
|
|
} else
|
|
/* Fullscreen started */
|
|
[self hasBecomeFullscreen];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Accessibility stuff
|
|
|
|
- (NSArray *)accessibilityAttributeNames
|
|
{
|
|
if (!b_dark_interface || !o_titlebar_view)
|
|
return [super accessibilityAttributeNames];
|
|
|
|
static NSMutableArray *attributes = nil;
|
|
if (attributes == nil) {
|
|
attributes = [[super accessibilityAttributeNames] mutableCopy];
|
|
NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
|
|
NSAccessibilityCloseButtonAttribute,
|
|
NSAccessibilityMinimizeButtonAttribute,
|
|
NSAccessibilityZoomButtonAttribute,
|
|
nil];
|
|
|
|
for(NSString *attribute in appendAttributes) {
|
|
if (![attributes containsObject:attribute])
|
|
[attributes addObject:attribute];
|
|
}
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
- (id)accessibilityAttributeValue: (NSString*)o_attribute_name
|
|
{
|
|
if (b_dark_interface && o_titlebar_view) {
|
|
VLCMainWindowTitleView *o_tbv = o_titlebar_view;
|
|
|
|
if ([o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute])
|
|
return NSAccessibilityStandardWindowSubrole;
|
|
|
|
if ([o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute])
|
|
return [[o_tbv closeButton] cell];
|
|
|
|
if ([o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute])
|
|
return [[o_tbv minimizeButton] cell];
|
|
|
|
if ([o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute])
|
|
return [[o_tbv zoomButton] cell];
|
|
}
|
|
|
|
return [super accessibilityAttributeValue: o_attribute_name];
|
|
}
|
|
|
|
@end
|
|
|