X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fmacosx%2Fvoutgl.m;h=41ebd2b549334628d92c4ba3722b00801903227f;hb=1cd238173cdde8be8123093a76ea79f64d48a0c6;hp=02496cae8aa131a12a51fdfd9eb9b7d855fc0765;hpb=e90c868f4cba1ec0b18c5be5d230e32f1aa99c8a;p=vlc diff --git a/modules/gui/macosx/voutgl.m b/modules/gui/macosx/voutgl.m index 02496cae8a..41ebd2b549 100644 --- a/modules/gui/macosx/voutgl.m +++ b/modules/gui/macosx/voutgl.m @@ -1,8 +1,8 @@ /***************************************************************************** * voutgl.m: MacOS X OpenGL provider ***************************************************************************** - * Copyright (C) 2001-2004 the VideoLAN team - * $Id: vout.m 8351 2004-08-02 13:06:38Z hartman $ + * Copyright (C) 2001-2004, 2007-2009 the VideoLAN team + * $Id$ * * Authors: Colin Delacroix * Florian G. Pflug @@ -10,6 +10,7 @@ * Derk-Jan Hartman * Eric Petit * Benjamin Pracht + * Damien Fouilleul * * 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 @@ -31,7 +32,7 @@ *****************************************************************************/ #include /* ENOMEM */ #include /* free() */ -#include /* strerror() */ +#include #include @@ -41,28 +42,37 @@ #include #include +#include + /***************************************************************************** - * VLCView interface + * VLCGLView interface *****************************************************************************/ -@interface VLCGLView : NSOpenGLView +@interface VLCGLView : NSOpenGLView { vout_thread_t * p_vout; } ++ (void)resetVout: (NSValue *) voutValue; - (id) initWithVout: (vout_thread_t *) p_vout; @end struct vout_sys_t { - NSAutoreleasePool * o_pool; VLCGLView * o_glview; VLCVoutView * o_vout_view; - vlc_bool_t b_saved_frame; + bool b_saved_frame; NSRect s_frame; - vlc_bool_t b_got_frame; - vlc_mutex_t lock; - int i_vout_size_update_counter; - int i_x, i_y; + bool b_got_frame; + /* Mozilla plugin-related variables */ + bool b_embedded; + AGLContext agl_ctx; + AGLDrawable agl_drawable; + int i_offx, i_offy; + int i_width, i_height; + WindowRef theWindow; + WindowGroupRef winGroup; + bool b_clipped_out; + Rect clipBounds, viewBounds; }; /***************************************************************************** @@ -77,157 +87,183 @@ static void Swap ( vout_thread_t * p_vout ); static int Lock ( vout_thread_t * p_vout ); static void Unlock ( vout_thread_t * p_vout ); -static int AspectCropCallback( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); +static int aglInit ( vout_thread_t * p_vout ); +static void aglEnd ( vout_thread_t * p_vout ); +static int aglManage ( vout_thread_t * p_vout ); +static int aglControl( vout_thread_t *, int, va_list ); +static void aglSwap ( vout_thread_t * p_vout ); +static int aglLock ( vout_thread_t * p_vout ); +static void aglUnlock ( vout_thread_t * p_vout ); -int E_(OpenVideoGL) ( vlc_object_t * p_this ) +int OpenVideoGL ( vlc_object_t * p_this ) { vout_thread_t * p_vout = (vout_thread_t *) p_this; + vlc_value_t value_drawable; if( !CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay ) ) { msg_Warn( p_vout, "no OpenGL hardware acceleration found. " - "Video display will be slow" ); - return( 1 ); + "Video display might be slow" ); } msg_Dbg( p_vout, "display is Quartz Extreme accelerated" ); p_vout->p_sys = malloc( sizeof( vout_sys_t ) ); if( p_vout->p_sys == NULL ) - { - msg_Err( p_vout, "out of memory" ); - return( 1 ); - } + return VLC_ENOMEM; memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); - p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init]; - vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); + var_Get( p_vout->p_libvlc, "drawable-agl", &value_drawable ); + if( value_drawable.i_int != 0 ) + { + static const GLint ATTRIBUTES[] = { + AGL_WINDOW, + AGL_RGBA, + AGL_NO_RECOVERY, + AGL_ACCELERATED, + AGL_DOUBLEBUFFER, + AGL_RED_SIZE, 8, + AGL_GREEN_SIZE, 8, + AGL_BLUE_SIZE, 8, + AGL_ALPHA_SIZE, 8, + AGL_DEPTH_SIZE, 24, + AGL_NONE }; + + AGLPixelFormat pixFormat; + + p_vout->p_sys->b_embedded = true; + + pixFormat = aglChoosePixelFormat(NULL, 0, ATTRIBUTES); + if( NULL == pixFormat ) + { + msg_Err( p_vout, "no screen renderer available for required attributes." ); + free( p_vout->p_sys ); + return VLC_EGENERIC; + } + + p_vout->p_sys->agl_ctx = aglCreateContext(pixFormat, NULL); + aglDestroyPixelFormat(pixFormat); + if( NULL == p_vout->p_sys->agl_ctx ) + { + msg_Err( p_vout, "cannot create AGL context." ); + free( p_vout->p_sys ); + return VLC_EGENERIC; + } + else + { + // tell opengl not to sync buffer swap with vertical retrace (too inefficient) + GLint param = 0; + aglSetInteger(p_vout->p_sys->agl_ctx, AGL_SWAP_INTERVAL, ¶m); + aglEnable(p_vout->p_sys->agl_ctx, AGL_SWAP_INTERVAL); + } - /* Create the GL view */ - p_vout->p_sys->o_glview = [[VLCGLView alloc] initWithVout: p_vout]; - [p_vout->p_sys->o_glview autorelease]; + p_vout->pf_init = aglInit; + p_vout->pf_end = aglEnd; + p_vout->pf_manage = aglManage; + p_vout->pf_control = aglControl; + p_vout->pf_swap = aglSwap; + p_vout->pf_lock = aglLock; + p_vout->pf_unlock = aglUnlock; + } + else + { + NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; - /* Spawn the window */ + p_vout->p_sys->b_embedded = false; - if( !(p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout - subView: p_vout->p_sys->o_glview frame: nil]) ) - { - return VLC_EGENERIC; - } + [VLCGLView performSelectorOnMainThread:@selector(initVout:) withObject:[NSValue valueWithPointer:p_vout] waitUntilDone:YES]; - p_vout->p_sys->b_got_frame = VLC_FALSE; + [o_pool release]; - p_vout->pf_init = Init; - p_vout->pf_end = End; - p_vout->pf_manage = Manage; - p_vout->pf_control= Control; - p_vout->pf_swap = Swap; - p_vout->pf_lock = Lock; - p_vout->pf_unlock = Unlock; + /* Check to see if initVout: was successfull */ + if( !p_vout->p_sys->o_vout_view ) + { + free( p_vout->p_sys ); + return VLC_EGENERIC; + } + + p_vout->pf_init = Init; + p_vout->pf_end = End; + p_vout->pf_manage = Manage; + p_vout->pf_control= Control; + p_vout->pf_swap = Swap; + p_vout->pf_lock = Lock; + p_vout->pf_unlock = Unlock; + } + p_vout->p_sys->b_got_frame = false; return VLC_SUCCESS; } -void E_(CloseVideoGL) ( vlc_object_t * p_this ) +void CloseVideoGL ( vlc_object_t * p_this ) { vout_thread_t * p_vout = (vout_thread_t *) p_this; - NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; - /* Close the window */ - [p_vout->p_sys->o_vout_view closeVout]; + msg_Dbg( p_this, "Closing" ); + if( p_vout->p_sys->b_embedded ) + { + if( p_vout->p_sys->agl_ctx ) + { + aglEnd( p_vout ); + aglDestroyContext(p_vout->p_sys->agl_ctx); + } + } + else if(VLCIntf && vlc_object_alive (VLCIntf)) + { + NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; + + /* Close the window */ + [p_vout->p_sys->o_vout_view performSelectorOnMainThread:@selector(closeVout) withObject:NULL waitUntilDone:YES]; + + [o_pool release]; + } /* Clean up */ - vlc_mutex_destroy( &p_vout->p_sys->lock ); - [o_pool release]; free( p_vout->p_sys ); } static int Init( vout_thread_t * p_vout ) { - /* The variable is in fact changed on the parent vout */ - if( !var_Type( p_vout->p_parent, "aspect-ratio" ) ) - { - var_Create( p_vout->p_parent, "aspect-ratio", - VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - } - var_AddCallback( p_vout->p_parent, "aspect-ratio", AspectCropCallback, p_vout ); - if( !var_Type( p_vout->p_parent, "crop" ) ) - { - var_Create( p_vout->p_parent, "crop", - VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - } - var_AddCallback( p_vout->p_parent, "crop", AspectCropCallback, p_vout ); [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext]; return VLC_SUCCESS; } static void End( vout_thread_t * p_vout ) { - var_DelCallback( p_vout->p_parent, "aspect-ratio", AspectCropCallback, p_vout ); - var_DelCallback( p_vout->p_parent, "crop", AspectCropCallback, p_vout ); [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext]; } static int Manage( vout_thread_t * p_vout ) { + if( p_vout->i_changes & VOUT_ASPECT_CHANGE ) + { + [p_vout->p_sys->o_glview reshape]; + p_vout->i_changes &= ~VOUT_ASPECT_CHANGE; + } + if( p_vout->i_changes & VOUT_CROP_CHANGE ) + { + [p_vout->p_sys->o_glview reshape]; + p_vout->i_changes &= ~VOUT_CROP_CHANGE; + } + if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE ) { NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; - if( !p_vout->b_fullscreen ) - { - /* Save window size and position */ - p_vout->p_sys->s_frame.size = - [p_vout->p_sys->o_vout_view frame].size; - p_vout->p_sys->s_frame.origin = - [[p_vout->p_sys->o_vout_view getWindow ]frame].origin; - p_vout->p_sys->b_saved_frame = VLC_TRUE; - } - [p_vout->p_sys->o_vout_view closeVout]; - p_vout->b_fullscreen = !p_vout->b_fullscreen; -#define o_glview p_vout->p_sys->o_glview - o_glview = [[VLCGLView alloc] initWithVout: p_vout]; - [o_glview autorelease]; - - if( p_vout->p_sys->b_saved_frame ) - { - p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout - subView: o_glview - frame: &p_vout->p_sys->s_frame]; - } + if( p_vout->b_fullscreen ) + [p_vout->p_sys->o_vout_view enterFullscreen]; else - { - p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout - subView: o_glview frame: nil]; - - } - - [[o_glview openGLContext] makeCurrentContext]; -#undef o_glview + [p_vout->p_sys->o_vout_view leaveFullscreen]; [o_pool release]; p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; } - if( p_vout->p_sys->i_vout_size_update_counter ) - { - int i_old_x = p_vout->p_sys->i_x, i_old_y = p_vout->p_sys->i_y; - [p_vout->p_sys->o_glview reshape]; - if( p_vout->p_sys->i_x != i_old_x || p_vout->p_sys->i_y != i_old_y ) - { - p_vout->p_sys->i_vout_size_update_counter = 0; - } - else if( p_vout->p_sys->i_vout_size_update_counter > 0 ) - { - p_vout->p_sys->i_vout_size_update_counter--; - } - } - - [p_vout->p_sys->o_vout_view manage]; + if( p_vout->p_sys->o_vout_view ) + [p_vout->p_sys->o_vout_view manage]; return VLC_SUCCESS; } @@ -236,69 +272,112 @@ static int Manage( vout_thread_t * p_vout ) *****************************************************************************/ static int Control( vout_thread_t *p_vout, int i_query, va_list args ) { - vlc_bool_t b_arg; + bool b_arg; switch( i_query ) { case VOUT_SET_STAY_ON_TOP: - b_arg = va_arg( args, vlc_bool_t ); + b_arg = (bool) va_arg( args, int ); [p_vout->p_sys->o_vout_view setOnTop: b_arg]; return VLC_SUCCESS; - case VOUT_CLOSE: - case VOUT_REPARENT: default: - return vout_vaControlDefault( p_vout, i_query, args ); + return VLC_EGENERIC; } } static void Swap( vout_thread_t * p_vout ) { - p_vout->p_sys->b_got_frame = VLC_TRUE; - [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext]; - glFlush(); + p_vout->p_sys->b_got_frame = true; + [[p_vout->p_sys->o_glview openGLContext] flushBuffer]; } static int Lock( vout_thread_t * p_vout ) { - vlc_mutex_lock( &p_vout->p_sys->lock ); - return 0; + if( kCGLNoError == CGLLockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]) ) + { + [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext]; + return 0; + } + return 1; } static void Unlock( vout_thread_t * p_vout ) { - vlc_mutex_unlock( &p_vout->p_sys->lock ); -} - -static int AspectCropCallback( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) -{ - /* Only update the vout size if the aspect ratio has actually been changed*/ - /* We cannot change the size directly in this callback, since fmt_in - hasn't been updated yet, so do it in Manage */ - if( strcmp( oldval.psz_string, newval.psz_string ) ) - { - /* khludge ! Here, we are not sure that the vout size will actually - change (for instance if we go from Predefined to 4:3 on a 4:3 - stream). So, to to trigger reshape endlessly, we decrease that - counter each time we call reshape. We put it to 0 directly if - we actually change the vout size. */ - ((vout_thread_t *)p_data)->p_sys->i_vout_size_update_counter = 2; - } - return VLC_SUCCESS; + CGLUnlockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]); } /***************************************************************************** * VLCGLView implementation *****************************************************************************/ @implementation VLCGLView ++ (void)initVout:(NSValue *)arg +{ + vout_thread_t * p_vout = [arg pointerValue]; + + /* Create the GL view */ + p_vout->p_sys->o_glview = [[VLCGLView alloc] initWithVout: p_vout]; + [p_vout->p_sys->o_glview autorelease]; + + /* Spawn the window */ + id old_vout = p_vout->p_sys->o_vout_view; + p_vout->p_sys->o_vout_view = [[VLCVoutView voutView: p_vout + subView: p_vout->p_sys->o_glview frame: nil] retain]; + [old_vout release]; +} + +/* This function will reset the o_vout_view. It's useful to go fullscreen. */ ++ (void)resetVout:(NSValue *) voutValue +{ + vout_thread_t * p_vout = [voutValue pointerValue]; + if( p_vout->b_fullscreen ) + { + /* Save window size and position */ + p_vout->p_sys->s_frame.size = + [p_vout->p_sys->o_vout_view frame].size; + p_vout->p_sys->s_frame.origin = + [[p_vout->p_sys->o_vout_view getWindow ]frame].origin; + p_vout->p_sys->b_saved_frame = true; + } + + [p_vout->p_sys->o_vout_view closeVout]; + +#define o_glview p_vout->p_sys->o_glview + o_glview = [[VLCGLView alloc] initWithVout: p_vout]; + [o_glview autorelease]; + + if( p_vout->p_sys->b_saved_frame ) + { + id old_vout = p_vout->p_sys->o_vout_view; + p_vout->p_sys->o_vout_view = [[VLCVoutView voutView: p_vout + subView: o_glview + frame: &p_vout->p_sys->s_frame] retain]; + [old_vout release]; + } + else + { + id old_vout = p_vout->p_sys->o_vout_view; + p_vout->p_sys->o_vout_view = [[VLCVoutView voutView: p_vout + subView: o_glview frame: nil] retain]; + [old_vout release]; + } +#undef o_glview +} - (id) initWithVout: (vout_thread_t *) vout { + /* Must be called from main thread: + * "The NSView class is generally thread-safe, with a few exceptions. You + * should create, destroy, resize, move, and perform other operations on NSView + * objects only from the main thread of an application. Drawing from secondary + * threads is thread-safe as long as you bracket drawing calls with calls to + * lockFocusIfCanDraw and unlockFocus." Cocoa Thread Safety */ + p_vout = vout; NSOpenGLPixelFormatAttribute attribs[] = { + NSOpenGLPFADoubleBuffer, NSOpenGLPFAAccelerated, NSOpenGLPFANoRecovery, NSOpenGLPFAColorSize, 24, @@ -326,24 +405,24 @@ static int AspectCropCallback( vlc_object_t *p_this, char const *psz_cmd, /* 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 ); + GLint params[] = { 1 }; + CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, params ); return self; } +- (BOOL)mouseDownCanMoveWindow +{ + return YES; +} + - (void) reshape { int x, y; - vlc_value_t val; Lock( p_vout ); NSRect bounds = [self bounds]; - [[self openGLContext] makeCurrentContext]; - - var_Get( p_vout, "macosx-stretch", &val ); - if( val.b_bool ) + if( var_GetBool( p_vout, "macosx-stretch" ) ) { x = bounds.size.width; y = bounds.size.height; @@ -367,12 +446,11 @@ static int AspectCropCallback( vlc_object_t *p_this, char const *psz_cmd, ( p_vout->fmt_in.i_visible_width * p_vout->fmt_in.i_sar_num ); } - p_vout->p_sys->i_x = x; - p_vout->p_sys->i_y = y; - glViewport( ( bounds.size.width - x ) / 2, ( bounds.size.height - y ) / 2, x, y ); + [super reshape]; + if( p_vout->p_sys->b_got_frame ) { /* Ask the opengl module to redraw */ @@ -389,7 +467,6 @@ static int AspectCropCallback( vlc_object_t *p_this, char const *psz_cmd, glClear( GL_COLOR_BUFFER_BIT ); Unlock( p_vout ); } - [super reshape]; } - (void) update @@ -402,12 +479,592 @@ static int AspectCropCallback( vlc_object_t *p_this, char const *psz_cmd, - (void) drawRect: (NSRect) rect { Lock( p_vout ); - [[self openGLContext] makeCurrentContext]; - glFlush(); + [[p_vout->p_sys->o_glview openGLContext] flushBuffer]; [super drawRect:rect]; Unlock( p_vout ); } @end +/***************************************************************************** + * embedded AGL context implementation + *****************************************************************************/ + +static void aglSetViewport( vout_thread_t *p_vout, Rect viewBounds, Rect clipBounds ); +static void aglReshape( vout_thread_t * p_vout ); +static OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData); + +static int aglInit( vout_thread_t * p_vout ) +{ + Rect viewBounds; + Rect clipBounds; + + p_vout->p_sys->agl_drawable = (AGLDrawable) + var_GetInteger( p_vout->p_libvlc, "drawable-agl" ); + aglSetDrawable(p_vout->p_sys->agl_ctx, p_vout->p_sys->agl_drawable); + + viewBounds.top = var_GetInteger( p_vout->p_libvlc, "drawable-view-top" ); + viewBounds.left = var_GetInteger( p_vout->p_libvlc, "drawable-view-left" ); + viewBounds.bottom = var_GetInteger( p_vout->p_libvlc, "drawable-view-bottom" ); + viewBounds.right = var_GetInteger( p_vout->p_libvlc, "drawable-view-right" ); + clipBounds.top = var_GetInteger( p_vout->p_libvlc, "drawable-clip-top" ); + clipBounds.left = var_GetInteger( p_vout->p_libvlc, "drawable-clip-left" ); + clipBounds.bottom = var_GetInteger( p_vout->p_libvlc, "drawable-clip-bottom" ); + clipBounds.right = var_GetInteger( p_vout->p_libvlc, "drawable-clip-right" ); + + p_vout->p_sys->b_clipped_out = (clipBounds.top == clipBounds.bottom) + || (clipBounds.left == clipBounds.right); + if( ! p_vout->p_sys->b_clipped_out ) + { + aglLock(p_vout); + aglSetViewport(p_vout, viewBounds, clipBounds); + aglReshape(p_vout); + aglUnlock(p_vout); + } + p_vout->p_sys->clipBounds = clipBounds; + p_vout->p_sys->viewBounds = viewBounds; + + return VLC_SUCCESS; +} + +static void aglEnd( vout_thread_t * p_vout ) +{ + aglSetCurrentContext(NULL); + if( p_vout->p_sys->theWindow ) + { + DisposeWindow( p_vout->p_sys->theWindow ); + p_vout->p_sys->theWindow = NULL; + } +} + +static void aglReshape( vout_thread_t * p_vout ) +{ + unsigned int x, y; + unsigned int i_height = p_vout->p_sys->i_height; + unsigned int i_width = p_vout->p_sys->i_width; + + vout_PlacePicture(p_vout, i_width, i_height, &x, &y, &i_width, &i_height); + + glViewport( p_vout->p_sys->i_offx + x, p_vout->p_sys->i_offy + y, i_width, i_height ); + + if( p_vout->p_sys->b_got_frame ) + { + /* Ask the opengl module to redraw */ + vout_thread_t * p_parent; + p_parent = (vout_thread_t *) p_vout->p_parent; + if( p_parent && p_parent->pf_display ) + { + p_parent->pf_display( p_parent, NULL ); + } + } + else + { + glClear( GL_COLOR_BUFFER_BIT ); + } +} + +/* private event class */ +enum +{ + kEventClassVLCPlugin = 'vlcp', +}; +/* private event kinds */ +enum +{ + kEventVLCPluginShowFullscreen = 32768, + kEventVLCPluginHideFullscreen, +}; + +static void sendEventToMainThread(EventTargetRef target, UInt32 class, UInt32 kind) +{ + EventRef myEvent; + if( noErr == CreateEvent(NULL, class, kind, 0, kEventAttributeNone, &myEvent) ) + { + if( noErr == SetEventParameter(myEvent, kEventParamPostTarget, typeEventTargetRef, sizeof(EventTargetRef), &target) ) + { + PostEventToQueue(GetMainEventQueue(), myEvent, kEventPriorityStandard); + } + ReleaseEvent(myEvent); + } +} + +static int aglManage( vout_thread_t * p_vout ) +{ + if( p_vout->i_changes & VOUT_ASPECT_CHANGE ) + { + aglLock( p_vout ); + aglReshape(p_vout); + aglUnlock( p_vout ); + p_vout->i_changes &= ~VOUT_ASPECT_CHANGE; + } + if( p_vout->i_changes & VOUT_CROP_CHANGE ) + { + aglLock( p_vout ); + aglReshape(p_vout); + aglUnlock( p_vout ); + p_vout->i_changes &= ~VOUT_CROP_CHANGE; + } + if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE ) + { + aglSetDrawable(p_vout->p_sys->agl_ctx, NULL); + aglLock( p_vout ); + if( p_vout->b_fullscreen ) + { + /* Close the fullscreen window and resume normal drawing */ + Rect viewBounds; + Rect clipBounds; + + p_vout->p_sys->agl_drawable = (AGLDrawable) + var_GetInteger( p_vout->p_libvlc, "drawable-agl" ); + + aglSetDrawable(p_vout->p_sys->agl_ctx, p_vout->p_sys->agl_drawable); + + viewBounds.top = var_GetInteger( p_vout->p_libvlc, "drawable-view-top" ); + viewBounds.left = var_GetInteger( p_vout->p_libvlc, "drawable-view-left" ); + viewBounds.bottom = var_GetInteger( p_vout->p_libvlc, "drawable-view-bottom" ); + viewBounds.right = var_GetInteger( p_vout->p_libvlc, "drawable-view-right" ); + clipBounds.top = var_GetInteger( p_vout->p_libvlc, "drawable-clip-top" ); + clipBounds.left = var_GetInteger( p_vout->p_libvlc, "drawable-clip-left" ); + clipBounds.bottom = var_GetInteger( p_vout->p_libvlc, "drawable-clip-bottom" ); + clipBounds.right = var_GetInteger( p_vout->p_libvlc, "drawable-clip-right" ); + + aglSetCurrentContext(p_vout->p_sys->agl_ctx); + aglSetViewport(p_vout, viewBounds, clipBounds); + + if( p_vout->p_sys->theWindow ) + { + /* Most Carbon APIs are not thread-safe, therefore delagate some GUI visibilty + * update to the main thread */ + sendEventToMainThread(GetWindowEventTarget(p_vout->p_sys->theWindow), + kEventClassVLCPlugin, kEventVLCPluginHideFullscreen); + } + } + else + { + /* Go into fullscreen */ + Rect deviceRect; + + GDHandle deviceHdl = GetMainDevice(); + deviceRect = (*deviceHdl)->gdRect; + + if( !p_vout->p_sys->theWindow ) + { + /* Create a window */ + WindowAttributes windowAttrs; + + windowAttrs = kWindowStandardDocumentAttributes + | kWindowStandardHandlerAttribute + | kWindowLiveResizeAttribute + | kWindowNoShadowAttribute; + + windowAttrs &= (~kWindowResizableAttribute); + + CreateNewWindow(kDocumentWindowClass, windowAttrs, &deviceRect, &p_vout->p_sys->theWindow); + if( !p_vout->p_sys->winGroup ) + { + CreateWindowGroup(0, &p_vout->p_sys->winGroup); + SetWindowGroup(p_vout->p_sys->theWindow, p_vout->p_sys->winGroup); + SetWindowGroupParent( p_vout->p_sys->winGroup, GetWindowGroupOfClass(kDocumentWindowClass) ) ; + } + + // Window title + CFStringRef titleKey = CFSTR("Fullscreen VLC media plugin"); + CFStringRef windowTitle = CFCopyLocalizedString(titleKey, NULL); + SetWindowTitleWithCFString(p_vout->p_sys->theWindow, windowTitle); + CFRelease(titleKey); + CFRelease(windowTitle); + + //Install event handler + static const EventTypeSpec win_events[] = { + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassWindow, kEventWindowClosed }, + { kEventClassWindow, kEventWindowBoundsChanged }, + { kEventClassCommand, kEventCommandProcess }, + { kEventClassVLCPlugin, kEventVLCPluginShowFullscreen }, + { kEventClassVLCPlugin, kEventVLCPluginHideFullscreen }, + }; + InstallWindowEventHandler (p_vout->p_sys->theWindow, NewEventHandlerUPP (WindowEventHandler), GetEventTypeCount(win_events), win_events, p_vout, NULL); + } + else + { + /* just in case device resolution changed */ + SetWindowBounds(p_vout->p_sys->theWindow, kWindowContentRgn, &deviceRect); + } + glClear( GL_COLOR_BUFFER_BIT ); + p_vout->p_sys->agl_drawable = (AGLDrawable)GetWindowPort(p_vout->p_sys->theWindow); + aglSetDrawable(p_vout->p_sys->agl_ctx, p_vout->p_sys->agl_drawable); + aglSetCurrentContext(p_vout->p_sys->agl_ctx); + aglSetViewport(p_vout, deviceRect, deviceRect); + //aglSetFullScreen(p_vout->p_sys->agl_ctx, device_width, device_height, 0, 0); + + if( p_vout->p_sys->theWindow ) + { + /* Most Carbon APIs are not thread-safe, therefore delagate some GUI visibilty + * update to the main thread */ + sendEventToMainThread(GetWindowEventTarget(p_vout->p_sys->theWindow), + kEventClassVLCPlugin, kEventVLCPluginShowFullscreen); + } + } + aglReshape(p_vout); + aglUnlock( p_vout ); + p_vout->b_fullscreen = !p_vout->b_fullscreen; + p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; + } + return VLC_SUCCESS; +} + +static int aglControl( vout_thread_t *p_vout, int i_query, va_list args ) +{ + switch( i_query ) + { + case VOUT_SET_VIEWPORT: + { + Rect viewBounds, clipBounds; + viewBounds.top = va_arg( args, int); + viewBounds.left = va_arg( args, int); + viewBounds.bottom = va_arg( args, int); + viewBounds.right = va_arg( args, int); + clipBounds.top = va_arg( args, int); + clipBounds.left = va_arg( args, int); + clipBounds.bottom = va_arg( args, int); + clipBounds.right = va_arg( args, int); + + if( !p_vout->b_fullscreen ) + { + /* + ** check that the clip rect is not empty, as this is used + ** by Firefox to prevent a plugin from displaying during + ** a scrolling event. In this case we just prevent buffers + ** from being swapped and ignore clipping as this is less + ** disruptive than a GL geometry change + */ + + p_vout->p_sys->b_clipped_out = (clipBounds.top == clipBounds.bottom) + || (clipBounds.left == clipBounds.right); + if( ! p_vout->p_sys->b_clipped_out ) + { + /* ignore consecutive viewport update with identical parameters */ + if( memcmp(&clipBounds, &(p_vout->p_sys->clipBounds), sizeof(clipBounds) ) + && memcmp(&viewBounds, &(p_vout->p_sys->viewBounds), sizeof(viewBounds)) ) + { + aglLock( p_vout ); + aglSetViewport(p_vout, viewBounds, clipBounds); + aglReshape( p_vout ); + aglUnlock( p_vout ); + p_vout->p_sys->clipBounds = clipBounds; + p_vout->p_sys->viewBounds = viewBounds; + } + } + } + return VLC_SUCCESS; + } + + case VOUT_REDRAW_RECT: + { + vout_thread_t * p_parent; + Rect areaBounds; + + areaBounds.top = va_arg( args, int); + areaBounds.left = va_arg( args, int); + areaBounds.bottom = va_arg( args, int); + areaBounds.right = va_arg( args, int); + + /* Ask the opengl module to redraw */ + p_parent = (vout_thread_t *) p_vout->p_parent; + if( p_parent && p_parent->pf_display ) + { + p_parent->pf_display( p_parent, NULL ); + } + return VLC_SUCCESS; + } + + default: + return VLC_EGENERIC; + } +} + +static void aglSwap( vout_thread_t * p_vout ) +{ + if( ! p_vout->p_sys->b_clipped_out ) + { + p_vout->p_sys->b_got_frame = true; + aglSwapBuffers(p_vout->p_sys->agl_ctx); + } + else + { + /* drop frame */ + glFlush(); + } +} + +/* Enter this function with the p_vout locked */ +static void aglSetViewport( vout_thread_t *p_vout, Rect viewBounds, Rect clipBounds ) +{ + // mozilla plugin provides coordinates based on port bounds + // however AGL coordinates are based on window structure region + // and are vertically flipped + GLint rect[4]; + CGrafPtr port = (CGrafPtr)p_vout->p_sys->agl_drawable; + Rect winBounds, clientBounds; + + GetWindowBounds(GetWindowFromPort(port), + kWindowStructureRgn, &winBounds); + GetWindowBounds(GetWindowFromPort(port), + kWindowContentRgn, &clientBounds); + + /* update video clipping bounds in drawable */ + rect[0] = (clientBounds.left-winBounds.left) + + clipBounds.left; // from window left edge + rect[1] = (winBounds.bottom-winBounds.top) + - (clientBounds.top-winBounds.top) + - clipBounds.bottom; // from window bottom edge + rect[2] = clipBounds.right-clipBounds.left; // width + rect[3] = clipBounds.bottom-clipBounds.top; // height + + aglSetInteger(p_vout->p_sys->agl_ctx, AGL_BUFFER_RECT, rect); + aglEnable(p_vout->p_sys->agl_ctx, AGL_BUFFER_RECT); + + /* update video internal bounds in drawable */ + p_vout->p_sys->i_width = viewBounds.right-viewBounds.left; + p_vout->p_sys->i_height = viewBounds.bottom-viewBounds.top; + p_vout->p_sys->i_offx = -clipBounds.left - viewBounds.left; + p_vout->p_sys->i_offy = clipBounds.bottom + viewBounds.top + - p_vout->p_sys->i_height; + + aglUpdateContext(p_vout->p_sys->agl_ctx); +} + +//default window event handler +static pascal OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData) +{ + OSStatus result = noErr; + UInt32 class = GetEventClass (event); + UInt32 kind = GetEventKind (event); + vout_thread_t *p_vout = (vout_thread_t *)userData; + + result = CallNextEventHandler(nextHandler, event); + if(class == kEventClassCommand) + { + HICommand theHICommand; + GetEventParameter( event, kEventParamDirectObject, typeHICommand, NULL, sizeof( HICommand ), NULL, &theHICommand ); + + switch ( theHICommand.commandID ) + { + default: + result = eventNotHandledErr; + } + } + else if(class == kEventClassWindow) + { + WindowRef window; + Rect rectPort = {0,0,0,0}; + + GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window); + + if(window) + { + GetPortBounds(GetWindowPort(window), &rectPort); + } + + switch (kind) + { + case kEventWindowClosed: + case kEventWindowZoomed: + case kEventWindowBoundsChanged: + break; + + default: + result = eventNotHandledErr; + } + } + else if(class == kEventClassMouse) + { + switch (kind) + { + case kEventMouseDown: + { + UInt16 button; + + GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button); + switch (button) + { + case kEventMouseButtonPrimary: + { + vlc_value_t val; + + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int |= 1; + var_Set( p_vout, "mouse-button-down", val ); + break; + } + case kEventMouseButtonSecondary: + { + vlc_value_t val; + + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int |= 2; + var_Set( p_vout, "mouse-button-down", val ); + break; + } + case kEventMouseButtonTertiary: + { + vlc_value_t val; + + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int |= 4; + var_Set( p_vout, "mouse-button-down", val ); + break; + } + default: + result = eventNotHandledErr; + } + break; + } + + case kEventMouseUp: + { + UInt16 button; + + GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button); + switch (button) + { + case kEventMouseButtonPrimary: + { + UInt32 clickCount = 0; + GetEventParameter(event, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount); + if( clickCount > 1 ) + { + vlc_value_t val; + + val.b_bool = false; + var_Set((vout_thread_t *) p_vout->p_parent, "fullscreen", val); + } + else + { + vlc_value_t val; + + var_SetBool( p_vout, "mouse-clicked", true ); + + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int &= ~1; + var_Set( p_vout, "mouse-button-down", val ); + } + break; + } + case kEventMouseButtonSecondary: + { + vlc_value_t val; + + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int &= ~2; + var_Set( p_vout, "mouse-button-down", val ); + break; + } + case kEventMouseButtonTertiary: + { + vlc_value_t val; + + var_Get( p_vout, "mouse-button-down", &val ); + val.i_int &= ~2; + var_Set( p_vout, "mouse-button-down", val ); + break; + } + default: + result = eventNotHandledErr; + } + break; + } + + case kEventMouseMoved: + { + Point ml; + vlc_value_t val; + + unsigned int i_x, i_y; + unsigned int i_height = p_vout->p_sys->i_height; + unsigned int i_width = p_vout->p_sys->i_width; + + vout_PlacePicture(p_vout, i_width, i_height, &i_x, &i_y, &i_width, &i_height); + + GetEventParameter(event, kEventParamWindowMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &ml); + + val.i_int = ( ((int)ml.h) - i_x ) * + p_vout->render.i_width / i_width; + var_Set( p_vout, "mouse-x", val ); + + val.i_int = ( ((int)ml.v) - i_y ) * + p_vout->render.i_height / i_height; + + var_Set( p_vout, "mouse-y", val ); + + val.b_bool = true; + var_Set( p_vout, "mouse-moved", val ); + + break; + } + + default: + result = eventNotHandledErr; + } + } + else if(class == kEventClassTextInput) + { + switch (kind) + { + case kEventTextInputUnicodeForKeyEvent: + { + break; + } + default: + result = eventNotHandledErr; + } + } + else if(class == kEventClassVLCPlugin) + { + switch (kind) + { + case kEventVLCPluginShowFullscreen: + ShowWindow (p_vout->p_sys->theWindow); + SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar); + //CGDisplayHideCursor(kCGDirectMainDisplay); + break; + case kEventVLCPluginHideFullscreen: + HideWindow (p_vout->p_sys->theWindow); + SetSystemUIMode( kUIModeNormal, 0); + CGDisplayShowCursor(kCGDirectMainDisplay); + break; + default: + result = eventNotHandledErr; + break; + } + } + return result; +} + +static int aglLock( vout_thread_t * p_vout ) +{ + /* get the underlying CGL context */ + CGLContextObj cglContext; + if( aglGetCGLContext(p_vout->p_sys->agl_ctx, (void**)&cglContext) ) + { + if( kCGLNoError == CGLLockContext( cglContext ) ) + { + aglSetCurrentContext(p_vout->p_sys->agl_ctx); + return 0; + } + } + return 1; +} + +static void aglUnlock( vout_thread_t * p_vout ) +{ + /* get the underlying CGL context */ + CGLContextObj cglContext; + if( aglGetCGLContext(p_vout->p_sys->agl_ctx, (void**)&cglContext) ) + { + CGLUnlockContext( cglContext ); + } +}