]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
macosx/vout*: fixed a possible segfault on vout close
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id$
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 <thedj@users.sourceforge.net>
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 <QuickTime/QuickTime.h>
36
37 #include <OpenGL/OpenGL.h>
38 #include <OpenGL/gl.h>
39 #include <OpenGL/glext.h>
40
41 #include <vlc_keys.h>
42
43 #include "intf.h"
44 #include "vout.h"
45
46 #define QT_MAX_DIRECTBUFFERS 10
47 #define VL_MAX_DISPLAYS 16
48
49 #define OPENGL_EFFECT_NONE             1
50 #define OPENGL_EFFECT_CUBE             2
51 #define OPENGL_EFFECT_TRANSPARENT_CUBE 4
52
53 struct picture_sys_t
54 {
55     void *p_info;
56     unsigned int i_size;
57
58     /* When using I420 output */
59     PlanarPixmapInfoYUV420 pixmap_i420;
60 };
61
62 /*****************************************************************************
63  * Local prototypes
64  *****************************************************************************/
65
66 static int  vout_Init      ( vout_thread_t * );
67 static void vout_End       ( vout_thread_t * );
68 static int  vout_Manage    ( vout_thread_t * );
69 static void vout_Display   ( vout_thread_t *, picture_t * );
70
71 static int  CoSendRequest      ( vout_thread_t *, SEL );
72 static int  CoCreateWindow     ( vout_thread_t * );
73 static int  CoDestroyWindow    ( vout_thread_t * );
74 static int  CoToggleFullscreen ( vout_thread_t * );
75
76 static void VLCHideMouse       ( vout_thread_t *, BOOL );
77
78 static void QTScaleMatrix      ( vout_thread_t * );
79 static int  QTCreateSequence   ( vout_thread_t * );
80 static void QTDestroySequence  ( vout_thread_t * );
81 static int  QTNewPicture       ( vout_thread_t *, picture_t * );
82 static void QTFreePicture      ( vout_thread_t *, picture_t * );
83
84 /*****************************************************************************
85  * OpenVideo: allocates MacOS X video thread output method
86  *****************************************************************************
87  * This function allocates and initializes a MacOS X vout method.
88  *****************************************************************************/
89 int E_(OpenVideo) ( vlc_object_t *p_this )
90 {   
91     vout_thread_t * p_vout = (vout_thread_t *)p_this;
92     OSErr err;
93     int i_timeout;
94     char *psz_vout_type;
95
96     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
97     if( p_vout->p_sys == NULL )
98     {
99         msg_Err( p_vout, "out of memory" );
100         return( 1 );
101     }
102
103     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
104
105     /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
106     for( i_timeout = 20 ; i_timeout-- ; )
107     {
108         if( NSApp == NULL )
109         {
110             msleep( INTF_IDLE_SLEEP );
111         }
112     }
113
114     if( NSApp == NULL )
115     {
116         /* no MacOS X intf, unable to communicate with MT */
117         msg_Err( p_vout, "no MacOS X interface present" );
118         free( p_vout->p_sys );
119         return( 1 );
120     }
121
122     if( [NSApp respondsToSelector: @selector(getIntf)] )
123     {
124         intf_thread_t * p_intf;
125
126         for( i_timeout = 10 ; i_timeout-- ; )
127         {
128             if( ( p_intf = [NSApp getIntf] ) == NULL )
129             {
130                 msleep( INTF_IDLE_SLEEP );
131             }
132         }
133
134         if( p_intf == NULL )
135         {
136             msg_Err( p_vout, "MacOS X intf has getIntf, but is NULL" );
137             free( p_vout->p_sys );
138             return( 1 );
139         }
140     }
141
142     p_vout->p_sys->b_mouse_moved = VLC_TRUE;
143     p_vout->p_sys->i_time_mouse_last_moved = mdate();
144
145     /* set window size */
146     p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
147     p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
148
149     /* Check if we should use QuickTime or OpenGL */
150     psz_vout_type = config_GetPsz( p_vout, "macosx-vout" );
151
152     if( !strncmp( psz_vout_type, "auto", 4 ) )
153     {
154         p_vout->p_sys->i_opengl = CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay );
155     }
156     else if( !strncmp( psz_vout_type, "opengl", 6 ) )
157     {
158         p_vout->p_sys->i_opengl = VLC_TRUE;
159     }
160     else
161     {
162         p_vout->p_sys->i_opengl = VLC_FALSE;
163     }
164     free( psz_vout_type );
165     
166     if( !p_vout->p_sys->i_opengl )
167     {
168         /* Initialize QuickTime */
169         p_vout->p_sys->h_img_descr = 
170             (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
171         p_vout->p_sys->p_matrix =
172             (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
173         p_vout->p_sys->p_fullscreen_state = NULL;
174
175         if( ( err = EnterMovies() ) != noErr )
176         {
177             msg_Err( p_vout, "EnterMovies failed: %d", err );
178             free( p_vout->p_sys->p_matrix );
179             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
180             free( p_vout->p_sys );
181             return( 1 );
182         } 
183
184         /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
185         vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
186
187         err = FindCodec( kYUV420CodecType, bestSpeedCodec,
188                             nil, &p_vout->p_sys->img_dc );
189         
190         vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
191         if( err == noErr && p_vout->p_sys->img_dc != 0 )
192         {
193             p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
194             p_vout->p_sys->i_codec = kYUV420CodecType;
195         }
196         else
197         {
198             msg_Err( p_vout, "failed to find an appropriate codec" );
199         }
200
201         if( p_vout->p_sys->img_dc == 0 )
202         {
203             free( p_vout->p_sys->p_matrix );
204             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
205             free( p_vout->p_sys );
206             return VLC_EGENERIC;        
207         }
208         msg_Dbg( p_vout, "using Quartz mode" );
209     }
210     else
211     {
212         msg_Dbg( p_vout, "using OpenGL mode" );
213     }
214
215     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
216     NSArray * o_screens = [NSScreen screens];
217     if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
218     {
219         int i = 1;
220         vlc_value_t val, text;
221         NSScreen * o_screen;
222
223         int i_option = config_GetInt( p_vout, "macosx-vdev" );
224
225         var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
226                                             VLC_VAR_HASCHOICE ); 
227         text.psz_string = _("Video device");
228         var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
229         
230         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
231
232         while( (o_screen = [o_enumerator nextObject]) != NULL )
233         {
234             char psz_temp[255];
235             NSRect s_rect = [o_screen frame];
236
237             snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1, 
238                       "%s %d (%dx%d)", _("Screen"), i,
239                       (int)s_rect.size.width, (int)s_rect.size.height ); 
240
241             text.psz_string = psz_temp;
242             val.i_int = i;
243             var_Change( p_vout, "video-device",
244                         VLC_VAR_ADDCHOICE, &val, &text );
245
246             if( ( i - 1 ) == i_option )
247             {
248                 var_Set( p_vout, "video-device", val );
249             }
250             i++;
251         }
252
253         var_AddCallback( p_vout, "video-device", vout_VarCallback,
254                          NULL );
255
256         val.b_bool = VLC_TRUE;
257         var_Set( p_vout, "intf-change", val );
258     }
259     [o_pool release];
260
261     if( CoCreateWindow( p_vout ) )
262     {
263         msg_Err( p_vout, "unable to create window" );
264         if( !p_vout->p_sys->i_opengl )
265         {
266             free( p_vout->p_sys->p_matrix );
267             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
268         }
269         free( p_vout->p_sys ); 
270         return( 1 );
271     }
272
273     p_vout->pf_init = vout_Init;
274     p_vout->pf_end = vout_End;
275     p_vout->pf_manage = vout_Manage;
276     p_vout->pf_render = NULL;
277     p_vout->pf_display = vout_Display;
278
279     return( 0 );
280 }
281
282 /*****************************************************************************
283  * vout_Init: initialize video thread output method
284  *****************************************************************************/
285 static int vout_InitOpenGL( vout_thread_t *p_vout );
286 static int vout_InitQuickTime( vout_thread_t *p_vout );
287 static int vout_Init( vout_thread_t *p_vout )
288 {
289     I_OUTPUTPICTURES = 0;
290
291     /* Initialize the output structure; we already found a codec,
292      * and the corresponding chroma we will be using. Since we can
293      * arbitrary scale, stick to the coordinates and aspect. */
294     p_vout->output.i_width  = p_vout->render.i_width;
295     p_vout->output.i_height = p_vout->render.i_height;
296     p_vout->output.i_aspect = p_vout->render.i_aspect;
297
298     if( p_vout->p_sys->i_opengl )
299     {
300         return vout_InitOpenGL( p_vout );
301     }
302     else
303     {
304         return vout_InitQuickTime( p_vout );    
305     }
306 }
307
308 static int vout_InitOpenGL( vout_thread_t *p_vout )
309 {
310     picture_t *p_pic;
311     int i_bytes;
312     int i_index;
313     
314     /* Apple OpenGL extensions only accept YUV as YUY2 */
315     p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
316     p_vout->output.i_rmask  = 0xFF0000;
317     p_vout->output.i_gmask  = 0x00FF00;
318     p_vout->output.i_bmask  = 0x0000FF;
319
320     /* Allocate our 2 picture buffers */
321     i_bytes = 2 * p_vout->output.i_width * p_vout->output.i_height;
322     p_vout->p_sys->p_data[0] = vlc_memalign(
323             &p_vout->p_sys->p_data_orig[0], 16, i_bytes );
324     p_vout->p_sys->p_data[1] = vlc_memalign(
325             &p_vout->p_sys->p_data_orig[1], 16, i_bytes );
326     p_vout->p_sys->i_cur_pic = 1;
327
328     /* We declare only one picture and will switch buffers
329        manually */
330     p_pic = NULL;
331     while( I_OUTPUTPICTURES < 1 )
332     {
333         /* Find an empty picture slot */
334         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
335         {
336             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
337             {
338                 p_pic = p_vout->p_picture + i_index;
339                 break;
340             }
341         }
342         if( p_pic == NULL )
343         {
344             break;
345         }
346
347         vout_InitPicture( VLC_OBJECT( p_vout ), p_pic,
348                 p_vout->output.i_chroma, p_vout->output.i_width,
349                 p_vout->output.i_height, p_vout->output.i_aspect );
350         p_pic->p_data = p_vout->p_sys->p_data[0];
351         p_pic->p[0].p_pixels = p_pic->p_data;
352         for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
353         {
354             p_pic->p[i_index].p_pixels =
355                 p_pic->p[i_index-1].p_pixels +
356                 p_pic->p[i_index-1].i_lines *
357                 p_pic->p[i_index-1].i_pitch;
358         }
359
360         p_pic->i_status = DESTROYED_PICTURE;
361         p_pic->i_type   = DIRECT_PICTURE;
362
363         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
364         I_OUTPUTPICTURES++;
365     }
366
367     [p_vout->p_sys->o_glview lockFocus];
368     [p_vout->p_sys->o_glview initTextures];
369     [p_vout->p_sys->o_glview reshape];
370     [p_vout->p_sys->o_glview unlockFocus];
371
372     return 0;
373 }
374
375 static int vout_InitQuickTime( vout_thread_t *p_vout )
376 {
377     picture_t *p_pic;
378     int i_index;
379
380     SetPort( p_vout->p_sys->p_qdport );
381     QTScaleMatrix( p_vout );
382
383     if( QTCreateSequence( p_vout ) )
384     {
385         msg_Err( p_vout, "unable to create sequence" );
386         return( 1 );
387     }
388
389     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
390     while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
391     {
392         p_pic = NULL;
393
394         /* Find an empty picture slot */
395         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
396         {
397             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
398             {
399                 p_pic = p_vout->p_picture + i_index;
400                 break;
401             }
402         }
403         if( p_pic == NULL )
404         {
405             break;
406         }
407
408         /* Allocate the picture */
409         if( QTNewPicture( p_vout, p_pic ) )
410         {
411             break;
412         }
413
414         p_pic->i_status = DESTROYED_PICTURE;
415         p_pic->i_type   = DIRECT_PICTURE;
416
417         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
418         I_OUTPUTPICTURES++;
419     }
420     return 0;
421 }
422
423 /*****************************************************************************
424  * vout_End: terminate video thread output method
425  *****************************************************************************/
426 static void vout_End( vout_thread_t *p_vout )
427 {
428     int i_index;
429
430     if( !p_vout->p_sys->i_opengl )
431     {
432         QTDestroySequence( p_vout );
433     }
434
435     /* Free the direct buffers we allocated */
436     for( i_index = I_OUTPUTPICTURES; i_index; )
437     {
438         i_index--;
439         if( !p_vout->p_sys->i_opengl )
440         {
441             QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
442         }
443         else
444         {
445             free( p_vout->p_sys->p_data_orig[0] );
446             free( p_vout->p_sys->p_data_orig[1] );
447         }
448     }
449 }
450
451 /*****************************************************************************
452  * CloseVideo: destroy video thread output method
453  *****************************************************************************/
454 void E_(CloseVideo) ( vlc_object_t *p_this )
455 {       
456     vout_thread_t * p_vout = (vout_thread_t *)p_this;     
457
458     if( p_vout->p_sys->i_opengl )
459     {
460         [p_vout->p_sys->o_glview cleanUp];
461     }
462
463     if( CoDestroyWindow( p_vout ) )
464     {
465         msg_Err( p_vout, "unable to destroy window" );
466     }
467
468     if ( p_vout->p_sys->p_fullscreen_state != NULL )
469     {
470         EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
471     }
472
473     if( !p_vout->p_sys->i_opengl )
474     {
475         ExitMovies();
476         free( p_vout->p_sys->p_matrix );
477         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
478     }
479
480     free( p_vout->p_sys );
481 }
482
483 /*****************************************************************************
484  * vout_Manage: handle events
485  *****************************************************************************
486  * This function should be called regularly by video output thread. It manages
487  * console events. It returns a non null value on error.
488  *****************************************************************************/
489 static int vout_Manage( vout_thread_t *p_vout )
490 {
491     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
492     {
493         if( CoToggleFullscreen( p_vout ) )  
494         {
495             return( 1 );
496         }
497
498         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
499     }
500
501     if( p_vout->i_changes & VOUT_SIZE_CHANGE ) 
502     {
503         if( !p_vout->p_sys->i_opengl )
504         {
505             QTScaleMatrix( p_vout );
506             SetDSequenceMatrix( p_vout->p_sys->i_seq, 
507                                 p_vout->p_sys->p_matrix );
508         }
509  
510         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
511     }
512
513     /* hide/show mouse cursor 
514      * this code looks unnecessarily complicated, but is necessary like this.
515      * it has to deal with multiple monitors and therefore checks a lot */
516     if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
517     {
518         if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
519         {
520             VLCHideMouse( p_vout, YES );
521         }
522     }
523     else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
524     {
525         VLCHideMouse( p_vout, NO );
526     }
527     
528     /* disable screen saver */
529     UpdateSystemActivity( UsrActivity );
530     
531     return( 0 );
532 }
533
534 /*****************************************************************************
535  * vout_Display: displays previously rendered output
536  *****************************************************************************
537  * This function sends the currently rendered image to the display.
538  *****************************************************************************/
539 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
540 {
541     if( !p_vout->p_sys->i_opengl )
542     {
543         OSErr err;
544         CodecFlags flags;
545
546         if( ( err = DecompressSequenceFrameS( 
547                         p_vout->p_sys->i_seq,
548                         p_pic->p_sys->p_info,
549                         p_pic->p_sys->i_size,                    
550                         codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
551         {
552             msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
553         }
554         else
555         {
556             QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
557         }
558     }
559     else
560     {
561         if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
562         {
563             int i_index;
564             int i_old = p_vout->p_sys->i_cur_pic;
565             int i_new = ( i_old + 1 ) % 2;
566  
567             /* Draw the new picture */
568             p_vout->p_sys->i_cur_pic = i_new;
569             [p_vout->p_sys->o_glview drawRect:
570                 [p_vout->p_sys->o_glview bounds]];
571            
572             /* Reload the other texture. Textures have to be reloaded
573                before the buffer is filled (thanks to gcc from
574                arstechnica forums) */
575             [p_vout->p_sys->o_glview reloadTexture: i_old];
576
577             /* Switch buffers */
578             p_pic->p_data = p_vout->p_sys->p_data[i_old];
579             p_pic->p[0].p_pixels = p_pic->p_data;
580             for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
581             {
582                 p_pic->p[i_index].p_pixels =
583                     p_pic->p[i_index-1].p_pixels +
584                     p_pic->p[i_index-1].i_lines *
585                     p_pic->p[i_index-1].i_pitch;
586             }
587             [p_vout->p_sys->o_glview unlockFocus];
588         }
589     }
590 }
591
592 /*****************************************************************************
593  * CoSendRequest: send request to interface thread
594  *****************************************************************************
595  * Returns 0 on success, 1 otherwise
596  *****************************************************************************/
597 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
598 {
599     int i_ret = 0;
600     vlc_value_t val;
601     intf_thread_t * p_intf;
602
603     VLCVout * o_vlv = [[VLCVout alloc] init];
604
605     if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
606     {
607         msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
608     }
609
610     [o_vlv release];
611
612     /*This makes this function dependant of the presence of a macosx 
613     interface. We do not check if this interface exists, since it has 
614     already been done before.*/
615
616     p_intf = [NSApp getIntf];
617
618     val.b_bool = VLC_TRUE;
619     var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
620     var_Set(p_intf, "intf-change",val);
621
622     return( i_ret );
623 }
624
625 /*****************************************************************************
626  * CoCreateWindow: create new window 
627  *****************************************************************************
628  * Returns 0 on success, 1 otherwise
629  *****************************************************************************/
630 static int CoCreateWindow( vout_thread_t *p_vout )
631 {
632     if( CoSendRequest( p_vout, @selector(createWindow:) ) )
633     {
634         msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
635         return( 1 );
636     }
637     
638     return( 0 );
639 }
640
641 /*****************************************************************************
642  * CoDestroyWindow: destroy window 
643  *****************************************************************************
644  * Returns 0 on success, 1 otherwise
645  *****************************************************************************/
646 static int CoDestroyWindow( vout_thread_t *p_vout )
647 {
648
649     VLCHideMouse( p_vout, NO );
650
651     if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
652     {
653         msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
654         return( 1 );
655     }
656
657     return( 0 );
658 }
659
660 /*****************************************************************************
661  * CoToggleFullscreen: toggle fullscreen 
662  *****************************************************************************
663  * Returns 0 on success, 1 otherwise
664  *****************************************************************************/
665 static int CoToggleFullscreen( vout_thread_t *p_vout )
666 {
667     if( !p_vout->p_sys->i_opengl )
668     {
669         QTDestroySequence( p_vout );
670     }
671
672     if( CoDestroyWindow( p_vout ) )
673     {
674         msg_Err( p_vout, "unable to destroy window" );
675         return( 1 );
676     }
677     
678     p_vout->b_fullscreen = !p_vout->b_fullscreen;
679
680     if( CoCreateWindow( p_vout ) )
681     {
682         msg_Err( p_vout, "unable to create window" );
683         return( 1 );
684     }
685
686     if( p_vout->p_sys->i_opengl )
687     {
688         [p_vout->p_sys->o_glview lockFocus];
689         [p_vout->p_sys->o_glview initTextures];
690         [p_vout->p_sys->o_glview reshape];
691         [p_vout->p_sys->o_glview drawRect:
692             [p_vout->p_sys->o_glview bounds]];
693         [p_vout->p_sys->o_glview unlockFocus];
694     }
695     else
696     {
697         SetPort( p_vout->p_sys->p_qdport );
698         QTScaleMatrix( p_vout );
699
700         if( QTCreateSequence( p_vout ) )
701         {
702             msg_Err( p_vout, "unable to create sequence" );
703             return( 1 ); 
704         } 
705     }
706
707     return( 0 );
708 }
709
710 /*****************************************************************************
711  * VLCHideMouse: if b_hide then hide the cursor
712  *****************************************************************************/
713 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
714 {
715     BOOL b_inside;
716     NSRect s_rect;
717     NSPoint ml;
718     NSWindow *o_window = p_vout->p_sys->o_window;
719     NSView *o_contents = [o_window contentView];
720     
721     s_rect = [o_contents bounds];
722     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
723     ml = [o_contents convertPoint:ml fromView:nil];
724     b_inside = [o_contents mouse: ml inRect: s_rect];
725     
726     if ( b_hide && b_inside )
727     {
728         /* only hide if mouse over VLCQTView */
729         [NSCursor setHiddenUntilMouseMoves: YES];
730     }
731     else if ( !b_hide )
732     {
733         [NSCursor setHiddenUntilMouseMoves: NO];
734     }
735     p_vout->p_sys->b_mouse_moved = NO;
736     p_vout->p_sys->i_time_mouse_last_moved = mdate();
737     return;
738 }
739
740 /*****************************************************************************
741  * QTScaleMatrix: scale matrix 
742  *****************************************************************************/
743 static void QTScaleMatrix( vout_thread_t *p_vout )
744 {
745     Rect s_rect;
746     unsigned int i_width, i_height;
747     Fixed factor_x, factor_y;
748     unsigned int i_offset_x = 0;
749     unsigned int i_offset_y = 0;
750
751     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
752
753     i_width = s_rect.right - s_rect.left;
754     i_height = s_rect.bottom - s_rect.top;
755
756     if( config_GetInt( p_vout, "macosx-stretch" ) )
757     {
758         factor_x = FixDiv( Long2Fix( i_width ),
759                            Long2Fix( p_vout->output.i_width ) );
760         factor_y = FixDiv( Long2Fix( i_height ),
761                            Long2Fix( p_vout->output.i_height ) );
762                            
763     }
764     else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
765     {
766         int i_adj_width = i_height * p_vout->output.i_aspect /
767                           VOUT_ASPECT_FACTOR;
768
769         factor_x = FixDiv( Long2Fix( i_adj_width ),
770                            Long2Fix( p_vout->output.i_width ) );
771         factor_y = FixDiv( Long2Fix( i_height ),
772                            Long2Fix( p_vout->output.i_height ) );
773
774         i_offset_x = (i_width - i_adj_width) / 2;
775     }
776     else
777     {
778         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
779                            p_vout->output.i_aspect;
780
781         factor_x = FixDiv( Long2Fix( i_width ),
782                            Long2Fix( p_vout->output.i_width ) );
783         factor_y = FixDiv( Long2Fix( i_adj_height ),
784                            Long2Fix( p_vout->output.i_height ) );
785
786         i_offset_y = (i_height - i_adj_height) / 2;
787     }
788     
789     SetIdentityMatrix( p_vout->p_sys->p_matrix );
790
791     ScaleMatrix( p_vout->p_sys->p_matrix,
792                  factor_x, factor_y,
793                  Long2Fix(0), Long2Fix(0) );
794                  
795     TranslateMatrix( p_vout->p_sys->p_matrix,
796                  Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
797 }
798
799 /*****************************************************************************
800  * QTCreateSequence: create a new sequence 
801  *****************************************************************************
802  * Returns 0 on success, 1 otherwise
803  *****************************************************************************/
804 static int QTCreateSequence( vout_thread_t *p_vout )
805 {
806     OSErr err;
807     ImageDescriptionPtr p_descr;
808
809     HLock( (Handle)p_vout->p_sys->h_img_descr );
810     p_descr = *p_vout->p_sys->h_img_descr;
811
812     p_descr->idSize = sizeof(ImageDescription);
813     p_descr->cType = p_vout->p_sys->i_codec;
814     p_descr->version = 1;
815     p_descr->revisionLevel = 0;
816     p_descr->vendor = 'appl';
817     p_descr->width = p_vout->output.i_width;
818     p_descr->height = p_vout->output.i_height;
819     p_descr->hRes = Long2Fix(72);
820     p_descr->vRes = Long2Fix(72);
821     p_descr->spatialQuality = codecLosslessQuality;
822     p_descr->frameCount = 1;
823     p_descr->clutID = -1;
824     p_descr->dataSize = 0;
825     p_descr->depth = 24;
826
827     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
828
829     if( ( err = DecompressSequenceBeginS( 
830                               &p_vout->p_sys->i_seq,
831                               p_vout->p_sys->h_img_descr,
832                               NULL, 0,
833                               p_vout->p_sys->p_qdport,
834                               NULL, NULL,
835                               p_vout->p_sys->p_matrix,
836                               0, NULL,
837                               codecFlagUseImageBuffer,
838                               codecLosslessQuality,
839                               p_vout->p_sys->img_dc ) ) )
840     {
841         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
842         return( 1 );
843     }
844
845     return( 0 );
846 }
847
848 /*****************************************************************************
849  * QTDestroySequence: destroy sequence 
850  *****************************************************************************/
851 static void QTDestroySequence( vout_thread_t *p_vout )
852 {
853     CDSequenceEnd( p_vout->p_sys->i_seq );
854 }
855
856 /*****************************************************************************
857  * QTNewPicture: allocate a picture
858  *****************************************************************************
859  * Returns 0 on success, 1 otherwise
860  *****************************************************************************/
861 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
862 {
863     int i_width  = p_vout->output.i_width;
864     int i_height = p_vout->output.i_height;
865
866     /* We know the chroma, allocate a buffer which will be used
867      * directly by the decoder */
868     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
869
870     if( p_pic->p_sys == NULL )
871     {
872         return( -1 );
873     }
874
875     switch( p_vout->output.i_chroma )
876     {
877         case VLC_FOURCC('I','4','2','0'):
878
879             p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
880             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
881
882             /* Allocate the memory buffer */
883             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
884                                           16, i_width * i_height * 3 / 2 );
885
886             /* Y buffer */
887             p_pic->Y_PIXELS = p_pic->p_data; 
888             p_pic->p[Y_PLANE].i_lines = i_height;
889             p_pic->p[Y_PLANE].i_pitch = i_width;
890             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
891             p_pic->p[Y_PLANE].i_visible_pitch = i_width;
892
893             /* U buffer */
894             p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
895             p_pic->p[U_PLANE].i_lines = i_height / 2;
896             p_pic->p[U_PLANE].i_pitch = i_width / 2;
897             p_pic->p[U_PLANE].i_pixel_pitch = 1;
898             p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
899
900             /* V buffer */
901             p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
902             p_pic->p[V_PLANE].i_lines = i_height / 2;
903             p_pic->p[V_PLANE].i_pitch = i_width / 2;
904             p_pic->p[V_PLANE].i_pixel_pitch = 1;
905             p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
906
907             /* We allocated 3 planes */
908             p_pic->i_planes = 3;
909
910 #define P p_pic->p_sys->pixmap_i420
911             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
912                                        - p_pic->p_sys->p_info;
913             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
914                                         - p_pic->p_sys->p_info;
915             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
916                                         - p_pic->p_sys->p_info;
917
918             P.componentInfoY.rowBytes = i_width;
919             P.componentInfoCb.rowBytes = i_width / 2;
920             P.componentInfoCr.rowBytes = i_width / 2;
921 #undef P
922
923             break;
924
925     default:
926         /* Unknown chroma, tell the guy to get lost */
927         free( p_pic->p_sys );
928         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
929                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
930         p_pic->i_planes = 0;
931         return( -1 );
932     }
933
934     return( 0 );
935 }
936
937 /*****************************************************************************
938  * QTFreePicture: destroy a picture allocated with QTNewPicture
939  *****************************************************************************/
940 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
941 {
942     switch( p_vout->output.i_chroma )
943     {
944         case VLC_FOURCC('I','4','2','0'):
945             free( p_pic->p_data_orig );
946             break;
947     }
948
949     free( p_pic->p_sys );
950 }
951
952 /*****************************************************************************
953  * VLCWindow implementation
954  *****************************************************************************/
955 @implementation VLCWindow
956
957 - (void)setVout:(vout_thread_t *)_p_vout
958 {
959     p_vout = _p_vout;
960 }
961
962 - (vout_thread_t *)getVout
963 {
964     return( p_vout );
965 }
966
967 - (void)scaleWindowWithFactor: (float)factor
968 {
969     NSSize newsize;
970     int i_corrected_height, i_corrected_width;
971     NSPoint topleftbase;
972     NSPoint topleftscreen;
973     
974     if ( !p_vout->b_fullscreen )
975     {
976         topleftbase.x = 0;
977         topleftbase.y = [self frame].size.height;
978         topleftscreen = [self convertBaseToScreen: topleftbase];
979         
980         if( p_vout->output.i_height * p_vout->output.i_aspect > 
981                         p_vout->output.i_width * VOUT_ASPECT_FACTOR )
982         {
983             i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
984                                             VOUT_ASPECT_FACTOR;
985             newsize.width = (int) ( i_corrected_width * factor );
986             newsize.height = (int) ( p_vout->render.i_height * factor );
987         }
988         else
989         {
990             i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
991                                             p_vout->output.i_aspect;
992             newsize.width = (int) ( p_vout->render.i_width * factor );
993             newsize.height = (int) ( i_corrected_height * factor );
994         }
995     
996         [self setContentSize: newsize];
997         
998         [self setFrameTopLeftPoint: topleftscreen];
999         p_vout->i_changes |= VOUT_SIZE_CHANGE;
1000     }
1001 }
1002
1003 - (void)toggleFloatOnTop
1004 {
1005     vlc_value_t val;
1006     if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
1007     {
1008         val.b_bool = VLC_FALSE;
1009         var_Set( p_vout, "video-on-top", val );
1010         [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
1011     }
1012     else
1013     {
1014         val.b_bool = VLC_TRUE;
1015         var_Set( p_vout, "video-on-top", val );
1016         [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1017     }
1018 }
1019
1020 - (void)toggleFullscreen
1021 {
1022     vlc_value_t val;
1023     val.b_bool = !p_vout->b_fullscreen;
1024     var_Set( p_vout, "fullscreen", val );
1025 }
1026
1027 - (BOOL)isFullscreen
1028 {
1029     return( p_vout->b_fullscreen );
1030 }
1031
1032 - (BOOL)canBecomeKeyWindow
1033 {
1034     return( YES );
1035 }
1036
1037 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1038 {
1039     return [(VLCApplication *) [VLCApplication sharedApplication]
1040             hasDefinedShortcutKey:o_event];
1041 }
1042
1043 - (void)keyDown:(NSEvent *)o_event
1044 {
1045     unichar key = 0;
1046     vlc_value_t val;
1047     unsigned int i_pressed_modifiers = 0;
1048     val.i_int = 0;
1049     
1050     i_pressed_modifiers = [o_event modifierFlags];
1051
1052     if( i_pressed_modifiers & NSShiftKeyMask )
1053         val.i_int |= KEY_MODIFIER_SHIFT;
1054     if( i_pressed_modifiers & NSControlKeyMask )
1055         val.i_int |= KEY_MODIFIER_CTRL;
1056     if( i_pressed_modifiers & NSAlternateKeyMask )
1057         val.i_int |= KEY_MODIFIER_ALT;
1058     if( i_pressed_modifiers & NSCommandKeyMask )
1059         val.i_int |= KEY_MODIFIER_COMMAND;
1060
1061     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1062
1063     if( key )
1064     {
1065         /* Escape should always get you out of fullscreen */
1066         if( key == (unichar) 0x1b )
1067         {
1068              if( [self isFullscreen] )
1069              {
1070                  [self toggleFullscreen];
1071              }
1072         }
1073         else if ( key == ' ' )
1074         {
1075              playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1076                                                      FIND_ANYWHERE );
1077              if ( p_playlist != NULL )
1078              {
1079                  playlist_Pause( p_playlist );
1080                  vlc_object_release( p_playlist);
1081              }
1082         }
1083         else
1084         {
1085             val.i_int |= CocoaKeyToVLC( key );
1086             var_Set( p_vout->p_vlc, "key-pressed", val );
1087         }
1088     }
1089     else
1090     {
1091         [super keyDown: o_event];
1092     }
1093 }
1094
1095 - (void)updateTitle
1096 {
1097     NSMutableString * o_title;
1098     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1099                                                        FIND_ANYWHERE );
1100     
1101     if( p_playlist == NULL )
1102     {
1103         return;
1104     }
1105
1106     vlc_mutex_lock( &p_playlist->object_lock );
1107     o_title = [NSMutableString stringWithUTF8String: 
1108         p_playlist->pp_items[p_playlist->i_index]->input.psz_uri]; 
1109     vlc_mutex_unlock( &p_playlist->object_lock );
1110
1111     vlc_object_release( p_playlist );
1112
1113     if( o_title != nil )
1114     {
1115         NSRange prefix_range = [o_title rangeOfString: @"file:"];
1116         if( prefix_range.location != NSNotFound )
1117         {
1118             [o_title deleteCharactersInRange: prefix_range];
1119         }
1120
1121         [self setTitleWithRepresentedFilename: o_title];
1122     }
1123     else
1124     {
1125         [self setTitle:
1126             [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1127     }
1128 }
1129
1130 /* This is actually the same as VLCControls::stop. */
1131 - (BOOL)windowShouldClose:(id)sender
1132 {
1133     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1134                                                        FIND_ANYWHERE );
1135     if( p_playlist == NULL )      
1136     {
1137         return NO;
1138     }
1139
1140     playlist_Stop( p_playlist );
1141     vlc_object_release( p_playlist );
1142
1143     /* The window will be closed by the intf later. */
1144     return NO;
1145 }
1146
1147 @end
1148
1149 /* Common QT and OpenGL code to catch mouse events */
1150 #define CATCH_MOUSE_EVENTS \
1151 - (BOOL)acceptsFirstResponder \
1152 { \
1153     return( YES ); \
1154 } \
1155  \
1156 - (BOOL)becomeFirstResponder \
1157 { \
1158     id o_window = [self window]; \
1159      \
1160     [o_window setAcceptsMouseMovedEvents: YES]; \
1161     return( YES ); \
1162 } \
1163  \
1164 - (BOOL)resignFirstResponder \
1165 { \
1166     vout_thread_t * vout; \
1167     id o_window = [self window]; \
1168     vout = (vout_thread_t *)[o_window getVout]; \
1169      \
1170     [o_window setAcceptsMouseMovedEvents: NO]; \
1171     VLCHideMouse( vout, NO ); \
1172     return( YES ); \
1173 } \
1174  \
1175 - (void)mouseDown:(NSEvent *)o_event \
1176 { \
1177     vout_thread_t * vout; \
1178     id o_window = [self window]; \
1179     vout = (vout_thread_t *)[o_window getVout]; \
1180     vlc_value_t val; \
1181  \
1182     switch( [o_event type] ) \
1183     {         \
1184         case NSLeftMouseDown: \
1185         { \
1186             var_Get( vout, "mouse-button-down", &val ); \
1187             val.i_int |= 1; \
1188             var_Set( vout, "mouse-button-down", val ); \
1189         } \
1190         break; \
1191          \
1192         default: \
1193             [super mouseDown: o_event]; \
1194         break; \
1195     } \
1196 } \
1197  \
1198 - (void)otherMouseDown:(NSEvent *)o_event \
1199 { \
1200     vout_thread_t * vout; \
1201     id o_window = [self window]; \
1202     vout = (vout_thread_t *)[o_window getVout]; \
1203     vlc_value_t val; \
1204  \
1205     switch( [o_event type] ) \
1206     { \
1207         case NSOtherMouseDown: \
1208         { \
1209             var_Get( vout, "mouse-button-down", &val ); \
1210             val.i_int |= 2; \
1211             var_Set( vout, "mouse-button-down", val ); \
1212         } \
1213         break; \
1214          \
1215         default: \
1216             [super mouseDown: o_event]; \
1217         break; \
1218     } \
1219 } \
1220  \
1221 - (void)rightMouseDown:(NSEvent *)o_event \
1222 { \
1223     vout_thread_t * vout; \
1224     id o_window = [self window]; \
1225     vout = (vout_thread_t *)[o_window getVout]; \
1226     vlc_value_t val; \
1227  \
1228     switch( [o_event type] ) \
1229     { \
1230         case NSRightMouseDown: \
1231         { \
1232             var_Get( vout, "mouse-button-down", &val ); \
1233             val.i_int |= 4; \
1234             var_Set( vout, "mouse-button-down", val ); \
1235         } \
1236         break; \
1237          \
1238         default: \
1239             [super mouseDown: o_event]; \
1240         break; \
1241     } \
1242 } \
1243  \
1244 - (void)mouseUp:(NSEvent *)o_event \
1245 { \
1246     vout_thread_t * vout; \
1247     id o_window = [self window]; \
1248     vout = (vout_thread_t *)[o_window getVout]; \
1249     vlc_value_t val; \
1250  \
1251     switch( [o_event type] ) \
1252     { \
1253         case NSLeftMouseUp: \
1254         { \
1255             vlc_value_t b_val; \
1256             b_val.b_bool = VLC_TRUE; \
1257             var_Set( vout, "mouse-clicked", b_val ); \
1258              \
1259             var_Get( vout, "mouse-button-down", &val ); \
1260             val.i_int &= ~1; \
1261             var_Set( vout, "mouse-button-down", val ); \
1262         } \
1263         break; \
1264                  \
1265         default: \
1266             [super mouseUp: o_event]; \
1267         break; \
1268     } \
1269 } \
1270  \
1271 - (void)otherMouseUp:(NSEvent *)o_event \
1272 { \
1273     vout_thread_t * vout; \
1274     id o_window = [self window]; \
1275     vout = (vout_thread_t *)[o_window getVout]; \
1276     vlc_value_t val; \
1277  \
1278     switch( [o_event type] ) \
1279     { \
1280         case NSOtherMouseUp: \
1281         { \
1282             var_Get( vout, "mouse-button-down", &val ); \
1283             val.i_int &= ~2; \
1284             var_Set( vout, "mouse-button-down", val ); \
1285         } \
1286         break; \
1287                  \
1288         default: \
1289             [super mouseUp: o_event]; \
1290         break; \
1291     } \
1292 } \
1293  \
1294 - (void)rightMouseUp:(NSEvent *)o_event \
1295 { \
1296     vout_thread_t * vout; \
1297     id o_window = [self window]; \
1298     vout = (vout_thread_t *)[o_window getVout]; \
1299     vlc_value_t val; \
1300  \
1301     switch( [o_event type] ) \
1302     { \
1303         case NSRightMouseUp: \
1304         { \
1305             var_Get( vout, "mouse-button-down", &val ); \
1306             val.i_int &= ~4; \
1307             var_Set( vout, "mouse-button-down", val ); \
1308         } \
1309         break; \
1310          \
1311         default: \
1312             [super mouseUp: o_event]; \
1313         break; \
1314     } \
1315 } \
1316  \
1317 - (void)mouseDragged:(NSEvent *)o_event \
1318 { \
1319     [self mouseMoved:o_event]; \
1320 } \
1321  \
1322 - (void)otherMouseDragged:(NSEvent *)o_event \
1323 { \
1324     [self mouseMoved:o_event]; \
1325 } \
1326  \
1327 - (void)rightMouseDragged:(NSEvent *)o_event \
1328 { \
1329     [self mouseMoved:o_event]; \
1330 }
1331
1332 /*****************************************************************************
1333  * VLCQTView implementation
1334  *****************************************************************************/
1335 @implementation VLCQTView
1336
1337 - (void)drawRect:(NSRect)rect
1338 {
1339     vout_thread_t * p_vout;
1340     id o_window = [self window];
1341     p_vout = (vout_thread_t *)[o_window getVout];
1342     
1343     [[NSColor blackColor] set];
1344     NSRectFill( rect );
1345     [super drawRect: rect];
1346
1347     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1348 }
1349
1350 CATCH_MOUSE_EVENTS
1351
1352 - (void)mouseMoved:(NSEvent *)o_event
1353 {
1354     NSPoint ml;
1355     NSRect s_rect;
1356     BOOL b_inside;
1357
1358     vout_thread_t * p_vout;
1359     id o_window = [self window];
1360     p_vout = (vout_thread_t *)[o_window getVout];
1361
1362     s_rect = [self bounds];
1363     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1364     b_inside = [self mouse: ml inRect: s_rect];
1365
1366     if( b_inside )
1367     {
1368         vlc_value_t val;
1369         int i_width, i_height, i_x, i_y;
1370         
1371         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1372                                    (unsigned int)s_rect.size.height,
1373                                    &i_x, &i_y, &i_width, &i_height );
1374
1375         val.i_int = ( ((int)ml.x) - i_x ) * 
1376                     p_vout->render.i_width / i_width;
1377         var_Set( p_vout, "mouse-x", val );
1378
1379         val.i_int = ( ((int)ml.y) - i_y ) * 
1380                     p_vout->render.i_height / i_height;
1381         var_Set( p_vout, "mouse-y", val );
1382             
1383         val.b_bool = VLC_TRUE;
1384         var_Set( p_vout, "mouse-moved", val );
1385         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1386         p_vout->p_sys->b_mouse_moved = YES;
1387     }
1388
1389     [super mouseMoved: o_event];
1390 }
1391
1392 @end
1393
1394 /*****************************************************************************
1395  * VLCGLView implementation
1396  *****************************************************************************/
1397 @implementation VLCGLView
1398
1399
1400 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1401 {
1402     char * psz_effect;
1403     p_vout = _p_vout;
1404     
1405     NSOpenGLPixelFormatAttribute attribs[] =
1406     {
1407         NSOpenGLPFAAccelerated,
1408         NSOpenGLPFANoRecovery,
1409         NSOpenGLPFAColorSize, 24,
1410         NSOpenGLPFAAlphaSize, 8,
1411         NSOpenGLPFADepthSize, 24,
1412         NSOpenGLPFAWindow,
1413         0
1414     };
1415
1416     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1417         initWithAttributes: attribs];
1418
1419     if( !fmt )
1420     {
1421         msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1422         return nil;
1423     }
1424
1425     self = [super initWithFrame:frame pixelFormat: fmt];
1426     [fmt release];
1427
1428     [[self openGLContext] makeCurrentContext];
1429     [[self openGLContext] update];
1430
1431     /* Black background */
1432     glClearColor( 0.0, 0.0, 0.0, 0.0 );
1433
1434     /* Check if the user asked for useless visual effects */
1435     psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" );
1436     if( !psz_effect || !strcmp( psz_effect, "none" ))
1437     {
1438         i_effect = OPENGL_EFFECT_NONE;
1439     }
1440     else if( !strcmp( psz_effect, "cube" ) )
1441     {
1442         i_effect = OPENGL_EFFECT_CUBE;
1443
1444         glEnable( GL_DEPTH_TEST );
1445     }
1446     else if( !strcmp( psz_effect, "transparent-cube" ) )
1447     {
1448         i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1449
1450         glDisable( GL_DEPTH_TEST );
1451         glEnable( GL_BLEND );
1452         glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1453     }
1454     else
1455     {
1456         msg_Warn( p_vout, "no valid opengl effect provided, using "
1457                   "\"none\"" );
1458         i_effect = OPENGL_EFFECT_NONE;
1459     }
1460
1461     if( i_effect & ( OPENGL_EFFECT_CUBE |
1462                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1463     {
1464         /* Set the perpective */
1465         glMatrixMode( GL_PROJECTION );
1466         glLoadIdentity();
1467         glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1468         glMatrixMode( GL_MODELVIEW );
1469         glLoadIdentity();
1470         glTranslatef( 0.0, 0.0, - 5.0 );
1471     }
1472
1473     pi_textures[0] = 0;
1474     pi_textures[1] = 0;
1475     initDone       = 0;
1476
1477     return self;
1478 }
1479
1480 - (void) reshape
1481 {
1482     if( !initDone )
1483     {
1484         return;
1485     }
1486     
1487     [[self openGLContext] makeCurrentContext];
1488
1489     NSRect bounds = [self bounds];
1490     glViewport( 0, 0, (GLint) bounds.size.width,
1491                 (GLint) bounds.size.height );
1492
1493     if( config_GetInt( p_vout, "macosx-stretch" ) )
1494     {
1495         f_x = 1.0;
1496         f_y = 1.0;
1497     }
1498     else
1499     {
1500         /* Quad size is set in order to preserve the aspect ratio */
1501         int fill = ( config_GetInt( p_vout, "macosx-fill" ) &&
1502                      p_vout->b_fullscreen );
1503         int large = ( bounds.size.height * p_vout->output.i_aspect <
1504                       bounds.size.width * VOUT_ASPECT_FACTOR );
1505         if( ( large && !fill ) || ( !large && fill ) )
1506         {
1507             f_x = bounds.size.height * p_vout->output.i_aspect /
1508                 VOUT_ASPECT_FACTOR / bounds.size.width;
1509             f_y = 1.0;
1510         }
1511         else
1512         {
1513             f_x = 1.0;
1514             f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1515                 p_vout->output.i_aspect / bounds.size.height;
1516         }
1517     }
1518 }
1519
1520 - (void) initTextures
1521 {
1522     int i;
1523     [[self openGLContext] makeCurrentContext];
1524
1525     /* Free previous texture if any */
1526     if( initDone )
1527     {
1528         glDeleteTextures( 2, pi_textures );
1529     }
1530
1531     glEnable( GL_TEXTURE_RECTANGLE_EXT );
1532     glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1533
1534     glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1535     glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1536
1537     /* Tell the driver not to make a copy of the texture but to use
1538        our buffer */
1539     glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1540
1541     /* Create textures */
1542     glGenTextures( 2, pi_textures );
1543
1544     for( i = 0; i < 2; i++ )
1545     {
1546         glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1547         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1548
1549         /* Linear interpolation */
1550         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1551                 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1552         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1553                 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1554
1555         /* Use VRAM texturing */
1556         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1557                 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1558
1559         glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB8,
1560                 p_vout->output.i_width, p_vout->output.i_height, 0,
1561                 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1562                 p_vout->p_sys->p_data[i] );
1563     }
1564
1565     /* Swap buffers only during the vertical retrace of the monitor.
1566        http://developer.apple.com/documentation/GraphicsImaging/
1567        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1568     long params[] = { 1 };
1569     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1570                      params );
1571
1572     initDone = 1;
1573 }
1574
1575 - (void)reloadTexture: (int) index
1576 {
1577     if( !initDone )
1578     {
1579         return;
1580     }
1581     
1582     [[self openGLContext] makeCurrentContext];
1583
1584     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1585     glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1586
1587     /* glTexSubImage2D is faster than glTexImage2D
1588        http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1589        TextureRange/MainOpenGLView.m.htm */
1590     glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1591             p_vout->output.i_width, p_vout->output.i_height,
1592             GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1593             p_vout->p_sys->p_data[index] );
1594 }
1595
1596 - (void) cleanUp
1597 {
1598     initDone = 0;
1599 }
1600
1601 - (void) drawQuad
1602 {
1603     glBegin( GL_QUADS );
1604         /* Top left */
1605         glTexCoord2f( 0.0, 0.0 );
1606         glVertex2f( - f_x, f_y );
1607         /* Bottom left */
1608         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1609         glVertex2f( - f_x, - f_y );
1610         /* Bottom right */
1611         glTexCoord2f( (float) p_vout->output.i_width,
1612                       (float) p_vout->output.i_height );
1613         glVertex2f( f_x, - f_y );
1614         /* Top right */
1615         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1616         glVertex2f( f_x, f_y );
1617     glEnd();
1618 }
1619
1620 - (void) drawCube
1621 {
1622     glBegin( GL_QUADS );
1623         /* Front */
1624         glTexCoord2f( 0.0, 0.0 );
1625         glVertex3f( - 1.0, 1.0, 1.0 );
1626         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1627         glVertex3f( - 1.0, - 1.0, 1.0 );
1628         glTexCoord2f( (float) p_vout->output.i_width,
1629                       (float) p_vout->output.i_height );
1630         glVertex3f( 1.0, - 1.0, 1.0 );
1631         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1632         glVertex3f( 1.0, 1.0, 1.0 );
1633
1634         /* Left */
1635         glTexCoord2f( 0.0, 0.0 );
1636         glVertex3f( - 1.0, 1.0, - 1.0 );
1637         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1638         glVertex3f( - 1.0, - 1.0, - 1.0 );
1639         glTexCoord2f( (float) p_vout->output.i_width,
1640                       (float) p_vout->output.i_height );
1641         glVertex3f( - 1.0, - 1.0, 1.0 );
1642         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1643         glVertex3f( - 1.0, 1.0, 1.0 );
1644
1645         /* Back */
1646         glTexCoord2f( 0.0, 0.0 );
1647         glVertex3f( 1.0, 1.0, - 1.0 );
1648         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1649         glVertex3f( 1.0, - 1.0, - 1.0 );
1650         glTexCoord2f( (float) p_vout->output.i_width,
1651                       (float) p_vout->output.i_height );
1652         glVertex3f( - 1.0, - 1.0, - 1.0 );
1653         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1654         glVertex3f( - 1.0, 1.0, - 1.0 );
1655
1656         /* Right */
1657         glTexCoord2f( 0.0, 0.0 );
1658         glVertex3f( 1.0, 1.0, 1.0 );
1659         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1660         glVertex3f( 1.0, - 1.0, 1.0 );
1661         glTexCoord2f( (float) p_vout->output.i_width,
1662                       (float) p_vout->output.i_height );
1663         glVertex3f( 1.0, - 1.0, - 1.0 );
1664         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1665         glVertex3f( 1.0, 1.0, - 1.0 );
1666
1667         /* Top */
1668         glTexCoord2f( 0.0, 0.0 );
1669         glVertex3f( - 1.0, 1.0, - 1.0 );
1670         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1671         glVertex3f( - 1.0, 1.0, 1.0 );
1672         glTexCoord2f( (float) p_vout->output.i_width,
1673                       (float) p_vout->output.i_height );
1674         glVertex3f( 1.0, 1.0, 1.0 );
1675         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1676         glVertex3f( 1.0, 1.0, - 1.0 );
1677
1678         /* Bottom */
1679         glTexCoord2f( 0.0, 0.0 );
1680         glVertex3f( - 1.0, - 1.0, 1.0 );
1681         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1682         glVertex3f( - 1.0, - 1.0, - 1.0 );
1683         glTexCoord2f( (float) p_vout->output.i_width,
1684                       (float) p_vout->output.i_height );
1685         glVertex3f( 1.0, - 1.0, - 1.0 );
1686         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1687         glVertex3f( 1.0, - 1.0, 1.0 );
1688     glEnd();
1689 }
1690
1691 - (void) drawRect: (NSRect) rect
1692 {
1693     [[self openGLContext] makeCurrentContext];
1694
1695     /* Black background */
1696     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1697
1698     if( !initDone )
1699     {
1700         glFlush();
1701         return;
1702     }
1703
1704     /* Draw */
1705     glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1706                    pi_textures[p_vout->p_sys->i_cur_pic] );
1707     if( i_effect & ( OPENGL_EFFECT_CUBE |
1708                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1709     {
1710         glRotatef( 1.0, 0.3, 0.5, 0.7 );
1711         [self drawCube];
1712     }
1713     else
1714     {
1715         [self drawQuad];
1716     }
1717
1718     /* Draw */
1719     glFlush();
1720 }
1721
1722 CATCH_MOUSE_EVENTS
1723
1724 - (void)mouseMoved:(NSEvent *)o_event
1725 {
1726     NSPoint ml;
1727     NSRect s_rect;
1728     BOOL b_inside;
1729
1730     s_rect = [self bounds];
1731     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1732     b_inside = [self mouse: ml inRect: s_rect];
1733
1734     if( b_inside )
1735     {
1736         vlc_value_t val;
1737         int i_width, i_height, i_x, i_y;
1738         
1739         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1740                                    (unsigned int)s_rect.size.height,
1741                                    &i_x, &i_y, &i_width, &i_height );
1742
1743         val.i_int = ( (int)ml.x - i_x ) * 
1744                     p_vout->render.i_width / i_width;
1745         var_Set( p_vout, "mouse-x", val );
1746
1747         /* Y coordinate is inverted in OpenGL */
1748         val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
1749                     p_vout->render.i_height / i_height;
1750         var_Set( p_vout, "mouse-y", val );
1751             
1752         val.b_bool = VLC_TRUE;
1753         var_Set( p_vout, "mouse-moved", val );
1754         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1755         p_vout->p_sys->b_mouse_moved = YES;
1756     }
1757
1758     [super mouseMoved: o_event];
1759 }
1760
1761 @end
1762
1763 /*****************************************************************************
1764  * VLCVout implementation
1765  *****************************************************************************/
1766 @implementation VLCVout
1767
1768 - (void)createWindow:(NSValue *)o_value
1769 {
1770     vlc_value_t val;
1771     VLCQTView * o_view;
1772     NSScreen * o_screen;
1773     vout_thread_t * p_vout;
1774     vlc_bool_t b_main_screen;
1775     
1776     p_vout = (vout_thread_t *)[o_value pointerValue];
1777
1778     p_vout->p_sys->o_window = [VLCWindow alloc];
1779     [p_vout->p_sys->o_window setVout: p_vout];
1780     [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1781
1782     if( var_Get( p_vout, "video-device", &val ) < 0 )
1783     {
1784         o_screen = [NSScreen mainScreen];
1785         b_main_screen = 1;
1786     }
1787     else
1788     {
1789         NSArray *o_screens = [NSScreen screens];
1790         unsigned int i_index = val.i_int;
1791         
1792         if( [o_screens count] < i_index )
1793         {
1794             o_screen = [NSScreen mainScreen];
1795             b_main_screen = 1;
1796         }
1797         else
1798         {
1799             i_index--;
1800             o_screen = [o_screens objectAtIndex: i_index];
1801             config_PutInt( p_vout, "macosx-vdev", i_index );
1802             b_main_screen = (i_index == 0);
1803         }
1804     } 
1805
1806     if( p_vout->b_fullscreen )
1807     {
1808         NSRect screen_rect = [o_screen frame];
1809         screen_rect.origin.x = screen_rect.origin.y = 0;
1810
1811         if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1812             BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1813                              NULL, NULL, fullScreenAllowEvents );
1814
1815         [p_vout->p_sys->o_window 
1816             initWithContentRect: screen_rect
1817             styleMask: NSBorderlessWindowMask
1818             backing: NSBackingStoreBuffered
1819             defer: NO screen: o_screen];
1820
1821         //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1822         p_vout->p_sys->b_mouse_moved = YES;
1823         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1824     }
1825     else
1826     {
1827         unsigned int i_stylemask = NSTitledWindowMask |
1828                                    NSMiniaturizableWindowMask |
1829                                    NSClosableWindowMask |
1830                                    NSResizableWindowMask;
1831         
1832         if ( p_vout->p_sys->p_fullscreen_state != NULL )
1833             EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1834         p_vout->p_sys->p_fullscreen_state = NULL;
1835
1836         [p_vout->p_sys->o_window 
1837             initWithContentRect: p_vout->p_sys->s_rect
1838             styleMask: i_stylemask
1839             backing: NSBackingStoreBuffered
1840             defer: NO screen: o_screen];
1841
1842         [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1843         
1844         if( config_GetInt( p_vout, "video-on-top" ) )
1845         {
1846             [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1847         }
1848         
1849         if( !p_vout->p_sys->b_pos_saved )   
1850         {
1851             [p_vout->p_sys->o_window center];
1852         }
1853     }
1854
1855     if( !p_vout->p_sys->i_opengl )
1856     {
1857         o_view = [[VLCQTView alloc] init];
1858         /* FIXME: [o_view setMenu:] */
1859         [p_vout->p_sys->o_window setContentView: o_view];
1860         [o_view autorelease];
1861
1862         [o_view lockFocus];
1863         p_vout->p_sys->p_qdport = [o_view qdPort];
1864         [o_view unlockFocus];
1865     }
1866     else
1867     {
1868 #define o_glview p_vout->p_sys->o_glview
1869         o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
1870         [p_vout->p_sys->o_window setContentView: o_glview];
1871         [o_glview autorelease];
1872 #undef o_glview
1873     }
1874     
1875     [p_vout->p_sys->o_window updateTitle];
1876     [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1877
1878 }
1879
1880 - (void)destroyWindow:(NSValue *)o_value
1881 {
1882     vout_thread_t * p_vout;
1883
1884     p_vout = (vout_thread_t *)[o_value pointerValue];
1885
1886     if( !p_vout->b_fullscreen )
1887     {
1888         NSRect s_rect;
1889
1890         s_rect = [[p_vout->p_sys->o_window contentView] frame];
1891         p_vout->p_sys->s_rect.size = s_rect.size;
1892
1893         s_rect = [p_vout->p_sys->o_window frame];
1894         p_vout->p_sys->s_rect.origin = s_rect.origin;
1895
1896         p_vout->p_sys->b_pos_saved = YES;
1897     }
1898     
1899     p_vout->p_sys->p_qdport = nil;
1900     [p_vout->p_sys->o_window close];
1901     p_vout->p_sys->o_window = nil;
1902 }
1903
1904 @end