X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fmacosx%2Fembeddedwindow.m;h=b5d4a02d065ce5a2c4a58ed316718106c9ebb6a5;hb=6ee1e193fd896ab9a4729fde14f009d9ce629815;hp=661d0f9f589bcdb4d3481afd436b9dec253e53bc;hpb=2f78783f5a6c12321088f02423a590f1af58f8ef;p=vlc diff --git a/modules/gui/macosx/embeddedwindow.m b/modules/gui/macosx/embeddedwindow.m index 661d0f9f58..b5d4a02d06 100644 --- a/modules/gui/macosx/embeddedwindow.m +++ b/modules/gui/macosx/embeddedwindow.m @@ -1,8 +1,8 @@ /***************************************************************************** * embeddedwindow.m: MacOS X interface module ***************************************************************************** - * Copyright (C) 2002-2005 the VideoLAN team - * $Id: playlistinfo.m 12560 2005-09-15 14:21:38Z hartman $ + * Copyright (C) 2005-2006 the VideoLAN team + * $Id$ * * Authors: Benjamin Pracht * @@ -18,16 +18,21 @@ * * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include "intf.h" -#include "vout.h" -#include "embeddedwindow.h" +/* DisableScreenUpdates, SetSystemUIMode, ... */ +#import + +#import "intf.h" +#import "controls.h" +#import "vout.h" +#import "embeddedwindow.h" +#import "fspanel.h" /***************************************************************************** * VLCEmbeddedWindow Implementation @@ -37,7 +42,7 @@ - (void)awakeFromNib { - [o_window setDelegate: self]; + [self setDelegate: self]; [o_btn_backward setToolTip: _NS("Rewind")]; [o_btn_forward setToolTip: _NS("Fast Forward")]; @@ -49,6 +54,22 @@ o_img_play_pressed = [NSImage imageNamed: @"play_embedded_blue"]; o_img_pause = [NSImage imageNamed: @"pause_embedded"]; o_img_pause_pressed = [NSImage imageNamed: @"pause_embedded_blue"]; + + /* Useful to save o_view frame in fullscreen mode */ + o_temp_view = [[NSView alloc] init]; + [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable]; + + o_fullscreen_window = nil; + o_fullscreen_anim1 = o_fullscreen_anim2 = nil; + + /* Not fullscreen when we wake up */ + [o_btn_fullscreen setState: NO]; + b_fullscreen = NO; + /* Use a recursive lock to be able to trigger enter/leavefullscreen + * in middle of an animation, providing that the enter/leave functions + * are called from the same thread */ + o_animation_lock = [[NSRecursiveLock alloc] init]; + b_animation_lock_alreadylocked = NO; } - (void)setTime:(NSString *)o_arg_time position:(float)f_position @@ -80,23 +101,398 @@ [o_slider setEnabled: b_seekable]; } -- (void)setFullscreen:(BOOL)b_fullscreen +- (BOOL)windowShouldZoom:(NSWindow *)sender toFrame:(NSRect)newFrame { - [o_btn_fullscreen setState: b_fullscreen]; + [self setFrame: newFrame display: YES animate: YES]; + return YES; } - (BOOL)windowShouldClose:(id)sender { - playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - if( p_playlist == NULL ) - { - return NO; - } + playlist_t * p_playlist = pl_Yield( VLCIntf ); playlist_Stop( p_playlist ); vlc_object_release( p_playlist ); return YES; } +- (NSView *)mainView +{ + if (o_fullscreen_window) + return o_temp_view; + else + return o_view; +} + +/***************************************************************************** + * Fullscreen support + */ + +- (BOOL)isFullscreen +{ + return b_fullscreen; +} + +- (void)lockFullscreenAnimation +{ + [o_animation_lock lock]; +} + +- (void)unlockFullscreenAnimation +{ + [o_animation_lock unlock]; +} + +- (void)enterFullscreen +{ + NSMutableDictionary *dict1, *dict2; + NSScreen *screen; + NSRect screen_rect; + NSRect rect; + vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT, FIND_ANYWHERE ); + BOOL blackout_other_displays = var_GetBool( p_vout, "macosx-black" ); + + screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_GetInteger( p_vout, "video-device" )]; + + vlc_object_release( p_vout ); + + [self lockFullscreenAnimation]; + + if (!screen) + screen = [self screen]; + + screen_rect = [screen frame]; + + [o_btn_fullscreen setState: YES]; + + [NSCursor setHiddenUntilMouseMoves: YES]; + + if (blackout_other_displays) + [screen blackoutOtherScreens]; /* We should do something like [screen blackoutOtherScreens]; */ + + /* 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 an other window, and do eye catching stuff */ + + rect = [[o_view superview] convertRect: [o_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]; + + if (![self isVisible] || [self alphaValue] == 0.0 || MACOS_VERSION < 10.4f) + { + /* We don't animate if we are not visible or if we are running on + * Mac OS X <10.4 which doesn't support NSAnimation, instead we + * simply fade the display */ + CGDisplayFadeReservationToken token; + + [o_fullscreen_window setFrame:screen_rect display:NO]; + + CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token); + CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES ); + + if ([screen isMainScreen]) + SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar); + + [[self contentView] replaceSubview:o_view with:o_temp_view]; + [o_temp_view setFrame:[o_view frame]]; + [o_fullscreen_window setContentView:o_view]; + [o_fullscreen_window makeKeyAndOrderFront:self]; + [self orderOut: self]; + + CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO ); + CGReleaseDisplayFadeReservation( token); + + /* Will release the lock */ + [self hasBecomeFullscreen]; + + return; + } + + /* Make sure we don't see the o_view disappearing of the screen during this operation */ + DisableScreenUpdates(); + [[self contentView] replaceSubview:o_view with:o_temp_view]; + [o_temp_view setFrame:[o_view frame]]; + [o_fullscreen_window setContentView:o_view]; + [o_fullscreen_window makeKeyAndOrderFront:self]; + EnableScreenUpdates(); + } + + if (MACOS_VERSION < 10.4f) + { + /* We were already fullscreen nothing to do when NSAnimation + * is not supported */ + b_animation_lock_alreadylocked = NO; + [self unlockFullscreenAnimation]; + return; + } + + /* We are in fullscreen (and no animation is running) */ + if (b_fullscreen) + { + /* Make sure we are hidden */ + [super orderOut: self]; + b_animation_lock_alreadylocked = NO; + [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]; + } + + /* This is a recursive lock. If we are already in the middle of an animation we + * unlock it. We don't add an extra locking here, because enter/leavefullscreen + * are executed always in the same thread */ + if (b_animation_lock_alreadylocked) + [self unlockFullscreenAnimation]; + b_animation_lock_alreadylocked = YES; + + if ([screen isMainScreen]) + SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar); + + 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 arrayWithObjects:dict1, nil]]; + o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]]; + + [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 +{ + [o_fullscreen_window makeFirstResponder: [[[VLCMain sharedInstance] getControls] getVoutView]]; + + [o_fullscreen_window makeKeyWindow]; + [o_fullscreen_window setAcceptsMouseMovedEvents: TRUE]; + + /* tell the fspanel to move itself to front next time it's triggered */ + [[[[VLCMain sharedInstance] getControls] getFSPanel] setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]]; + + [super orderOut: self]; + + [[[[VLCMain sharedInstance] getControls] getFSPanel] setActive: nil]; + b_fullscreen = YES; + [self unlockFullscreenAnimation]; +} + +- (void)leaveFullscreen +{ + [self leaveFullscreenAndFadeOut: NO]; +} + +- (void)leaveFullscreenAndFadeOut: (BOOL)fadeout +{ + NSMutableDictionary *dict1, *dict2; + NSRect frame; + + [self lockFullscreenAnimation]; + + b_fullscreen = NO; + [o_btn_fullscreen setState: NO]; + + /* We always try to do so */ + [NSScreen unblackoutScreens]; + + /* Don't do anything if o_fullscreen_window is already closed */ + if (!o_fullscreen_window) + { + b_animation_lock_alreadylocked = NO; + [self unlockFullscreenAnimation]; + return; + } + + if (fadeout || MACOS_VERSION < 10.4f) + { + /* We don't animate if we are not visible or if we are running on + * Mac OS X <10.4 which doesn't support NSAnimation, instead we + * simply fade the display */ + CGDisplayFadeReservationToken token; + + CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token); + CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES ); + + [[[[VLCMain sharedInstance] getControls] getFSPanel] setNonActive: nil]; + SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar); + + /* Will release the lock */ + [self hasEndedFullscreen]; + + CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO ); + CGReleaseDisplayFadeReservation( token); + return; + } + + [self setAlphaValue: 0.0]; + [self orderFront: self]; + + [[[[VLCMain sharedInstance] getControls] getFSPanel] setNonActive: nil]; + SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar); + + if (o_fullscreen_anim1) + { + [o_fullscreen_anim1 stopAnimation]; + [o_fullscreen_anim1 release]; + } + if (o_fullscreen_anim2) + { + [o_fullscreen_anim2 stopAnimation]; + [o_fullscreen_anim2 release]; + } + + /* This is a recursive lock. If we are already in the middle of an animation we + * unlock it. We don't add an extra locking here, because enter/leavefullscreen + * are executed always in the same thread */ + if (b_animation_lock_alreadylocked) + [self unlockFullscreenAnimation]; + b_animation_lock_alreadylocked = YES; + + 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 +{ + /* 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_view disappearing of the screen during this operation */ + DisableScreenUpdates(); + [o_view retain]; + [o_view removeFromSuperviewWithoutNeedingDisplay]; + [[self contentView] replaceSubview:o_temp_view with:o_view]; + [o_view release]; + [o_view setFrame:[o_temp_view frame]]; + [self makeFirstResponder: o_view]; + if ([self isVisible]) + [self makeKeyAndOrderFront:self]; + [o_fullscreen_window orderOut: self]; + EnableScreenUpdates(); + + [o_fullscreen_window release]; + o_fullscreen_window = nil; + b_animation_lock_alreadylocked = NO; + [self unlockFullscreenAnimation]; +} + +- (void)animationDidEnd:(NSAnimation*)animation +{ + NSArray *viewAnimations; + + 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]; + } +} + +- (void)orderOut: (id)sender +{ + [super orderOut: sender]; + /* Make sure we leave fullscreen */ + [self leaveFullscreenAndFadeOut: YES]; +} + +/* Make sure setFrame gets executed on main thread especially if we are animating. + * (Thus we won't block the video output thread) */ +- (void)setFrame:(NSRect)frame display:(BOOL)display animate:(BOOL)animate +{ + struct { NSRect frame; BOOL display; BOOL animate;} args; + NSData *packedargs; + + args.frame = frame; + args.display = display; + args.animate = animate; + + packedargs = [NSData dataWithBytes:&args length:sizeof(args)]; + + [self performSelectorOnMainThread:@selector(setFrameOnMainThread:) + withObject: packedargs waitUntilDone: YES]; +} + +- (void)setFrameOnMainThread:(NSData*)packedargs +{ + struct args { NSRect frame; BOOL display; BOOL animate; } * args = (struct args*)[packedargs bytes]; + + [super setFrame: args->frame display: args->display animate:args->animate]; +} @end