aa7fe067586617c77213718e1eeaee7c01ad5e87
[vlc.git] / modules / gui / minimal_macosx / VLCOpenGLVoutView.m
1 /*****************************************************************************
2  * VLCOpenGLVoutView.m: MacOS X OpenGL provider
3  *****************************************************************************
4  * Copyright (C) 2001-2004 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  *          Pierr 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", VLC_VAR_DOINHERIT );
53     var_Get( p_vout, "drawable", &value_drawable );
54
55     o_cocoaglview_container = (id) value_drawable.i_int;
56     if (!o_cocoaglview_container)
57     {
58         msg_Warn( p_vout, "No drawable!, spawing a window" );
59         o_cocoaglview_container = [[VLCMinimalVoutWindow alloc] initWithContentRect: NSMakeRect( 0, 0, 200, 200 )];
60     }
61
62     p_vout->p_sys->b_embedded = VLC_FALSE;
63
64     p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
65
66     /* Create the GL view */
67     struct args { vout_thread_t * p_vout; id <VLCOpenGLVoutEmbedding> container; } args = { p_vout, o_cocoaglview_container };
68
69     [VLCOpenGLVoutView performSelectorOnMainThread:@selector(autoinitOpenGLVoutViewIntVoutWithContainer:)
70                         withObject:[NSData dataWithBytes: &args length: sizeof(struct args)] waitUntilDone:YES];
71
72     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
73     return VLC_SUCCESS;
74 }
75
76 /*****************************************************************************
77  * cocoaglvoutviewEnd
78  *****************************************************************************/
79 void cocoaglvoutviewEnd( vout_thread_t * p_vout )
80 {
81     id <VLCOpenGLVoutEmbedding> o_cocoaglview_container;
82
83     msg_Dbg( p_vout, "Mac OS X Vout is closing" );
84     var_Destroy( p_vout, "drawable" );
85
86     o_cocoaglview_container = [p_vout->p_sys->o_glview container];
87
88     /* Make sure our view won't request the vout now */
89     [p_vout->p_sys->o_glview detachFromVout];
90     msg_Dbg( p_vout, "Mac OS X Vout is closing" );
91
92     /* Let the view go, _without_blocking_ */
93     [p_vout->p_sys->o_glview performSelectorOnMainThread:@selector(removeFromSuperview) withObject:NULL waitUntilDone:NO];
94
95     if( [(id)o_cocoaglview_container respondsToSelector:@selector(removeVoutSubview:)] )
96         [o_cocoaglview_container removeVoutSubview: p_vout->p_sys->o_glview];
97
98     [p_vout->p_sys->o_pool release];
99     
100 }
101
102 /*****************************************************************************
103  * cocoaglvoutviewManage
104  *****************************************************************************/
105 int cocoaglvoutviewManage( vout_thread_t * p_vout )
106 {
107     if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
108     {
109         [p_vout->p_sys->o_glview reshape];
110         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
111     }
112     if( p_vout->i_changes & VOUT_CROP_CHANGE )
113     {
114         [p_vout->p_sys->o_glview reshape];
115         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
116     }
117
118     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
119     {
120         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
121
122         p_vout->b_fullscreen = !p_vout->b_fullscreen;
123
124         if( p_vout->b_fullscreen )
125             [[p_vout->p_sys->o_glview container] enterFullscreen];
126         else
127             [[p_vout->p_sys->o_glview container] leaveFullscreen];
128
129         [o_pool release];
130
131         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
132     }
133
134     //[[p_vout->p_sys->o_glview container] manage];
135     return VLC_SUCCESS;
136 }
137
138 /*****************************************************************************
139  * cocoaglvoutviewControl: control facility for the vout
140  *****************************************************************************/
141 int cocoaglvoutviewControl( vout_thread_t *p_vout, int i_query, va_list args )
142 {
143     vlc_bool_t b_arg;
144
145     switch( i_query )
146     {
147         case VOUT_SET_STAY_ON_TOP:
148             b_arg = va_arg( args, vlc_bool_t );
149             [[p_vout->p_sys->o_glview container] setOnTop: b_arg];
150             return VLC_SUCCESS;
151
152         case VOUT_CLOSE:
153         case VOUT_REPARENT:
154         default:
155             return vout_vaControlDefault( p_vout, i_query, args );
156     }
157 }
158
159 /*****************************************************************************
160  * cocoaglvoutviewSwap
161  *****************************************************************************/
162 void cocoaglvoutviewSwap( vout_thread_t * p_vout )
163 {
164     p_vout->p_sys->b_got_frame = VLC_TRUE;
165     [[p_vout->p_sys->o_glview openGLContext] flushBuffer];
166 }
167
168 /*****************************************************************************
169  * cocoaglvoutviewLock
170  *****************************************************************************/
171 int cocoaglvoutviewLock( vout_thread_t * p_vout )
172 {
173     if( kCGLNoError == CGLLockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]) )
174     {
175         [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
176         return 0;
177     }
178     return 1;
179 }
180
181 /*****************************************************************************
182  * cocoaglvoutviewUnlock
183  *****************************************************************************/
184 void cocoaglvoutviewUnlock( vout_thread_t * p_vout )
185 {
186     CGLUnlockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]);
187 }
188
189 /*****************************************************************************
190  * VLCOpenGLVoutView implementation
191  *****************************************************************************/
192 @implementation VLCOpenGLVoutView
193
194 /* Init a new gl view and register it to both the framework and the
195  * vout_thread_t. Must be called from main thread. */
196 + (void) autoinitOpenGLVoutViewIntVoutWithContainer: (NSData *) argsAsData
197 {
198     struct args { vout_thread_t * p_vout; id <VLCOpenGLVoutEmbedding> container; } *
199         args = (struct args *)[argsAsData bytes];
200     VLCOpenGLVoutView * oglview;
201
202     oglview = [[VLCOpenGLVoutView alloc] initWithVout: args->p_vout container: args->container];
203
204     args->p_vout->p_sys->o_glview = [oglview autorelease];
205
206     [args->container addVoutSubview: oglview];
207 }
208
209 - (void)dealloc
210 {
211     [objectLock dealloc]; 
212     [super dealloc];
213 }
214
215 - (void)removeFromSuperview
216 {
217     [super removeFromSuperview];
218 }
219
220
221 - (id) initWithVout: (vout_thread_t *) vout container: (id <VLCOpenGLVoutEmbedding>) aContainer
222 {
223     NSOpenGLPixelFormatAttribute attribs[] =
224     {
225         NSOpenGLPFADoubleBuffer,
226         NSOpenGLPFAAccelerated,
227         NSOpenGLPFANoRecovery,
228         NSOpenGLPFAColorSize, 24,
229         NSOpenGLPFAAlphaSize, 8,
230         NSOpenGLPFADepthSize, 24,
231         NSOpenGLPFAWindow,
232         0
233     };
234
235     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
236         initWithAttributes: attribs];
237
238     if( !fmt )
239     {
240         msg_Warn( p_vout, "could not create OpenGL video output" );
241         return nil;
242     }
243
244     if( self = [super initWithFrame: NSMakeRect(0,0,10,10) pixelFormat: fmt] )
245     {
246         p_vout = vout;
247         container = aContainer;
248         objectLock = [[NSLock alloc] init];
249
250         [fmt release];
251
252         [[self openGLContext] makeCurrentContext];
253         [[self openGLContext] update];
254
255         /* Swap buffers only during the vertical retrace of the monitor.
256         http://developer.apple.com/documentation/GraphicsImaging/
257         Conceptual/OpenGL/chap5/chapter_5_section_44.html */
258         long params[] = { 1 };
259         CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
260                      params );
261     }
262     return self;
263 }
264
265 - (void) detachFromVout
266 {
267     [objectLock lock];
268     p_vout = NULL;
269     [objectLock unlock];
270 }
271
272 - (id <VLCOpenGLVoutEmbedding>) container
273 {
274     return container;
275 }
276
277 - (void) destroyVout
278 {
279     [objectLock lock];
280     if( p_vout )
281     {
282         vlc_object_detach( p_vout );
283         vlc_object_release( p_vout );
284         vout_Destroy( (vout_thread_t *)p_vout );
285     }
286     [objectLock unlock];
287 }
288
289 - (void) reshape
290 {
291     int x, y;
292     vlc_value_t val;
293
294     [objectLock lock];
295     if( !p_vout )
296     {
297         [objectLock unlock];
298         return;
299     }
300
301     cocoaglvoutviewLock( p_vout );
302     NSRect bounds = [self bounds];
303
304     if( [[self container] stretchesVideo] )
305     {
306         x = bounds.size.width;
307         y = bounds.size.height;
308     }
309     else if( bounds.size.height * p_vout->fmt_in.i_visible_width *
310              p_vout->fmt_in.i_sar_num <
311              bounds.size.width * p_vout->fmt_in.i_visible_height *
312              p_vout->fmt_in.i_sar_den )
313     {
314         x = ( bounds.size.height * p_vout->fmt_in.i_visible_width *
315               p_vout->fmt_in.i_sar_num ) /
316             ( p_vout->fmt_in.i_visible_height * p_vout->fmt_in.i_sar_den);
317
318         y = bounds.size.height;
319     }
320     else
321     {
322         x = bounds.size.width;
323         y = ( bounds.size.width * p_vout->fmt_in.i_visible_height *
324               p_vout->fmt_in.i_sar_den) /
325             ( p_vout->fmt_in.i_visible_width * p_vout->fmt_in.i_sar_num  );
326     }
327
328     glViewport( ( bounds.size.width - x ) / 2,
329                 ( bounds.size.height - y ) / 2, x, y );
330
331     if( p_vout->p_sys->b_got_frame )
332     {
333         /* Ask the opengl module to redraw */
334         vout_thread_t * p_parent;
335         p_parent = (vout_thread_t *) p_vout->p_parent;
336         cocoaglvoutviewUnlock( p_vout );
337         if( p_parent && p_parent->pf_display )
338         {
339             p_parent->pf_display( p_parent, NULL );
340         }
341     }
342     else
343     {
344         glClear( GL_COLOR_BUFFER_BIT );
345         cocoaglvoutviewUnlock( p_vout );
346     }
347     [objectLock unlock];
348     [super reshape];
349 }
350
351 - (void) update
352 {
353     if( kCGLNoError != CGLLockContext([[self openGLContext] CGLContextObj]) )
354         return;
355     [super update];
356     CGLUnlockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]);
357 }
358
359 - (void) drawRect: (NSRect) rect
360 {
361     if( kCGLNoError != CGLLockContext([[self openGLContext] CGLContextObj]) )
362         return;
363     [[self openGLContext] flushBuffer];
364     [super drawRect:rect];
365     CGLUnlockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]);
366 }
367
368 @end
369