]> git.sesse.net Git - vlc/blob - modules/gui/macosx/voutgl.m
ead0d1380ad78526142c53dc035f14707ed84c28
[vlc] / modules / gui / macosx / voutgl.m
1 /*****************************************************************************
2  * voutgl.m: MacOS X OpenGL provider
3  *****************************************************************************
4  * Copyright (C) 2001-2004 VideoLAN (Centrale Réseaux) and its contributors
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) initWithVout: (vout_thread_t *) p_vout;
52 @end
53
54 struct vout_sys_t
55 {
56     NSAutoreleasePool * o_pool;
57     VLCWindow         * o_window;
58     VLCGLView         * o_glview;
59     vlc_bool_t          b_saved_frame;
60     NSRect              s_frame;
61     vlc_bool_t          b_got_frame;
62     vlc_mutex_t         lock;
63 };
64
65 /*****************************************************************************
66  * Local prototypes
67  *****************************************************************************/
68
69 static int  Init   ( vout_thread_t * p_vout );
70 static void End    ( vout_thread_t * p_vout );
71 static int  Manage ( vout_thread_t * p_vout );
72 static int  Control( vout_thread_t *, int, va_list );
73 static void Swap   ( vout_thread_t * p_vout );
74 static int  Lock   ( vout_thread_t * p_vout );
75 static void Unlock ( vout_thread_t * p_vout );
76
77 int E_(OpenVideoGL)  ( vlc_object_t * p_this )
78 {
79     vout_thread_t * p_vout = (vout_thread_t *) p_this;
80
81     if( !CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay ) )
82     {
83         msg_Warn( p_vout, "no hardware acceleration" );
84         return( 1 );
85     }
86     msg_Dbg( p_vout, "display is Quartz Extreme accelerated" );
87
88     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
89     if( p_vout->p_sys == NULL )
90     {
91         msg_Err( p_vout, "out of memory" );
92         return( 1 );
93     }
94
95     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
96
97     p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
98     vlc_mutex_init( p_vout, &p_vout->p_sys->lock );
99
100     /* Create the GL view */
101     p_vout->p_sys->o_glview = [[VLCGLView alloc] initWithVout: p_vout];
102     [p_vout->p_sys->o_glview autorelease];
103
104     /* Spawn the window */
105     p_vout->p_sys->b_got_frame = VLC_FALSE;
106     p_vout->p_sys->o_window = [[VLCWindow alloc] initWithVout: p_vout
107         view: p_vout->p_sys->o_glview frame: nil];
108     if( !p_vout->p_sys->o_window )
109     {
110         return VLC_EGENERIC;
111     }
112
113     p_vout->pf_init   = Init;
114     p_vout->pf_end    = End;
115     p_vout->pf_manage = Manage;
116     p_vout->pf_control= Control;
117     p_vout->pf_swap   = Swap;
118     p_vout->pf_lock   = Lock;
119     p_vout->pf_unlock = Unlock;
120
121     return VLC_SUCCESS;
122 }
123
124 void E_(CloseVideoGL) ( vlc_object_t * p_this )
125 {
126     vout_thread_t * p_vout = (vout_thread_t *) p_this;
127     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
128
129     /* Close the window */
130     [p_vout->p_sys->o_window close];
131
132     /* Clean up */
133     vlc_mutex_destroy( &p_vout->p_sys->lock );
134     [o_pool release];
135     free( p_vout->p_sys );
136 }
137
138 static int Init( vout_thread_t * p_vout )
139 {
140     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
141     return VLC_SUCCESS;
142 }
143
144 static void End( vout_thread_t * p_vout )
145 {
146     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
147 }
148
149 static int Manage( vout_thread_t * p_vout )
150 {
151     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
152     {
153         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
154
155         if( !p_vout->b_fullscreen )
156         {
157             /* Save window size and position */
158             p_vout->p_sys->s_frame.size =
159                 [[p_vout->p_sys->o_window contentView] frame].size;
160             p_vout->p_sys->s_frame.origin =
161                 [p_vout->p_sys->o_window frame].origin;
162             p_vout->p_sys->b_saved_frame = VLC_TRUE;
163         }
164         [p_vout->p_sys->o_window close];
165
166         p_vout->b_fullscreen = !p_vout->b_fullscreen;
167
168 #define o_glview p_vout->p_sys->o_glview
169         o_glview = [[VLCGLView alloc] initWithVout: p_vout];
170         [o_glview autorelease];
171
172         if( p_vout->p_sys->b_saved_frame )
173         {
174             p_vout->p_sys->o_window = [[VLCWindow alloc]
175                 initWithVout: p_vout view: o_glview
176                 frame: &p_vout->p_sys->s_frame];
177         }
178         else
179         {
180             p_vout->p_sys->o_window = [[VLCWindow alloc]
181                 initWithVout: p_vout view: o_glview frame: nil];
182         }
183
184         [[o_glview openGLContext] makeCurrentContext];
185 #undef o_glview
186
187         [o_pool release];
188
189         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
190     }
191     [p_vout->p_sys->o_window manage];
192     return VLC_SUCCESS;
193 }
194
195 /*****************************************************************************
196  * Control: control facility for the vout
197  *****************************************************************************/
198 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
199 {
200     vlc_bool_t b_arg;
201
202     switch( i_query )
203     {
204         case VOUT_SET_STAY_ON_TOP:
205             b_arg = va_arg( args, vlc_bool_t );
206             [p_vout->p_sys->o_window setOnTop: b_arg];
207             return VLC_SUCCESS;
208
209         case VOUT_CLOSE:
210         case VOUT_REPARENT:
211         default:
212             return vout_vaControlDefault( p_vout, i_query, args );
213     }
214 }
215
216 static void Swap( vout_thread_t * p_vout )
217 {
218     p_vout->p_sys->b_got_frame = VLC_TRUE;
219     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
220     glFlush();
221 }
222
223 static int Lock( vout_thread_t * p_vout )
224 {
225     vlc_mutex_lock( &p_vout->p_sys->lock );
226     return 0;
227 }
228
229 static void Unlock( vout_thread_t * p_vout )
230 {
231     vlc_mutex_unlock( &p_vout->p_sys->lock );
232 }
233
234 /*****************************************************************************
235  * VLCGLView implementation
236  *****************************************************************************/
237 @implementation VLCGLView
238
239 - (id) initWithVout: (vout_thread_t *) vout
240 {
241     p_vout = vout;
242
243     NSOpenGLPixelFormatAttribute attribs[] =
244     {
245         NSOpenGLPFAAccelerated,
246         NSOpenGLPFANoRecovery,
247         NSOpenGLPFAColorSize, 24,
248         NSOpenGLPFAAlphaSize, 8,
249         NSOpenGLPFADepthSize, 24,
250         NSOpenGLPFAWindow,
251         0
252     };
253
254     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
255         initWithAttributes: attribs];
256
257     if( !fmt )
258     {
259         msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
260         return nil;
261     }
262
263     self = [super initWithFrame: NSMakeRect(0,0,10,10) pixelFormat: fmt];
264     [fmt release];
265
266     [[self openGLContext] makeCurrentContext];
267     [[self openGLContext] update];
268
269     /* Swap buffers only during the vertical retrace of the monitor.
270        http://developer.apple.com/documentation/GraphicsImaging/
271        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
272     long params[] = { 1 };
273     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
274                      params );
275     return self;
276 }
277
278 - (void) reshape
279 {
280     int x, y;
281     vlc_value_t val;
282
283     Lock( p_vout );
284     NSRect bounds = [self bounds];
285
286     [[self openGLContext] makeCurrentContext];
287
288     var_Get( p_vout, "macosx-stretch", &val );
289     if( val.b_bool )
290     {
291         x = bounds.size.width;
292         y = bounds.size.height;
293     }
294     else if( bounds.size.height * p_vout->render.i_aspect <
295              bounds.size.width * VOUT_ASPECT_FACTOR )
296     {
297         x = bounds.size.height * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
298         y = bounds.size.height;
299     }
300     else
301     {
302         x = bounds.size.width;
303         y = bounds.size.width * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
304     }
305
306     glViewport( ( bounds.size.width - x ) / 2,
307                 ( bounds.size.height - y ) / 2, x, y );
308
309     if( p_vout->p_sys->b_got_frame )
310     {
311         /* Ask the opengl module to redraw */
312         vout_thread_t * p_parent;
313         p_parent = (vout_thread_t *) p_vout->p_parent;
314         Unlock( p_vout );
315         if( p_parent && p_parent->pf_display )
316         {
317             p_parent->pf_display( p_parent, NULL );
318         }
319     }
320     else
321     {
322         glClear( GL_COLOR_BUFFER_BIT );
323         Unlock( p_vout );
324     }
325     [super reshape];
326 }
327
328 - (void) update
329 {
330     Lock( p_vout );
331     [super update];
332     Unlock( p_vout );
333 }
334
335 - (void) drawRect: (NSRect) rect
336 {
337     Lock( p_vout );
338     [[self openGLContext] makeCurrentContext];
339     glFlush();
340     [super drawRect:rect];
341     Unlock( p_vout );
342 }
343
344 @end
345
346