1 /*****************************************************************************
2 * voutgl.m: MacOS X OpenGL provider
3 *****************************************************************************
4 * Copyright (C) 2001-2004 the VideoLAN team
5 * $Id: vout.m 8351 2004-08-02 13:06:38Z hartman $
7 * Authors: Colin Delacroix <colin@zoy.org>
8 * Florian G. Pflug <fgp@phlo.org>
9 * Jon Lech Johansen <jon-vl@nanocrew.net>
10 * Derk-Jan Hartman <hartman at videolan dot org>
11 * Eric Petit <titer@m0k.org>
12 * Benjamin Pracht <bigben at videolan dot org>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
32 #include <errno.h> /* ENOMEM */
33 #include <stdlib.h> /* free() */
34 #include <string.h> /* strerror() */
41 #include <OpenGL/OpenGL.h>
42 #include <OpenGL/gl.h>
46 /*****************************************************************************
48 *****************************************************************************/
49 @interface VLCGLView : NSOpenGLView
51 vout_thread_t * p_vout;
54 - (id) initWithVout: (vout_thread_t *) p_vout;
59 NSAutoreleasePool * o_pool;
61 VLCVoutView * o_vout_view;
62 vlc_bool_t b_saved_frame;
64 vlc_bool_t b_got_frame;
66 /* Mozilla plugin-related variables */
67 vlc_bool_t b_embedded;
69 AGLDrawable agl_drawable;
71 int i_width, i_height;
74 /*****************************************************************************
76 *****************************************************************************/
78 static int Init ( vout_thread_t * p_vout );
79 static void End ( vout_thread_t * p_vout );
80 static int Manage ( vout_thread_t * p_vout );
81 static int Control( vout_thread_t *, int, va_list );
82 static void Swap ( vout_thread_t * p_vout );
83 static int Lock ( vout_thread_t * p_vout );
84 static void Unlock ( vout_thread_t * p_vout );
86 static int aglInit ( vout_thread_t * p_vout );
87 static void aglEnd ( vout_thread_t * p_vout );
88 static int aglManage ( vout_thread_t * p_vout );
89 static void aglSwap ( vout_thread_t * p_vout );
91 static int DrawableRedraw( vlc_object_t *p_this, const char *psz_name,
92 vlc_value_t oval, vlc_value_t nval, void *param);
94 int E_(OpenVideoGL) ( vlc_object_t * p_this )
96 vout_thread_t * p_vout = (vout_thread_t *) p_this;
97 vlc_value_t value_drawable;
99 if( !CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay ) )
101 msg_Warn( p_vout, "no OpenGL hardware acceleration found. "
102 "Video display will be slow" );
105 msg_Dbg( p_vout, "display is Quartz Extreme accelerated" );
107 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
108 if( p_vout->p_sys == NULL )
110 msg_Err( p_vout, "out of memory" );
114 memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
116 vlc_mutex_init( p_vout, &p_vout->p_sys->lock );
118 var_Get( p_vout->p_vlc, "drawable", &value_drawable );
119 if( value_drawable.i_int != 0 )
121 static const GLint ATTRIBUTES[] = {
135 AGLPixelFormat pixFormat;
137 p_vout->p_sys->b_embedded = VLC_TRUE;
139 screen = GetGWorldDevice((CGrafPtr)value_drawable.i_int);
142 msg_Err( p_vout, "can't find screen device for drawable" );
146 pixFormat = aglChoosePixelFormat(&screen, 1, ATTRIBUTES);
147 if( NULL == pixFormat )
149 msg_Err( p_vout, "no screen renderer available for required attributes." );
153 p_vout->p_sys->agl_ctx = aglCreateContext(pixFormat, NULL);
154 aglDestroyPixelFormat(pixFormat);
155 if( NULL == p_vout->p_sys->agl_ctx )
157 msg_Err( p_vout, "cannot create AGL context." );
161 // tell opengl to sync buffer swap with vertical retrace
163 aglSetInteger(p_vout->p_sys->agl_ctx, AGL_SWAP_INTERVAL, ¶m);
164 aglEnable(p_vout->p_sys->agl_ctx, AGL_SWAP_INTERVAL);
167 p_vout->pf_init = aglInit;
168 p_vout->pf_end = aglEnd;
169 p_vout->pf_manage = aglManage;
170 p_vout->pf_control = NULL;
171 p_vout->pf_swap = aglSwap;
172 p_vout->pf_lock = Lock;
173 p_vout->pf_unlock = Unlock;
177 p_vout->p_sys->b_embedded = VLC_FALSE;
179 p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
181 /* Create the GL view */
182 p_vout->p_sys->o_glview = [[VLCGLView alloc] initWithVout: p_vout];
183 [p_vout->p_sys->o_glview autorelease];
185 /* Spawn the window */
187 if( !(p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
188 subView: p_vout->p_sys->o_glview frame: nil]) )
192 p_vout->pf_init = Init;
193 p_vout->pf_end = End;
194 p_vout->pf_manage = Manage;
195 p_vout->pf_control= Control;
196 p_vout->pf_swap = Swap;
197 p_vout->pf_lock = Lock;
198 p_vout->pf_unlock = Unlock;
200 p_vout->p_sys->b_got_frame = VLC_FALSE;
205 void E_(CloseVideoGL) ( vlc_object_t * p_this )
207 vout_thread_t * p_vout = (vout_thread_t *) p_this;
208 if( p_vout->p_sys->b_embedded )
210 var_DelCallback(p_vout->p_vlc, "drawableredraw", DrawableRedraw, p_vout);
211 aglDestroyContext(p_vout->p_sys->agl_ctx);
215 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
217 /* Close the window */
218 [p_vout->p_sys->o_vout_view closeVout];
223 vlc_mutex_destroy( &p_vout->p_sys->lock );
224 free( p_vout->p_sys );
227 static int Init( vout_thread_t * p_vout )
229 [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
233 static void End( vout_thread_t * p_vout )
235 [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
238 static int Manage( vout_thread_t * p_vout )
240 if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
242 [p_vout->p_sys->o_glview reshape];
243 p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
245 if( p_vout->i_changes & VOUT_CROP_CHANGE )
247 [p_vout->p_sys->o_glview reshape];
248 p_vout->i_changes &= ~VOUT_CROP_CHANGE;
251 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
253 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
255 if( !p_vout->b_fullscreen )
257 /* Save window size and position */
258 p_vout->p_sys->s_frame.size =
259 [p_vout->p_sys->o_vout_view frame].size;
260 p_vout->p_sys->s_frame.origin =
261 [[p_vout->p_sys->o_vout_view getWindow ]frame].origin;
262 p_vout->p_sys->b_saved_frame = VLC_TRUE;
264 [p_vout->p_sys->o_vout_view closeVout];
266 p_vout->b_fullscreen = !p_vout->b_fullscreen;
268 #define o_glview p_vout->p_sys->o_glview
269 o_glview = [[VLCGLView alloc] initWithVout: p_vout];
270 [o_glview autorelease];
272 if( p_vout->p_sys->b_saved_frame )
274 p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
276 frame: &p_vout->p_sys->s_frame];
280 p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
281 subView: o_glview frame: nil];
285 [[o_glview openGLContext] makeCurrentContext];
290 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
293 [p_vout->p_sys->o_vout_view manage];
297 /*****************************************************************************
298 * Control: control facility for the vout
299 *****************************************************************************/
300 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
306 case VOUT_SET_STAY_ON_TOP:
307 b_arg = va_arg( args, vlc_bool_t );
308 [p_vout->p_sys->o_vout_view setOnTop: b_arg];
314 return vout_vaControlDefault( p_vout, i_query, args );
318 static void Swap( vout_thread_t * p_vout )
320 p_vout->p_sys->b_got_frame = VLC_TRUE;
321 [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
325 static int Lock( vout_thread_t * p_vout )
327 vlc_mutex_lock( &p_vout->p_sys->lock );
331 static void Unlock( vout_thread_t * p_vout )
333 vlc_mutex_unlock( &p_vout->p_sys->lock );
336 /*****************************************************************************
337 * VLCGLView implementation
338 *****************************************************************************/
339 @implementation VLCGLView
341 - (id) initWithVout: (vout_thread_t *) vout
345 NSOpenGLPixelFormatAttribute attribs[] =
347 NSOpenGLPFAAccelerated,
348 NSOpenGLPFANoRecovery,
349 NSOpenGLPFAColorSize, 24,
350 NSOpenGLPFAAlphaSize, 8,
351 NSOpenGLPFADepthSize, 24,
356 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
357 initWithAttributes: attribs];
361 msg_Warn( p_vout, "could not create OpenGL video output" );
365 self = [super initWithFrame: NSMakeRect(0,0,10,10) pixelFormat: fmt];
368 [[self openGLContext] makeCurrentContext];
369 [[self openGLContext] update];
371 /* Swap buffers only during the vertical retrace of the monitor.
372 http://developer.apple.com/documentation/GraphicsImaging/
373 Conceptual/OpenGL/chap5/chapter_5_section_44.html */
374 long params[] = { 1 };
375 CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
386 NSRect bounds = [self bounds];
388 [[self openGLContext] makeCurrentContext];
390 var_Get( p_vout, "macosx-stretch", &val );
393 x = bounds.size.width;
394 y = bounds.size.height;
396 else if( bounds.size.height * p_vout->fmt_in.i_visible_width *
397 p_vout->fmt_in.i_sar_num <
398 bounds.size.width * p_vout->fmt_in.i_visible_height *
399 p_vout->fmt_in.i_sar_den )
401 x = ( bounds.size.height * p_vout->fmt_in.i_visible_width *
402 p_vout->fmt_in.i_sar_num ) /
403 ( p_vout->fmt_in.i_visible_height * p_vout->fmt_in.i_sar_den);
405 y = bounds.size.height;
409 x = bounds.size.width;
410 y = ( bounds.size.width * p_vout->fmt_in.i_visible_height *
411 p_vout->fmt_in.i_sar_den) /
412 ( p_vout->fmt_in.i_visible_width * p_vout->fmt_in.i_sar_num );
415 glViewport( ( bounds.size.width - x ) / 2,
416 ( bounds.size.height - y ) / 2, x, y );
418 if( p_vout->p_sys->b_got_frame )
420 /* Ask the opengl module to redraw */
421 vout_thread_t * p_parent;
422 p_parent = (vout_thread_t *) p_vout->p_parent;
424 if( p_parent && p_parent->pf_display )
426 p_parent->pf_display( p_parent, NULL );
431 glClear( GL_COLOR_BUFFER_BIT );
444 - (void) drawRect: (NSRect) rect
447 [[self openGLContext] makeCurrentContext];
449 [super drawRect:rect];
455 /*****************************************************************************
456 * embedded AGL context implementation
457 *****************************************************************************/
459 static void UpdateEmbeddedGeometry( vout_thread_t *p_vout );
460 static void aglReshape( vout_thread_t * p_vout );
462 static int aglInit( vout_thread_t * p_vout )
464 UpdateEmbeddedGeometry(p_vout);
465 var_AddCallback(p_vout->p_vlc, "drawableredraw", DrawableRedraw, p_vout);
467 aglSetCurrentContext(p_vout->p_sys->agl_ctx);
471 static void aglEnd( vout_thread_t * p_vout )
473 aglSetCurrentContext(NULL);
476 static void aglReshape( vout_thread_t * p_vout )
480 int i_offx = p_vout->p_sys->i_offx;
481 int i_offy = p_vout->p_sys->i_offy;
482 int i_height = p_vout->p_sys->i_height;
483 int i_width = p_vout->p_sys->i_width;
487 aglSetCurrentContext(p_vout->p_sys->agl_ctx);
489 var_Get( p_vout, "macosx-stretch", &val );
495 else if( i_height * p_vout->fmt_in.i_visible_width *
496 p_vout->fmt_in.i_sar_num <
497 i_width * p_vout->fmt_in.i_visible_height *
498 p_vout->fmt_in.i_sar_den )
500 x = ( i_height * p_vout->fmt_in.i_visible_width *
501 p_vout->fmt_in.i_sar_num ) /
502 ( p_vout->fmt_in.i_visible_height * p_vout->fmt_in.i_sar_den);
509 y = ( i_width * p_vout->fmt_in.i_visible_height *
510 p_vout->fmt_in.i_sar_den) /
511 ( p_vout->fmt_in.i_visible_width * p_vout->fmt_in.i_sar_num );
514 glViewport( i_offx+( i_width - x ) / 2,
515 i_offy+( i_height - y ) / 2, x, y );
517 if( p_vout->p_sys->b_got_frame )
519 /* Ask the opengl module to redraw */
520 vout_thread_t * p_parent;
521 p_parent = (vout_thread_t *) p_vout->p_parent;
523 if( p_parent && p_parent->pf_display )
525 p_parent->pf_display( p_parent, NULL );
530 glClear( GL_COLOR_BUFFER_BIT );
535 static int aglManage( vout_thread_t * p_vout )
537 if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
540 p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
542 if( p_vout->i_changes & VOUT_CROP_CHANGE )
545 p_vout->i_changes &= ~VOUT_CROP_CHANGE;
550 static void aglSwap( vout_thread_t * p_vout )
552 p_vout->p_sys->b_got_frame = VLC_TRUE;
553 aglSwapBuffers(p_vout->p_sys->agl_ctx);
556 static void UpdateEmbeddedGeometry( vout_thread_t *p_vout )
559 vlc_value_t valt, vall, valb, valr, valx, valy, valw, valh,
567 var_Get( p_vout->p_vlc, "drawable", &val );
568 var_Get( p_vout->p_vlc, "drawablet", &valt );
569 var_Get( p_vout->p_vlc, "drawablel", &vall );
570 var_Get( p_vout->p_vlc, "drawableb", &valb );
571 var_Get( p_vout->p_vlc, "drawabler", &valr );
572 var_Get( p_vout->p_vlc, "drawablex", &valx );
573 var_Get( p_vout->p_vlc, "drawabley", &valy );
574 var_Get( p_vout->p_vlc, "drawablew", &valw );
575 var_Get( p_vout->p_vlc, "drawableh", &valh );
576 var_Get( p_vout->p_vlc, "drawableportx", &valportx );
577 var_Get( p_vout->p_vlc, "drawableporty", &valporty );
579 // mozilla plugin provides coordinates based on port bounds
580 // however AGL coordinates are based on window structure region
581 // and are vertically flipped
583 GetWindowBounds(GetWindowFromPort((CGrafPtr)val.i_int),
584 kWindowStructureRgn, &winBounds);
585 GetWindowBounds(GetWindowFromPort((CGrafPtr)val.i_int),
586 kWindowContentRgn, &clientBounds);
588 /* update video clipping bounds in drawable */
589 rect[0] = (clientBounds.left-winBounds.left)
590 + vall.i_int; // from window left edge
591 rect[1] = (winBounds.bottom-winBounds.top)
592 - (clientBounds.top-winBounds.top)
593 - valb.i_int; // from window bottom edge
594 rect[2] = valr.i_int-vall.i_int; // width
595 rect[3] = valb.i_int-valt.i_int; // height
596 aglSetInteger(p_vout->p_sys->agl_ctx, AGL_BUFFER_RECT, rect);
597 aglEnable(p_vout->p_sys->agl_ctx, AGL_BUFFER_RECT);
599 /* update video internal bounds in drawable */
600 p_vout->p_sys->i_offx = -vall.i_int - valportx.i_int;
601 p_vout->p_sys->i_offy = valb.i_int + valporty.i_int - valh.i_int;
602 p_vout->p_sys->i_width = valw.i_int;
603 p_vout->p_sys->i_height = valh.i_int;
605 if( p_vout->p_sys->agl_drawable == (AGLDrawable)val.i_int )
607 aglUpdateContext(p_vout->p_sys->agl_ctx);
611 p_vout->p_sys->agl_drawable = (AGLDrawable)val.i_int;
612 aglSetDrawable(p_vout->p_sys->agl_ctx, p_vout->p_sys->agl_drawable);
614 aglReshape( p_vout );
617 /* If we're embedded, the application is expected to indicate a
618 * window change (move/resize/etc) via the "drawableredraw" value.
621 static int DrawableRedraw( vlc_object_t *p_this, const char *psz_name,
622 vlc_value_t oval, vlc_value_t nval, void *param)
624 vout_thread_t *p_vout = (vout_thread_t *)param;
626 UpdateEmbeddedGeometry( p_vout );