* First string review of the OSX interface
[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 OpenGL hardware acceleration found. "
85                           "Video display will be slow" );
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     /* Create the GL view */
103     p_vout->p_sys->o_glview = [[VLCGLView alloc] initWithVout: p_vout];
104     [p_vout->p_sys->o_glview autorelease];
105
106     /* Spawn the window */
107
108     if( !(p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
109                     subView: p_vout->p_sys->o_glview frame: nil]) )
110     {
111         return VLC_EGENERIC;
112     }
113
114     p_vout->p_sys->b_got_frame = VLC_FALSE;
115
116     p_vout->pf_init   = Init;
117     p_vout->pf_end    = End;
118     p_vout->pf_manage = Manage;
119     p_vout->pf_control= Control;
120     p_vout->pf_swap   = Swap;
121     p_vout->pf_lock   = Lock;
122     p_vout->pf_unlock = Unlock;
123
124     return VLC_SUCCESS;
125 }
126
127 void E_(CloseVideoGL) ( vlc_object_t * p_this )
128 {
129     vout_thread_t * p_vout = (vout_thread_t *) p_this;
130     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
131
132     /* Close the window */
133     [p_vout->p_sys->o_vout_view closeVout];
134
135     /* Clean up */
136     vlc_mutex_destroy( &p_vout->p_sys->lock );
137     [o_pool release];
138     free( p_vout->p_sys );
139 }
140
141 static int Init( vout_thread_t * p_vout )
142 {
143     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
144     return VLC_SUCCESS;
145 }
146
147 static void End( vout_thread_t * p_vout )
148 {
149     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
150 }
151
152 static int Manage( vout_thread_t * p_vout )
153 {
154     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
155     {
156         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
157
158         if( !p_vout->b_fullscreen )
159         {
160             /* Save window size and position */
161             p_vout->p_sys->s_frame.size =
162                 [p_vout->p_sys->o_vout_view frame].size;
163             p_vout->p_sys->s_frame.origin =
164                 [[p_vout->p_sys->o_vout_view getWindow ]frame].origin;
165             p_vout->p_sys->b_saved_frame = VLC_TRUE;
166         }
167         [p_vout->p_sys->o_vout_view closeVout];
168
169         p_vout->b_fullscreen = !p_vout->b_fullscreen;
170
171 #define o_glview p_vout->p_sys->o_glview
172         o_glview = [[VLCGLView alloc] initWithVout: p_vout];
173         [o_glview autorelease];
174
175         if( p_vout->p_sys->b_saved_frame )
176         {
177             p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
178                         subView: o_glview
179                         frame: &p_vout->p_sys->s_frame];
180         }
181         else
182         {
183             p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
184                         subView: o_glview frame: nil];
185
186         }
187
188         [[o_glview openGLContext] makeCurrentContext];
189 #undef o_glview
190
191         [o_pool release];
192
193         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
194     }
195     [p_vout->p_sys->o_vout_view manage];
196     return VLC_SUCCESS;
197 }
198
199 /*****************************************************************************
200  * Control: control facility for the vout
201  *****************************************************************************/
202 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
203 {
204     vlc_bool_t b_arg;
205
206     switch( i_query )
207     {
208         case VOUT_SET_STAY_ON_TOP:
209             b_arg = va_arg( args, vlc_bool_t );
210             [p_vout->p_sys->o_vout_view setOnTop: b_arg];
211             return VLC_SUCCESS;
212
213         case VOUT_CLOSE:
214         case VOUT_REPARENT:
215         default:
216             return vout_vaControlDefault( p_vout, i_query, args );
217     }
218 }
219
220 static void Swap( vout_thread_t * p_vout )
221 {
222     p_vout->p_sys->b_got_frame = VLC_TRUE;
223     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
224     glFlush();
225 }
226
227 static int Lock( vout_thread_t * p_vout )
228 {
229     vlc_mutex_lock( &p_vout->p_sys->lock );
230     return 0;
231 }
232
233 static void Unlock( vout_thread_t * p_vout )
234 {
235     vlc_mutex_unlock( &p_vout->p_sys->lock );
236 }
237
238 /*****************************************************************************
239  * VLCGLView implementation
240  *****************************************************************************/
241 @implementation VLCGLView
242
243 - (id) initWithVout: (vout_thread_t *) vout
244 {
245     p_vout = vout;
246
247     NSOpenGLPixelFormatAttribute attribs[] =
248     {
249         NSOpenGLPFAAccelerated,
250         NSOpenGLPFANoRecovery,
251         NSOpenGLPFAColorSize, 24,
252         NSOpenGLPFAAlphaSize, 8,
253         NSOpenGLPFADepthSize, 24,
254         NSOpenGLPFAWindow,
255         0
256     };
257
258     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
259         initWithAttributes: attribs];
260
261     if( !fmt )
262     {
263         msg_Warn( p_vout, "Could not create OpenGL video output" );
264         return nil;
265     }
266
267     self = [super initWithFrame: NSMakeRect(0,0,10,10) pixelFormat: fmt];
268     [fmt release];
269
270     [[self openGLContext] makeCurrentContext];
271     [[self openGLContext] update];
272
273     /* Swap buffers only during the vertical retrace of the monitor.
274        http://developer.apple.com/documentation/GraphicsImaging/
275        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
276     long params[] = { 1 };
277     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
278                      params );
279     return self;
280 }
281
282 - (void) reshape
283 {
284     int x, y;
285     vlc_value_t val;
286
287     Lock( p_vout );
288     NSRect bounds = [self bounds];
289
290     [[self openGLContext] makeCurrentContext];
291
292     var_Get( p_vout, "macosx-stretch", &val );
293     if( val.b_bool )
294     {
295         x = bounds.size.width;
296         y = bounds.size.height;
297     }
298     else if( bounds.size.height * p_vout->render.i_aspect <
299              bounds.size.width * VOUT_ASPECT_FACTOR )
300     {
301         x = bounds.size.height * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
302         y = bounds.size.height;
303     }
304     else
305     {
306         x = bounds.size.width;
307         y = bounds.size.width * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
308     }
309
310     glViewport( ( bounds.size.width - x ) / 2,
311                 ( bounds.size.height - y ) / 2, x, y );
312
313     if( p_vout->p_sys->b_got_frame )
314     {
315         /* Ask the opengl module to redraw */
316         vout_thread_t * p_parent;
317         p_parent = (vout_thread_t *) p_vout->p_parent;
318         Unlock( p_vout );
319         if( p_parent && p_parent->pf_display )
320         {
321             p_parent->pf_display( p_parent, NULL );
322         }
323     }
324     else
325     {
326         glClear( GL_COLOR_BUFFER_BIT );
327         Unlock( p_vout );
328     }
329     [super reshape];
330 }
331
332 - (void) update
333 {
334     Lock( p_vout );
335     [super update];
336     Unlock( p_vout );
337 }
338
339 - (void) drawRect: (NSRect) rect
340 {
341     Lock( p_vout );
342     [[self openGLContext] makeCurrentContext];
343     glFlush();
344     [super drawRect:rect];
345     Unlock( p_vout );
346 }
347
348 @end
349
350