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