X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fmacosx%2Fvout.m;h=386149ea0b67ba8cb605d43aaed7feca9201201b;hb=79559bdf7ff37fb54364a3ef7917b1e5481dc4c2;hp=51eb40b48d2af1c1cd0371f6ccd769b9067de307;hpb=7bc4719af79e76665fd425cc3e6ea1f581d0006c;p=vlc diff --git a/modules/gui/macosx/vout.m b/modules/gui/macosx/vout.m index 51eb40b48d..bdda538bf2 100644 --- a/modules/gui/macosx/vout.m +++ b/modules/gui/macosx/vout.m @@ -1,20 +1,22 @@ /***************************************************************************** * vout.m: MacOS X video output module ***************************************************************************** - * Copyright (C) 2001-2003 VideoLAN - * $Id: vout.m,v 1.80 2004/02/09 17:42:12 titer Exp $ + * Copyright (C) 2001-2008 the VideoLAN team + * $Id$ * * Authors: Colin Delacroix * Florian G. Pflug * Jon Lech Johansen - * Derk-Jan Hartman + * 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,1609 +32,1082 @@ *****************************************************************************/ #include /* ENOMEM */ #include /* free() */ -#include /* strerror() */ +#include +/* BeginFullScreen, EndFullScreen */ #include -#include -#include -#include - #include #include "intf.h" +#include "fspanel.h" #include "vout.h" - -#define QT_MAX_DIRECTBUFFERS 10 -#define VL_MAX_DISPLAYS 16 - -#define OPENGL_EFFECT_NONE 1 -#define OPENGL_EFFECT_CUBE 2 - -struct picture_sys_t -{ - void *p_info; - unsigned int i_size; - - /* When using I420 output */ - PlanarPixmapInfoYUV420 pixmap_i420; -}; +#import "controls.h" +#import "embeddedwindow.h" /***************************************************************************** - * Local prototypes + * 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; -static int vout_Init ( vout_thread_t * ); -static void vout_End ( vout_thread_t * ); -static int vout_Manage ( vout_thread_t * ); -static void vout_Display ( vout_thread_t *, picture_t * ); - -static int CoSendRequest ( vout_thread_t *, SEL ); -static int CoCreateWindow ( vout_thread_t * ); -static int CoDestroyWindow ( vout_thread_t * ); -static int CoToggleFullscreen ( vout_thread_t * ); + 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 ); -static void VLCHideMouse ( vout_thread_t *, BOOL ); + val.b_bool = true; + var_Set( p_vout, "intf-change", val ); + return VLC_SUCCESS; +} -static void QTScaleMatrix ( vout_thread_t * ); -static int QTCreateSequence ( vout_thread_t * ); -static void QTDestroySequence ( vout_thread_t * ); -static int QTNewPicture ( vout_thread_t *, picture_t * ); -static void QTFreePicture ( vout_thread_t *, picture_t * ); /***************************************************************************** - * OpenVideo: allocates MacOS X video thread output method - ***************************************************************************** - * This function allocates and initializes a MacOS X vout method. + * VLCEmbeddedList implementation *****************************************************************************/ -int E_(OpenVideo) ( vlc_object_t *p_this ) -{ - vout_thread_t * p_vout = (vout_thread_t *)p_this; - OSErr err; - int i_timeout; +@implementation VLCEmbeddedList - p_vout->p_sys = malloc( sizeof( vout_sys_t ) ); - if( p_vout->p_sys == NULL ) - { - msg_Err( p_vout, "out of memory" ); - return( 1 ); - } +- (id)init +{ + [super init]; + o_embedded_array = [NSMutableArray array]; + return self; +} - memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); +- (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] ) + { + [o_vout_view setUsed: NO]; + } + else { - /* no MacOS X intf, unable to communicate with MT */ - msg_Err( p_vout, "no MacOS X interface present" ); - free( p_vout->p_sys ); - return( 1 ); + msg_Warn( VLCIntf, "cannot find Video Output"); } +} - if( [NSApp respondsToSelector: @selector(getIntf)] ) +- (void)addEmbeddedVout: (id)o_vout_view +{ + if( ![o_embedded_array containsObject: o_vout_view] ) { - intf_thread_t * p_intf; + [o_embedded_array addObject: o_vout_view]; + } +} - for( i_timeout = 10 ; i_timeout-- ; ) - { - if( ( p_intf = [NSApp getIntf] ) == NULL ) - { - msleep( INTF_IDLE_SLEEP ); - } - } +- (BOOL)windowContainsEmbedded: (id)o_window +{ +/* if( ![[o_window className] isEqualToString: @"VLCVoutWindow"] ) + { + 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; - if( p_intf == NULL ) + while( (o_current_embedded = [o_enumerator nextObject]) ) + { + if( [o_current_embedded getWindow] == o_window ) { - msg_Err( p_vout, "MacOS X intf has getIntf, but is NULL" ); - free( p_vout->p_sys ); - return( 1 ); + 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_vout->p_sys->b_mouse_moved = VLC_TRUE; - p_vout->p_sys->i_time_mouse_last_moved = mdate(); + p_real_vout = NULL; + o_window = nil; + return self; +} - /* set window size */ - p_vout->p_sys->s_rect.size.width = p_vout->i_window_width; - p_vout->p_sys->s_rect.size.height = p_vout->i_window_height; +- (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]; - /* Check if we should use QuickTime or OpenGL */ - p_vout->p_sys->i_opengl = config_GetInt( p_vout, "macosx-opengl" ); + p_vout = vout; + o_view = view; + s_frame = frame; - if( !p_vout->p_sys->i_opengl ) + if( [o_screens count] <= 0 ) { - /* Initialize QuickTime */ - p_vout->p_sys->h_img_descr = - (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) ); - p_vout->p_sys->p_matrix = - (MatrixRecordPtr)malloc( sizeof(MatrixRecord) ); - p_vout->p_sys->p_fullscreen_state = NULL; - - if( ( err = EnterMovies() ) != noErr ) - { - msg_Err( p_vout, "EnterMovies failed: %d", err ); - free( p_vout->p_sys->p_matrix ); - DisposeHandle( (Handle)p_vout->p_sys->h_img_descr ); - free( p_vout->p_sys ); - return( 1 ); - } - - /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */ - vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock ); - - err = FindCodec( kYUV420CodecType, bestSpeedCodec, - nil, &p_vout->p_sys->img_dc ); - - vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock ); - if( err == noErr && p_vout->p_sys->img_dc != 0 ) - { - p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0'); - p_vout->p_sys->i_codec = kYUV420CodecType; - } - else - { - msg_Err( p_vout, "failed to find an appropriate codec" ); - } + msg_Err( p_vout, "no OSX screens available" ); + return NO; + } - if( p_vout->p_sys->img_dc == 0 ) - { - free( p_vout->p_sys->p_matrix ); - DisposeHandle( (Handle)p_vout->p_sys->h_img_descr ); - free( p_vout->p_sys ); - return VLC_EGENERIC; - } + 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" ); } - NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init]; - NSArray * o_screens = [NSScreen screens]; - if( [o_screens count] > 0 && var_Type( p_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, text; + vlc_value_t val2, text; NSScreen * o_screen; - int i_option = config_GetInt( p_vout, "macosx-vdev" ); + var_Create( p_real_vout, "video-device", VLC_VAR_INTEGER | + VLC_VAR_HASCHOICE ); + text.psz_string = _("Fullscreen Video Device"); + var_Change( p_real_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL ); - var_Create( p_vout, "video-device", VLC_VAR_INTEGER | - VLC_VAR_HASCHOICE ); - text.psz_string = _("Video device"); - var_Change( p_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]; NSRect s_rect = [o_screen frame]; - snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1, + snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1, "%s %d (%dx%d)", _("Screen"), i, - (int)s_rect.size.width, (int)s_rect.size.height ); + (int)s_rect.size.width, (int)s_rect.size.height ); text.psz_string = psz_temp; - val.i_int = i; - var_Change( p_vout, "video-device", - VLC_VAR_ADDCHOICE, &val, &text ); - - if( ( i - 1 ) == i_option ) + val2.i_int = (int)[o_screen displayID]; + var_Change( p_real_vout, "video-device", + VLC_VAR_ADDCHOICE, &val2, &text ); + if( (int)[o_screen displayID] == i_device ) { - var_Set( p_vout, "video-device", val ); + var_Set( p_real_vout, "video-device", val2 ); } i++; } - var_AddCallback( p_vout, "video-device", vout_VarCallback, + var_AddCallback( p_real_vout, "video-device", DeviceCallback, NULL ); - val.b_bool = VLC_TRUE; - var_Set( p_vout, "intf-change", val ); + val2.b_bool = true; + var_Set( p_real_vout, "intf-change", val2 ); } + + /* Add the view. It's automatically resized to fit the window */ + [self addSubview: o_view]; + [self setAutoresizesSubviews: YES]; [o_pool release]; - if( CoCreateWindow( p_vout ) ) - { - msg_Err( p_vout, "unable to create window" ); - if( !p_vout->p_sys->i_opengl ) - { - free( p_vout->p_sys->p_matrix ); - DisposeHandle( (Handle)p_vout->p_sys->h_img_descr ); - } - free( p_vout->p_sys ); - return( 1 ); - } + return YES; +} - p_vout->pf_init = vout_Init; - p_vout->pf_end = vout_End; - p_vout->pf_manage = vout_Manage; - p_vout->pf_render = NULL; - p_vout->pf_display = vout_Display; +- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize +{ + [super resizeSubviewsWithOldSize: oldBoundsSize]; + [o_view setFrameSize: [self frame].size]; +} - return( 0 ); +- (void)drawRect:(NSRect)rect +{ + /* When there is no subview we draw a black background */ + [self lockFocus]; + [[NSColor blackColor] set]; + NSRectFill(rect); + [self unlockFocus]; } -/***************************************************************************** - * vout_Init: initialize video thread output method - *****************************************************************************/ -static int vout_Init( vout_thread_t *p_vout ) +- (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; +} + +- (void)updateTitle { - int i_index; - picture_t *p_pic; + NSString * o_title = nil; + NSMutableString * o_mrl = nil; + input_thread_t * p_input; + char * psz_title; + + if( !p_vout ) return; + + p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT ); + + if( !p_input ) return; - I_OUTPUTPICTURES = 0; + input_item_t * p_item = input_GetItem( p_input ); - /* Initialize the output structure; we already found a codec, - * and the corresponding chroma we will be using. Since we can - * arbitrary scale, stick to the coordinates and aspect. */ - p_vout->output.i_width = p_vout->render.i_width; - p_vout->output.i_height = p_vout->render.i_height; - p_vout->output.i_aspect = p_vout->render.i_aspect; + psz_title = input_item_GetNowPlaying ( p_item ); + if( !psz_title ) + psz_title = input_item_GetName( p_item ); - if( !p_vout->p_sys->i_opengl ) + if( psz_title ) + o_title = [NSString stringWithUTF8String: psz_title]; + + 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 ) { - SetPort( p_vout->p_sys->p_qdport ); - QTScaleMatrix( p_vout ); + /* FIXME once psz_access is exported, we could check if we are + * reading from a file in a smarter way. */ - if( QTCreateSequence( p_vout ) ) + 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 { - msg_Err( p_vout, "unable to create sequence" ); - return( 1 ); + /* it's from the network or somewhere else, + * we clear the previous path */ + [o_window setRepresentedFilename: @""]; } + [o_window setTitle: o_title]; } else { - p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2'); - p_vout->output.i_rmask = 0xFF0000; - p_vout->output.i_gmask = 0x00FF00; - p_vout->output.i_bmask = 0x0000FF; + [o_window setTitle: [NSString stringWithUTF8String: VOUT_TITLE]]; } + vlc_object_release( p_input ); +} - /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */ - while( I_OUTPUTPICTURES < - p_vout->p_sys->i_opengl ? 1 : QT_MAX_DIRECTBUFFERS ) + +- (void)setOnTop:(BOOL)b_on_top +{ + if( b_on_top ) { - p_pic = NULL; + [o_window setLevel: NSStatusWindowLevel]; + } + else + { + [o_window setLevel: NSNormalWindowLevel]; + } +} - /* Find an empty picture slot */ - for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ ) - { - if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE ) - { - p_pic = p_vout->p_picture + i_index; - break; - } - } +- (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate +{ + NSSize newsize; + int i_corrected_height, i_corrected_width; + NSPoint topleftbase; + NSPoint topleftscreen; - /* Allocate the picture */ - if( p_pic == NULL ) - { - break; - } + if ( !p_vout->b_fullscreen ) + { + NSView *mainView; + NSRect new_frame; + topleftbase.x = 0; + topleftbase.y = [o_window frame].size.height; + topleftscreen = [o_window convertBaseToScreen: topleftbase]; - if( !p_vout->p_sys->i_opengl ) + if( p_vout->render.i_height * p_vout->render.i_aspect > + p_vout->render.i_width * VOUT_ASPECT_FACTOR ) { - if( QTNewPicture( p_vout, p_pic ) ) - { - break; - } + i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect / + VOUT_ASPECT_FACTOR; + newsize.width = (int) ( i_corrected_width * factor ); + newsize.height = (int) ( p_vout->render.i_height * factor ); } else { - /* Nothing special to do, we just need a basic allocated - picture_t */ - vout_AllocatePicture( p_vout, p_pic, p_vout->output.i_chroma, - p_vout->output.i_width, p_vout->output.i_height, - p_vout->output.i_aspect ); + i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR / + p_vout->render.i_aspect; + newsize.width = (int) ( p_vout->render.i_width * factor ); + newsize.height = (int) ( i_corrected_height * factor ); } - p_pic->i_status = DESTROYED_PICTURE; - p_pic->i_type = DIRECT_PICTURE; + /* 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; - PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic; + /* 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; - I_OUTPUTPICTURES++; - } + new_frame.origin.x = topleftscreen.x; + new_frame.origin.y = topleftscreen.y - new_frame.size.height; - if( p_vout->p_sys->i_opengl ) - { - [p_vout->p_sys->o_glview lockFocus]; - [p_vout->p_sys->o_glview initTextures]; - [p_vout->p_sys->o_glview unlockFocus]; - } + [o_window setFrame: new_frame display: animate animate: animate]; - return( 0 ); + p_vout->i_changes |= VOUT_SIZE_CHANGE; + } } -/***************************************************************************** - * vout_End: terminate video thread output method - *****************************************************************************/ -static void vout_End( vout_thread_t *p_vout ) +- (void)toggleFloatOnTop { - int i_index; + vlc_value_t val; - if( !p_vout->p_sys->i_opengl ) + if( !p_real_vout ) return; + if( var_Get( p_real_vout, "video-on-top", &val )>=0 && val.b_bool) { - QTDestroySequence( p_vout ); + val.b_bool = false; } - - /* Free the direct buffers we allocated */ - for( i_index = I_OUTPUTPICTURES; i_index; ) + else { - i_index--; - if( !p_vout->p_sys->i_opengl ) - { - QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] ); - } - else - { - free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig ); - } + val.b_bool = true; } + var_Set( p_real_vout, "video-on-top", val ); } -/***************************************************************************** - * CloseVideo: destroy video thread output method - *****************************************************************************/ -void E_(CloseVideo) ( vlc_object_t *p_this ) -{ - vout_thread_t * p_vout = (vout_thread_t *)p_this; +- (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 ); +} - if( CoDestroyWindow( p_vout ) ) - { - msg_Err( p_vout, "unable to destroy window" ); - } +- (BOOL)isFullscreen +{ + vlc_value_t val; + if( !p_real_vout ) return NO; + var_Get( p_real_vout, "fullscreen", &val ); + return( val.b_bool ); +} - if( !p_vout->p_sys->i_opengl ) - { - if ( p_vout->p_sys->p_fullscreen_state != NULL ) - EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL ); +- (void)snapshot +{ + vout_Control( p_real_vout, VOUT_SNAPSHOT ); +} + +- (void)manage +{ + /* Disable Screensaver, when we're playing something, but allow it on pause */ + if( !VLCIntf || !VLCIntf->p_sys || !VLCIntf->p_sys->i_play_status ) + return; - ExitMovies(); + if( VLCIntf->p_sys->i_play_status == PLAYING_S ) + UpdateSystemActivity( UsrActivity ); +} - free( p_vout->p_sys->p_matrix ); - DisposeHandle( (Handle)p_vout->p_sys->h_img_descr ); - } +- (id)getWindow +{ + return o_window; +} - free( p_vout->p_sys ); +- (void)scrollWheel:(NSEvent *)theEvent +{ + VLCControls * o_controls = (VLCControls *)[[NSApp delegate] getControls]; + [o_controls scrollWheel: theEvent]; } -/***************************************************************************** - * vout_Manage: handle events - ***************************************************************************** - * This function should be called regularly by video output thread. It manages - * console events. It returns a non null value on error. - *****************************************************************************/ -static int vout_Manage( vout_thread_t *p_vout ) +- (void)keyDown:(NSEvent *)o_event { - if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE ) - { - if( CoToggleFullscreen( p_vout ) ) - { - return( 1 ); - } + unichar key = 0; + vlc_value_t val; + unsigned int i_pressed_modifiers = 0; + val.i_int = 0; - p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; - } + i_pressed_modifiers = [o_event modifierFlags]; + + if( i_pressed_modifiers & NSShiftKeyMask ) + val.i_int |= KEY_MODIFIER_SHIFT; + if( i_pressed_modifiers & NSControlKeyMask ) + val.i_int |= KEY_MODIFIER_CTRL; + if( i_pressed_modifiers & NSAlternateKeyMask ) + val.i_int |= KEY_MODIFIER_ALT; + if( i_pressed_modifiers & NSCommandKeyMask ) + val.i_int |= KEY_MODIFIER_COMMAND; - if( p_vout->i_changes & VOUT_SIZE_CHANGE ) + key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0]; + + if( key ) { - if( !p_vout->p_sys->i_opengl ) + /* Escape should always get you out of fullscreen */ + if( key == (unichar) 0x1b ) { - QTScaleMatrix( p_vout ); - SetDSequenceMatrix( p_vout->p_sys->i_seq, - p_vout->p_sys->p_matrix ); + if( p_real_vout && [self isFullscreen] ) + { + [self toggleFullscreen]; + } } - - p_vout->i_changes &= ~VOUT_SIZE_CHANGE; - } - - /* hide/show mouse cursor - * this code looks unnecessarily complicated, but is necessary like this. - * it has to deal with multiple monitors and therefore checks a lot */ - if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen ) - { - if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 ) + else if ( key == ' ' ) + { + vlc_value_t val; + val.i_int = config_GetInt( p_vout, "key-play-pause" ); + var_Set( p_vout->p_libvlc, "key-pressed", val ); + } + else { - VLCHideMouse( p_vout, YES ); + val.i_int |= CocoaKeyToVLC( key ); + var_Set( p_vout->p_libvlc, "key-pressed", val ); } } - else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen ) + else { - VLCHideMouse( p_vout, NO ); + [super keyDown: o_event]; } - - /* disable screen saver */ - UpdateSystemActivity( UsrActivity ); - - return( 0 ); } -/***************************************************************************** - * vout_Display: displays previously rendered output - ***************************************************************************** - * This function sends the currently rendered image to the display. - *****************************************************************************/ -static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic ) +- (void)mouseDown:(NSEvent *)o_event { - if( !p_vout->p_sys->i_opengl ) + vlc_value_t val; + + if( p_vout ) { - OSErr err; - CodecFlags flags; - - if( ( err = DecompressSequenceFrameS( - p_vout->p_sys->i_seq, - p_pic->p_sys->p_info, - p_pic->p_sys->i_size, - codecFlagUseImageBuffer, &flags, nil ) != noErr ) ) - { - msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err ); - } - else + if( ( [o_event type] == NSLeftMouseDown ) && + ( ! ( [o_event modifierFlags] & NSControlKeyMask ) ) ) { - QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil ); + 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( [p_vout->p_sys->o_glview lockFocusIfCanDraw] ) + else if( ( [o_event type] == NSRightMouseDown ) || + ( ( [o_event type] == NSLeftMouseDown ) && + ( [o_event modifierFlags] & NSControlKeyMask ) ) ) { - /* Texture gotta be reload before the buffer is filled - (thanks to gcc from arstechnica forums) */ - [p_vout->p_sys->o_glview drawRect: - [p_vout->p_sys->o_glview bounds]]; - [p_vout->p_sys->o_glview reloadTexture]; - [p_vout->p_sys->o_glview unlockFocus]; + msg_Dbg( p_vout, "received NSRightMouseDown (generic method) or Ctrl clic" ); + [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] getVoutView]]; } } + + [super mouseDown: o_event]; } -/***************************************************************************** - * CoSendRequest: send request to interface thread - ***************************************************************************** - * Returns 0 on success, 1 otherwise - *****************************************************************************/ -static int CoSendRequest( vout_thread_t *p_vout, SEL sel ) +- (void)otherMouseDown:(NSEvent *)o_event { - int i_ret = 0; vlc_value_t val; - intf_thread_t * p_intf; - VLCVout * o_vlv = [[VLCVout alloc] init]; - - if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) ) + if( p_vout && [o_event type] == NSOtherMouseDown ) { - msg_Err( p_vout, "SendRequest: no way to communicate with mt" ); + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int |= 2; + var_Set( p_vout, "mouse-button-down", val ); } - [o_vlv release]; - - /*This makes this function dependant of the presence of a macosx - interface. We do not check if this interface exists, since it has - already been done before.*/ - - p_intf = [NSApp getIntf]; - - val.b_bool = VLC_TRUE; - var_Create(p_intf,"intf-change",VLC_VAR_BOOL); - var_Set(p_intf, "intf-change",val); - - return( i_ret ); + [super mouseDown: o_event]; } -/***************************************************************************** - * CoCreateWindow: create new window - ***************************************************************************** - * Returns 0 on success, 1 otherwise - *****************************************************************************/ -static int CoCreateWindow( vout_thread_t *p_vout ) +- (void)rightMouseDown:(NSEvent *)o_event { - if( CoSendRequest( p_vout, @selector(createWindow:) ) ) + if( p_vout && [o_event type] == NSRightMouseDown ) { - msg_Err( p_vout, "CoSendRequest (createWindow) failed" ); - return( 1 ); + msg_Dbg( p_vout, "received NSRightMouseDown (specific method)" ); + [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] getVoutView]]; } - - return( 0 ); + + [super mouseDown: o_event]; } -/***************************************************************************** - * CoDestroyWindow: destroy window - ***************************************************************************** - * Returns 0 on success, 1 otherwise - *****************************************************************************/ -static int CoDestroyWindow( vout_thread_t *p_vout ) +- (void)mouseUp:(NSEvent *)o_event { + vlc_value_t val; - VLCHideMouse( p_vout, NO ); - - if( CoSendRequest( p_vout, @selector(destroyWindow:) ) ) + if( p_vout && [o_event type] == NSLeftMouseUp ) { - msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" ); - return( 1 ); + 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 ); } - return( 0 ); + [super mouseUp: o_event]; } -/***************************************************************************** - * CoToggleFullscreen: toggle fullscreen - ***************************************************************************** - * Returns 0 on success, 1 otherwise - *****************************************************************************/ -static int CoToggleFullscreen( vout_thread_t *p_vout ) +- (void)otherMouseUp:(NSEvent *)o_event { - if( p_vout->p_sys->i_opengl ) - { - /* TODO */ - return 0; - } - - QTDestroySequence( p_vout ); + vlc_value_t val; - if( CoDestroyWindow( p_vout ) ) + if( p_vout && [o_event type] == NSOtherMouseUp ) { - msg_Err( p_vout, "unable to destroy window" ); - return( 1 ); + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int &= ~2; + var_Set( p_vout, "mouse-button-down", val ); } - - p_vout->b_fullscreen = !p_vout->b_fullscreen; - if( CoCreateWindow( p_vout ) ) + [super mouseUp: o_event]; +} + +- (void)rightMouseUp:(NSEvent *)o_event +{ + if( p_vout && [o_event type] == NSRightMouseUp ) { - msg_Err( p_vout, "unable to create window" ); - return( 1 ); + /* 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]]; } - SetPort( p_vout->p_sys->p_qdport ); - QTScaleMatrix( p_vout ); - - if( QTCreateSequence( p_vout ) ) - { - msg_Err( p_vout, "unable to create sequence" ); - return( 1 ); - } - - return( 0 ); + [super mouseUp: o_event]; } -/***************************************************************************** - * VLCHideMouse: if b_hide then hide the cursor - *****************************************************************************/ -static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide ) +- (void)mouseDragged:(NSEvent *)o_event { - BOOL b_inside; - NSRect s_rect; - NSPoint ml; - NSWindow *o_window = p_vout->p_sys->o_window; - NSView *o_contents = [o_window contentView]; - - s_rect = [o_contents bounds]; - ml = [o_window convertScreenToBase:[NSEvent mouseLocation]]; - ml = [o_contents convertPoint:ml fromView:nil]; - b_inside = [o_contents mouse: ml inRect: s_rect]; - - if ( b_hide && b_inside ) - { - /* only hide if mouse over VLCQTView */ - [NSCursor setHiddenUntilMouseMoves: YES]; - } - else if ( !b_hide ) - { - [NSCursor setHiddenUntilMouseMoves: NO]; - } - p_vout->p_sys->b_mouse_moved = NO; - p_vout->p_sys->i_time_mouse_last_moved = mdate(); - return; + [self mouseMoved: o_event]; } -/***************************************************************************** - * QTScaleMatrix: scale matrix - *****************************************************************************/ -static void QTScaleMatrix( vout_thread_t *p_vout ) +- (void)otherMouseDragged:(NSEvent *)o_event { - Rect s_rect; - unsigned int i_width, i_height; - Fixed factor_x, factor_y; - unsigned int i_offset_x = 0; - unsigned int i_offset_y = 0; - - GetPortBounds( p_vout->p_sys->p_qdport, &s_rect ); - - i_width = s_rect.right - s_rect.left; - i_height = s_rect.bottom - s_rect.top; - - if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR ) - { - int i_adj_width = i_height * p_vout->output.i_aspect / - VOUT_ASPECT_FACTOR; - - factor_x = FixDiv( Long2Fix( i_adj_width ), - Long2Fix( p_vout->output.i_width ) ); - factor_y = FixDiv( Long2Fix( i_height ), - Long2Fix( p_vout->output.i_height ) ); - - i_offset_x = (i_width - i_adj_width) / 2; - } - else - { - int i_adj_height = i_width * VOUT_ASPECT_FACTOR / - p_vout->output.i_aspect; - - factor_x = FixDiv( Long2Fix( i_width ), - Long2Fix( p_vout->output.i_width ) ); - factor_y = FixDiv( Long2Fix( i_adj_height ), - Long2Fix( p_vout->output.i_height ) ); - - i_offset_y = (i_height - i_adj_height) / 2; - } - - SetIdentityMatrix( p_vout->p_sys->p_matrix ); - - ScaleMatrix( p_vout->p_sys->p_matrix, - factor_x, factor_y, - Long2Fix(0), Long2Fix(0) ); - - TranslateMatrix( p_vout->p_sys->p_matrix, - Long2Fix(i_offset_x), - Long2Fix(i_offset_y) ); - + [self mouseMoved: o_event]; } -/***************************************************************************** - * QTCreateSequence: create a new sequence - ***************************************************************************** - * Returns 0 on success, 1 otherwise - *****************************************************************************/ -static int QTCreateSequence( vout_thread_t *p_vout ) -{ - OSErr err; - ImageDescriptionPtr p_descr; - - HLock( (Handle)p_vout->p_sys->h_img_descr ); - p_descr = *p_vout->p_sys->h_img_descr; - - p_descr->idSize = sizeof(ImageDescription); - p_descr->cType = p_vout->p_sys->i_codec; - p_descr->version = 1; - p_descr->revisionLevel = 0; - p_descr->vendor = 'appl'; - p_descr->width = p_vout->output.i_width; - p_descr->height = p_vout->output.i_height; - p_descr->hRes = Long2Fix(72); - p_descr->vRes = Long2Fix(72); - p_descr->spatialQuality = codecLosslessQuality; - p_descr->frameCount = 1; - p_descr->clutID = -1; - p_descr->dataSize = 0; - p_descr->depth = 24; - - HUnlock( (Handle)p_vout->p_sys->h_img_descr ); - - if( ( err = DecompressSequenceBeginS( - &p_vout->p_sys->i_seq, - p_vout->p_sys->h_img_descr, - NULL, 0, - p_vout->p_sys->p_qdport, - NULL, NULL, - p_vout->p_sys->p_matrix, - 0, NULL, - codecFlagUseImageBuffer, - codecLosslessQuality, - p_vout->p_sys->img_dc ) ) ) - { - msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err ); - return( 1 ); - } - - return( 0 ); -} - -/***************************************************************************** - * QTDestroySequence: destroy sequence - *****************************************************************************/ -static void QTDestroySequence( vout_thread_t *p_vout ) +- (void)rightMouseDragged:(NSEvent *)o_event { - CDSequenceEnd( p_vout->p_sys->i_seq ); + [self mouseMoved: o_event]; } -/***************************************************************************** - * QTNewPicture: allocate a picture - ***************************************************************************** - * Returns 0 on success, 1 otherwise - *****************************************************************************/ -static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic ) +- (void)mouseMoved:(NSEvent *)o_event { - int i_width = p_vout->output.i_width; - int i_height = p_vout->output.i_height; - - /* We know the chroma, allocate a buffer which will be used - * directly by the decoder */ - p_pic->p_sys = malloc( sizeof( picture_sys_t ) ); + NSPoint ml; + NSRect s_rect; + BOOL b_inside; - if( p_pic->p_sys == NULL ) + if( p_vout ) { - return( -1 ); - } + s_rect = [o_view bounds]; + ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil]; + b_inside = [o_view mouse: ml inRect: s_rect]; - switch( p_vout->output.i_chroma ) - { - case VLC_FOURCC('I','4','2','0'): - - p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420; - p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420); - - /* Allocate the memory buffer */ - p_pic->p_data = vlc_memalign( &p_pic->p_data_orig, - 16, i_width * i_height * 3 / 2 ); - - /* Y buffer */ - p_pic->Y_PIXELS = p_pic->p_data; - p_pic->p[Y_PLANE].i_lines = i_height; - p_pic->p[Y_PLANE].i_pitch = i_width; - p_pic->p[Y_PLANE].i_pixel_pitch = 1; - p_pic->p[Y_PLANE].i_visible_pitch = i_width; - - /* U buffer */ - p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width; - p_pic->p[U_PLANE].i_lines = i_height / 2; - p_pic->p[U_PLANE].i_pitch = i_width / 2; - p_pic->p[U_PLANE].i_pixel_pitch = 1; - p_pic->p[U_PLANE].i_visible_pitch = i_width / 2; - - /* V buffer */ - p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4; - p_pic->p[V_PLANE].i_lines = i_height / 2; - p_pic->p[V_PLANE].i_pitch = i_width / 2; - p_pic->p[V_PLANE].i_pixel_pitch = 1; - p_pic->p[V_PLANE].i_visible_pitch = i_width / 2; - - /* We allocated 3 planes */ - p_pic->i_planes = 3; - -#define P p_pic->p_sys->pixmap_i420 - P.componentInfoY.offset = (void *)p_pic->Y_PIXELS - - p_pic->p_sys->p_info; - P.componentInfoCb.offset = (void *)p_pic->U_PIXELS - - p_pic->p_sys->p_info; - P.componentInfoCr.offset = (void *)p_pic->V_PIXELS - - p_pic->p_sys->p_info; - - P.componentInfoY.rowBytes = i_width; - P.componentInfoCb.rowBytes = i_width / 2; - P.componentInfoCr.rowBytes = i_width / 2; -#undef P - - break; - - default: - /* Unknown chroma, tell the guy to get lost */ - free( p_pic->p_sys ); - msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)", - p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma ); - p_pic->i_planes = 0; - return( -1 ); - } + if( b_inside ) + { + vlc_value_t val; + unsigned int i_width, i_height, i_x, i_y; - return( 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 ); -/***************************************************************************** - * QTFreePicture: destroy a picture allocated with QTNewPicture - *****************************************************************************/ -static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) -{ - switch( p_vout->output.i_chroma ) - { - case VLC_FOURCC('I','4','2','0'): - free( p_pic->p_data_orig ); - break; + 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]; } - free( p_pic->p_sys ); + [super mouseMoved: o_event]; } -/***************************************************************************** - * VLCWindow implementation - *****************************************************************************/ -@implementation VLCWindow - -- (void)setVout:(vout_thread_t *)_p_vout +- (BOOL)acceptsFirstResponder { - p_vout = _p_vout; + return YES; } -- (vout_thread_t *)getVout +- (BOOL)becomeFirstResponder { - return( p_vout ); + return YES; } -- (void)scaleWindowWithFactor: (float)factor +- (BOOL)resignFirstResponder { - NSSize newsize; - int i_corrected_height, i_corrected_width; - NSPoint topleftbase; - NSPoint topleftscreen; - - if ( !p_vout->b_fullscreen ) - { - topleftbase.x = 0; - topleftbase.y = [self frame].size.height; - topleftscreen = [self convertBaseToScreen: topleftbase]; - - if( p_vout->output.i_height * p_vout->output.i_aspect > - p_vout->output.i_width * VOUT_ASPECT_FACTOR ) - { - i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect / - VOUT_ASPECT_FACTOR; - newsize.width = (int) ( i_corrected_width * factor ); - newsize.height = (int) ( p_vout->render.i_height * factor ); - } - else - { - i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR / - p_vout->output.i_aspect; - newsize.width = (int) ( p_vout->render.i_width * factor ); - newsize.height = (int) ( i_corrected_height * factor ); - } - - [self setContentSize: newsize]; - - [self setFrameTopLeftPoint: topleftscreen]; - p_vout->i_changes |= VOUT_SIZE_CHANGE; - } + /* We need to stay the first responder or we'll miss some + events */ + return NO; } -- (void)toggleFloatOnTop +/* Class methods used by the different vout modules */ + ++ (vout_thread_t *)getRealVout: (vout_thread_t *)p_vout { - if( config_GetInt( p_vout, "video-on-top" ) ) + /* 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 ) { - config_PutInt( p_vout, "video-on-top", 0 ); - [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel]; + return (vout_thread_t *) p_vout->p_parent; } else { - config_PutInt( p_vout, "video-on-top", 1 ); - [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel]; + return p_vout; } -} - -- (void)toggleFullscreen -{ - config_PutInt(p_vout, "fullscreen", !p_vout->b_fullscreen); - p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE; -} - -- (BOOL)isFullscreen -{ - return( p_vout->b_fullscreen ); -} -- (BOOL)canBecomeKeyWindow -{ - return( YES ); } -- (void)keyDown:(NSEvent *)o_event ++ (id)getVoutView: (vout_thread_t *)p_vout subView: (NSView *)view + frame: (NSRect *)s_frame { - unichar key = 0; - vlc_value_t val; - unsigned int i_pressed_modifiers = 0; - val.i_int = 0; - - i_pressed_modifiers = [o_event modifierFlags]; + vlc_value_t value_drawable; + int i_timeout; + id o_return = nil; - if( i_pressed_modifiers & NSShiftKeyMask ) - val.i_int |= KEY_MODIFIER_SHIFT; - if( i_pressed_modifiers & NSControlKeyMask ) - val.i_int |= KEY_MODIFIER_CTRL; - if( i_pressed_modifiers & NSAlternateKeyMask ) - val.i_int |= KEY_MODIFIER_ALT; - if( i_pressed_modifiers & NSCommandKeyMask ) - val.i_int |= KEY_MODIFIER_COMMAND; + var_Get( p_vout->p_libvlc, "drawable", &value_drawable ); - key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0]; + 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 ); - if( key ) + /* 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 ) { - /* Escape should always get you out of fullscreen */ - if( key == (unichar) 0x1b ) + /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */ + for( i_timeout = 20 ; i_timeout-- ; ) { - if( [self isFullscreen] ) - { - [self toggleFullscreen]; - } + if( NSApp == NULL ) + { + msleep( INTF_IDLE_SLEEP ); + } } - else if ( key == ' ' ) + + if( NSApp == NULL ) { - playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - if ( p_playlist != NULL ) - { - playlist_Pause( p_playlist ); - vlc_object_release( p_playlist); - } + /* No MacOS X intf, unable to communicate with MT */ + msg_Err( p_vout, "no MacOS X interface present" ); + return nil; } else { - val.i_int |= CocoaKeyToVLC( key ); - var_Set( p_vout->p_vlc, "key-pressed", 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]; + } } } - else + + /* No embedded vout is available */ + if( o_return == nil ) { - [super keyDown: o_event]; + 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)updateTitle +- (void)enterFullscreen { - NSMutableString * o_title; - playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - - if( p_playlist == NULL ) - { - return; - } - - vlc_mutex_lock( &p_playlist->object_lock ); - o_title = [NSMutableString stringWithUTF8String: - p_playlist->pp_items[p_playlist->i_index]->psz_uri]; - vlc_mutex_unlock( &p_playlist->object_lock ); - - vlc_object_release( p_playlist ); - - if( o_title != nil ) - { - NSRange prefix_range = [o_title rangeOfString: @"file:"]; - if( prefix_range.location != NSNotFound ) - { - [o_title deleteCharactersInRange: prefix_range]; - } - - [self setTitleWithRepresentedFilename: o_title]; - } - else - { - [self setTitle: - [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]]; - } + /* 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 ); } -/* This is actually the same as VLCControls::stop. */ -- (BOOL)windowShouldClose:(id)sender +- (void)leaveFullscreen { - playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - 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; + /* 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 /***************************************************************************** - * VLCQTView implementation + * VLCDetachedVoutView implementation *****************************************************************************/ -@implementation VLCQTView +@implementation VLCDetachedVoutView -- (void)drawRect:(NSRect)rect +- (id)initWithFrame: (NSRect)frameRect { - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; - - [[NSColor blackColor] set]; - NSRectFill( rect ); - [super drawRect: rect]; - - p_vout->i_changes |= VOUT_SIZE_CHANGE; + [super initWithFrame: frameRect]; + i_time_mouse_last_moved = 0; + return self; } -- (BOOL)acceptsFirstResponder +- (bool)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view + frame: (NSRect *) s_arg_frame { - return( YES ); -} + 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]]; -- (BOOL)becomeFirstResponder -{ - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; - - [o_window setAcceptsMouseMovedEvents: YES]; - return( YES ); + if( var_GetBool( p_real_vout, "video-on-top" ) ) + { + [o_window setLevel: NSStatusWindowLevel]; + } + + [o_window setAcceptsMouseMovedEvents: TRUE]; + return b_return; } -- (BOOL)resignFirstResponder +- (void)closeVout { - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; - + [o_window closeWindow]; [o_window setAcceptsMouseMovedEvents: NO]; - VLCHideMouse( p_vout, NO ); - return( YES ); + i_time_mouse_last_moved = 0; + [super closeVout]; } -- (void)mouseDown:(NSEvent *)o_event +- (void)mouseMoved:(NSEvent *)o_event { - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; - vlc_value_t val; - - switch( [o_event type] ) - { - case NSLeftMouseDown: - { - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int |= 1; - var_Set( p_vout, "mouse-button-down", val ); - } - break; - - default: - [super mouseDown: o_event]; - break; - } + i_time_mouse_last_moved = mdate(); + [super mouseMoved: o_event]; } -- (void)otherMouseDown:(NSEvent *)o_event +- (void)hideMouse:(BOOL)b_hide { - /* This is not the the wheel button. you need to poll the - * mouseWheel event for that. other is a third, forth or fifth button */ - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; - vlc_value_t val; + 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]]; - switch( [o_event type] ) + if( b_hide && b_inside ) { - case NSOtherMouseDown: - { - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int |= 2; - var_Set( p_vout, "mouse-button-down", val ); - } - break; - - default: - [super mouseDown: o_event]; - break; + [NSCursor setHiddenUntilMouseMoves: YES]; } -} - -- (void)rightMouseDown:(NSEvent *)o_event -{ - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; - vlc_value_t val; - - switch( [o_event type] ) + else if( !b_hide ) { - case NSRightMouseDown: - { - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int |= 4; - var_Set( p_vout, "mouse-button-down", val ); - } - break; - - default: - [super mouseDown: o_event]; - break; + [NSCursor setHiddenUntilMouseMoves: NO]; } } -- (void)mouseUp:(NSEvent *)o_event +- (void)manage { - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; - vlc_value_t val; - - switch( [o_event type] ) + [super manage]; + unsigned int i_mouse_hide_timeout = + var_GetInteger(p_vout, "mouse-hide-timeout") * 1000; + if( p_vout->b_fullscreen ) { - case NSLeftMouseUp: + if( mdate() - i_time_mouse_last_moved > i_mouse_hide_timeout ) { - vlc_value_t b_val; - b_val.b_bool = VLC_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 ); + [self hideMouse: YES]; } - break; - - default: - [super mouseUp: o_event]; - break; + } + else + { + [self hideMouse: NO]; } } -- (void)otherMouseUp:(NSEvent *)o_event + +- (void)enterFullscreen { - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; - vlc_value_t val; + [super enterFullscreen]; - switch( [o_event type] ) + if( var_GetBool( p_real_vout, "video-on-top" ) ) { - 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; + [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 { - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; - vlc_value_t val; + [super leaveFullscreen]; - switch( [o_event type] ) + if( var_GetBool( p_real_vout, "video-on-top" ) ) { - case NSRightMouseUp: - { - var_Get( p_vout, "mouse-button-down", &val ); - val.i_int &= ~4; - var_Set( p_vout, "mouse-button-down", val ); - } - break; - - default: - [super mouseUp: o_event]; - break; + [o_window setLevel: NSStatusWindowLevel]; } -} -- (void)mouseDragged:(NSEvent *)o_event -{ - [self mouseMoved:o_event]; + [[o_view class] performSelectorOnMainThread:@selector(resetVout:) withObject:[NSValue valueWithPointer:p_vout] waitUntilDone:YES]; + [[[[VLCMain sharedInstance] getControls] getFSPanel] setNonActive: nil]; } -- (void)otherMouseDragged:(NSEvent *)o_event +@end + +/***************************************************************************** + * VLCEmbeddedVoutView implementation + *****************************************************************************/ + +@implementation VLCEmbeddedVoutView + +- (void)awakeFromNib { - [self mouseMoved:o_event]; + o_embeddedwindow = [self window]; } -- (void)rightMouseDragged:(NSEvent *)o_event +- (id)initWithFrame: (NSRect)frameRect { - [self mouseMoved:o_event]; + if(self = [super initWithFrame: frameRect]) + { + b_used = NO; + [[[VLCMain sharedInstance] getEmbeddedList] addEmbeddedVout: self]; + o_embeddedwindow = nil; /* Filled later on in -awakeFromNib */ + } + return self; } -- (void)mouseMoved:(NSEvent *)o_event +- (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view + frame: (NSRect *)s_arg_frame { - NSPoint ml; - NSRect s_rect; - BOOL b_inside; - - vout_thread_t * p_vout; - id o_window = [self window]; - p_vout = (vout_thread_t *)[o_window getVout]; + BOOL b_return; - s_rect = [self bounds]; - ml = [self convertPoint: [o_event locationInWindow] fromView: nil]; - b_inside = [self mouse: ml inRect: s_rect]; + [NSObject cancelPreviousPerformRequestsWithTarget:o_window]; - if( b_inside ) + b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame]; + if( b_return ) { - vlc_value_t val; - 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 ); - - 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 = VLC_TRUE; - var_Set( p_vout, "mouse-moved", val ); - p_vout->p_sys->i_time_mouse_last_moved = mdate(); - p_vout->p_sys->b_mouse_moved = YES; - } - - [super mouseMoved: o_event]; -} + o_window = [self window]; -@end + [o_window setAcceptsMouseMovedEvents: TRUE]; -/***************************************************************************** - * VLCGLView implementation - *****************************************************************************/ -@implementation VLCGLView + if( var_GetBool( p_real_vout, "video-on-top" ) ) + { + [o_window setLevel: NSStatusWindowLevel]; + } + [view setFrameSize: [self frame].size]; + } -- (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout -{ - char * psz_effect; - p_vout = _p_vout; - - NSOpenGLPixelFormatAttribute attribs[] = + /* 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 ) { - NSOpenGLPFAAccelerated, - NSOpenGLPFANoRecovery, - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAWindow, - 0 - }; + [o_window lockFullscreenAnimation]; - NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc] - initWithAttributes: attribs]; + [o_window setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )]; - if( !fmt ) - { - fprintf( stderr, "Cannot create NSOpenGLPixelFormat\n" ); - return nil; - } + [self updateTitle]; - self = [super initWithFrame:frame pixelFormat: fmt]; + [NSObject cancelPreviousPerformRequestsWithTarget:o_window]; - [[self openGLContext] makeCurrentContext]; - [[self openGLContext] update]; + /* 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])]; - /* Black background */ - glClearColor( 0.0, 0.0, 0.0, 0.0 ); + [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]; - /* Check if the user asked for useless visual effects */ - psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" ); - if( !strcmp( psz_effect, "none" ) ) - { - i_effect = OPENGL_EFFECT_NONE; - } - else if( !strcmp( psz_effect, "cube" ) ) - { - i_effect = OPENGL_EFFECT_CUBE; - - glEnable( GL_DEPTH_TEST ); - glPolygonMode( GL_FRONT, GL_FILL ); - glPolygonMode( GL_BACK, GL_POINT ); - - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 ); - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - glTranslatef( 0.0, 0.0, - 5.0 ); - } - else if( !strcmp( psz_effect, "transparent-cube" ) ) - { - i_effect = OPENGL_EFFECT_CUBE; - - glEnable( GL_BLEND ); - glEnable( GL_POLYGON_SMOOTH ); - glDisable( GL_DEPTH_TEST ); - - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 ); - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - glTranslatef( 0.0, 0.0, - 5.0 ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE ); - } - else - { - msg_Warn( p_vout, "no valid opengl effect provided, using " - "\"none\"" ); - i_effect = OPENGL_EFFECT_NONE; } - b_init_done = 0; + return b_return; +} - return self; +- (void)setUsed: (BOOL)b_new_used +{ + b_used = b_new_used; } -- (void) reshape +- (BOOL)isUsed { - [[self openGLContext] makeCurrentContext]; + return b_used; +} - NSRect bounds = [self bounds]; +- (void)closeVout +{ + [super closeVout]; - glViewport( 0, 0, (GLint) bounds.size.width, - (GLint) bounds.size.height ); + /* 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]; - /* Quad size is set in order to preserve the aspect ratio */ - if( bounds.size.height * p_vout->output.i_aspect < - bounds.size.width * VOUT_ASPECT_FACTOR ) - { - f_x = bounds.size.height * p_vout->render.i_aspect / - VOUT_ASPECT_FACTOR / bounds.size.width; - f_y = 1.0; - } - else - { - f_x = 1.0; - f_y = bounds.size.width * VOUT_ASPECT_FACTOR / - p_vout->render.i_aspect / bounds.size.height; - } + [o_window setAcceptsMouseMovedEvents: NO]; + [[[VLCMain sharedInstance] getEmbeddedList] releaseEmbeddedVout: self]; } -- (void) initTextures -{ - [[self openGLContext] makeCurrentContext]; - - /* Create textures */ - glGenTextures( 1, &i_texture ); - - glEnable( GL_TEXTURE_RECTANGLE_EXT ); - glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE ); - - glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture ); - - /* Use VRAM texturing */ - glTexParameteri( GL_TEXTURE_RECTANGLE_EXT, - GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE ); - - /* Tell the driver not to make a copy of the texture but to use - our buffer */ - glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE ); - - /* Linear interpolation */ - glTexParameteri( GL_TEXTURE_RECTANGLE_EXT, - GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_RECTANGLE_EXT, - GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - - /* I have no idea what this exactly does, but it seems to be - necessary for scaling */ - glTexParameteri( GL_TEXTURE_RECTANGLE_EXT, - GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameteri( GL_TEXTURE_RECTANGLE_EXT, - GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); - - glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, - p_vout->output.i_width, p_vout->output.i_height, 0, - GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, - PP_OUTPUTPICTURE[0]->p_data ); - - b_init_done = 1; -} - -- (void) reloadTexture -{ - [[self openGLContext] makeCurrentContext]; - - glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture ); - - /* glTexSubImage2D is faster than glTexImage2D - http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/ - TextureRange/MainOpenGLView.m.htm */ - glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, - p_vout->output.i_width, p_vout->output.i_height, - GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, - PP_OUTPUTPICTURE[0]->p_data ); -} - -- (void) drawQuad -{ - glBegin( GL_QUADS ); - /* Top left */ - glTexCoord2f( 0.0, 0.0 ); - glVertex2f( - f_x, f_y ); - /* Bottom left */ - glTexCoord2f( 0.0, (float) p_vout->output.i_height ); - glVertex2f( - f_x, - f_y ); - /* Bottom right */ - glTexCoord2f( (float) p_vout->output.i_width, - (float) p_vout->output.i_height ); - glVertex2f( f_x, - f_y ); - /* Top right */ - glTexCoord2f( (float) p_vout->output.i_width, 0.0 ); - glVertex2f( f_x, f_y ); - glEnd(); -} - -- (void) drawCube -{ - glBegin( GL_QUADS ); - glTexCoord2f( 0.0, 0.0 ); - glVertex3f( - 1.0, 1.0, 1.0 ); - glTexCoord2f( 0.0, (float) p_vout->output.i_height ); - glVertex3f( - 1.0, - 1.0, 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, - (float) p_vout->output.i_height ); - glVertex3f( 1.0, - 1.0, 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, 0.0 ); - glVertex3f( 1.0, 1.0, 1.0 ); - glEnd(); - glBegin( GL_QUADS ); - glTexCoord2f( 0.0, 0.0 ); - glVertex3f( - 1.0, 1.0, - 1.0 ); - glTexCoord2f( 0.0, (float) p_vout->output.i_height ); - glVertex3f( - 1.0, - 1.0, - 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, - (float) p_vout->output.i_height ); - glVertex3f( - 1.0, - 1.0, 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, 0.0 ); - glVertex3f( - 1.0, 1.0, 1.0 ); - glEnd(); - glBegin( GL_QUADS ); - glTexCoord2f( 0.0, 0.0 ); - glVertex3f( 1.0, 1.0, - 1.0 ); - glTexCoord2f( 0.0, (float) p_vout->output.i_height ); - glVertex3f( 1.0, - 1.0, - 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, - (float) p_vout->output.i_height ); - glVertex3f( - 1.0, - 1.0, - 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, 0.0 ); - glVertex3f( - 1.0, 1.0, - 1.0 ); - glEnd(); - glBegin( GL_QUADS ); - glTexCoord2f( 0.0, 0.0 ); - glVertex3f( 1.0, 1.0, 1.0 ); - glTexCoord2f( 0.0, (float) p_vout->output.i_height ); - glVertex3f( 1.0, - 1.0, 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, - (float) p_vout->output.i_height ); - glVertex3f( 1.0, - 1.0, - 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, 0.0 ); - glVertex3f( 1.0, 1.0, - 1.0 ); - glEnd(); - glBegin( GL_QUADS ); - glTexCoord2f( 0.0, 0.0 ); - glVertex3f( - 1.0, 1.0, - 1.0 ); - glTexCoord2f( 0.0, (float) p_vout->output.i_height ); - glVertex3f( - 1.0, 1.0, 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, - (float) p_vout->output.i_height ); - glVertex3f( 1.0, 1.0, 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, 0.0 ); - glVertex3f( 1.0, 1.0, - 1.0 ); - glEnd(); - glBegin( GL_QUADS ); - glTexCoord2f( 0.0, 0.0 ); - glVertex3f( - 1.0, - 1.0, 1.0 ); - glTexCoord2f( 0.0, (float) p_vout->output.i_height ); - glVertex3f( - 1.0, - 1.0, - 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, - (float) p_vout->output.i_height ); - glVertex3f( 1.0, - 1.0, - 1.0 ); - glTexCoord2f( (float) p_vout->output.i_width, 0.0 ); - glVertex3f( 1.0, - 1.0, 1.0 ); - glEnd(); -} - -- (void) drawRect: (NSRect) rect -{ - [[self openGLContext] makeCurrentContext]; - - /* Swap buffers only during the vertical retrace of the monitor. - http://developer.apple.com/documentation/GraphicsImaging/ - Conceptual/OpenGL/chap5/chapter_5_section_44.html */ - long params[] = { 1 }; - CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, - params ); - - /* Black background */ - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - if( !b_init_done ) - { - [[self openGLContext] flushBuffer]; - return; - } - - /* Draw */ - glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture ); - if( i_effect == OPENGL_EFFECT_CUBE ) - { - glRotatef( 1.0, 0.3, 0.5, 0.7 ); - [self drawCube]; - } - else - { - [self drawQuad]; - } +- (void)enterFullscreen +{ + /* Save settings */ + [super enterFullscreen]; - /* Wait for the job to be done */ - [[self openGLContext] flushBuffer]; + /* 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 /***************************************************************************** - * VLCVout implementation + * VLCVoutWindow implementation *****************************************************************************/ -@implementation VLCVout +@implementation VLCVoutWindow -- (void)createWindow:(NSValue *)o_value +- (id) initWithVout: (vout_thread_t *) vout view: (VLCVoutView *) view + frame: (NSRect *) frame { - vlc_value_t val; - VLCQTView * o_view; - NSScreen * o_screen; - vout_thread_t * p_vout; - vlc_bool_t b_main_screen; - - p_vout = (vout_thread_t *)[o_value pointerValue]; - - p_vout->p_sys->o_window = [VLCWindow alloc]; - [p_vout->p_sys->o_window setVout: p_vout]; - [p_vout->p_sys->o_window setReleasedWhenClosed: YES]; - - if( var_Get( p_vout, "video-device", &val ) < 0 ) + p_vout = vout; + o_view = view; + s_frame = frame; + + [self performSelectorOnMainThread: @selector(initMainThread:) + withObject: NULL waitUntilDone: YES]; + + if( !b_init_ok ) { - o_screen = [NSScreen mainScreen]; - b_main_screen = 1; + return NULL; } - else + + 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 ) { - NSArray *o_screens = [NSScreen screens]; - unsigned int i_index = val.i_int; - - if( [o_screens count] < i_index ) + 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 ) { - o_screen = [NSScreen mainScreen]; - b_main_screen = 1; + SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar); } - else + if( b_black == true ) { - i_index--; - o_screen = [o_screens objectAtIndex: i_index]; - config_PutInt( p_vout, "macosx-vdev", i_index ); - b_main_screen = (i_index == 0); - } - } + CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token); + CGDisplayFade( token, 0.6 , kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES ); - if( p_vout->p_sys->i_opengl ) - { - /* XXX Fix fullscreen mode */ - p_vout->b_fullscreen = 0; - } + [o_screen blackoutOtherScreens]; - if( p_vout->b_fullscreen ) + 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; - if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL ) - BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0, - NULL, NULL, fullScreenAllowEvents ); + /* Creates a window with size: screen_rect on o_screen */ + [self initWithContentRect: screen_rect + styleMask: NSBorderlessWindowMask + backing: NSBackingStoreBuffered + defer: YES screen: o_screen]; - [p_vout->p_sys->o_window - initWithContentRect: screen_rect - styleMask: NSBorderlessWindowMask - backing: NSBackingStoreBuffered - defer: NO screen: o_screen]; - - //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1]; - p_vout->p_sys->b_mouse_moved = YES; - p_vout->p_sys->i_time_mouse_last_moved = mdate(); + [self setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey)]; } else { @@ -1640,80 +1115,116 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) NSMiniaturizableWindowMask | NSClosableWindowMask | NSResizableWindowMask; - - if( !p_vout->p_sys->i_opengl ) + + NSRect s_rect; + if( !s_frame ) { - if ( p_vout->p_sys->p_fullscreen_state != NULL ) - EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL ); - p_vout->p_sys->p_fullscreen_state = NULL; + s_rect.size.width = p_vout->i_window_width; + s_rect.size.height = p_vout->i_window_height; } - - [p_vout->p_sys->o_window - initWithContentRect: p_vout->p_sys->s_rect - styleMask: i_stylemask - backing: NSBackingStoreBuffered - defer: NO screen: o_screen]; - - [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )]; - - if( config_GetInt( p_vout, "video-on-top" ) ) + else { - [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel]; + s_rect = *s_frame; } - - if( !p_vout->p_sys->b_pos_saved ) + + [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 ) { - [p_vout->p_sys->o_window center]; + [self center]; } } - if( !p_vout->p_sys->i_opengl ) - { - o_view = [[VLCQTView alloc] init]; - /* FIXME: [o_view setMenu:] */ - [p_vout->p_sys->o_window setContentView: o_view]; - [o_view autorelease]; - - [o_view lockFocus]; - p_vout->p_sys->p_qdport = [o_view qdPort]; - [o_view unlockFocus]; - } - else - { -#define o_glview p_vout->p_sys->o_glview - o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout]; - [p_vout->p_sys->o_window setContentView: o_glview]; - [o_glview autorelease]; -#undef o_glview - } - - [p_vout->p_sys->o_window updateTitle]; - [p_vout->p_sys->o_window makeKeyAndOrderFront: nil]; + [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)destroyWindow:(NSValue *)o_value +- (void)close { - vout_thread_t * p_vout; + [o_view closeVout]; +} - p_vout = (vout_thread_t *)[o_value pointerValue]; +- (void)closeWindow +{ + /* XXX waitUntilDone = NO to avoid a possible deadlock when hitting + Command-Q */ + [self performSelectorOnMainThread: @selector(closeMainThread:) + withObject: NULL waitUntilDone: NO]; +} - if( !p_vout->b_fullscreen ) +- (id)closeMainThread:(id)sender +{ + if( b_black == true ) { - NSRect s_rect; + CGDisplayFadeReservationToken token; + CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token); + CGDisplayFade( token, 0.3 , kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES ); + + [self disableScreenUpdatesUntilFlush]; + [self orderOut: self]; - s_rect = [[p_vout->p_sys->o_window contentView] frame]; - p_vout->p_sys->s_rect.size = s_rect.size; + CGDisplayFade( token, 0.6 , kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, YES ); + CGReleaseDisplayFadeReservation( token); + CGDisplayRestoreColorSyncSettings(); + } + [NSScreen unblackoutScreens]; - s_rect = [p_vout->p_sys->o_window frame]; - p_vout->p_sys->s_rect.origin = s_rect.origin; + SetSystemUIMode( kUIModeNormal, 0); + [super close]; + /* this does only work in embedded mode */ + if( b_embedded == true ) + [[[[VLCMain sharedInstance] getControls] getFSPanel] orderOut: self]; - p_vout->p_sys->b_pos_saved = YES; + 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; } - - p_vout->p_sys->p_qdport = nil; - [p_vout->p_sys->o_window close]; - p_vout->p_sys->o_window = nil; + + playlist_Stop( p_playlist ); + vlc_object_release( p_playlist ); + + /* The window will be closed by the intf later. */ + return NO; } @end