]> git.sesse.net Git - vlc/blob - modules/gui/macosx/voutgl.m
* Reading APIs before starting to code can allow to spare time and to avoid writing...
[vlc] / 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_ASPECT_CHANGE )
155     {
156         [p_vout->p_sys->o_glview reshape];
157         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
158     }
159     if( p_vout->i_changes & VOUT_CROP_CHANGE )
160     {
161         [p_vout->p_sys->o_glview reshape];
162         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
163     }
164
165     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
166     {
167         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
168
169         if( !p_vout->b_fullscreen )
170         {
171             /* Save window size and position */
172             p_vout->p_sys->s_frame.size =
173                 [p_vout->p_sys->o_vout_view frame].size;
174             p_vout->p_sys->s_frame.origin =
175                 [[p_vout->p_sys->o_vout_view getWindow ]frame].origin;
176             p_vout->p_sys->b_saved_frame = VLC_TRUE;
177         }
178         [p_vout->p_sys->o_vout_view closeVout];
179
180         p_vout->b_fullscreen = !p_vout->b_fullscreen;
181
182 #define o_glview p_vout->p_sys->o_glview
183         o_glview = [[VLCGLView alloc] initWithVout: p_vout];
184         [o_glview autorelease];
185
186         if( p_vout->p_sys->b_saved_frame )
187         {
188             p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
189                         subView: o_glview
190                         frame: &p_vout->p_sys->s_frame];
191         }
192         else
193         {
194             p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
195                         subView: o_glview frame: nil];
196
197         }
198
199         [[o_glview openGLContext] makeCurrentContext];
200 #undef o_glview
201
202         [o_pool release];
203
204         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
205     }
206
207     [p_vout->p_sys->o_vout_view manage];
208     return VLC_SUCCESS;
209 }
210
211 /*****************************************************************************
212  * Control: control facility for the vout
213  *****************************************************************************/
214 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
215 {
216     vlc_bool_t b_arg;
217
218     switch( i_query )
219     {
220         case VOUT_SET_STAY_ON_TOP:
221             b_arg = va_arg( args, vlc_bool_t );
222             [p_vout->p_sys->o_vout_view setOnTop: b_arg];
223             return VLC_SUCCESS;
224
225         case VOUT_CLOSE:
226         case VOUT_REPARENT:
227         default:
228             return vout_vaControlDefault( p_vout, i_query, args );
229     }
230 }
231
232 static void Swap( vout_thread_t * p_vout )
233 {
234     p_vout->p_sys->b_got_frame = VLC_TRUE;
235     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
236     glFlush();
237 }
238
239 static int Lock( vout_thread_t * p_vout )
240 {
241     vlc_mutex_lock( &p_vout->p_sys->lock );
242     return 0;
243 }
244
245 static void Unlock( vout_thread_t * p_vout )
246 {
247     vlc_mutex_unlock( &p_vout->p_sys->lock );
248 }
249
250 /*****************************************************************************
251  * VLCGLView implementation
252  *****************************************************************************/
253 @implementation VLCGLView
254
255 - (id) initWithVout: (vout_thread_t *) vout
256 {
257     p_vout = vout;
258
259     NSOpenGLPixelFormatAttribute attribs[] =
260     {
261         NSOpenGLPFAAccelerated,
262         NSOpenGLPFANoRecovery,
263         NSOpenGLPFAColorSize, 24,
264         NSOpenGLPFAAlphaSize, 8,
265         NSOpenGLPFADepthSize, 24,
266         NSOpenGLPFAWindow,
267         0
268     };
269
270     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
271         initWithAttributes: attribs];
272
273     if( !fmt )
274     {
275         msg_Warn( p_vout, "could not create OpenGL video output" );
276         return nil;
277     }
278
279     self = [super initWithFrame: NSMakeRect(0,0,10,10) pixelFormat: fmt];
280     [fmt release];
281
282     [[self openGLContext] makeCurrentContext];
283     [[self openGLContext] update];
284
285     /* Swap buffers only during the vertical retrace of the monitor.
286        http://developer.apple.com/documentation/GraphicsImaging/
287        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
288     long params[] = { 1 };
289     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
290                      params );
291     return self;
292 }
293
294 - (void) reshape
295 {
296     int x, y;
297     vlc_value_t val;
298
299     Lock( p_vout );
300     NSRect bounds = [self bounds];
301
302     [[self openGLContext] makeCurrentContext];
303
304     var_Get( p_vout, "macosx-stretch", &val );
305     if( val.b_bool )
306     {
307         x = bounds.size.width;
308         y = bounds.size.height;
309     }
310     else if( bounds.size.height * p_vout->fmt_in.i_visible_width *
311              p_vout->fmt_in.i_sar_num <
312              bounds.size.width * p_vout->fmt_in.i_visible_height *
313              p_vout->fmt_in.i_sar_den )
314     {
315         x = ( bounds.size.height * p_vout->fmt_in.i_visible_width *
316               p_vout->fmt_in.i_sar_num ) /
317             ( p_vout->fmt_in.i_visible_height * p_vout->fmt_in.i_sar_den);
318
319         y = bounds.size.height;
320     }
321     else
322     {
323         x = bounds.size.width;
324         y = ( bounds.size.width * p_vout->fmt_in.i_visible_height *
325               p_vout->fmt_in.i_sar_den) /
326             ( p_vout->fmt_in.i_visible_width * p_vout->fmt_in.i_sar_num  );
327     }
328
329     glViewport( ( bounds.size.width - x ) / 2,
330                 ( bounds.size.height - y ) / 2, x, y );
331
332     if( p_vout->p_sys->b_got_frame )
333     {
334         /* Ask the opengl module to redraw */
335         vout_thread_t * p_parent;
336         p_parent = (vout_thread_t *) p_vout->p_parent;
337         Unlock( p_vout );
338         if( p_parent && p_parent->pf_display )
339         {
340             p_parent->pf_display( p_parent, NULL );
341         }
342     }
343     else
344     {
345         glClear( GL_COLOR_BUFFER_BIT );
346         Unlock( p_vout );
347     }
348     [super reshape];
349 }
350
351 - (void) update
352 {
353     Lock( p_vout );
354     [super update];
355     Unlock( p_vout );
356 }
357
358 - (void) drawRect: (NSRect) rect
359 {
360     Lock( p_vout );
361     [[self openGLContext] makeCurrentContext];
362     glFlush();
363     [super drawRect:rect];
364     Unlock( p_vout );
365 }
366
367 @end
368
369