]> git.sesse.net Git - vlc/blob - modules/gui/minimal_macosx/VLCOpenGLVoutView.m
Merge branch 'master' into lpcm_encoder
[vlc] / modules / gui / minimal_macosx / VLCOpenGLVoutView.m
1 /*****************************************************************************
2  * VLCOpenGLVoutView.m: MacOS X OpenGL provider
3  *****************************************************************************
4  * Copyright (C) 2001-2009 the VideoLAN team
5  * $Id$
6  *
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>
13  *          Damien Fouilleul <damienf at videolan dot org>
14  *          Pierre d'Herbemont <pdherbemont at videolan dot org>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
29  *****************************************************************************/
30
31 /*****************************************************************************
32  * Preamble
33  *****************************************************************************/
34 #include "intf.h"
35 #include "voutgl.h"
36 #include "VLCOpenGLVoutView.h"
37 #include "VLCMinimalVoutWindow.h"
38
39 #include <OpenGL/OpenGL.h>
40 #include <OpenGL/gl.h>
41
42 /*****************************************************************************
43  * cocoaglvoutviewInit
44  *****************************************************************************/
45 int cocoaglvoutviewInit( vout_thread_t * p_vout )
46 {
47     vlc_value_t value_drawable;
48     id <VLCOpenGLVoutEmbedding> o_cocoaglview_container;
49
50     msg_Dbg( p_vout, "Mac OS X Vout is opening" );
51
52     var_Create( p_vout, "drawable-nsobject", VLC_VAR_DOINHERIT );
53     var_Get( p_vout, "drawable-nsobject", &value_drawable );
54
55     p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
56
57     /* This will be released in cocoaglvoutviewEnd(), on
58      * main thread, after we are done using it. */
59     o_cocoaglview_container = [(id) value_drawable.p_address retain];
60     if (!o_cocoaglview_container)
61     {
62         msg_Warn( p_vout, "No drawable!, spawing a window" );
63     }
64
65     p_vout->p_sys->b_embedded = false;
66
67
68     /* Create the GL view */
69     struct args { vout_thread_t * p_vout; id <VLCOpenGLVoutEmbedding> container; } args = { p_vout, o_cocoaglview_container };
70
71     [VLCOpenGLVoutView performSelectorOnMainThread:@selector(autoinitOpenGLVoutViewIntVoutWithContainer:)
72                         withObject:[NSData dataWithBytes: &args length: sizeof(struct args)] waitUntilDone:YES];
73
74     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
75     return VLC_SUCCESS;
76 }
77
78 /*****************************************************************************
79  * cocoaglvoutviewEnd
80  *****************************************************************************/
81 void cocoaglvoutviewEnd( vout_thread_t * p_vout )
82 {
83     id <VLCOpenGLVoutEmbedding> o_cocoaglview_container;
84
85     if (!p_vout->p_sys->o_glview)
86         return;
87
88     msg_Dbg( p_vout, "Mac OS X Vout is closing" );
89     var_Destroy( p_vout, "drawable-nsobject" );
90
91     o_cocoaglview_container = [p_vout->p_sys->o_glview container];
92
93     /* Make sure our view won't request the vout now */
94     [p_vout->p_sys->o_glview detachFromVout];
95     msg_Dbg( p_vout, "Mac OS X Vout is closing" );
96
97     if( [(id)o_cocoaglview_container respondsToSelector:@selector(removeVoutSubview:)] )
98         [o_cocoaglview_container performSelectorOnMainThread:@selector(removeVoutSubview:) withObject:p_vout->p_sys->o_glview waitUntilDone:NO];
99
100     /* Let the view go and release it, _without_blocking_ */
101     [p_vout->p_sys->o_glview performSelectorOnMainThread:@selector(removeFromSuperviewAndRelease) withObject:nil waitUntilDone:NO];
102     p_vout->p_sys->o_glview = nil;
103
104     /* Release the container now that we don't use it */
105     [o_cocoaglview_container performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
106
107     [p_vout->p_sys->o_pool release];
108     p_vout->p_sys->o_pool = nil;
109  
110 }
111
112 /*****************************************************************************
113  * cocoaglvoutviewManage
114  *****************************************************************************/
115 int cocoaglvoutviewManage( vout_thread_t * p_vout )
116 {
117     if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
118     {
119         [p_vout->p_sys->o_glview reshape];
120         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
121     }
122     if( p_vout->i_changes & VOUT_CROP_CHANGE )
123     {
124         [p_vout->p_sys->o_glview reshape];
125         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
126     }
127
128     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
129     {
130         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
131
132         p_vout->b_fullscreen = !p_vout->b_fullscreen;
133
134         if( p_vout->b_fullscreen )
135             [[p_vout->p_sys->o_glview container] enterFullscreen];
136         else
137             [[p_vout->p_sys->o_glview container] leaveFullscreen];
138
139         [o_pool release];
140
141         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
142     }
143
144     //[[p_vout->p_sys->o_glview container] manage];
145     return VLC_SUCCESS;
146 }
147
148 /*****************************************************************************
149  * cocoaglvoutviewControl: control facility for the vout
150  *****************************************************************************/
151 int cocoaglvoutviewControl( vout_thread_t *p_vout, int i_query, va_list args )
152 {
153     bool b_arg;
154
155     switch( i_query )
156     {
157         case VOUT_SET_STAY_ON_TOP:
158             b_arg = (bool) va_arg( args, int );
159             [[p_vout->p_sys->o_glview container] setOnTop: b_arg];
160             return VLC_SUCCESS;
161
162         default:
163             return VLC_EGENERIC;
164     }
165 }
166
167 /*****************************************************************************
168  * cocoaglvoutviewSwap
169  *****************************************************************************/
170 void cocoaglvoutviewSwap( vout_thread_t * p_vout )
171 {
172     p_vout->p_sys->b_got_frame = true;
173     [[p_vout->p_sys->o_glview openGLContext] flushBuffer];
174 }
175
176 /*****************************************************************************
177  * cocoaglvoutviewLock
178  *****************************************************************************/
179 int cocoaglvoutviewLock( vout_thread_t * p_vout )
180 {
181     if( kCGLNoError == CGLLockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]) )
182     {
183         [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
184         return 0;
185     }
186     return 1;
187 }
188
189 /*****************************************************************************
190  * cocoaglvoutviewUnlock
191  *****************************************************************************/
192 void cocoaglvoutviewUnlock( vout_thread_t * p_vout )
193 {
194     CGLUnlockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]);
195 }
196
197 /*****************************************************************************
198  * VLCOpenGLVoutView implementation
199  *****************************************************************************/
200 @implementation VLCOpenGLVoutView
201
202 /* Init a new gl view and register it to both the framework and the
203  * vout_thread_t. Must be called from main thread. */
204 + (void) autoinitOpenGLVoutViewIntVoutWithContainer: (NSData *) argsAsData
205 {
206     NSAutoreleasePool   *pool = [[NSAutoreleasePool alloc] init];
207     struct args { vout_thread_t * p_vout; id <VLCOpenGLVoutEmbedding> container; } *
208         args = (struct args *)[argsAsData bytes];
209     VLCOpenGLVoutView * oglview;
210
211     if( !args->container )
212     {
213         args->container = [[VLCMinimalVoutWindow alloc] initWithContentRect: NSMakeRect( 0, 0, args->p_vout->i_window_width, args->p_vout->i_window_height )];
214         [(VLCMinimalVoutWindow *)args->container makeKeyAndOrderFront: nil];
215     }
216     oglview = [[VLCOpenGLVoutView alloc] initWithVout: args->p_vout container: args->container];
217
218     args->p_vout->p_sys->o_glview = oglview;
219     [args->container addVoutSubview: oglview];
220
221     [pool release];
222 }
223
224 - (void)dealloc
225 {
226     [objectLock dealloc];
227     [super dealloc];
228 }
229
230 - (void)removeFromSuperviewAndRelease
231 {
232     [self removeFromSuperview];
233     [self release];
234 }
235
236 - (id) initWithVout: (vout_thread_t *) vout container: (id <VLCOpenGLVoutEmbedding>) aContainer
237 {
238     NSOpenGLPixelFormatAttribute attribs[] =
239     {
240         NSOpenGLPFADoubleBuffer,
241         NSOpenGLPFAAccelerated,
242         NSOpenGLPFANoRecovery,
243         NSOpenGLPFAColorSize, 24,
244         NSOpenGLPFAAlphaSize, 8,
245         NSOpenGLPFADepthSize, 24,
246         NSOpenGLPFAWindow,
247         0
248     };
249
250     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
251         initWithAttributes: attribs];
252
253     if( !fmt )
254     {
255         msg_Warn( p_vout, "could not create OpenGL video output" );
256         return nil;
257     }
258
259     if( self = [super initWithFrame: NSMakeRect(0,0,10,10) pixelFormat: fmt] )
260     {
261         p_vout = vout;
262         container = aContainer;
263         objectLock = [[NSLock alloc] init];
264
265         [fmt release];
266
267         [[self openGLContext] makeCurrentContext];
268         [[self openGLContext] update];
269
270         /* Swap buffers only during the vertical retrace of the monitor.
271         http://developer.apple.com/documentation/GraphicsImaging/
272         Conceptual/OpenGL/chap5/chapter_5_section_44.html */
273         GLint params[] = { 1 };
274         CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
275                      params );
276     }
277     return self;
278 }
279
280 - (void) detachFromVout
281 {
282     [objectLock lock];
283     p_vout = NULL;
284     [objectLock unlock];
285 }
286
287 - (id <VLCOpenGLVoutEmbedding>) container
288 {
289     return container;
290 }
291
292 - (void) destroyVout
293 {
294     [objectLock lock];
295     if( p_vout )
296     {
297         vlc_object_release( p_vout );
298         vlc_object_release( p_vout );
299     }
300     [objectLock unlock];
301 }
302
303 - (void) reshape
304 {
305     int x, y;
306     vlc_value_t val;
307
308     [objectLock lock];
309     if( !p_vout )
310     {
311         [objectLock unlock];
312         return;
313     }
314
315     cocoaglvoutviewLock( p_vout );
316     NSRect bounds = [self bounds];
317
318     if( [[self container] stretchesVideo] )
319     {
320         x = bounds.size.width;
321         y = bounds.size.height;
322     }
323     else if( bounds.size.height * p_vout->fmt_in.i_visible_width *
324              p_vout->fmt_in.i_sar_num <
325              bounds.size.width * p_vout->fmt_in.i_visible_height *
326              p_vout->fmt_in.i_sar_den )
327     {
328         x = ( bounds.size.height * p_vout->fmt_in.i_visible_width *
329               p_vout->fmt_in.i_sar_num ) /
330             ( p_vout->fmt_in.i_visible_height * p_vout->fmt_in.i_sar_den);
331
332         y = bounds.size.height;
333     }
334     else
335     {
336         x = bounds.size.width;
337         y = ( bounds.size.width * p_vout->fmt_in.i_visible_height *
338               p_vout->fmt_in.i_sar_den) /
339             ( p_vout->fmt_in.i_visible_width * p_vout->fmt_in.i_sar_num  );
340     }
341
342     glViewport( ( bounds.size.width - x ) / 2,
343                 ( bounds.size.height - y ) / 2, x, y );
344
345     if( p_vout->p_sys->b_got_frame )
346     {
347         /* Ask the opengl module to redraw */
348         vout_thread_t * p_parent;
349         p_parent = (vout_thread_t *) p_vout->p_parent;
350         cocoaglvoutviewUnlock( p_vout );
351         if( p_parent && p_parent->pf_display )
352         {
353             p_parent->pf_display( p_parent, NULL );
354         }
355     }
356     else
357     {
358         glClear( GL_COLOR_BUFFER_BIT );
359         cocoaglvoutviewUnlock( p_vout );
360     }
361     [objectLock unlock];
362     [super reshape];
363 }
364
365 - (void) update
366 {
367     if (!p_vout)
368         return;
369     if( kCGLNoError != CGLLockContext([[self openGLContext] CGLContextObj]) )
370         return;
371     [super update];
372     CGLUnlockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]);
373 }
374
375 - (void) drawRect: (NSRect) rect
376 {
377     if (!p_vout)
378         return;
379     if( kCGLNoError != CGLLockContext([[self openGLContext] CGLContextObj]) )
380         return;
381     [[self openGLContext] flushBuffer];
382     [super drawRect:rect];
383     CGLUnlockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]);
384 }
385
386 - (BOOL)mouseDownCanMoveWindow
387 {
388     return YES;
389 }
390 @end
391