]> git.sesse.net Git - vlc/blob - modules/gui/macosx/voutgl.m
gui/macos/*: 2nd String Review (refs #438)
[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     int                 i_vout_size_update_counter;
65     int                 i_x, i_y;
66 };
67
68 /*****************************************************************************
69  * Local prototypes
70  *****************************************************************************/
71
72 static int  Init   ( vout_thread_t * p_vout );
73 static void End    ( vout_thread_t * p_vout );
74 static int  Manage ( vout_thread_t * p_vout );
75 static int  Control( vout_thread_t *, int, va_list );
76 static void Swap   ( vout_thread_t * p_vout );
77 static int  Lock   ( vout_thread_t * p_vout );
78 static void Unlock ( vout_thread_t * p_vout );
79
80 static int AspectCropCallback( vlc_object_t *, char const *,
81                            vlc_value_t, vlc_value_t, void * );
82
83 int E_(OpenVideoGL)  ( vlc_object_t * p_this )
84 {
85     vout_thread_t * p_vout = (vout_thread_t *) p_this;
86
87     if( !CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay ) )
88     {
89         msg_Warn( p_vout, "no OpenGL hardware acceleration found. "
90                           "Video display will be slow" );
91         return( 1 );
92     }
93     msg_Dbg( p_vout, "display is Quartz Extreme accelerated" );
94
95     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
96     if( p_vout->p_sys == NULL )
97     {
98         msg_Err( p_vout, "out of memory" );
99         return( 1 );
100     }
101
102     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
103
104     p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
105     vlc_mutex_init( p_vout, &p_vout->p_sys->lock );
106
107     /* Create the GL view */
108     p_vout->p_sys->o_glview = [[VLCGLView alloc] initWithVout: p_vout];
109     [p_vout->p_sys->o_glview autorelease];
110
111     /* Spawn the window */
112
113     if( !(p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
114                     subView: p_vout->p_sys->o_glview frame: nil]) )
115     {
116         return VLC_EGENERIC;
117     }
118
119     p_vout->p_sys->b_got_frame = VLC_FALSE;
120
121     p_vout->pf_init   = Init;
122     p_vout->pf_end    = End;
123     p_vout->pf_manage = Manage;
124     p_vout->pf_control= Control;
125     p_vout->pf_swap   = Swap;
126     p_vout->pf_lock   = Lock;
127     p_vout->pf_unlock = Unlock;
128
129     return VLC_SUCCESS;
130 }
131
132 void E_(CloseVideoGL) ( vlc_object_t * p_this )
133 {
134     vout_thread_t * p_vout = (vout_thread_t *) p_this;
135     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
136
137     /* Close the window */
138     [p_vout->p_sys->o_vout_view closeVout];
139
140     /* Clean up */
141     vlc_mutex_destroy( &p_vout->p_sys->lock );
142     [o_pool release];
143     free( p_vout->p_sys );
144 }
145
146 static int Init( vout_thread_t * p_vout )
147 {
148     /* The variable is in fact changed on the parent vout */
149     if( !var_Type( p_vout->p_parent, "aspect-ratio" ) )
150     {
151         var_Create( p_vout->p_parent, "aspect-ratio",
152                                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
153     }
154     var_AddCallback( p_vout->p_parent, "aspect-ratio", AspectCropCallback, p_vout );
155     if( !var_Type( p_vout->p_parent, "crop" ) )
156     {
157         var_Create( p_vout->p_parent, "crop",
158                                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
159     }
160     var_AddCallback( p_vout->p_parent, "crop", AspectCropCallback, p_vout );
161     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
162     return VLC_SUCCESS;
163 }
164
165 static void End( vout_thread_t * p_vout )
166 {
167     var_DelCallback( p_vout->p_parent, "aspect-ratio", AspectCropCallback, p_vout );
168     var_DelCallback( p_vout->p_parent, "crop", AspectCropCallback, p_vout );
169     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
170 }
171
172 static int Manage( vout_thread_t * p_vout )
173 {
174     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
175     {
176         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
177
178         if( !p_vout->b_fullscreen )
179         {
180             /* Save window size and position */
181             p_vout->p_sys->s_frame.size =
182                 [p_vout->p_sys->o_vout_view frame].size;
183             p_vout->p_sys->s_frame.origin =
184                 [[p_vout->p_sys->o_vout_view getWindow ]frame].origin;
185             p_vout->p_sys->b_saved_frame = VLC_TRUE;
186         }
187         [p_vout->p_sys->o_vout_view closeVout];
188
189         p_vout->b_fullscreen = !p_vout->b_fullscreen;
190
191 #define o_glview p_vout->p_sys->o_glview
192         o_glview = [[VLCGLView alloc] initWithVout: p_vout];
193         [o_glview autorelease];
194
195         if( p_vout->p_sys->b_saved_frame )
196         {
197             p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
198                         subView: o_glview
199                         frame: &p_vout->p_sys->s_frame];
200         }
201         else
202         {
203             p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
204                         subView: o_glview frame: nil];
205
206         }
207
208         [[o_glview openGLContext] makeCurrentContext];
209 #undef o_glview
210
211         [o_pool release];
212
213         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
214     }
215
216     if( p_vout->p_sys->i_vout_size_update_counter )
217     {
218         int i_old_x = p_vout->p_sys->i_x, i_old_y = p_vout->p_sys->i_y;
219         [p_vout->p_sys->o_glview reshape];
220         if( p_vout->p_sys->i_x != i_old_x || p_vout->p_sys->i_y != i_old_y )
221         {
222             p_vout->p_sys->i_vout_size_update_counter = 0;
223         }
224         else if( p_vout->p_sys->i_vout_size_update_counter > 0 )
225         {
226             p_vout->p_sys->i_vout_size_update_counter--;
227         }
228     }
229
230     [p_vout->p_sys->o_vout_view manage];
231     return VLC_SUCCESS;
232 }
233
234 /*****************************************************************************
235  * Control: control facility for the vout
236  *****************************************************************************/
237 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
238 {
239     vlc_bool_t b_arg;
240
241     switch( i_query )
242     {
243         case VOUT_SET_STAY_ON_TOP:
244             b_arg = va_arg( args, vlc_bool_t );
245             [p_vout->p_sys->o_vout_view setOnTop: b_arg];
246             return VLC_SUCCESS;
247
248         case VOUT_CLOSE:
249         case VOUT_REPARENT:
250         default:
251             return vout_vaControlDefault( p_vout, i_query, args );
252     }
253 }
254
255 static void Swap( vout_thread_t * p_vout )
256 {
257     p_vout->p_sys->b_got_frame = VLC_TRUE;
258     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
259     glFlush();
260 }
261
262 static int Lock( vout_thread_t * p_vout )
263 {
264     vlc_mutex_lock( &p_vout->p_sys->lock );
265     return 0;
266 }
267
268 static void Unlock( vout_thread_t * p_vout )
269 {
270     vlc_mutex_unlock( &p_vout->p_sys->lock );
271 }
272
273 static int AspectCropCallback( vlc_object_t *p_this, char const *psz_cmd,
274                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
275 {
276     /* Only update the vout size if the aspect ratio has actually been changed*/
277     /* We cannot change the size directly in this callback, since fmt_in
278        hasn't been updated yet, so do it in Manage */
279     if( strcmp( oldval.psz_string, newval.psz_string ) )
280     {
281         /* khludge ! Here, we are not sure that the vout size will actually
282            change (for instance if we go from Predefined to 4:3 on a 4:3
283            stream). So, to to trigger reshape endlessly, we decrease that
284            counter each time we call reshape. We put it to 0 directly if
285            we actually change the vout size. */
286         ((vout_thread_t *)p_data)->p_sys->i_vout_size_update_counter = 2;
287     }
288     return VLC_SUCCESS;
289 }
290
291 /*****************************************************************************
292  * VLCGLView implementation
293  *****************************************************************************/
294 @implementation VLCGLView
295
296 - (id) initWithVout: (vout_thread_t *) vout
297 {
298     p_vout = vout;
299
300     NSOpenGLPixelFormatAttribute attribs[] =
301     {
302         NSOpenGLPFAAccelerated,
303         NSOpenGLPFANoRecovery,
304         NSOpenGLPFAColorSize, 24,
305         NSOpenGLPFAAlphaSize, 8,
306         NSOpenGLPFADepthSize, 24,
307         NSOpenGLPFAWindow,
308         0
309     };
310
311     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
312         initWithAttributes: attribs];
313
314     if( !fmt )
315     {
316         msg_Warn( p_vout, "could not create OpenGL video output" );
317         return nil;
318     }
319
320     self = [super initWithFrame: NSMakeRect(0,0,10,10) pixelFormat: fmt];
321     [fmt release];
322
323     [[self openGLContext] makeCurrentContext];
324     [[self openGLContext] update];
325
326     /* Swap buffers only during the vertical retrace of the monitor.
327        http://developer.apple.com/documentation/GraphicsImaging/
328        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
329     long params[] = { 1 };
330     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
331                      params );
332     return self;
333 }
334
335 - (void) reshape
336 {
337     int x, y;
338     vlc_value_t val;
339
340     Lock( p_vout );
341     NSRect bounds = [self bounds];
342
343     [[self openGLContext] makeCurrentContext];
344
345     var_Get( p_vout, "macosx-stretch", &val );
346     if( val.b_bool )
347     {
348         x = bounds.size.width;
349         y = bounds.size.height;
350     }
351     else if( bounds.size.height * p_vout->fmt_in.i_visible_width *
352              p_vout->fmt_in.i_sar_num <
353              bounds.size.width * p_vout->fmt_in.i_visible_height *
354              p_vout->fmt_in.i_sar_den )
355     {
356         x = ( bounds.size.height * p_vout->fmt_in.i_visible_width *
357               p_vout->fmt_in.i_sar_num ) /
358             ( p_vout->fmt_in.i_visible_height * p_vout->fmt_in.i_sar_den);
359
360         y = bounds.size.height;
361     }
362     else
363     {
364         x = bounds.size.width;
365         y = ( bounds.size.width * p_vout->fmt_in.i_visible_height *
366               p_vout->fmt_in.i_sar_den) /
367             ( p_vout->fmt_in.i_visible_width * p_vout->fmt_in.i_sar_num  );
368     }
369
370     p_vout->p_sys->i_x = x;
371     p_vout->p_sys->i_y = y;
372
373     glViewport( ( bounds.size.width - x ) / 2,
374                 ( bounds.size.height - y ) / 2, x, y );
375
376     if( p_vout->p_sys->b_got_frame )
377     {
378         /* Ask the opengl module to redraw */
379         vout_thread_t * p_parent;
380         p_parent = (vout_thread_t *) p_vout->p_parent;
381         Unlock( p_vout );
382         if( p_parent && p_parent->pf_display )
383         {
384             p_parent->pf_display( p_parent, NULL );
385         }
386     }
387     else
388     {
389         glClear( GL_COLOR_BUFFER_BIT );
390         Unlock( p_vout );
391     }
392     [super reshape];
393 }
394
395 - (void) update
396 {
397     Lock( p_vout );
398     [super update];
399     Unlock( p_vout );
400 }
401
402 - (void) drawRect: (NSRect) rect
403 {
404     Lock( p_vout );
405     [[self openGLContext] makeCurrentContext];
406     glFlush();
407     [super drawRect:rect];
408     Unlock( p_vout );
409 }
410
411 @end
412
413