X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fmacosx%2Fvout.m;h=ea68589cdcb4e8da5c61a6050ace0b0bb464a041;hb=9c623237a0965223c78d8544708d7c1bac62d1ec;hp=c34f714e779a4d155115383609a642fe3f39a923;hpb=915d39ba8cca888e8e1adc80c39ccff6b3f30e78;p=vlc diff --git a/modules/gui/macosx/vout.m b/modules/gui/macosx/vout.m index c34f714e77..ea68589cdc 100644 --- a/modules/gui/macosx/vout.m +++ b/modules/gui/macosx/vout.m @@ -1,13 +1,14 @@ /***************************************************************************** - * vout.m: MacOS X video output plugin + * vout.m: MacOS X video output module ***************************************************************************** * Copyright (C) 2001-2003 VideoLAN - * $Id: vout.m,v 1.42 2003/03/18 23:51:29 jlj Exp $ + * $Id$ * * Authors: Colin Delacroix * Florian G. Pflug * Jon Lech Johansen * Derk-Jan Hartman + * Eric Petit * * 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 @@ -33,12 +34,22 @@ #include +#include +#include +#include + +#include + #include "intf.h" #include "vout.h" #define QT_MAX_DIRECTBUFFERS 10 #define VL_MAX_DISPLAYS 16 +#define OPENGL_EFFECT_NONE 1 +#define OPENGL_EFFECT_CUBE 2 +#define OPENGL_EFFECT_TRANSPARENT_CUBE 4 + struct picture_sys_t { void *p_info; @@ -51,6 +62,7 @@ struct picture_sys_t /***************************************************************************** * Local prototypes *****************************************************************************/ + static int vout_Init ( vout_thread_t * ); static void vout_End ( vout_thread_t * ); static int vout_Manage ( vout_thread_t * ); @@ -79,6 +91,7 @@ int E_(OpenVideo) ( vlc_object_t *p_this ) vout_thread_t * p_vout = (vout_thread_t *)p_this; OSErr err; int i_timeout; + char *psz_vout_type; p_vout->p_sys = malloc( sizeof( vout_sys_t ) ); if( p_vout->p_sys == NULL ) @@ -126,32 +139,55 @@ int E_(OpenVideo) ( vlc_object_t *p_this ) } } - 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; - - p_vout->p_sys->b_mouse_pointer_visible = YES; - p_vout->p_sys->b_mouse_moved = YES; + p_vout->p_sys->b_mouse_moved = VLC_TRUE; p_vout->p_sys->i_time_mouse_last_moved = mdate(); /* 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; - 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 ); - } + /* Check if we should use QuickTime or OpenGL */ + psz_vout_type = config_GetPsz( p_vout, "macosx-vout" ); - if( vout_ChromaCmp( p_vout->render.i_chroma, VLC_FOURCC('I','4','2','0') ) ) + if( !strncmp( psz_vout_type, "auto", 4 ) ) { + p_vout->p_sys->i_opengl = CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay ); + } + else if( !strncmp( psz_vout_type, "opengl", 6 ) ) + { + p_vout->p_sys->i_opengl = VLC_TRUE; + } + else + { + p_vout->p_sys->i_opengl = VLC_FALSE; + } + free( psz_vout_type ); + + if( !p_vout->p_sys->i_opengl ) + { + /* 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 ); + 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'); @@ -161,19 +197,19 @@ int E_(OpenVideo) ( vlc_object_t *p_this ) { msg_Err( p_vout, "failed to find an appropriate codec" ); } + + 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; + } + msg_Dbg( p_vout, "using Quartz mode" ); } else { - msg_Err( p_vout, "chroma 0x%08x not supported", - p_vout->render.i_chroma ); - } - - 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( 1 ); + msg_Dbg( p_vout, "using OpenGL mode" ); } NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init]; @@ -181,14 +217,16 @@ int E_(OpenVideo) ( vlc_object_t *p_this ) if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 ) { int i = 1; - vlc_value_t val; + vlc_value_t val, text; NSScreen * o_screen; int i_option = config_GetInt( p_vout, "macosx-vdev" ); - var_Create( p_vout, "video-device", VLC_VAR_STRING | + 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]; while( (o_screen = [o_enumerator nextObject]) != NULL ) @@ -200,14 +238,15 @@ int E_(OpenVideo) ( vlc_object_t *p_this ) "%s %d (%dx%d)", _("Screen"), i, (int)s_rect.size.width, (int)s_rect.size.height ); - val.psz_string = psz_temp; - var_Change( p_vout, "video-device", VLC_VAR_ADDCHOICE, &val ); + 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 ) { var_Set( p_vout, "video-device", val ); } - i++; } @@ -222,8 +261,11 @@ int E_(OpenVideo) ( vlc_object_t *p_this ) if( CoCreateWindow( p_vout ) ) { msg_Err( p_vout, "unable to create window" ); - free( p_vout->p_sys->p_matrix ); - DisposeHandle( (Handle)p_vout->p_sys->h_img_descr ); + 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 ); } @@ -254,17 +296,28 @@ static int vout_Init( vout_thread_t *p_vout ) p_vout->output.i_height = p_vout->render.i_height; p_vout->output.i_aspect = p_vout->render.i_aspect; - SetPort( p_vout->p_sys->p_qdport ); - QTScaleMatrix( p_vout ); + if( !p_vout->p_sys->i_opengl ) + { + SetPort( p_vout->p_sys->p_qdport ); + QTScaleMatrix( p_vout ); - if( QTCreateSequence( p_vout ) ) + if( QTCreateSequence( p_vout ) ) + { + msg_Err( p_vout, "unable to create sequence" ); + return( 1 ); + } + } + else { - msg_Err( p_vout, "unable to create sequence" ); - return( 1 ); + 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; } /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */ - while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS ) + while( I_OUTPUTPICTURES < + p_vout->p_sys->i_opengl ? 1 : QT_MAX_DIRECTBUFFERS ) { p_pic = NULL; @@ -279,11 +332,27 @@ static int vout_Init( vout_thread_t *p_vout ) } /* Allocate the picture */ - if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) ) + if( p_pic == NULL ) { break; } + if( !p_vout->p_sys->i_opengl ) + { + if( QTNewPicture( p_vout, p_pic ) ) + { + break; + } + } + else + { + /* Nothing special to do, we just need a basic allocated + picture_t */ + vout_AllocatePicture( VLC_OBJECT( p_vout ), p_pic, + p_vout->output.i_chroma, p_vout->output.i_width, + p_vout->output.i_height, p_vout->output.i_aspect ); + } + p_pic->i_status = DESTROYED_PICTURE; p_pic->i_type = DIRECT_PICTURE; @@ -292,6 +361,14 @@ static int vout_Init( vout_thread_t *p_vout ) I_OUTPUTPICTURES++; } + 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 reshape]; + [p_vout->p_sys->o_glview unlockFocus]; + } + return( 0 ); } @@ -302,13 +379,27 @@ static void vout_End( vout_thread_t *p_vout ) { int i_index; - QTDestroySequence( p_vout ); + if( !p_vout->p_sys->i_opengl ) + { + QTDestroySequence( p_vout ); + } + else + { + [p_vout->p_sys->o_glview cleanUp]; + } /* Free the direct buffers we allocated */ for( i_index = I_OUTPUTPICTURES; i_index; ) { i_index--; - QTFreePicture( p_vout, PP_OUTPUTPICTURE[ 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 ); + } } } @@ -324,13 +415,16 @@ void E_(CloseVideo) ( vlc_object_t *p_this ) msg_Err( p_vout, "unable to destroy window" ); } - if ( p_vout->p_sys->p_fullscreen_state != NULL ) - EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL ); + 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 ); - ExitMovies(); + ExitMovies(); - free( p_vout->p_sys->p_matrix ); - DisposeHandle( (Handle)p_vout->p_sys->h_img_descr ); + free( p_vout->p_sys->p_matrix ); + DisposeHandle( (Handle)p_vout->p_sys->h_img_descr ); + } free( p_vout->p_sys ); } @@ -355,9 +449,12 @@ static int vout_Manage( vout_thread_t *p_vout ) if( p_vout->i_changes & VOUT_SIZE_CHANGE ) { - QTScaleMatrix( p_vout ); - SetDSequenceMatrix( p_vout->p_sys->i_seq, - p_vout->p_sys->p_matrix ); + if( !p_vout->p_sys->i_opengl ) + { + QTScaleMatrix( p_vout ); + SetDSequenceMatrix( p_vout->p_sys->i_seq, + p_vout->p_sys->p_matrix ); + } p_vout->i_changes &= ~VOUT_SIZE_CHANGE; } @@ -367,47 +464,19 @@ static int vout_Manage( vout_thread_t *p_vout ) * 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 > 2000000 && - p_vout->p_sys->b_mouse_pointer_visible ) + if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 ) { VLCHideMouse( p_vout, YES ); } - else if ( !p_vout->p_sys->b_mouse_pointer_visible ) - { - vlc_bool_t b_playing = NO; - playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - - if ( p_playlist != nil ) - { - vlc_mutex_lock( &p_playlist->object_lock ); - if( p_playlist->p_input != NULL ) - { - vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock ); - b_playing = p_playlist->p_input->stream.control.i_status != PAUSE_S; - vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock ); - } - vlc_mutex_unlock( &p_playlist->object_lock ); - vlc_object_release( p_playlist ); - } - if ( ![p_vout->p_sys->o_window isKeyWindow] || !b_playing ) - { - VLCHideMouse( p_vout, NO ); - } - } } else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen ) { - if( !p_vout->p_sys->b_mouse_pointer_visible ) - { - VLCHideMouse( p_vout, NO ); - } - else - { - p_vout->p_sys->b_mouse_moved = NO; - } + VLCHideMouse( p_vout, NO ); } + /* disable screen saver */ + UpdateSystemActivity( UsrActivity ); + return( 0 ); } @@ -418,20 +487,35 @@ static int vout_Manage( vout_thread_t *p_vout ) *****************************************************************************/ static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic ) { - 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 ) ) + if( !p_vout->p_sys->i_opengl ) { - msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err ); + 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 + { + QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil ); + } } else { - QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil ); + if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] ) + { + /* 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]; + } } } @@ -443,6 +527,8 @@ static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic ) static int CoSendRequest( vout_thread_t *p_vout, SEL sel ) { int i_ret = 0; + vlc_value_t val; + intf_thread_t * p_intf; VLCVout * o_vlv = [[VLCVout alloc] init]; @@ -453,6 +539,16 @@ static int CoSendRequest( vout_thread_t *p_vout, SEL sel ) [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 ); } @@ -468,7 +564,7 @@ static int CoCreateWindow( vout_thread_t *p_vout ) msg_Err( p_vout, "CoSendRequest (createWindow) failed" ); return( 1 ); } - + return( 0 ); } @@ -479,10 +575,8 @@ static int CoCreateWindow( vout_thread_t *p_vout ) *****************************************************************************/ static int CoDestroyWindow( vout_thread_t *p_vout ) { - if( !p_vout->p_sys->b_mouse_pointer_visible ) - { - VLCHideMouse( p_vout, NO ); - } + + VLCHideMouse( p_vout, NO ); if( CoSendRequest( p_vout, @selector(destroyWindow:) ) ) { @@ -500,6 +594,34 @@ static int CoDestroyWindow( vout_thread_t *p_vout ) *****************************************************************************/ static int CoToggleFullscreen( vout_thread_t *p_vout ) { + + vlc_value_t val; + intf_thread_t * p_intf; + + if( p_vout->p_sys->i_opengl ) + { + p_vout->b_fullscreen = !p_vout->b_fullscreen; + if( p_vout->b_fullscreen ) + { + [p_vout->p_sys->o_glview goFullScreen]; + } + else + { + [p_vout->p_sys->o_glview exitFullScreen]; + } + /*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 0; + } + QTDestroySequence( p_vout ); if( CoDestroyWindow( p_vout ) ) @@ -510,8 +632,6 @@ static int CoToggleFullscreen( vout_thread_t *p_vout ) p_vout->b_fullscreen = !p_vout->b_fullscreen; - config_PutInt( p_vout, "fullscreen", p_vout->b_fullscreen ); - if( CoCreateWindow( p_vout ) ) { msg_Err( p_vout, "unable to create window" ); @@ -548,21 +668,12 @@ static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide ) if ( b_hide && b_inside ) { - /* only hide if mouse over VLCView */ - [NSCursor hide]; - p_vout->p_sys->b_mouse_pointer_visible = 0; + /* only hide if mouse over VLCQTView */ + [NSCursor setHiddenUntilMouseMoves: YES]; } else if ( !b_hide ) { - if ( ![o_window isKeyWindow] && b_inside ) - { - /* be nice for ppl with multi monitors */ - p_vout->p_sys->b_mouse_moved = NO; - p_vout->p_sys->i_time_mouse_last_moved = mdate(); - return; - } - [NSCursor unhide]; - p_vout->p_sys->b_mouse_pointer_visible = 1; + [NSCursor setHiddenUntilMouseMoves: NO]; } p_vout->p_sys->b_mouse_moved = NO; p_vout->p_sys->i_time_mouse_last_moved = mdate(); @@ -585,7 +696,15 @@ static void QTScaleMatrix( vout_thread_t *p_vout ) 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 ) + if( config_GetInt( p_vout, "macosx-stretch" ) ) + { + factor_x = FixDiv( Long2Fix( i_width ), + Long2Fix( p_vout->output.i_width ) ); + factor_y = FixDiv( Long2Fix( i_height ), + Long2Fix( p_vout->output.i_height ) ); + + } + else 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; @@ -614,11 +733,10 @@ static void QTScaleMatrix( vout_thread_t *p_vout ) 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) ); + Long2Fix(0), Long2Fix(0) ); + + TranslateMatrix( p_vout->p_sys->p_matrix, + Long2Fix(i_offset_x), Long2Fix(i_offset_y) ); } /***************************************************************************** @@ -825,8 +943,23 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) } } +- (void)toggleFloatOnTop +{ + if( config_GetInt( p_vout, "video-on-top" ) ) + { + config_PutInt( p_vout, "video-on-top", 0 ); + [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel]; + } + else + { + config_PutInt( p_vout, "video-on-top", 1 ); + [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel]; + } +} + - (void)toggleFullscreen { + config_PutInt(p_vout, "fullscreen", !p_vout->b_fullscreen); p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE; } @@ -840,39 +973,61 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) return( YES ); } +- (BOOL)performKeyEquivalent:(NSEvent *)o_event +{ + return [(VLCApplication *) [VLCApplication sharedApplication] + hasDefinedShortcutKey:o_event]; +} + - (void)keyDown:(NSEvent *)o_event { unichar key = 0; + 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 ) + 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; + + key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0]; - if( [[o_event characters] length] ) + if( key ) { - key = [[o_event characters] characterAtIndex: 0]; + /* Escape should always get you out of fullscreen */ + if( key == (unichar) 0x1b ) + { + if( [self isFullscreen] ) + { + [self toggleFullscreen]; + } + } + else if ( key == ' ' ) + { + 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); + } + } + else + { + val.i_int |= CocoaKeyToVLC( key ); + var_Set( p_vout->p_vlc, "key-pressed", val ); + } } - - switch( key ) + else { - case 'f': case 'F': - [self toggleFullscreen]; - break; - - case (unichar)0x1b: /* escape */ - if( [self isFullscreen] ) - { - [self toggleFullscreen]; - } - break; - - case 'q': case 'Q': - p_vout->p_vlc->b_die = VLC_TRUE; - break; - - case ' ': - input_SetStatus( p_vout, INPUT_STATUS_PAUSE ); - break; - - default: - [super keyDown: o_event]; - break; + [super keyDown: o_event]; } } @@ -889,7 +1044,7 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) vlc_mutex_lock( &p_playlist->object_lock ); o_title = [NSMutableString stringWithUTF8String: - p_playlist->pp_items[p_playlist->i_index]->psz_name]; + p_playlist->pp_items[p_playlist->i_index]->input.psz_uri]; vlc_mutex_unlock( &p_playlist->object_lock ); vlc_object_release( p_playlist ); @@ -931,9 +1086,9 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) @end /***************************************************************************** - * VLCView implementation + * VLCQTView implementation *****************************************************************************/ -@implementation VLCView +@implementation VLCQTView - (void)drawRect:(NSRect)rect { @@ -1169,14 +1324,432 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) p_vout->p_sys->i_time_mouse_last_moved = mdate(); p_vout->p_sys->b_mouse_moved = YES; } - else if ( !b_inside && !p_vout->p_sys->b_mouse_pointer_visible ) + + [super mouseMoved: o_event]; +} + +@end + +/***************************************************************************** + * VLCGLView implementation + *****************************************************************************/ +@implementation VLCGLView + + +- (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout +{ + char * psz_effect; + p_vout = _p_vout; + + NSOpenGLPixelFormatAttribute attribs[] = + { + NSOpenGLPFAAccelerated, + NSOpenGLPFANoRecovery, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAWindow, + 0 + }; + + NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc] + initWithAttributes: attribs]; + + if( !fmt ) + { + msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" ); + return nil; + } + + self = [super initWithFrame:frame pixelFormat: fmt]; + + currentContext = [self openGLContext]; + [currentContext makeCurrentContext]; + [currentContext update]; + + /* Black background */ + glClearColor( 0.0, 0.0, 0.0, 0.0 ); + + /* Check if the user asked for useless visual effects */ + psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" ); + if( !psz_effect || !strcmp( psz_effect, "none" )) { - /* people with multiple monitors need their mouse, - * even if VLCView in fullscreen. */ - VLCHideMouse( p_vout, NO ); + i_effect = OPENGL_EFFECT_NONE; + } + else if( !strcmp( psz_effect, "cube" ) ) + { + i_effect = OPENGL_EFFECT_CUBE; + + glEnable( GL_DEPTH_TEST ); + } + else if( !strcmp( psz_effect, "transparent-cube" ) ) + { + i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE; + + glDisable( GL_DEPTH_TEST ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE ); + } + else + { + msg_Warn( p_vout, "no valid opengl effect provided, using " + "\"none\"" ); + i_effect = OPENGL_EFFECT_NONE; + } + + if( i_effect & ( OPENGL_EFFECT_CUBE | + OPENGL_EFFECT_TRANSPARENT_CUBE ) ) + { + /* Set the perpective */ + 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 ); + } + + i_texture = 0; + initDone = 0; + isFullScreen = 0; + + return self; +} + +- (void) reshape +{ + if( !initDone ) + { + return; } - [super mouseMoved: o_event]; + [currentContext makeCurrentContext]; + + NSRect bounds = [self bounds]; + glViewport( 0, 0, (GLint) bounds.size.width, + (GLint) bounds.size.height ); + + /* Quad size is set in order to preserve the aspect ratio */ + if( config_GetInt( p_vout, "macosx-stretch" ) ) + { + f_x = 1.0; + f_y = 1.0; + } + else if( bounds.size.height * p_vout->output.i_aspect < + bounds.size.width * VOUT_ASPECT_FACTOR ) + { + f_x = bounds.size.height * p_vout->output.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->output.i_aspect / bounds.size.height; + } +} + +- (void) initTextures +{ + [currentContext makeCurrentContext]; + + /* Free previous texture if any */ + if( i_texture ) + { + glDeleteTextures( 1, &i_texture ); + } + + /* 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 ); + + initDone = 1; +} + +- (void) reloadTexture +{ + if( !initDone ) + { + return; + } + + [currentContext 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) goFullScreen +{ + /* Create the new pixel format */ + NSOpenGLPixelFormatAttribute attribs[] = + { + NSOpenGLPFAAccelerated, + NSOpenGLPFANoRecovery, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAFullScreen, + NSOpenGLPFAScreenMask, + /* TODO handle macosx-vdev */ + CGDisplayIDToOpenGLDisplayMask( kCGDirectMainDisplay ), + 0 + }; + NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc] + initWithAttributes: attribs]; + if( !fmt ) + { + msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" ); + return; + } + + /* Create the new OpenGL context */ + fullScreenContext = [[NSOpenGLContext alloc] + initWithFormat: fmt shareContext: nil]; + if( !fullScreenContext ) + { + msg_Warn( p_vout, "Failed to create new NSOpenGLContext" ); + return; + } + currentContext = fullScreenContext; + + /* Capture display, switch to fullscreen */ + if( CGCaptureAllDisplays() != CGDisplayNoErr ) + { + msg_Warn( p_vout, "CGCaptureAllDisplays() failed" ); + return; + } + [fullScreenContext setFullScreen]; + [fullScreenContext makeCurrentContext]; + + /* Ratio */ + unsigned width = CGDisplayPixelsWide( kCGDirectMainDisplay ); + unsigned height = CGDisplayPixelsHigh( kCGDirectMainDisplay ); + int stretch = config_GetInt( p_vout, "macosx-stretch" ); + int fill = config_GetInt( p_vout, "macosx-fill" ); + int bigRatio = ( height * p_vout->output.i_aspect < + width * VOUT_ASPECT_FACTOR ); + if( stretch ) + { + f_x = 1.0; + f_y = 1.0; + } + else if( ( bigRatio && !fill ) || ( !bigRatio && fill ) ) + { + f_x = (float) height * p_vout->output.i_aspect / + width / VOUT_ASPECT_FACTOR; + f_y = 1.0; + } + else + { + f_x = 1.0; + f_y = (float) width * VOUT_ASPECT_FACTOR / + p_vout->output.i_aspect / height; + } + + /* Update viewport, re-init textures */ + glViewport( 0, 0, width, height ); + [self initTextures]; + + /* Redraw the last picture */ + [self setNeedsDisplay: YES]; + + isFullScreen = 1; +} + +- (void) exitFullScreen +{ + /* Free current OpenGL context */ + [NSOpenGLContext clearCurrentContext]; + [fullScreenContext clearDrawable]; + [fullScreenContext release]; + CGReleaseAllDisplays(); + + currentContext = [self openGLContext]; + [self initTextures]; + [self reshape]; + + /* Redraw the last picture */ + [self setNeedsDisplay: YES]; + + isFullScreen = 0; +} + +- (void) cleanUp +{ + if( isFullScreen ) + { + [self exitFullScreen]; + } + initDone = 0; +} + +- (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 ); + /* Front */ + 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 ); + + /* Left */ + 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 ); + + /* Back */ + 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 ); + + /* Right */ + 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 ); + + /* Top */ + 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 ); + + /* Bottom */ + 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 +{ + [currentContext 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( !initDone ) + { + [currentContext flushBuffer]; + return; + } + + /* Draw */ + glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture ); + if( i_effect & ( OPENGL_EFFECT_CUBE | + OPENGL_EFFECT_TRANSPARENT_CUBE ) ) + { + glRotatef( 1.0, 0.3, 0.5, 0.7 ); + [self drawCube]; + } + else + { + [self drawQuad]; + } + + /* Wait for the job to be done */ + [currentContext flushBuffer]; } @end @@ -1189,7 +1762,7 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) - (void)createWindow:(NSValue *)o_value { vlc_value_t val; - VLCView * o_view; + VLCQTView * o_view; NSScreen * o_screen; vout_thread_t * p_vout; vlc_bool_t b_main_screen; @@ -1207,11 +1780,10 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) } else { - unsigned int i_index = 0; NSArray *o_screens = [NSScreen screens]; - - if( !sscanf( val.psz_string, _("Screen %d"), &i_index ) || - [o_screens count] < i_index ) + unsigned int i_index = val.i_int; + + if( [o_screens count] < i_index ) { o_screen = [NSScreen mainScreen]; b_main_screen = 1; @@ -1222,12 +1794,10 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) o_screen = [o_screens objectAtIndex: i_index]; config_PutInt( p_vout, "macosx-vdev", i_index ); b_main_screen = (i_index == 0); - } - - free( val.psz_string ); + } } - if( p_vout->b_fullscreen ) + if( p_vout->b_fullscreen && !p_vout->p_sys->i_opengl ) { NSRect screen_rect = [o_screen frame]; screen_rect.origin.x = screen_rect.origin.y = 0; @@ -1242,8 +1812,8 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) backing: NSBackingStoreBuffered defer: NO screen: o_screen]; - [p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1]; - p_vout->p_sys->b_mouse_moved = 1; + //[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(); } else @@ -1253,9 +1823,12 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) NSClosableWindowMask | NSResizableWindowMask; - 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; + 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 ); + p_vout->p_sys->p_fullscreen_state = NULL; + } [p_vout->p_sys->o_window initWithContentRect: p_vout->p_sys->s_rect @@ -1263,23 +1836,42 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) 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" ) ) + { + [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel]; + } + if( !p_vout->p_sys->b_pos_saved ) { [p_vout->p_sys->o_window center]; } } - o_view = [[VLCView 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]; + 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]; + } - (void)destroyWindow:(NSValue *)o_value @@ -1298,7 +1890,7 @@ static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic ) s_rect = [p_vout->p_sys->o_window frame]; p_vout->p_sys->s_rect.origin = s_rect.origin; - p_vout->p_sys->b_pos_saved = 1; + p_vout->p_sys->b_pos_saved = YES; } p_vout->p_sys->p_qdport = nil;