]> git.sesse.net Git - vlc/blob - modules/gui/macosx/voutgl.m
* New menu-items referencing forum
[vlc] / modules / gui / macosx / voutgl.m
1 /*****************************************************************************
2  * voutgl.m: MacOS X OpenGL provider
3  *****************************************************************************
4  * Copyright (C) 2001-2004 VideoLAN
5  * $Id: vout.m 8351 2004-08-02 13:06:38Z hartman $
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  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <errno.h>                                                 /* ENOMEM */
32 #include <stdlib.h>                                                /* free() */
33 #include <string.h>                                            /* strerror() */
34
35 #include <vlc_keys.h>
36
37 #include "intf.h"
38 #include "vout.h"
39
40 #include <OpenGL/OpenGL.h>
41 #include <OpenGL/gl.h>
42
43 /*****************************************************************************
44  * VLCView interface
45  *****************************************************************************/
46 @interface VLCGLView : NSOpenGLView
47 {
48     vout_thread_t * p_vout;
49 }
50
51 - (id)initWithFrame: (NSRect) frame vout: (vout_thread_t*) p_vout;
52
53 @end
54
55
56 struct vout_sys_t
57 {
58     NSAutoreleasePool * o_pool;
59     VLCWindow         * o_window;
60     VLCGLView         * o_glview;
61     vlc_bool_t          b_saved_frame;
62     NSRect              s_frame;
63     vlc_bool_t          b_got_frame;
64     vlc_mutex_t         lock;
65 };
66
67 /*****************************************************************************
68  * Local prototypes
69  *****************************************************************************/
70
71 static int  Init   ( vout_thread_t * p_vout );
72 static void End    ( vout_thread_t * p_vout );
73 static int  Manage ( vout_thread_t * p_vout );
74 static int  Control( vout_thread_t *, int, va_list );
75 static void Swap   ( vout_thread_t * p_vout );
76 static int  Lock   ( vout_thread_t * p_vout );
77 static void Unlock ( vout_thread_t * p_vout );
78
79 int E_(OpenVideoGL)  ( vlc_object_t * p_this )
80 {
81     vout_thread_t * p_vout = (vout_thread_t *) p_this;
82
83     if( !CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay ) )
84     {
85         msg_Warn( p_vout, "no hardware acceleration" );
86         return( 1 );
87     }
88     msg_Dbg( p_vout, "display is Quartz Extreme accelerated" );
89
90     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
91     if( p_vout->p_sys == NULL )
92     {
93         msg_Err( p_vout, "out of memory" );
94         return( 1 );
95     }
96
97     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
98
99     p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
100     vlc_mutex_init( p_vout, &p_vout->p_sys->lock );
101
102     /* Spawn window */
103     p_vout->p_sys->b_got_frame = VLC_FALSE;
104     p_vout->p_sys->o_window = [[VLCWindow alloc] initWithVout: p_vout
105                                                  frame: nil];
106     if( !p_vout->p_sys->o_window )
107     {
108         return VLC_EGENERIC;
109     }
110
111     /* Add OpenGL view */
112 #define o_glview p_vout->p_sys->o_glview
113     o_glview = [[VLCGLView alloc] initWithFrame:
114                 [p_vout->p_sys->o_window frame] vout: p_vout];
115     [p_vout->p_sys->o_window setContentView: o_glview];
116     [o_glview autorelease];
117 #undef o_glview
118
119     p_vout->pf_init   = Init;
120     p_vout->pf_end    = End;
121     p_vout->pf_manage = Manage;
122     p_vout->pf_control= Control;
123     p_vout->pf_swap   = Swap;
124     p_vout->pf_lock   = Lock;
125     p_vout->pf_unlock = Unlock;
126
127     return VLC_SUCCESS;
128 }
129
130 void E_(CloseVideoGL) ( vlc_object_t * p_this )
131 {
132     vout_thread_t * p_vout = (vout_thread_t *) p_this;
133     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
134
135     /* Remove the GLView from the window, because we are not sure OS X
136        will actually close the window right away. When it doesn't,
137        VLCGLView's reshape is called while p_vout and p_vout->p_sys
138        aren't valid anymore and crashes. */
139     [p_vout->p_sys->o_window setContentView: NULL];
140
141     /* Close the window */
142     [p_vout->p_sys->o_window close];
143
144     /* Clean up */
145     vlc_mutex_destroy( &p_vout->p_sys->lock );
146     [o_pool release];
147     free( p_vout->p_sys );
148 }
149
150 static int Init( vout_thread_t * p_vout )
151 {
152     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
153     return VLC_SUCCESS;
154 }
155
156 static void End( vout_thread_t * p_vout )
157 {
158     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
159 }
160
161 static int Manage( vout_thread_t * p_vout )
162 {
163     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
164     {
165         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
166
167         if( !p_vout->b_fullscreen )
168         {
169             /* Save window size and position */
170             p_vout->p_sys->s_frame.size =
171                 [[p_vout->p_sys->o_window contentView] frame].size;
172             p_vout->p_sys->s_frame.origin =
173                 [p_vout->p_sys->o_window frame].origin;
174             p_vout->p_sys->b_saved_frame = VLC_TRUE;
175         }
176         [p_vout->p_sys->o_window close];
177
178         p_vout->b_fullscreen = !p_vout->b_fullscreen;
179
180         if( p_vout->p_sys->b_saved_frame )
181         {
182             p_vout->p_sys->o_window = [[VLCWindow alloc]
183                 initWithVout: p_vout frame: &p_vout->p_sys->s_frame];
184         }
185         else
186         {
187             p_vout->p_sys->o_window = [[VLCWindow alloc]
188                 initWithVout: p_vout frame: nil];
189         }
190
191 #define o_glview p_vout->p_sys->o_glview
192         o_glview = [[VLCGLView alloc] initWithFrame: [p_vout->p_sys->o_window frame] vout: p_vout];
193         [p_vout->p_sys->o_window setContentView: o_glview];
194         [o_glview autorelease];
195         [[o_glview openGLContext] makeCurrentContext];
196 #undef o_glview
197         [o_pool release];
198
199         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
200     }
201     [p_vout->p_sys->o_window manage];
202     return VLC_SUCCESS;
203 }
204
205 /*****************************************************************************
206  * Control: control facility for the vout
207  *****************************************************************************/
208 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
209 {
210     vlc_bool_t b_arg;
211
212     switch( i_query )
213     {
214         case VOUT_SET_STAY_ON_TOP:
215             b_arg = va_arg( args, vlc_bool_t );
216             [p_vout->p_sys->o_window setOnTop: b_arg];
217             return VLC_SUCCESS;
218
219         case VOUT_CLOSE:
220         case VOUT_REPARENT:
221         default:
222             return vout_vaControlDefault( p_vout, i_query, args );
223     }
224 }
225
226 static void Swap( vout_thread_t * p_vout )
227 {
228     p_vout->p_sys->b_got_frame = VLC_TRUE;
229     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
230     glFlush();
231 }
232
233 static int Lock( vout_thread_t * p_vout )
234 {
235     vlc_mutex_lock( &p_vout->p_sys->lock );
236     return 0;
237 }
238
239 static void Unlock( vout_thread_t * p_vout )
240 {
241     vlc_mutex_unlock( &p_vout->p_sys->lock );
242 }
243
244 /*****************************************************************************
245  * VLCGLView implementation
246  *****************************************************************************/
247 @implementation VLCGLView
248
249 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
250 {
251     p_vout = _p_vout;
252
253     NSOpenGLPixelFormatAttribute attribs[] =
254     {
255         NSOpenGLPFAAccelerated,
256         NSOpenGLPFANoRecovery,
257         NSOpenGLPFAColorSize, 24,
258         NSOpenGLPFAAlphaSize, 8,
259         NSOpenGLPFADepthSize, 24,
260         NSOpenGLPFAWindow,
261         0
262     };
263
264     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
265         initWithAttributes: attribs];
266
267     if( !fmt )
268     {
269         msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
270         return nil;
271     }
272
273     self = [super initWithFrame:frame pixelFormat: fmt];
274     [fmt release];
275
276     [[self openGLContext] makeCurrentContext];
277     [[self openGLContext] update];
278
279     /* Swap buffers only during the vertical retrace of the monitor.
280        http://developer.apple.com/documentation/GraphicsImaging/
281        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
282     long params[] = { 1 };
283     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
284                      params );
285     return self;
286 }
287
288 - (void) reshape
289 {
290     int x, y;
291     vlc_value_t val;
292     NSRect bounds = [self bounds];
293
294     [[self openGLContext] makeCurrentContext];
295
296     var_Get( p_vout, "macosx-stretch", &val );
297     if( val.b_bool )
298     {
299         x = bounds.size.width;
300         y = bounds.size.height;
301     }
302     else if( bounds.size.height * p_vout->render.i_aspect <
303              bounds.size.width * VOUT_ASPECT_FACTOR )
304     {
305         x = bounds.size.height * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
306         y = bounds.size.height;
307     }
308     else
309     {
310         x = bounds.size.width;
311         y = bounds.size.width * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
312     }
313
314     Lock( p_vout );
315     glViewport( ( bounds.size.width - x ) / 2,
316                 ( bounds.size.height - y ) / 2, x, y );
317     Unlock( p_vout );
318
319     if( p_vout->p_sys->b_got_frame )
320     {
321         /* Ask the opengl module to redraw */
322         vout_thread_t * p_parent;
323         p_parent = (vout_thread_t *) p_vout->p_parent;
324         if( p_parent && p_parent->pf_display )
325         {
326             p_parent->pf_display( p_parent, NULL );
327         }
328     }
329     else
330     {
331         Lock( p_vout );
332         glClear( GL_COLOR_BUFFER_BIT );
333         Unlock( p_vout );
334     }
335 }
336
337 - (void) drawRect: (NSRect) rect
338 {
339     [[self openGLContext] makeCurrentContext];
340     Lock( p_vout );
341     glFlush();
342     Unlock( p_vout );
343 }
344
345 @end
346
347