X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fmacosx%2Fvout.m;h=bdda538bf230ee4b1903bda306fd3cafeae2b305;hb=79559bdf7ff37fb54364a3ef7917b1e5481dc4c2;hp=b3fb79cf5414ed5796700d4aff746d0b1c9569b1;hpb=ed203c00ea3f6bda55f0494247a5e3e15a6ae748;p=vlc diff --git a/modules/gui/macosx/vout.m b/modules/gui/macosx/vout.m index b3fb79cf54..bdda538bf2 100644 --- a/modules/gui/macosx/vout.m +++ b/modules/gui/macosx/vout.m @@ -1,7 +1,7 @@ /***************************************************************************** * vout.m: MacOS X video output module ***************************************************************************** - * Copyright (C) 2001-2005 VideoLAN + * Copyright (C) 2001-2008 the VideoLAN team * $Id$ * * Authors: Colin Delacroix @@ -9,12 +9,14 @@ * Jon Lech Johansen * Derk-Jan Hartman * Eric Petit + * Benjamin Pracht + * Felix Paul Kühne * * 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 @@ -22,7 +24,7 @@ * * 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. *****************************************************************************/ /***************************************************************************** @@ -30,7 +32,7 @@ *****************************************************************************/ #include /* ENOMEM */ #include /* free() */ -#include /* strerror() */ +#include /* BeginFullScreen, EndFullScreen */ #include @@ -38,75 +40,171 @@ #include #include "intf.h" +#include "fspanel.h" #include "vout.h" +#import "controls.h" +#import "embeddedwindow.h" + +/***************************************************************************** + * DeviceCallback: Callback triggered when the video-device variable is changed + *****************************************************************************/ +int DeviceCallback( vlc_object_t *p_this, const char *psz_variable, + vlc_value_t old_val, vlc_value_t new_val, void *param ) +{ + vlc_value_t val; + vout_thread_t *p_vout = (vout_thread_t *)p_this; + + msg_Dbg( p_vout, "set %d", new_val.i_int ); + var_Create( p_vout->p_libvlc, "video-device", VLC_VAR_INTEGER ); + var_Set( p_vout->p_libvlc, "video-device", new_val ); + + val.b_bool = true; + var_Set( p_vout, "intf-change", val ); + return VLC_SUCCESS; +} + /***************************************************************************** - * VLCWindow implementation + * VLCEmbeddedList implementation *****************************************************************************/ -@implementation VLCWindow +@implementation VLCEmbeddedList -- (id)initWithVout:(vout_thread_t *)_p_vout frame:(NSRect *)s_frame +- (id)init { - int i_timeout; + [super init]; + o_embedded_array = [NSMutableArray array]; + return self; +} - p_vout = _p_vout; +- (id)getEmbeddedVout +{ + unsigned int i; - /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */ - for( i_timeout = 20 ; i_timeout-- ; ) + for( i = 0; i < [o_embedded_array count]; i++ ) { - if( NSApp == NULL ) + id o_vout_view = [o_embedded_array objectAtIndex: i]; + if( ![o_vout_view isUsed] ) { - msleep( INTF_IDLE_SLEEP ); + [o_vout_view setUsed: YES]; + return o_vout_view; } } + return nil; +} - if( NSApp == NULL ) +- (void)releaseEmbeddedVout: (id)o_vout_view +{ + if( [o_embedded_array containsObject: o_vout_view] ) { - /* No MacOS X intf, unable to communicate with MT */ - msg_Err( p_vout, "no MacOS X interface present" ); - return NULL; - } + [o_vout_view setUsed: NO]; + } + else + { + msg_Warn( VLCIntf, "cannot find Video Output"); + } +} - /* p_real_vout: the vout we have to use to check for video-on-top - and a few other things. If we are the QuickTime output, it's us. - It we are the OpenGL provider, it is our parent. */ - if( p_vout->i_object_type == VLC_OBJECT_OPENGL ) +- (void)addEmbeddedVout: (id)o_vout_view +{ + if( ![o_embedded_array containsObject: o_vout_view] ) { - p_real_vout = (vout_thread_t *) p_vout->p_parent; + [o_embedded_array addObject: o_vout_view]; } - else +} + +- (BOOL)windowContainsEmbedded: (id)o_window +{ +/* if( ![[o_window className] isEqualToString: @"VLCVoutWindow"] ) { - p_real_vout = p_vout; + NSLog( @"We were not given a VLCVoutWindow" ); + }*/ + return ([self getViewForWindow: o_window] == nil ? NO : YES ); +} + +- (id)getViewForWindow: (id)o_window +{ + id o_enumerator = [o_embedded_array objectEnumerator]; + id o_current_embedded; + + while( (o_current_embedded = [o_enumerator nextObject]) ) + { + if( [o_current_embedded getWindow] == o_window ) + { + return o_current_embedded; + } } + return nil; +} - p_fullscreen_state = NULL; - i_time_mouse_last_moved = mdate(); +@end - NSScreen * o_screen; - vlc_bool_t b_main_screen; +/***************************************************************************** + * VLCVoutView implementation + *****************************************************************************/ +@implementation VLCVoutView - var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); +- (id)initWithFrame: (NSRect)frameRect +{ + [super initWithFrame: frameRect]; + p_vout = NULL; + o_view = nil; + s_frame = &frameRect; + + p_real_vout = NULL; + o_window = nil; + return self; +} + +- (BOOL)setVout: (vout_thread_t *) vout + subView: (NSView *) view + frame: (NSRect *) frame +{ + int i_device; + NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; + NSArray *o_screens = [NSScreen screens]; + + p_vout = vout; + o_view = view; + s_frame = frame; + + if( [o_screens count] <= 0 ) + { + msg_Err( p_vout, "no OSX screens available" ); + return NO; + } + + p_real_vout = [VLCVoutView getRealVout: p_vout]; + + /* Get the pref value when this is the first time, otherwise retrieve the device from the top level video-device var */ + if( var_Type( p_real_vout->p_libvlc, "video-device" ) == 0 ) + { + i_device = var_GetInteger( p_vout, "macosx-vdev" ); + } + else + { + i_device = var_GetInteger( p_real_vout->p_libvlc, "video-device" ); + } - /* Setup the menuitem for the multiple displays. Read the vlc preference (macosx-vdev) for the primary display */ - NSArray * o_screens = [NSScreen screens]; - if( [o_screens count] > 0 && var_Type( p_real_vout, "video-device" ) == 0 ) + /* Setup the menuitem for the multiple displays. */ + if( var_Type( p_real_vout, "video-device" ) == 0 ) { int i = 1; - vlc_value_t val, val2, text; + vlc_value_t val2, text; NSScreen * o_screen; - var_Get( p_real_vout, "macosx-vdev", &val ); - var_Create( p_real_vout, "video-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); - text.psz_string = _("Video device"); + text.psz_string = _("Fullscreen Video Device"); var_Change( p_real_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL ); NSEnumerator * o_enumerator = [o_screens objectEnumerator]; + val2.i_int = 0; + text.psz_string = _("Default"); + var_Change( p_real_vout, "video-device", + VLC_VAR_ADDCHOICE, &val2, &text ); + var_Set( p_real_vout, "video-device", val2 ); + while( (o_screen = [o_enumerator nextObject]) != NULL ) { char psz_temp[255]; @@ -117,190 +215,150 @@ (int)s_rect.size.width, (int)s_rect.size.height ); text.psz_string = psz_temp; - val2.i_int = i; + val2.i_int = (int)[o_screen displayID]; var_Change( p_real_vout, "video-device", VLC_VAR_ADDCHOICE, &val2, &text ); - - if( ( i - 1 ) == val.i_int ) + if( (int)[o_screen displayID] == i_device ) { var_Set( p_real_vout, "video-device", val2 ); } i++; } - var_AddCallback( p_real_vout, "video-device", vout_VarCallback, + var_AddCallback( p_real_vout, "video-device", DeviceCallback, NULL ); - val2.b_bool = VLC_TRUE; + val2.b_bool = true; var_Set( p_real_vout, "intf-change", val2 ); } - /* Find out on which screen to open the window */ - int i_device = var_GetInteger( p_real_vout, "video-device" ); - if( i_device < 0 ) - { - /* No preference specified. Use the main screen */ - o_screen = [NSScreen mainScreen]; - b_main_screen = 1; - } - else - { - NSArray *o_screens = [NSScreen screens]; - - if( [o_screens count] < (unsigned) i_device ) - { - o_screen = [NSScreen mainScreen]; - b_main_screen = 1; - } - else - { - i_device--; - o_screen = [o_screens objectAtIndex: i_device]; - var_SetInteger( p_real_vout, "macosx-vdev", i_device ); - b_main_screen = ( i_device == 0 ); - } - } + /* Add the view. It's automatically resized to fit the window */ + [self addSubview: o_view]; + [self setAutoresizesSubviews: YES]; + [o_pool release]; - NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; + return YES; +} - if( p_vout->b_fullscreen ) - { - NSRect screen_rect = [o_screen frame]; - screen_rect.origin.x = screen_rect.origin.y = 0; +- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize +{ + [super resizeSubviewsWithOldSize: oldBoundsSize]; + [o_view setFrameSize: [self frame].size]; +} - /* Creates a window with size: screen_rect on o_screen */ - [self initWithContentRect: screen_rect - styleMask: NSBorderlessWindowMask - backing: NSBackingStoreBuffered - defer: YES screen: o_screen]; +- (void)drawRect:(NSRect)rect +{ + /* When there is no subview we draw a black background */ + [self lockFocus]; + [[NSColor blackColor] set]; + NSRectFill(rect); + [self unlockFocus]; +} - if( b_main_screen ) - { - BeginFullScreen( &p_fullscreen_state, NULL, 0, 0, - NULL, NULL, fullScreenAllowEvents ); - } - } - else - { - unsigned int i_stylemask = NSTitledWindowMask | - NSMiniaturizableWindowMask | - NSClosableWindowMask | - NSResizableWindowMask; +- (void)closeVout +{ + [[[[VLCMain sharedInstance] getControls] getFSPanel] fadeOut]; + + /* Make sure we don't see a white flash */ + [[self window] disableScreenUpdatesUntilFlush]; + [o_view removeFromSuperview]; + o_view = nil; + p_vout = NULL; + s_frame = nil; + o_window = nil; + p_real_vout = NULL; +} - NSRect s_rect; - if( !s_frame ) - { - s_rect.size.width = p_vout->i_window_width; - s_rect.size.height = p_vout->i_window_height; - } - else - { - s_rect = *s_frame; - } - - [self initWithContentRect: s_rect - styleMask: i_stylemask - backing: NSBackingStoreBuffered - defer: YES screen: o_screen]; +- (void)updateTitle +{ + NSString * o_title = nil; + NSMutableString * o_mrl = nil; + input_thread_t * p_input; + char * psz_title; - [self setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )]; + if( !p_vout ) return; - if( var_GetBool( p_real_vout, "video-on-top" ) ) - { - [self setLevel: NSStatusWindowLevel]; - } + p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT ); - if( !s_frame ) - { - [self center]; - } - } + if( !p_input ) return; - [self updateTitle]; - [self makeKeyAndOrderFront: nil]; - [self setReleasedWhenClosed: YES]; + input_item_t * p_item = input_GetItem( p_input ); - /* We'll catch mouse events */ - [self setAcceptsMouseMovedEvents: YES]; - [self makeFirstResponder: self]; - - [o_pool release]; - return self; -} + psz_title = input_item_GetNowPlaying ( p_item ); + if( !psz_title ) + psz_title = input_item_GetName( p_item ); -- (void)close -{ - if( p_fullscreen_state ) - { - EndFullScreen( p_fullscreen_state, 0 ); - } - [super close]; -} + if( psz_title ) + o_title = [NSString stringWithUTF8String: psz_title]; -- (void)setOnTop:(BOOL)b_on_top -{ - if( b_on_top ) + char *psz_uri = input_item_GetURI( p_item ); + if( psz_uri ) + o_mrl = [NSMutableString stringWithUTF8String: psz_uri]; + + free( psz_title ); + free( psz_uri ); + + if( !o_title ) + o_title = o_mrl; + + if( o_mrl != nil ) { - [self setLevel: NSStatusWindowLevel]; + /* FIXME once psz_access is exported, we could check if we are + * reading from a file in a smarter way. */ + + NSRange prefix_range = [o_mrl rangeOfString: @"file:"]; + if( prefix_range.location != NSNotFound ) + [o_mrl deleteCharactersInRange: prefix_range]; + + if( [o_mrl characterAtIndex:0] == '/' ) + { + /* it's a local file */ + [o_window setRepresentedFilename: o_mrl]; + } + else + { + /* it's from the network or somewhere else, + * we clear the previous path */ + [o_window setRepresentedFilename: @""]; + } + [o_window setTitle: o_title]; } else { - [self setLevel: NSNormalWindowLevel]; + [o_window setTitle: [NSString stringWithUTF8String: VOUT_TITLE]]; } + vlc_object_release( p_input ); } -- (void)hideMouse:(BOOL)b_hide -{ - BOOL b_inside; - NSPoint ml; - NSView *o_contents = [self contentView]; - - ml = [self convertScreenToBase:[NSEvent mouseLocation]]; - ml = [o_contents convertPoint:ml fromView:nil]; - b_inside = [o_contents mouse: ml inRect: [o_contents bounds]]; - - if( b_hide && b_inside ) - { - [NSCursor setHiddenUntilMouseMoves: YES]; - } - else if( !b_hide ) - { - [NSCursor setHiddenUntilMouseMoves: NO]; - } -} -- (void)manage +- (void)setOnTop:(BOOL)b_on_top { - if( p_fullscreen_state ) + if( b_on_top ) { - if( mdate() - i_time_mouse_last_moved > 3000000 ) - { - [self hideMouse: YES]; - } + [o_window setLevel: NSStatusWindowLevel]; } else { - [self hideMouse: NO]; + [o_window setLevel: NSNormalWindowLevel]; } - - /* Disable screensaver */ - UpdateSystemActivity( UsrActivity ); } -- (void)scaleWindowWithFactor: (float)factor +- (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate { NSSize newsize; int i_corrected_height, i_corrected_width; NSPoint topleftbase; NSPoint topleftscreen; - + if ( !p_vout->b_fullscreen ) { + NSView *mainView; + NSRect new_frame; topleftbase.x = 0; - topleftbase.y = [self frame].size.height; - topleftscreen = [self convertBaseToScreen: topleftbase]; - - if( p_vout->render.i_height * p_vout->render.i_aspect > + topleftbase.y = [o_window frame].size.height; + topleftscreen = [o_window convertBaseToScreen: topleftbase]; + + if( p_vout->render.i_height * p_vout->render.i_aspect > p_vout->render.i_width * VOUT_ASPECT_FACTOR ) { i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect / @@ -315,10 +373,25 @@ newsize.width = (int) ( p_vout->render.i_width * factor ); newsize.height = (int) ( i_corrected_height * factor ); } - - [self setContentSize: newsize]; - - [self setFrameTopLeftPoint: topleftscreen]; + + /* In fullscreen mode we need to use a view that is different from + * ourselves, with the VLCEmbeddedWindow */ + if([o_window isKindOfClass:[VLCEmbeddedWindow class]]) + mainView = [o_window mainView]; + else + mainView = self; + + /* Calculate the window's new size */ + new_frame.size.width = [o_window frame].size.width - + [mainView frame].size.width + newsize.width; + new_frame.size.height = [o_window frame].size.height - + [mainView frame].size.height + newsize.height; + + new_frame.origin.x = topleftscreen.x; + new_frame.origin.y = topleftscreen.y - new_frame.size.height; + + [o_window setFrame: new_frame display: animate animate: animate]; + p_vout->i_changes |= VOUT_SIZE_CHANGE; } } @@ -327,44 +400,60 @@ { vlc_value_t val; - if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool) + if( !p_real_vout ) return; + if( var_Get( p_real_vout, "video-on-top", &val )>=0 && val.b_bool) { - val.b_bool = VLC_FALSE; + val.b_bool = false; } else { - val.b_bool = VLC_TRUE; + val.b_bool = true; } - var_Set( p_vout, "video-on-top", val ); + var_Set( p_real_vout, "video-on-top", val ); } - (void)toggleFullscreen { vlc_value_t val; - val.b_bool = !p_real_vout->b_fullscreen; + if( !p_real_vout ) return; + var_Get( p_real_vout, "fullscreen", &val ); + val.b_bool = !val.b_bool; var_Set( p_real_vout, "fullscreen", val ); } - (BOOL)isFullscreen { - return( p_vout->b_fullscreen ); + vlc_value_t val; + if( !p_real_vout ) return NO; + var_Get( p_real_vout, "fullscreen", &val ); + return( val.b_bool ); } - (void)snapshot { - vout_Control( p_vout, VOUT_SNAPSHOT ); + vout_Control( p_real_vout, VOUT_SNAPSHOT ); } -- (BOOL)canBecomeKeyWindow +- (void)manage { - return YES; + /* Disable Screensaver, when we're playing something, but allow it on pause */ + if( !VLCIntf || !VLCIntf->p_sys || !VLCIntf->p_sys->i_play_status ) + return; + + if( VLCIntf->p_sys->i_play_status == PLAYING_S ) + UpdateSystemActivity( UsrActivity ); } -/* Sometimes crashes VLC.... -- (BOOL)performKeyEquivalent:(NSEvent *)o_event +- (id)getWindow { - return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event]; -}*/ + return o_window; +} + +- (void)scrollWheel:(NSEvent *)theEvent +{ + VLCControls * o_controls = (VLCControls *)[[NSApp delegate] getControls]; + [o_controls scrollWheel: theEvent]; +} - (void)keyDown:(NSEvent *)o_event { @@ -372,7 +461,7 @@ vlc_value_t val; unsigned int i_pressed_modifiers = 0; val.i_int = 0; - + i_pressed_modifiers = [o_event modifierFlags]; if( i_pressed_modifiers & NSShiftKeyMask ) @@ -391,7 +480,7 @@ /* Escape should always get you out of fullscreen */ if( key == (unichar) 0x1b ) { - if( [self isFullscreen] ) + if( p_real_vout && [self isFullscreen] ) { [self toggleFullscreen]; } @@ -400,12 +489,12 @@ { vlc_value_t val; val.i_int = config_GetInt( p_vout, "key-play-pause" ); - var_Set( p_vout->p_vlc, "key-pressed", val ); + var_Set( p_vout->p_libvlc, "key-pressed", val ); } else { val.i_int |= CocoaKeyToVLC( key ); - var_Set( p_vout->p_vlc, "key-pressed", val ); + var_Set( p_vout->p_libvlc, "key-pressed", val ); } } else @@ -414,271 +503,728 @@ } } -- (void)updateTitle +- (void)mouseDown:(NSEvent *)o_event { - NSMutableString * o_title,* o_mrl; - vlc_bool_t b_file = VLC_FALSE; - input_thread_t * p_input; - - if( p_vout == NULL ) - { - return; - } - - p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, - FIND_PARENT ); - - if( p_input == NULL ) - { - return; - } - - if( ! strcmp( p_input->input.p_access->p_module->psz_shortname, "File" ) ) - b_file = VLC_TRUE; - if( p_input->input.p_item->psz_name != NULL ) - o_title = [NSMutableString stringWithUTF8String: - p_input->input.p_item->psz_name]; - if( p_input->input.p_item->psz_uri != NULL ) - o_mrl = [NSMutableString stringWithUTF8String: - p_input->input.p_item->psz_uri]; - if( o_title == nil ) - o_title = o_mrl; + vlc_value_t val; - vlc_object_release( p_input ); - if( o_mrl != nil ) + if( p_vout ) { - if( b_file == VLC_TRUE ) + if( ( [o_event type] == NSLeftMouseDown ) && + ( ! ( [o_event modifierFlags] & NSControlKeyMask ) ) ) { - NSRange prefix_range = [o_mrl rangeOfString: @"file:"]; - if( prefix_range.location != NSNotFound ) - [o_mrl deleteCharactersInRange: prefix_range]; - [self setRepresentedFilename: o_mrl]; + if( [o_event clickCount] <= 1 ) + { + /* single clicking */ + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int |= 1; + var_Set( p_vout, "mouse-button-down", val ); + } + else + { + /* multiple clicking */ + [self toggleFullscreen]; + } + } + else if( ( [o_event type] == NSRightMouseDown ) || + ( ( [o_event type] == NSLeftMouseDown ) && + ( [o_event modifierFlags] & NSControlKeyMask ) ) ) + { + msg_Dbg( p_vout, "received NSRightMouseDown (generic method) or Ctrl clic" ); + [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] getVoutView]]; } - [self setTitle: o_title]; - } - else - { - [self setTitle: [NSString stringWithCString: VOUT_TITLE]]; } + + [super mouseDown: o_event]; } -/* This is actually the same as VLCControls::stop. */ -- (BOOL)windowShouldClose:(id)sender +- (void)otherMouseDown:(NSEvent *)o_event { - playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - if( p_playlist == NULL ) + vlc_value_t val; + + if( p_vout && [o_event type] == NSOtherMouseDown ) { - return NO; + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int |= 2; + var_Set( p_vout, "mouse-button-down", val ); } - playlist_Stop( p_playlist ); - vlc_object_release( p_playlist ); - - /* The window will be closed by the intf later. */ - return NO; + [super mouseDown: o_event]; } -- (BOOL)acceptsFirstResponder +- (void)rightMouseDown:(NSEvent *)o_event { - return YES; -} + if( p_vout && [o_event type] == NSRightMouseDown ) + { + msg_Dbg( p_vout, "received NSRightMouseDown (specific method)" ); + [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] getVoutView]]; + } -- (BOOL)becomeFirstResponder -{ - return YES; + [super mouseDown: o_event]; } -- (BOOL)resignFirstResponder +- (void)mouseUp:(NSEvent *)o_event { - /* We need to stay the first responder or we'll miss some - events */ - return NO; + vlc_value_t val; + + if( p_vout && [o_event type] == NSLeftMouseUp ) + { + vlc_value_t b_val; + b_val.b_bool = true; + var_Set( p_vout, "mouse-clicked", b_val ); + + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int &= ~1; + var_Set( p_vout, "mouse-button-down", val ); + } + + [super mouseUp: o_event]; } -- (void)mouseDown:(NSEvent *)o_event +- (void)otherMouseUp:(NSEvent *)o_event { vlc_value_t val; - switch( [o_event type] ) + if( p_vout && [o_event type] == NSOtherMouseUp ) { - case NSLeftMouseDown: - { - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int |= 1; - var_Set( p_vout, "mouse-button-down", val ); - } - break; + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int &= ~2; + var_Set( p_vout, "mouse-button-down", val ); + } - default: - [super mouseDown: o_event]; - break; + [super mouseUp: o_event]; +} + +- (void)rightMouseUp:(NSEvent *)o_event +{ + if( p_vout && [o_event type] == NSRightMouseUp ) + { + /* FIXME: this isn't the appropriate place, but we can't receive + * NSRightMouseDown some how */ + msg_Dbg( p_vout, "received NSRightMouseUp" ); + [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] getVoutView]]; } + + [super mouseUp: o_event]; } -- (void)otherMouseDown:(NSEvent *)o_event +- (void)mouseDragged:(NSEvent *)o_event { - vlc_value_t val; + [self mouseMoved: o_event]; +} + +- (void)otherMouseDragged:(NSEvent *)o_event +{ + [self mouseMoved: o_event]; +} + +- (void)rightMouseDragged:(NSEvent *)o_event +{ + [self mouseMoved: o_event]; +} - switch( [o_event type] ) +- (void)mouseMoved:(NSEvent *)o_event +{ + NSPoint ml; + NSRect s_rect; + BOOL b_inside; + + if( p_vout ) { - case NSOtherMouseDown: + s_rect = [o_view bounds]; + ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil]; + b_inside = [o_view mouse: ml inRect: s_rect]; + + if( b_inside ) { - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int |= 2; - var_Set( p_vout, "mouse-button-down", val ); + vlc_value_t val; + unsigned int i_width, i_height, i_x, i_y; + + vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width, + (unsigned int)s_rect.size.height, + &i_x, &i_y, &i_width, &i_height ); + + val.i_int = ( ((int)ml.x) - i_x ) * + p_vout->render.i_width / i_width; + var_Set( p_vout, "mouse-x", val ); + + if( [[o_view className] isEqualToString: @"VLCGLView"] ) + { + val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) * + p_vout->render.i_height / i_height; + } + else + { + val.i_int = ( ((int)ml.y) - i_y ) * + p_vout->render.i_height / i_height; + } + var_Set( p_vout, "mouse-y", val ); + + val.b_bool = true; + var_Set( p_vout, "mouse-moved", val ); } - break; + if( [self isFullscreen] ) + [[[[VLCMain sharedInstance] getControls] getFSPanel] fadeIn]; + } + + [super mouseMoved: o_event]; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)becomeFirstResponder +{ + return YES; +} + +- (BOOL)resignFirstResponder +{ + /* We need to stay the first responder or we'll miss some + events */ + return NO; +} - default: - [super mouseDown: o_event]; - break; +/* Class methods used by the different vout modules */ + ++ (vout_thread_t *)getRealVout: (vout_thread_t *)p_vout +{ + /* p_real_vout: the vout we have to use to check for video-on-top + and a few other things. If we are the QuickTime output, it's us. + It we are the OpenGL provider, it is our parent. */ + if( p_vout->i_object_type == VLC_OBJECT_OPENGL ) + { + return (vout_thread_t *) p_vout->p_parent; + } + else + { + return p_vout; } + } -- (void)rightMouseDown:(NSEvent *)o_event ++ (id)getVoutView: (vout_thread_t *)p_vout subView: (NSView *)view + frame: (NSRect *)s_frame { - vlc_value_t val; + vlc_value_t value_drawable; + int i_timeout; + id o_return = nil; - switch( [o_event type] ) + var_Get( p_vout->p_libvlc, "drawable", &value_drawable ); + + var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "macosx-background", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "macosx-black", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "embedded-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + + /* We only wait for NSApp to initialise if we're not embedded (as in the + * case of the Mozilla plugin). We can tell whether we're embedded or not + * by examining the "drawable" value: if it's zero, we're running in the + * main Mac intf; if it's non-zero, we're embedded. */ + if( value_drawable.i_int == 0 ) { - case NSRightMouseDown: + /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */ + for( i_timeout = 20 ; i_timeout-- ; ) + { + if( NSApp == NULL ) + { + msleep( INTF_IDLE_SLEEP ); + } + } + + if( NSApp == NULL ) + { + /* No MacOS X intf, unable to communicate with MT */ + msg_Err( p_vout, "no MacOS X interface present" ); + return nil; + } + else { - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int |= 4; - var_Set( p_vout, "mouse-button-down", val ); + if ( VLCIntf && !(p_vout->b_fullscreen) && + !(var_GetBool( p_vout, "macosx-background" )) && + var_GetBool( p_vout, "embedded-video") ) + { + o_return = [[[VLCMain sharedInstance] getEmbeddedList] + getEmbeddedVout]; + } } - break; + } - default: - [super mouseDown: o_event]; - break; + /* No embedded vout is available */ + if( o_return == nil ) + { + NSRect null_rect; + bzero( &null_rect, sizeof( NSRect ) ); + o_return = [[VLCDetachedVoutView alloc] initWithFrame: null_rect ]; } + [o_return setVout: p_vout subView: view frame: s_frame]; + return o_return; } -- (void)mouseUp:(NSEvent *)o_event +- (void)enterFullscreen { - vlc_value_t val; + /* Save the settings for next playing item */ + playlist_t * p_playlist = pl_Yield( p_real_vout ); + var_SetBool( p_playlist, "fullscreen", true ); + pl_Release( p_real_vout ); +} - switch( [o_event type] ) - { - case NSLeftMouseUp: - { - vlc_value_t b_val; - b_val.b_bool = VLC_TRUE; - var_Set( p_vout, "mouse-clicked", b_val ); +- (void)leaveFullscreen +{ + /* Save the settings for next playing item */ + playlist_t * p_playlist = pl_Yield( p_real_vout ); + var_SetBool( p_playlist, "fullscreen", false ); + pl_Release( p_real_vout ); +} - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int &= ~1; - var_Set( p_vout, "mouse-button-down", val ); - } - break; +@end + +/***************************************************************************** + * VLCDetachedVoutView implementation + *****************************************************************************/ +@implementation VLCDetachedVoutView + +- (id)initWithFrame: (NSRect)frameRect +{ + [super initWithFrame: frameRect]; + i_time_mouse_last_moved = 0; + return self; +} + +- (bool)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view + frame: (NSRect *) s_arg_frame +{ + BOOL b_return = [super setVout: p_arg_vout subView: view frame:s_arg_frame]; + i_time_mouse_last_moved = mdate(); + o_window = [[VLCVoutWindow alloc] initWithVout: p_arg_vout view: self + frame: s_arg_frame]; + [self updateTitle]; + [view setFrame: [self frame]]; - default: - [super mouseUp: o_event]; - break; + if( var_GetBool( p_real_vout, "video-on-top" ) ) + { + [o_window setLevel: NSStatusWindowLevel]; } + + [o_window setAcceptsMouseMovedEvents: TRUE]; + return b_return; } -- (void)otherMouseUp:(NSEvent *)o_event +- (void)closeVout { - vlc_value_t val; + [o_window closeWindow]; + [o_window setAcceptsMouseMovedEvents: NO]; + i_time_mouse_last_moved = 0; + [super closeVout]; +} + +- (void)mouseMoved:(NSEvent *)o_event +{ + i_time_mouse_last_moved = mdate(); + [super mouseMoved: o_event]; +} + +- (void)hideMouse:(BOOL)b_hide +{ + BOOL b_inside; + NSPoint ml; + NSView *o_contents = [o_window contentView]; + + ml = [o_window convertScreenToBase:[NSEvent mouseLocation]]; + ml = [o_contents convertPoint:ml fromView:nil]; + b_inside = [o_contents mouse: ml inRect: [o_contents bounds]]; + + if( b_hide && b_inside ) + { + [NSCursor setHiddenUntilMouseMoves: YES]; + } + else if( !b_hide ) + { + [NSCursor setHiddenUntilMouseMoves: NO]; + } +} - switch( [o_event type] ) +- (void)manage +{ + [super manage]; + unsigned int i_mouse_hide_timeout = + var_GetInteger(p_vout, "mouse-hide-timeout") * 1000; + if( p_vout->b_fullscreen ) { - case NSOtherMouseUp: + if( mdate() - i_time_mouse_last_moved > i_mouse_hide_timeout ) { - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int &= ~2; - var_Set( p_vout, "mouse-button-down", val ); + [self hideMouse: YES]; } - break; + } + else + { + [self hideMouse: NO]; + } +} + + +- (void)enterFullscreen +{ + [super enterFullscreen]; - default: - [super mouseUp: o_event]; - break; + if( var_GetBool( p_real_vout, "video-on-top" ) ) + { + [o_window setLevel: NSNormalWindowLevel]; } + + [[o_view class] performSelectorOnMainThread:@selector(resetVout:) withObject:[NSValue valueWithPointer:p_vout] waitUntilDone:YES]; + [[[[VLCMain sharedInstance] getControls] getFSPanel] setActive: nil]; } -- (void)rightMouseUp:(NSEvent *)o_event +- (void)leaveFullscreen { - vlc_value_t val; + [super leaveFullscreen]; + + if( var_GetBool( p_real_vout, "video-on-top" ) ) + { + [o_window setLevel: NSStatusWindowLevel]; + } + + [[o_view class] performSelectorOnMainThread:@selector(resetVout:) withObject:[NSValue valueWithPointer:p_vout] waitUntilDone:YES]; + [[[[VLCMain sharedInstance] getControls] getFSPanel] setNonActive: nil]; +} + +@end - switch( [o_event type] ) +/***************************************************************************** + * VLCEmbeddedVoutView implementation + *****************************************************************************/ + +@implementation VLCEmbeddedVoutView + +- (void)awakeFromNib +{ + o_embeddedwindow = [self window]; +} + +- (id)initWithFrame: (NSRect)frameRect +{ + if(self = [super initWithFrame: frameRect]) { - case NSRightMouseUp: + b_used = NO; + [[[VLCMain sharedInstance] getEmbeddedList] addEmbeddedVout: self]; + o_embeddedwindow = nil; /* Filled later on in -awakeFromNib */ + } + return self; +} + +- (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view + frame: (NSRect *)s_arg_frame +{ + BOOL b_return; + + [NSObject cancelPreviousPerformRequestsWithTarget:o_window]; + + b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame]; + if( b_return ) + { + o_window = [self window]; + + [o_window setAcceptsMouseMovedEvents: TRUE]; + + if( var_GetBool( p_real_vout, "video-on-top" ) ) { - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int &= ~4; - var_Set( p_vout, "mouse-button-down", val ); + [o_window setLevel: NSStatusWindowLevel]; } - break; - default: - [super mouseUp: o_event]; - break; + [view setFrameSize: [self frame].size]; } + + /* o_window needs to point to our o_embeddedwindow, super might have set it + * to the fullscreen window that o_embeddedwindow setups during fullscreen */ + o_window = o_embeddedwindow; + + if( b_return ) + { + [o_window lockFullscreenAnimation]; + + [o_window setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )]; + + [self updateTitle]; + + [NSObject cancelPreviousPerformRequestsWithTarget:o_window]; + + /* Make the window the front and key window before animating */ + if ([o_window isVisible] && (![o_window isFullscreen])) + [o_window makeKeyAndOrderFront: self]; + + [self scaleWindowWithFactor: 1.0 animate: [o_window isVisible] && (![o_window isFullscreen])]; + + [o_window setAspectRatio:NSMakeSize([o_window frame].size.width, [o_window frame].size.height)]; + + /* Make sure our window is visible, if we are not in fullscreen */ + if (![o_window isFullscreen]) + [o_window makeKeyAndOrderFront: self]; + [o_window unlockFullscreenAnimation]; + + } + + return b_return; } -- (void)mouseDragged:(NSEvent *)o_event +- (void)setUsed: (BOOL)b_new_used { - [self mouseMoved: o_event]; + b_used = b_new_used; } -- (void)otherMouseDragged:(NSEvent *)o_event +- (BOOL)isUsed { - [self mouseMoved: o_event]; + return b_used; } -- (void)rightMouseDragged:(NSEvent *)o_event +- (void)closeVout { - [self mouseMoved: o_event]; + [super closeVout]; + + /* Don't close the window yet, wait a bit to see if a new input is poping up */ + /* FIXME: Probably fade the window In and Out */ + /* FIXME: fix core */ + [o_embeddedwindow performSelector:@selector(orderOut:) withObject:nil afterDelay:1.5]; + + [o_window setAcceptsMouseMovedEvents: NO]; + [[[VLCMain sharedInstance] getEmbeddedList] releaseEmbeddedVout: self]; } -- (void)mouseMoved:(NSEvent *)o_event -{ - NSPoint ml; - NSRect s_rect; - BOOL b_inside; - NSView * o_view; +- (void)enterFullscreen +{ + /* Save settings */ + [super enterFullscreen]; - i_time_mouse_last_moved = mdate(); + /* We are in a VLCEmbeddedWindow */ + [o_embeddedwindow performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES]; +} + +- (void)leaveFullscreen +{ + /* Save settings */ + [super leaveFullscreen]; + + /* We are in a VLCEmbeddedWindow */ + [o_embeddedwindow performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: YES]; +} +@end + +/***************************************************************************** + * VLCVoutWindow implementation + *****************************************************************************/ +@implementation VLCVoutWindow + +- (id) initWithVout: (vout_thread_t *) vout view: (VLCVoutView *) view + frame: (NSRect *) frame +{ + p_vout = vout; + o_view = view; + s_frame = frame; + + [self performSelectorOnMainThread: @selector(initMainThread:) + withObject: NULL waitUntilDone: YES]; + + if( !b_init_ok ) + { + return NULL; + } + + return self; +} + +- (id)initMainThread: (id) sender +{ + NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; + NSArray *o_screens = [NSScreen screens]; + NSScreen *o_screen; + bool b_menubar_screen = false; + int i_device; - o_view = [self contentView]; - s_rect = [o_view bounds]; - ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil]; - b_inside = [o_view mouse: ml inRect: s_rect]; + b_init_ok = false; - if( b_inside ) + p_real_vout = [VLCVoutView getRealVout: p_vout]; + i_device = var_GetInteger( p_real_vout->p_libvlc, "video-device" ); + b_black = NO; + b_embedded = var_GetBool( p_vout, "embedded-video" ); + + /* Find out on which screen to open the window */ + o_screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device]; + + if( !o_screen ) + o_screen = [NSScreen mainScreen]; + + if( [o_screen isMainScreen] ) + b_menubar_screen = true; + + if( p_vout->b_fullscreen ) { - vlc_value_t val; - int i_width, i_height, i_x, i_y; + CGDisplayFadeReservationToken token; + NSRect screen_rect = [o_screen frame]; + screen_rect.origin.x = screen_rect.origin.y = 0; - vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width, - (unsigned int)s_rect.size.height, - &i_x, &i_y, &i_width, &i_height ); + b_black = var_GetBool( p_vout, "macosx-black" ); - val.i_int = ( ((int)ml.x) - i_x ) * - p_vout->render.i_width / i_width; - var_Set( p_vout, "mouse-x", val ); + /* move the FSPanel to front in case that it is currently shown + * this won't and is not supposed to work when it's fading right now */ + if( [[[[VLCMain sharedInstance] getControls] getFSPanel] isDisplayed] ) + [[[[VLCMain sharedInstance] getControls] getFSPanel] setActive: nil]; + + /* tell the fspanel to move itself to front next time it's triggered */ + [[[[VLCMain sharedInstance] getControls] getFSPanel] setVoutWasUpdated: i_device]; - if( [[o_view className] isEqualToString: @"VLCGLView"] ) + /* Creates a window with size: screen_rect on o_screen */ + [self initWithContentRect: screen_rect + styleMask: NSBorderlessWindowMask + backing: NSBackingStoreBuffered + defer: YES screen: o_screen]; + + if( b_menubar_screen ) { - val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) * - p_vout->render.i_height / i_height; + SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar); + } + if( b_black == true ) + { + CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token); + CGDisplayFade( token, 0.6 , kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES ); + + [o_screen blackoutOtherScreens]; + + CGDisplayFade( token, 0.3 , kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO ); + CGReleaseDisplayFadeReservation( token); + } + } + else if( var_GetBool( p_vout, "macosx-background" ) ) + { + NSRect screen_rect = [o_screen frame]; + screen_rect.origin.x = screen_rect.origin.y = 0; + + /* Creates a window with size: screen_rect on o_screen */ + [self initWithContentRect: screen_rect + styleMask: NSBorderlessWindowMask + backing: NSBackingStoreBuffered + defer: YES screen: o_screen]; + + [self setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey)]; + } + else + { + unsigned int i_stylemask = NSTitledWindowMask | + NSMiniaturizableWindowMask | + NSClosableWindowMask | + NSResizableWindowMask; + + NSRect s_rect; + if( !s_frame ) + { + s_rect.size.width = p_vout->i_window_width; + s_rect.size.height = p_vout->i_window_height; } else { - val.i_int = ( ((int)ml.y) - i_y ) * - p_vout->render.i_height / i_height; + s_rect = *s_frame; + } + + [self initWithContentRect: s_rect + styleMask: i_stylemask + backing: NSBackingStoreBuffered + defer: YES screen: o_screen]; + + [self setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )]; + + if( !s_frame ) + { + [self center]; } - var_Set( p_vout, "mouse-y", val ); - - val.b_bool = VLC_TRUE; - var_Set( p_vout, "mouse-moved", val ); } - [super mouseMoved: o_event]; + [self makeKeyAndOrderFront: nil]; + [self setReleasedWhenClosed: YES]; + + /* We'll catch mouse events */ + [self makeFirstResponder: o_view]; + + /* Add the view. It's automatically resized to fit the window */ + [self setContentView: o_view]; + + [o_pool release]; + + b_init_ok = true; + return self; +} + +- (void)close +{ + [o_view closeVout]; +} + +- (void)closeWindow +{ + /* XXX waitUntilDone = NO to avoid a possible deadlock when hitting + Command-Q */ + [self performSelectorOnMainThread: @selector(closeMainThread:) + withObject: NULL waitUntilDone: NO]; +} + +- (id)closeMainThread:(id)sender +{ + if( b_black == true ) + { + CGDisplayFadeReservationToken token; + CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token); + CGDisplayFade( token, 0.3 , kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES ); + + [self disableScreenUpdatesUntilFlush]; + [self orderOut: self]; + + CGDisplayFade( token, 0.6 , kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, YES ); + CGReleaseDisplayFadeReservation( token); + CGDisplayRestoreColorSyncSettings(); + } + [NSScreen unblackoutScreens]; + + SetSystemUIMode( kUIModeNormal, 0); + [super close]; + /* this does only work in embedded mode */ + if( b_embedded == true ) + [[[[VLCMain sharedInstance] getControls] getFSPanel] orderOut: self]; + + return NULL; +} + +- (id)getVoutView +{ + return o_view; +} + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +/* Sometimes crashes VLC.... +- (BOOL)performKeyEquivalent:(NSEvent *)o_event +{ + return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event]; +}*/ + +/* This is actually the same as VLCControls::stop. */ + +- (BOOL)windowShouldClose:(id)sender +{ + playlist_t * p_playlist = pl_Yield( p_vout ); + if( p_playlist == NULL ) + { + return NO; + } + + playlist_Stop( p_playlist ); + vlc_object_release( p_playlist ); + + /* The window will be closed by the intf later. */ + return NO; } @end