/*****************************************************************************
* voutgl.m: MacOS X OpenGL provider
*****************************************************************************
- * Copyright (C) 2001-2004 the VideoLAN team
+ * Copyright (C) 2001-2004, 2007 the VideoLAN team
* $Id$
*
* Authors: Colin Delacroix <colin@zoy.org>
*****************************************************************************/
#include <errno.h> /* ENOMEM */
#include <stdlib.h> /* free() */
-#include <string.h> /* strerror() */
+#include <string.h>
#include <vlc_keys.h>
vout_thread_t * p_vout;
}
-+ (void)resetVout: (vout_thread_t *)p_vout;
++ (void)resetVout: (NSValue *) voutValue;
- (id) initWithVout: (vout_thread_t *) p_vout;
@end
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;
+ bool b_got_frame;
/* Mozilla plugin-related variables */
- vlc_bool_t b_embedded;
+ 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;
};
/*****************************************************************************
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 );
- }
memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
var_Get( p_vout->p_libvlc, "drawable", &value_drawable );
if( value_drawable.i_int != 0 )
{
- static const GLint ATTRIBUTES[] = {
+ static const GLint ATTRIBUTES[] = {
AGL_WINDOW,
AGL_RGBA,
AGL_NO_RECOVERY,
AGLPixelFormat pixFormat;
- p_vout->p_sys->b_embedded = VLC_TRUE;
+ 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." );
return VLC_EGENERIC;
}
-
+
p_vout->p_sys->agl_ctx = aglCreateContext(pixFormat, NULL);
aglDestroyPixelFormat(pixFormat);
if( NULL == p_vout->p_sys->agl_ctx )
}
else
{
- p_vout->p_sys->b_embedded = VLC_FALSE;
+ NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
- p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
+ p_vout->p_sys->b_embedded = false;
- /* Create the GL view */
- p_vout->p_sys->o_glview = [[VLCGLView alloc] initWithVout: p_vout];
- [p_vout->p_sys->o_glview autorelease];
+ [VLCGLView performSelectorOnMainThread:@selector(initVout:) withObject:[NSValue valueWithPointer:p_vout] waitUntilDone:YES];
- /* Spawn the window */
+ [o_pool release];
+
+ /* Check to see if initVout: was successfull */
- if( !(p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
- subView: p_vout->p_sys->o_glview frame: nil]) )
+ if( !p_vout->p_sys->o_vout_view )
{
return VLC_EGENERIC;
}
+
p_vout->pf_init = Init;
p_vout->pf_end = End;
p_vout->pf_manage = Manage;
p_vout->pf_lock = Lock;
p_vout->pf_unlock = Unlock;
}
- p_vout->p_sys->b_got_frame = VLC_FALSE;
+ 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;
+
+ msg_Dbg( p_this, "Closing" );
+
if( p_vout->p_sys->b_embedded )
{
aglDestroyContext(p_vout->p_sys->agl_ctx);
}
- else if(!VLCIntf->b_die)
+ else if(VLCIntf && vlc_object_alive (VLCIntf))
{
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
/* Close the window */
- [p_vout->p_sys->o_vout_view closeVout];
+ [p_vout->p_sys->o_vout_view performSelectorOnMainThread:@selector(closeVout) withObject:NULL waitUntilDone:YES];
[o_pool release];
}
p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
}
- [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;
}
*****************************************************************************/
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->b_got_frame = true;
[[p_vout->p_sys->o_glview openGLContext] flushBuffer];
}
{
if( kCGLNoError == CGLLockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]) )
{
- [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
- return 0;
+ [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
+ return 0;
}
return 1;
}
* 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: (vout_thread_t *)p_vout
++ (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->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->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 )
{
- p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
- subView: o_glview
- frame: &p_vout->p_sys->s_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
{
- p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
- subView: o_glview frame: nil];
-
+ 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[] =
/* 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;
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 */
glClear( GL_COLOR_BUFFER_BIT );
Unlock( p_vout );
}
- [super reshape];
}
- (void) update
{
vlc_value_t val;
- Rect viewBounds;
+ Rect viewBounds;
Rect clipBounds;
-
+
var_Get( p_vout->p_libvlc, "drawable", &val );
p_vout->p_sys->agl_drawable = (AGLDrawable)val.i_int;
aglSetDrawable(p_vout->p_sys->agl_ctx, p_vout->p_sys->agl_drawable);
var_Get( p_vout->p_libvlc, "drawable-clip-right", &val );
clipBounds.right = val.i_int;
- aglLock(p_vout);
- aglSetViewport(p_vout, viewBounds, clipBounds);
- aglReshape(p_vout);
- aglUnlock(p_vout);
-
+ 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;
}
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);
+ 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 );
}
/* private event class */
-enum
+enum
{
kEventClassVLCPlugin = 'vlcp',
};
{
/* Close the fullscreen window and resume normal drawing */
vlc_value_t val;
- Rect viewBounds;
+ Rect viewBounds;
Rect clipBounds;
var_Get( p_vout->p_libvlc, "drawable", &val );
else
{
Rect deviceRect;
-
+
GDHandle deviceHdl = GetMainDevice();
deviceRect = (*deviceHdl)->gdRect;
-
+
if( !p_vout->p_sys->theWindow )
{
/* Create a window */
| kWindowStandardHandlerAttribute
| kWindowLiveResizeAttribute
| kWindowNoShadowAttribute;
-
+
windowAttrs &= (~kWindowResizableAttribute);
CreateNewWindow(kDocumentWindowClass, windowAttrs, &deviceRect, &p_vout->p_sys->theWindow);
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 },
clipBounds.left = va_arg( args, int);
clipBounds.bottom = va_arg( args, int);
clipBounds.right = va_arg( args, int);
-
- if( !p_vout->b_fullscreen )
+
+ 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 )
{
- aglLock( p_vout );
- aglSetViewport(p_vout, viewBounds, clipBounds);
- aglReshape( p_vout );
- aglUnlock( p_vout );
+ p_parent->pf_display( p_parent, NULL );
}
return VLC_SUCCESS;
}
}
default:
- return vout_vaControlDefault( p_vout, i_query, args );
+ return VLC_EGENERIC;
}
}
static void aglSwap( vout_thread_t * p_vout )
{
- p_vout->p_sys->b_got_frame = VLC_TRUE;
- aglSwapBuffers(p_vout->p_sys->agl_ctx);
+ 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 */
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;
+ - p_vout->p_sys->i_height;
aglUpdateContext(p_vout->p_sys->agl_ctx);
}
{
OSStatus result = noErr;
UInt32 class = GetEventClass (event);
- UInt32 kind = GetEventKind (event);
+ UInt32 kind = GetEventKind (event);
vout_thread_t *p_vout = (vout_thread_t *)userData;
result = CallNextEventHandler(nextHandler, event);
{
HICommand theHICommand;
GetEventParameter( event, kEventParamDirectObject, typeHICommand, NULL, sizeof( HICommand ), NULL, &theHICommand );
-
+
switch ( theHICommand.commandID )
{
default:
{
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 kEventWindowZoomed:
case kEventWindowBoundsChanged:
break;
-
+
default:
result = eventNotHandledErr;
}
case kEventMouseDown:
{
UInt16 button;
-
+
GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
switch (button)
{
case kEventMouseUp:
{
UInt16 button;
-
+
GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
switch (button)
{
{
vlc_value_t val;
- val.b_bool = VLC_FALSE;
+ val.b_bool = false;
var_Set((vout_thread_t *) p_vout->p_parent, "fullscreen", val);
}
else
{
vlc_value_t val;
- val.b_bool = VLC_TRUE;
- var_Set( p_vout, "mouse-clicked", val );
+ var_SetBool( p_vout, "mouse-clicked", true );
var_Get( p_vout, "mouse-button-down", &val );
val.i_int &= ~1;
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);
+ 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 );
var_Set( p_vout, "mouse-y", val );
- val.b_bool = VLC_TRUE;
+ val.b_bool = true;
var_Set( p_vout, "mouse-moved", val );
break;
}
-
+
default:
result = eventNotHandledErr;
}
static int aglLock( vout_thread_t * p_vout )
{
-#ifdef __ppc__
- /*
- * before 10.4, we set the AGL context as current and
- * then we retrieve and use the matching CGL context
- */
- aglSetCurrentContext(p_vout->p_sys->agl_ctx);
- return kCGLNoError != CGLLockContext( CGLGetCurrentContext() );
-#else
- /* since 10.4, this is the safe way to get the underlying CGL context */
+ /* 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;
- }
+ {
+ aglSetCurrentContext(p_vout->p_sys->agl_ctx);
+ return 0;
+ }
}
return 1;
-#endif
}
static void aglUnlock( vout_thread_t * p_vout )
{
-#ifdef __ppc__
- /*
- * before 10.4, we assume that the AGL context is current.
- * therefore, we use the current CGL context
- */
- CGLUnlockContext( CGLGetCurrentContext() );
-#else
- /* since 10.4, this is the safe way to get the underlying CGL context */
+ /* get the underlying CGL context */
CGLContextObj cglContext;
if( aglGetCGLContext(p_vout->p_sys->agl_ctx, (void**)&cglContext) )
{
CGLUnlockContext( cglContext );
}
-#endif
}