/*****************************************************************************
* vout.m: MacOS X video output module
*****************************************************************************
- * Copyright (C) 2001-2005 VideoLAN
+ * Copyright (C) 2001-2008 the VideoLAN team
* $Id$
*
* Authors: Colin Delacroix <colin@zoy.org>
* Jon Lech Johansen <jon-vl@nanocrew.net>
* Derk-Jan Hartman <hartman at videolan dot org>
* Eric Petit <titer@m0k.org>
+ * Benjamin Pracht <bigben at videolan dot org>
+ * Felix Paul Kühne <fkuehne at videolan dot org>
*
* 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
*
* 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.
*****************************************************************************/
/*****************************************************************************
*****************************************************************************/
#include <errno.h> /* ENOMEM */
#include <stdlib.h> /* free() */
-#include <string.h> /* strerror() */
+#include <string.h>
/* BeginFullScreen, EndFullScreen */
#include <QuickTime/QuickTime.h>
#include <vlc_keys.h>
#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
{
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_vlc, "video-device", VLC_VAR_INTEGER );
- var_Set( p_vout->p_vlc, "video-device", new_val );
-
- val.b_bool = VLC_TRUE;
+ 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 *) vout frame: (NSRect *) frame
+- (id)init
{
- p_vout = vout;
- s_frame = frame;
+ [super init];
+ o_embedded_array = [NSMutableArray array];
+ return self;
+}
+
+- (id)getEmbeddedVout
+{
+ unsigned int i;
+
+ for( i = 0; i < [o_embedded_array count]; i++ )
+ {
+ id o_vout_view = [o_embedded_array objectAtIndex: i];
+ if( ![o_vout_view isUsed] )
+ {
+ [o_vout_view setUsed: YES];
+ return o_vout_view;
+ }
+ }
+ return nil;
+}
- if( MACOS_VERSION >= 10.2 )
+- (void)releaseEmbeddedVout: (id)o_vout_view
+{
+ if( [o_embedded_array containsObject: o_vout_view] )
{
- [self performSelectorOnMainThread: @selector(initReal:)
- withObject: NULL waitUntilDone: YES];
+ [o_vout_view setUsed: NO];
}
else
{
- [self initReal: NULL];
+ msg_Warn( VLCIntf, "cannot find Video Output");
}
+}
- if( !b_init_ok )
+- (void)addEmbeddedVout: (id)o_vout_view
+{
+ if( ![o_embedded_array containsObject: o_vout_view] )
{
- return NULL;
+ [o_embedded_array addObject: o_vout_view];
}
-
- return self;
}
-- (id) initReal: (id) sender
+- (BOOL)windowContainsEmbedded: (id)o_window
{
- NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
- NSArray *o_screens = [NSScreen screens];
- NSScreen *o_screen;
- vlc_bool_t b_menubar_screen = VLC_FALSE;
- int i_timeout, i_device;
- vlc_value_t value_drawable;
-
- b_init_ok = VLC_FALSE;
+/* if( ![[o_window className] isEqualToString: @"VLCVoutWindow"] )
+ {
+ NSLog( @"We were not given a VLCVoutWindow" );
+ }*/
+ return ([self getViewForWindow: o_window] == nil ? NO : YES );
+}
- var_Get( p_vout->p_vlc, "drawable", &value_drawable );
+- (id)getViewForWindow: (id)o_window
+{
+ id o_enumerator = [o_embedded_array objectEnumerator];
+ id o_current_embedded;
- /* 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 )
+ while( (o_current_embedded = [o_enumerator nextObject]) )
{
- /* 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 )
+ if( [o_current_embedded getWindow] == o_window )
{
- /* No MacOS X intf, unable to communicate with MT */
- msg_Err( p_vout, "no MacOS X interface present" );
- return NULL;
+ return o_current_embedded;
}
}
-
+ return nil;
+}
+
+@end
+
+/*****************************************************************************
+ * VLCVoutView implementation
+ *****************************************************************************/
+@implementation VLCVoutView
+
+- (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 NULL;
- }
-
- /* 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 )
- {
- p_real_vout = (vout_thread_t *) p_vout->p_parent;
- }
- else
- {
- p_real_vout = p_vout;
+ return NO;
}
- p_fullscreen_state = NULL;
- i_time_mouse_last_moved = mdate();
-
- 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 );
+ 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_vlc, "video-device" ) == 0 )
+ 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_vlc, "video-device" );
+ i_device = var_GetInteger( p_real_vout->p_libvlc, "video-device" );
}
/* Setup the menuitem for the multiple displays. */
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];
(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 == i_device )
+ if( (int)[o_screen displayID] == i_device )
{
var_Set( p_real_vout, "video-device", val2 );
}
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 */
- if( i_device <= 0 || i_device > (int)[o_screens count] )
- {
- /* No preference specified. Use the main screen */
- o_screen = [NSScreen mainScreen];
- if( o_screen == [o_screens objectAtIndex: 0] )
- b_menubar_screen = VLC_TRUE;
- }
- else
- {
- i_device--;
- o_screen = [o_screens objectAtIndex: i_device];
- b_menubar_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];
- if( p_vout->b_fullscreen )
- {
- NSRect screen_rect = [o_screen frame];
- screen_rect.origin.x = screen_rect.origin.y = 0;
+ return YES;
+}
- /* Creates a window with size: screen_rect on o_screen */
- [self initWithContentRect: screen_rect
- styleMask: NSBorderlessWindowMask
- backing: NSBackingStoreBuffered
- defer: YES screen: o_screen];
+- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize
+{
+ [super resizeSubviewsWithOldSize: oldBoundsSize];
+ [o_view setFrameSize: [self frame].size];
+}
- if( b_menubar_screen )
- {
- BeginFullScreen( &p_fullscreen_state, NULL, 0, 0,
- NULL, NULL, fullScreenAllowEvents );
- }
- }
- else
- {
- unsigned int i_stylemask = NSTitledWindowMask |
- NSMiniaturizableWindowMask |
- NSClosableWindowMask |
- NSResizableWindowMask;
+- (void)drawRect:(NSRect)rect
+{
+ /* When there is no subview we draw a black background */
+ [self lockFocus];
+ [[NSColor blackColor] set];
+ NSRectFill(rect);
+ [self unlockFocus];
+}
- 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)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;
+}
- [self setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
+- (void)updateTitle
+{
+ NSString * o_title = nil;
+ NSMutableString * o_mrl = nil;
+ input_thread_t * p_input;
+ char * psz_title;
- if( var_GetBool( p_real_vout, "video-on-top" ) )
- {
- [self setLevel: NSStatusWindowLevel];
- }
+ if( !p_vout ) return;
- if( !s_frame )
- {
- [self center];
- }
- }
+ p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
- [self updateTitle];
- [self makeKeyAndOrderFront: nil];
- [self setReleasedWhenClosed: YES];
+ if( !p_input ) return;
- /* We'll catch mouse events */
- [self setAcceptsMouseMovedEvents: YES];
- [self makeFirstResponder: self];
-
- [o_pool release];
+ input_item_t * p_item = input_GetItem( p_input );
- b_init_ok = VLC_TRUE;
- return self;
-}
+ psz_title = input_item_GetNowPlaying ( p_item );
+ if( !psz_title )
+ psz_title = input_item_GetName( p_item );
-- (void)close
-{
- [super close];
- if( p_fullscreen_state )
- {
- EndFullScreen( p_fullscreen_state, 0 );
- }
-}
+ 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 /
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;
}
}
{
vlc_value_t val;
+ 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_real_vout, "video-on-top", val );
}
- (void)toggleFullscreen
{
vlc_value_t val;
+ 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
{
vlc_value_t val;
+ if( !p_real_vout ) return NO;
var_Get( p_real_vout, "fullscreen", &val );
return( val.b_bool );
}
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
{
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 )
/* Escape should always get you out of fullscreen */
if( key == (unichar) 0x1b )
{
- if( [self isFullscreen] )
+ if( p_real_vout && [self isFullscreen] )
{
[self toggleFullscreen];
}
{
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
}
}
-- (void)updateTitle
+- (void)mouseDown:(NSEvent *)o_event
{
- NSMutableString * o_title = NULL, * o_mrl = NULL;
- 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( 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( p_input->input.p_access && !strcmp( p_input->input.p_access->p_module->psz_shortname, "File" ) )
+ 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
+
+ [super mouseDown: o_event];
+}
+
+- (void)otherMouseDown:(NSEvent *)o_event
+{
+ vlc_value_t val;
+
+ if( p_vout && [o_event type] == NSOtherMouseDown )
{
- [self setTitle: [NSString stringWithCString: VOUT_TITLE]];
+ var_Get( p_vout, "mouse-button-down", &val );
+ val.i_int |= 2;
+ var_Set( p_vout, "mouse-button-down", val );
}
+
+ [super mouseDown: o_event];
}
-/* This is actually the same as VLCControls::stop. */
-- (BOOL)windowShouldClose:(id)sender
+- (void)rightMouseDown:(NSEvent *)o_event
{
- playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
- FIND_ANYWHERE );
- if( p_playlist == NULL )
+ if( p_vout && [o_event type] == NSRightMouseDown )
{
- return NO;
+ msg_Dbg( p_vout, "received NSRightMouseDown (specific method)" );
+ [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] getVoutView]];
}
- playlist_Stop( p_playlist );
- vlc_object_release( p_playlist );
-
- /* The window will be closed by the intf later. */
- return NO;
+ [super mouseDown: o_event];
+}
+
+- (void)mouseUp:(NSEvent *)o_event
+{
+ 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)otherMouseUp:(NSEvent *)o_event
+{
+ vlc_value_t val;
+
+ if( p_vout && [o_event type] == NSOtherMouseUp )
+ {
+ var_Get( p_vout, "mouse-button-down", &val );
+ val.i_int &= ~2;
+ var_Set( p_vout, "mouse-button-down", val );
+ }
+
+ [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)mouseDragged:(NSEvent *)o_event
+{
+ [self mouseMoved: o_event];
+}
+
+- (void)otherMouseDragged:(NSEvent *)o_event
+{
+ [self mouseMoved: o_event];
+}
+
+- (void)rightMouseDragged:(NSEvent *)o_event
+{
+ [self mouseMoved: o_event];
+}
+
+- (void)mouseMoved:(NSEvent *)o_event
+{
+ NSPoint ml;
+ NSRect s_rect;
+ BOOL b_inside;
+
+ if( p_vout )
+ {
+ 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 )
+ {
+ 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 );
+ }
+ if( [self isFullscreen] )
+ [[[[VLCMain sharedInstance] getControls] getFSPanel] fadeIn];
+ }
+
+ [super mouseMoved: o_event];
}
- (BOOL)acceptsFirstResponder
return NO;
}
-- (void)mouseDown:(NSEvent *)o_event
+/* Class methods used by the different vout modules */
+
++ (vout_thread_t *)getRealVout: (vout_thread_t *)p_vout
{
- vlc_value_t val;
+ /* 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;
+ }
+
+}
- switch( [o_event type] )
++ (id)getVoutView: (vout_thread_t *)p_vout subView: (NSView *)view
+ frame: (NSRect *)s_frame
+{
+ vlc_value_t value_drawable;
+ int i_timeout;
+ id o_return = nil;
+
+ 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 NSLeftMouseDown:
+ /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
+ for( i_timeout = 20 ; i_timeout-- ; )
{
- var_Get( p_vout, "mouse-button-down", &val );
- val.i_int |= 1;
- var_Set( p_vout, "mouse-button-down", val );
+ if( NSApp == NULL )
+ {
+ msleep( INTF_IDLE_SLEEP );
+ }
}
- break;
- default:
- [super mouseDown: o_event];
- break;
+ if( NSApp == NULL )
+ {
+ /* No MacOS X intf, unable to communicate with MT */
+ msg_Err( p_vout, "no MacOS X interface present" );
+ return nil;
+ }
+ else
+ {
+ if ( VLCIntf && !(p_vout->b_fullscreen) &&
+ !(var_GetBool( p_vout, "macosx-background" )) &&
+ var_GetBool( p_vout, "embedded-video") )
+ {
+ o_return = [[[VLCMain sharedInstance] getEmbeddedList]
+ getEmbeddedVout];
+ }
+ }
}
+
+ /* 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)otherMouseDown:(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 NSOtherMouseDown:
- {
- var_Get( p_vout, "mouse-button-down", &val );
- val.i_int |= 2;
- var_Set( p_vout, "mouse-button-down", val );
- }
- break;
+- (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 );
+}
+
+@end
- default:
- [super mouseDown: o_event];
- break;
+/*****************************************************************************
+ * 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]];
+
+ if( var_GetBool( p_real_vout, "video-on-top" ) )
+ {
+ [o_window setLevel: NSStatusWindowLevel];
}
+
+ [o_window setAcceptsMouseMovedEvents: TRUE];
+ return b_return;
}
-- (void)rightMouseDown:(NSEvent *)o_event
+- (void)closeVout
{
- vlc_value_t val;
+ [o_window closeWindow];
+ [o_window setAcceptsMouseMovedEvents: NO];
+ i_time_mouse_last_moved = 0;
+ [super closeVout];
+}
- switch( [o_event type] )
+- (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 )
{
- case NSRightMouseDown:
+ [NSCursor setHiddenUntilMouseMoves: NO];
+ }
+}
+
+- (void)manage
+{
+ [super manage];
+ unsigned int i_mouse_hide_timeout =
+ var_GetInteger(p_vout, "mouse-hide-timeout") * 1000;
+ if( p_vout->b_fullscreen )
+ {
+ if( mdate() - i_time_mouse_last_moved > i_mouse_hide_timeout )
{
- var_Get( p_vout, "mouse-button-down", &val );
- val.i_int |= 4;
- var_Set( p_vout, "mouse-button-down", val );
+ [self hideMouse: YES];
}
- break;
-
- default:
- [super mouseDown: o_event];
- break;
+ }
+ else
+ {
+ [self hideMouse: NO];
}
}
-- (void)mouseUp:(NSEvent *)o_event
+
+- (void)enterFullscreen
{
- vlc_value_t val;
+ [super enterFullscreen];
- switch( [o_event type] )
+ if( var_GetBool( p_real_vout, "video-on-top" ) )
{
- case NSLeftMouseUp:
- {
- vlc_value_t b_val;
- b_val.b_bool = VLC_TRUE;
- var_Set( p_vout, "mouse-clicked", b_val );
+ [o_window setLevel: NSNormalWindowLevel];
+ }
- var_Get( p_vout, "mouse-button-down", &val );
- val.i_int &= ~1;
- var_Set( p_vout, "mouse-button-down", val );
- }
- break;
+ [[o_view class] performSelectorOnMainThread:@selector(resetVout:) withObject:[NSValue valueWithPointer:p_vout] waitUntilDone:YES];
+ [[[[VLCMain sharedInstance] getControls] getFSPanel] setActive: nil];
+}
- default:
- [super mouseUp: o_event];
- break;
+- (void)leaveFullscreen
+{
+ [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];
}
-- (void)otherMouseUp:(NSEvent *)o_event
+@end
+
+/*****************************************************************************
+ * VLCEmbeddedVoutView implementation
+ *****************************************************************************/
+
+@implementation VLCEmbeddedVoutView
+
+- (void)awakeFromNib
{
- vlc_value_t val;
+ o_embeddedwindow = [self window];
+}
- switch( [o_event type] )
+- (id)initWithFrame: (NSRect)frameRect
+{
+ if(self = [super initWithFrame: frameRect])
{
- case NSOtherMouseUp:
- {
- var_Get( p_vout, "mouse-button-down", &val );
- val.i_int &= ~2;
- var_Set( p_vout, "mouse-button-down", val );
- }
- break;
-
- default:
- [super mouseUp: o_event];
- break;
+ b_used = NO;
+ [[[VLCMain sharedInstance] getEmbeddedList] addEmbeddedVout: self];
+ o_embeddedwindow = nil; /* Filled later on in -awakeFromNib */
}
+ return self;
}
-- (void)rightMouseUp:(NSEvent *)o_event
+- (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
+ frame: (NSRect *)s_arg_frame
{
- vlc_value_t val;
+ BOOL b_return;
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:o_window];
- switch( [o_event type] )
+ b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame];
+ if( b_return )
{
- case NSRightMouseUp:
+ 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;
+
+ b_init_ok = false;
+
+ 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 )
+ {
+ CGDisplayFadeReservationToken token;
+ NSRect screen_rect = [o_screen frame];
+ screen_rect.origin.x = screen_rect.origin.y = 0;
+
+ b_black = var_GetBool( p_vout, "macosx-black" );
+
+ /* 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];
+
+ /* 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 )
+ {
+ SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
+ }
+ if( b_black == true )
+ {
+ CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
+ CGDisplayFade( token, 0.6 , kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
- 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];
+ [o_screen blackoutOtherScreens];
- if( b_inside )
+ CGDisplayFade( token, 0.3 , kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
+ CGReleaseDisplayFadeReservation( token);
+ }
+ }
+ else if( var_GetBool( p_vout, "macosx-background" ) )
{
- vlc_value_t val;
- unsigned int i_width, i_height, i_x, i_y;
+ 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 );
+ /* Creates a window with size: screen_rect on o_screen */
+ [self initWithContentRect: screen_rect
+ styleMask: NSBorderlessWindowMask
+ backing: NSBackingStoreBuffered
+ defer: YES screen: o_screen];
- val.i_int = ( ((int)ml.x) - i_x ) *
- p_vout->render.i_width / i_width;
- var_Set( p_vout, "mouse-x", val );
+ [self setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey)];
+ }
+ else
+ {
+ unsigned int i_stylemask = NSTitledWindowMask |
+ NSMiniaturizableWindowMask |
+ NSClosableWindowMask |
+ NSResizableWindowMask;
- if( [[o_view className] isEqualToString: @"VLCGLView"] )
+ NSRect s_rect;
+ if( !s_frame )
{
- val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
- p_vout->render.i_height / i_height;
+ 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