]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
+ macosx/vout*: use two pictures and OpenGL textures so a texture
[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     else
435     {
436         [p_vout->p_sys->o_glview cleanUp];
437     }
438
439     /* Free the direct buffers we allocated */
440     for( i_index = I_OUTPUTPICTURES; i_index; )
441     {
442         i_index--;
443         if( !p_vout->p_sys->i_opengl )
444         {
445             QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
446         }
447         else
448         {
449             free( p_vout->p_sys->p_data_orig[0] );
450             free( p_vout->p_sys->p_data_orig[1] );
451         }
452     }
453 }
454
455 /*****************************************************************************
456  * CloseVideo: destroy video thread output method
457  *****************************************************************************/
458 void E_(CloseVideo) ( vlc_object_t *p_this )
459 {       
460     vout_thread_t * p_vout = (vout_thread_t *)p_this;     
461
462     if( CoDestroyWindow( p_vout ) )
463     {
464         msg_Err( p_vout, "unable to destroy window" );
465     }
466
467     if( !p_vout->p_sys->i_opengl )
468     {
469         if ( p_vout->p_sys->p_fullscreen_state != NULL )
470             EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
471
472         ExitMovies();
473
474         free( p_vout->p_sys->p_matrix );
475         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
476     }
477
478     free( p_vout->p_sys );
479 }
480
481 /*****************************************************************************
482  * vout_Manage: handle events
483  *****************************************************************************
484  * This function should be called regularly by video output thread. It manages
485  * console events. It returns a non null value on error.
486  *****************************************************************************/
487 static int vout_Manage( vout_thread_t *p_vout )
488 {
489     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
490     {
491         if( CoToggleFullscreen( p_vout ) )  
492         {
493             return( 1 );
494         }
495
496         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
497     }
498
499     if( p_vout->i_changes & VOUT_SIZE_CHANGE ) 
500     {
501         if( !p_vout->p_sys->i_opengl )
502         {
503             QTScaleMatrix( p_vout );
504             SetDSequenceMatrix( p_vout->p_sys->i_seq, 
505                                 p_vout->p_sys->p_matrix );
506         }
507  
508         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
509     }
510
511     /* hide/show mouse cursor 
512      * this code looks unnecessarily complicated, but is necessary like this.
513      * it has to deal with multiple monitors and therefore checks a lot */
514     if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
515     {
516         if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
517         {
518             VLCHideMouse( p_vout, YES );
519         }
520     }
521     else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
522     {
523         VLCHideMouse( p_vout, NO );
524     }
525     
526     /* disable screen saver */
527     UpdateSystemActivity( UsrActivity );
528     
529     return( 0 );
530 }
531
532 /*****************************************************************************
533  * vout_Display: displays previously rendered output
534  *****************************************************************************
535  * This function sends the currently rendered image to the display.
536  *****************************************************************************/
537 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
538 {
539     if( !p_vout->p_sys->i_opengl )
540     {
541         OSErr err;
542         CodecFlags flags;
543
544         if( ( err = DecompressSequenceFrameS( 
545                         p_vout->p_sys->i_seq,
546                         p_pic->p_sys->p_info,
547                         p_pic->p_sys->i_size,                    
548                         codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
549         {
550             msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
551         }
552         else
553         {
554             QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
555         }
556     }
557     else
558     {
559         if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
560         {
561             int i_index;
562             int i_old = p_vout->p_sys->i_cur_pic;
563             int i_new = ( i_old + 1 ) % 2;
564  
565             /* Draw the new picture */
566             p_vout->p_sys->i_cur_pic = i_new;
567             [p_vout->p_sys->o_glview drawRect:
568                 [p_vout->p_sys->o_glview bounds]];
569            
570             /* Reload the other texture. Textures have to be reloaded
571                before the buffer is filled (thanks to gcc from
572                arstechnica forums) */
573             [p_vout->p_sys->o_glview reloadTexture: i_old];
574
575             /* Switch buffers */
576             p_pic->p_data = p_vout->p_sys->p_data[i_old];
577             p_pic->p[0].p_pixels = p_pic->p_data;
578             for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
579             {
580                 p_pic->p[i_index].p_pixels =
581                     p_pic->p[i_index-1].p_pixels +
582                     p_pic->p[i_index-1].i_lines *
583                     p_pic->p[i_index-1].i_pitch;
584             }
585             [p_vout->p_sys->o_glview unlockFocus];
586         }
587     }
588 }
589
590 /*****************************************************************************
591  * CoSendRequest: send request to interface thread
592  *****************************************************************************
593  * Returns 0 on success, 1 otherwise
594  *****************************************************************************/
595 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
596 {
597     int i_ret = 0;
598     vlc_value_t val;
599     intf_thread_t * p_intf;
600
601     VLCVout * o_vlv = [[VLCVout alloc] init];
602
603     if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
604     {
605         msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
606     }
607
608     [o_vlv release];
609
610     /*This makes this function dependant of the presence of a macosx 
611     interface. We do not check if this interface exists, since it has 
612     already been done before.*/
613
614     p_intf = [NSApp getIntf];
615
616     val.b_bool = VLC_TRUE;
617     var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
618     var_Set(p_intf, "intf-change",val);
619
620     return( i_ret );
621 }
622
623 /*****************************************************************************
624  * CoCreateWindow: create new window 
625  *****************************************************************************
626  * Returns 0 on success, 1 otherwise
627  *****************************************************************************/
628 static int CoCreateWindow( vout_thread_t *p_vout )
629 {
630     if( CoSendRequest( p_vout, @selector(createWindow:) ) )
631     {
632         msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
633         return( 1 );
634     }
635     
636     return( 0 );
637 }
638
639 /*****************************************************************************
640  * CoDestroyWindow: destroy window 
641  *****************************************************************************
642  * Returns 0 on success, 1 otherwise
643  *****************************************************************************/
644 static int CoDestroyWindow( vout_thread_t *p_vout )
645 {
646
647     VLCHideMouse( p_vout, NO );
648
649     if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
650     {
651         msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
652         return( 1 );
653     }
654
655     return( 0 );
656 }
657
658 /*****************************************************************************
659  * CoToggleFullscreen: toggle fullscreen 
660  *****************************************************************************
661  * Returns 0 on success, 1 otherwise
662  *****************************************************************************/
663 static int CoToggleFullscreen( vout_thread_t *p_vout )
664 {
665
666     vlc_value_t val;
667     intf_thread_t * p_intf;
668
669     if( p_vout->p_sys->i_opengl )
670     {
671         p_vout->b_fullscreen = !p_vout->b_fullscreen;
672         if( p_vout->b_fullscreen )
673         {
674             [p_vout->p_sys->o_glview goFullScreen];
675         }
676         else
677         {
678             [p_vout->p_sys->o_glview exitFullScreen];
679         }
680         /*This makes this function dependant of the presence of a macosx 
681         interface. We do not check if this interface exists, since it has 
682         already been done before.*/
683
684         p_intf = [NSApp getIntf];
685
686         val.b_bool = VLC_TRUE;
687         var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
688         var_Set(p_intf, "intf-change",val);
689
690         return 0;
691     }
692     
693     QTDestroySequence( p_vout );
694
695     if( CoDestroyWindow( p_vout ) )
696     {
697         msg_Err( p_vout, "unable to destroy window" );
698         return( 1 );
699     }
700     
701     p_vout->b_fullscreen = !p_vout->b_fullscreen;
702
703     if( CoCreateWindow( p_vout ) )
704     {
705         msg_Err( p_vout, "unable to create window" );
706         return( 1 );
707     }
708
709     SetPort( p_vout->p_sys->p_qdport );
710     QTScaleMatrix( p_vout );
711
712     if( QTCreateSequence( p_vout ) )
713     {
714         msg_Err( p_vout, "unable to create sequence" );
715         return( 1 ); 
716     } 
717
718     return( 0 );
719 }
720
721 /*****************************************************************************
722  * VLCHideMouse: if b_hide then hide the cursor
723  *****************************************************************************/
724 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
725 {
726     BOOL b_inside;
727     NSRect s_rect;
728     NSPoint ml;
729     NSWindow *o_window = p_vout->p_sys->o_window;
730     NSView *o_contents = [o_window contentView];
731     
732     s_rect = [o_contents bounds];
733     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
734     ml = [o_contents convertPoint:ml fromView:nil];
735     b_inside = [o_contents mouse: ml inRect: s_rect];
736     
737     if ( b_hide && b_inside )
738     {
739         /* only hide if mouse over VLCQTView */
740         [NSCursor setHiddenUntilMouseMoves: YES];
741     }
742     else if ( !b_hide )
743     {
744         [NSCursor setHiddenUntilMouseMoves: NO];
745     }
746     p_vout->p_sys->b_mouse_moved = NO;
747     p_vout->p_sys->i_time_mouse_last_moved = mdate();
748     return;
749 }
750
751 /*****************************************************************************
752  * QTScaleMatrix: scale matrix 
753  *****************************************************************************/
754 static void QTScaleMatrix( vout_thread_t *p_vout )
755 {
756     Rect s_rect;
757     unsigned int i_width, i_height;
758     Fixed factor_x, factor_y;
759     unsigned int i_offset_x = 0;
760     unsigned int i_offset_y = 0;
761
762     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
763
764     i_width = s_rect.right - s_rect.left;
765     i_height = s_rect.bottom - s_rect.top;
766
767     if( config_GetInt( p_vout, "macosx-stretch" ) )
768     {
769         factor_x = FixDiv( Long2Fix( i_width ),
770                            Long2Fix( p_vout->output.i_width ) );
771         factor_y = FixDiv( Long2Fix( i_height ),
772                            Long2Fix( p_vout->output.i_height ) );
773                            
774     }
775     else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
776     {
777         int i_adj_width = i_height * p_vout->output.i_aspect /
778                           VOUT_ASPECT_FACTOR;
779
780         factor_x = FixDiv( Long2Fix( i_adj_width ),
781                            Long2Fix( p_vout->output.i_width ) );
782         factor_y = FixDiv( Long2Fix( i_height ),
783                            Long2Fix( p_vout->output.i_height ) );
784
785         i_offset_x = (i_width - i_adj_width) / 2;
786     }
787     else
788     {
789         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
790                            p_vout->output.i_aspect;
791
792         factor_x = FixDiv( Long2Fix( i_width ),
793                            Long2Fix( p_vout->output.i_width ) );
794         factor_y = FixDiv( Long2Fix( i_adj_height ),
795                            Long2Fix( p_vout->output.i_height ) );
796
797         i_offset_y = (i_height - i_adj_height) / 2;
798     }
799     
800     SetIdentityMatrix( p_vout->p_sys->p_matrix );
801
802     ScaleMatrix( p_vout->p_sys->p_matrix,
803                  factor_x, factor_y,
804                  Long2Fix(0), Long2Fix(0) );
805                  
806     TranslateMatrix( p_vout->p_sys->p_matrix,
807                  Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
808 }
809
810 /*****************************************************************************
811  * QTCreateSequence: create a new sequence 
812  *****************************************************************************
813  * Returns 0 on success, 1 otherwise
814  *****************************************************************************/
815 static int QTCreateSequence( vout_thread_t *p_vout )
816 {
817     OSErr err;
818     ImageDescriptionPtr p_descr;
819
820     HLock( (Handle)p_vout->p_sys->h_img_descr );
821     p_descr = *p_vout->p_sys->h_img_descr;
822
823     p_descr->idSize = sizeof(ImageDescription);
824     p_descr->cType = p_vout->p_sys->i_codec;
825     p_descr->version = 1;
826     p_descr->revisionLevel = 0;
827     p_descr->vendor = 'appl';
828     p_descr->width = p_vout->output.i_width;
829     p_descr->height = p_vout->output.i_height;
830     p_descr->hRes = Long2Fix(72);
831     p_descr->vRes = Long2Fix(72);
832     p_descr->spatialQuality = codecLosslessQuality;
833     p_descr->frameCount = 1;
834     p_descr->clutID = -1;
835     p_descr->dataSize = 0;
836     p_descr->depth = 24;
837
838     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
839
840     if( ( err = DecompressSequenceBeginS( 
841                               &p_vout->p_sys->i_seq,
842                               p_vout->p_sys->h_img_descr,
843                               NULL, 0,
844                               p_vout->p_sys->p_qdport,
845                               NULL, NULL,
846                               p_vout->p_sys->p_matrix,
847                               0, NULL,
848                               codecFlagUseImageBuffer,
849                               codecLosslessQuality,
850                               p_vout->p_sys->img_dc ) ) )
851     {
852         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
853         return( 1 );
854     }
855
856     return( 0 );
857 }
858
859 /*****************************************************************************
860  * QTDestroySequence: destroy sequence 
861  *****************************************************************************/
862 static void QTDestroySequence( vout_thread_t *p_vout )
863 {
864     CDSequenceEnd( p_vout->p_sys->i_seq );
865 }
866
867 /*****************************************************************************
868  * QTNewPicture: allocate a picture
869  *****************************************************************************
870  * Returns 0 on success, 1 otherwise
871  *****************************************************************************/
872 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
873 {
874     int i_width  = p_vout->output.i_width;
875     int i_height = p_vout->output.i_height;
876
877     /* We know the chroma, allocate a buffer which will be used
878      * directly by the decoder */
879     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
880
881     if( p_pic->p_sys == NULL )
882     {
883         return( -1 );
884     }
885
886     switch( p_vout->output.i_chroma )
887     {
888         case VLC_FOURCC('I','4','2','0'):
889
890             p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
891             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
892
893             /* Allocate the memory buffer */
894             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
895                                           16, i_width * i_height * 3 / 2 );
896
897             /* Y buffer */
898             p_pic->Y_PIXELS = p_pic->p_data; 
899             p_pic->p[Y_PLANE].i_lines = i_height;
900             p_pic->p[Y_PLANE].i_pitch = i_width;
901             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
902             p_pic->p[Y_PLANE].i_visible_pitch = i_width;
903
904             /* U buffer */
905             p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
906             p_pic->p[U_PLANE].i_lines = i_height / 2;
907             p_pic->p[U_PLANE].i_pitch = i_width / 2;
908             p_pic->p[U_PLANE].i_pixel_pitch = 1;
909             p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
910
911             /* V buffer */
912             p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
913             p_pic->p[V_PLANE].i_lines = i_height / 2;
914             p_pic->p[V_PLANE].i_pitch = i_width / 2;
915             p_pic->p[V_PLANE].i_pixel_pitch = 1;
916             p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
917
918             /* We allocated 3 planes */
919             p_pic->i_planes = 3;
920
921 #define P p_pic->p_sys->pixmap_i420
922             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
923                                        - p_pic->p_sys->p_info;
924             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
925                                         - p_pic->p_sys->p_info;
926             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
927                                         - p_pic->p_sys->p_info;
928
929             P.componentInfoY.rowBytes = i_width;
930             P.componentInfoCb.rowBytes = i_width / 2;
931             P.componentInfoCr.rowBytes = i_width / 2;
932 #undef P
933
934             break;
935
936     default:
937         /* Unknown chroma, tell the guy to get lost */
938         free( p_pic->p_sys );
939         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
940                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
941         p_pic->i_planes = 0;
942         return( -1 );
943     }
944
945     return( 0 );
946 }
947
948 /*****************************************************************************
949  * QTFreePicture: destroy a picture allocated with QTNewPicture
950  *****************************************************************************/
951 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
952 {
953     switch( p_vout->output.i_chroma )
954     {
955         case VLC_FOURCC('I','4','2','0'):
956             free( p_pic->p_data_orig );
957             break;
958     }
959
960     free( p_pic->p_sys );
961 }
962
963 /*****************************************************************************
964  * VLCWindow implementation
965  *****************************************************************************/
966 @implementation VLCWindow
967
968 - (void)setVout:(vout_thread_t *)_p_vout
969 {
970     p_vout = _p_vout;
971 }
972
973 - (vout_thread_t *)getVout
974 {
975     return( p_vout );
976 }
977
978 - (void)scaleWindowWithFactor: (float)factor
979 {
980     NSSize newsize;
981     int i_corrected_height, i_corrected_width;
982     NSPoint topleftbase;
983     NSPoint topleftscreen;
984     
985     if ( !p_vout->b_fullscreen )
986     {
987         topleftbase.x = 0;
988         topleftbase.y = [self frame].size.height;
989         topleftscreen = [self convertBaseToScreen: topleftbase];
990         
991         if( p_vout->output.i_height * p_vout->output.i_aspect > 
992                         p_vout->output.i_width * VOUT_ASPECT_FACTOR )
993         {
994             i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
995                                             VOUT_ASPECT_FACTOR;
996             newsize.width = (int) ( i_corrected_width * factor );
997             newsize.height = (int) ( p_vout->render.i_height * factor );
998         }
999         else
1000         {
1001             i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
1002                                             p_vout->output.i_aspect;
1003             newsize.width = (int) ( p_vout->render.i_width * factor );
1004             newsize.height = (int) ( i_corrected_height * factor );
1005         }
1006     
1007         [self setContentSize: newsize];
1008         
1009         [self setFrameTopLeftPoint: topleftscreen];
1010         p_vout->i_changes |= VOUT_SIZE_CHANGE;
1011     }
1012 }
1013
1014 - (void)toggleFloatOnTop
1015 {
1016     vlc_value_t val;
1017     if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
1018     {
1019         val.b_bool = VLC_FALSE;
1020         var_Set( p_vout, "video-on-top", val );
1021         [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
1022     }
1023     else
1024     {
1025         val.b_bool = VLC_TRUE;
1026         var_Set( p_vout, "video-on-top", val );
1027         [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1028     }
1029 }
1030
1031 - (void)toggleFullscreen
1032 {
1033     vlc_value_t val;
1034     val.b_bool = !p_vout->b_fullscreen;
1035     var_Set( p_vout, "fullscreen", val );
1036 }
1037
1038 - (BOOL)isFullscreen
1039 {
1040     return( p_vout->b_fullscreen );
1041 }
1042
1043 - (BOOL)canBecomeKeyWindow
1044 {
1045     return( YES );
1046 }
1047
1048 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1049 {
1050     return [(VLCApplication *) [VLCApplication sharedApplication]
1051             hasDefinedShortcutKey:o_event];
1052 }
1053
1054 - (void)keyDown:(NSEvent *)o_event
1055 {
1056     unichar key = 0;
1057     vlc_value_t val;
1058     unsigned int i_pressed_modifiers = 0;
1059     val.i_int = 0;
1060     
1061     i_pressed_modifiers = [o_event modifierFlags];
1062
1063     if( i_pressed_modifiers & NSShiftKeyMask )
1064         val.i_int |= KEY_MODIFIER_SHIFT;
1065     if( i_pressed_modifiers & NSControlKeyMask )
1066         val.i_int |= KEY_MODIFIER_CTRL;
1067     if( i_pressed_modifiers & NSAlternateKeyMask )
1068         val.i_int |= KEY_MODIFIER_ALT;
1069     if( i_pressed_modifiers & NSCommandKeyMask )
1070         val.i_int |= KEY_MODIFIER_COMMAND;
1071
1072     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1073
1074     if( key )
1075     {
1076         /* Escape should always get you out of fullscreen */
1077         if( key == (unichar) 0x1b )
1078         {
1079              if( [self isFullscreen] )
1080              {
1081                  [self toggleFullscreen];
1082              }
1083         }
1084         else if ( key == ' ' )
1085         {
1086              playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1087                                                      FIND_ANYWHERE );
1088              if ( p_playlist != NULL )
1089              {
1090                  playlist_Pause( p_playlist );
1091                  vlc_object_release( p_playlist);
1092              }
1093         }
1094         else
1095         {
1096             val.i_int |= CocoaKeyToVLC( key );
1097             var_Set( p_vout->p_vlc, "key-pressed", val );
1098         }
1099     }
1100     else
1101     {
1102         [super keyDown: o_event];
1103     }
1104 }
1105
1106 - (void)updateTitle
1107 {
1108     NSMutableString * o_title;
1109     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1110                                                        FIND_ANYWHERE );
1111     
1112     if( p_playlist == NULL )
1113     {
1114         return;
1115     }
1116
1117     vlc_mutex_lock( &p_playlist->object_lock );
1118     o_title = [NSMutableString stringWithUTF8String: 
1119         p_playlist->pp_items[p_playlist->i_index]->input.psz_uri]; 
1120     vlc_mutex_unlock( &p_playlist->object_lock );
1121
1122     vlc_object_release( p_playlist );
1123
1124     if( o_title != nil )
1125     {
1126         NSRange prefix_range = [o_title rangeOfString: @"file:"];
1127         if( prefix_range.location != NSNotFound )
1128         {
1129             [o_title deleteCharactersInRange: prefix_range];
1130         }
1131
1132         [self setTitleWithRepresentedFilename: o_title];
1133     }
1134     else
1135     {
1136         [self setTitle:
1137             [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1138     }
1139 }
1140
1141 /* This is actually the same as VLCControls::stop. */
1142 - (BOOL)windowShouldClose:(id)sender
1143 {
1144     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1145                                                        FIND_ANYWHERE );
1146     if( p_playlist == NULL )      
1147     {
1148         return NO;
1149     }
1150
1151     playlist_Stop( p_playlist );
1152     vlc_object_release( p_playlist );
1153
1154     /* The window will be closed by the intf later. */
1155     return NO;
1156 }
1157
1158 @end
1159
1160 /*****************************************************************************
1161  * VLCQTView implementation
1162  *****************************************************************************/
1163 @implementation VLCQTView
1164
1165 - (void)drawRect:(NSRect)rect
1166 {
1167     vout_thread_t * p_vout;
1168     id o_window = [self window];
1169     p_vout = (vout_thread_t *)[o_window getVout];
1170     
1171     [[NSColor blackColor] set];
1172     NSRectFill( rect );
1173     [super drawRect: rect];
1174
1175     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1176 }
1177
1178 - (BOOL)acceptsFirstResponder
1179 {
1180     return( YES );
1181 }
1182
1183 - (BOOL)becomeFirstResponder
1184 {
1185     vout_thread_t * p_vout;
1186     id o_window = [self window];
1187     p_vout = (vout_thread_t *)[o_window getVout];
1188     
1189     [o_window setAcceptsMouseMovedEvents: YES];
1190     return( YES );
1191 }
1192
1193 - (BOOL)resignFirstResponder
1194 {
1195     vout_thread_t * p_vout;
1196     id o_window = [self window];
1197     p_vout = (vout_thread_t *)[o_window getVout];
1198     
1199     [o_window setAcceptsMouseMovedEvents: NO];
1200     VLCHideMouse( p_vout, NO );
1201     return( YES );
1202 }
1203
1204 - (void)mouseDown:(NSEvent *)o_event
1205 {
1206     vout_thread_t * p_vout;
1207     id o_window = [self window];
1208     p_vout = (vout_thread_t *)[o_window getVout];
1209     vlc_value_t val;
1210
1211     switch( [o_event type] )
1212     {        
1213         case NSLeftMouseDown:
1214         {
1215             var_Get( p_vout, "mouse-button-down", &val );
1216             val.i_int |= 1;
1217             var_Set( p_vout, "mouse-button-down", val );
1218         }
1219         break;
1220         
1221         default:
1222             [super mouseDown: o_event];
1223         break;
1224     }
1225 }
1226
1227 - (void)otherMouseDown:(NSEvent *)o_event
1228 {
1229     /* This is not the the wheel button. you need to poll the
1230      * mouseWheel event for that. other is a third, forth or fifth button */
1231     vout_thread_t * p_vout;
1232     id o_window = [self window];
1233     p_vout = (vout_thread_t *)[o_window getVout];
1234     vlc_value_t val;
1235
1236     switch( [o_event type] )
1237     {
1238         case NSOtherMouseDown:
1239         {
1240             var_Get( p_vout, "mouse-button-down", &val );
1241             val.i_int |= 2;
1242             var_Set( p_vout, "mouse-button-down", val );
1243         }
1244         break;
1245         
1246         default:
1247             [super mouseDown: o_event];
1248         break;
1249     }
1250 }
1251
1252 - (void)rightMouseDown:(NSEvent *)o_event
1253 {
1254     vout_thread_t * p_vout;
1255     id o_window = [self window];
1256     p_vout = (vout_thread_t *)[o_window getVout];
1257     vlc_value_t val;
1258
1259     switch( [o_event type] )
1260     {
1261         case NSRightMouseDown:
1262         {
1263             var_Get( p_vout, "mouse-button-down", &val );
1264             val.i_int |= 4;
1265             var_Set( p_vout, "mouse-button-down", val );
1266         }
1267         break;
1268         
1269         default:
1270             [super mouseDown: o_event];
1271         break;
1272     }
1273 }
1274
1275 - (void)mouseUp:(NSEvent *)o_event
1276 {
1277     vout_thread_t * p_vout;
1278     id o_window = [self window];
1279     p_vout = (vout_thread_t *)[o_window getVout];
1280     vlc_value_t val;
1281
1282     switch( [o_event type] )
1283     {
1284         case NSLeftMouseUp:
1285         {
1286             vlc_value_t b_val;
1287             b_val.b_bool = VLC_TRUE;
1288             var_Set( p_vout, "mouse-clicked", b_val );
1289             
1290             var_Get( p_vout, "mouse-button-down", &val );
1291             val.i_int &= ~1;
1292             var_Set( p_vout, "mouse-button-down", val );
1293         }
1294         break;
1295                 
1296         default:
1297             [super mouseUp: o_event];
1298         break;
1299     }
1300 }
1301
1302 - (void)otherMouseUp:(NSEvent *)o_event
1303 {
1304     vout_thread_t * p_vout;
1305     id o_window = [self window];
1306     p_vout = (vout_thread_t *)[o_window getVout];
1307     vlc_value_t val;
1308
1309     switch( [o_event type] )
1310     {
1311         case NSOtherMouseUp:
1312         {
1313             var_Get( p_vout, "mouse-button-down", &val );
1314             val.i_int &= ~2;
1315             var_Set( p_vout, "mouse-button-down", val );
1316         }
1317         break;
1318                 
1319         default:
1320             [super mouseUp: o_event];
1321         break;
1322     }
1323 }
1324
1325 - (void)rightMouseUp:(NSEvent *)o_event
1326 {
1327     vout_thread_t * p_vout;
1328     id o_window = [self window];
1329     p_vout = (vout_thread_t *)[o_window getVout];
1330     vlc_value_t val;
1331
1332     switch( [o_event type] )
1333     {
1334         case NSRightMouseUp:
1335         {
1336             var_Get( p_vout, "mouse-button-down", &val );
1337             val.i_int &= ~4;
1338             var_Set( p_vout, "mouse-button-down", val );
1339         }
1340         break;
1341         
1342         default:
1343             [super mouseUp: o_event];
1344         break;
1345     }
1346 }
1347
1348 - (void)mouseDragged:(NSEvent *)o_event
1349 {
1350     [self mouseMoved:o_event];
1351 }
1352
1353 - (void)otherMouseDragged:(NSEvent *)o_event
1354 {
1355     [self mouseMoved:o_event];
1356 }
1357
1358 - (void)rightMouseDragged:(NSEvent *)o_event
1359 {
1360     [self mouseMoved:o_event];
1361 }
1362
1363 - (void)mouseMoved:(NSEvent *)o_event
1364 {
1365     NSPoint ml;
1366     NSRect s_rect;
1367     BOOL b_inside;
1368
1369     vout_thread_t * p_vout;
1370     id o_window = [self window];
1371     p_vout = (vout_thread_t *)[o_window getVout];
1372
1373     s_rect = [self bounds];
1374     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1375     b_inside = [self mouse: ml inRect: s_rect];
1376
1377     if( b_inside )
1378     {
1379         vlc_value_t val;
1380         int i_width, i_height, i_x, i_y;
1381         
1382         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1383                                    (unsigned int)s_rect.size.height,
1384                                    &i_x, &i_y, &i_width, &i_height );
1385
1386         val.i_int = ( ((int)ml.x) - i_x ) * 
1387                     p_vout->render.i_width / i_width;
1388         var_Set( p_vout, "mouse-x", val );
1389
1390         val.i_int = ( ((int)ml.y) - i_y ) * 
1391                     p_vout->render.i_height / i_height;
1392         var_Set( p_vout, "mouse-y", val );
1393             
1394         val.b_bool = VLC_TRUE;
1395         var_Set( p_vout, "mouse-moved", val );
1396         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1397         p_vout->p_sys->b_mouse_moved = YES;
1398     }
1399
1400     [super mouseMoved: o_event];
1401 }
1402
1403 @end
1404
1405 /*****************************************************************************
1406  * VLCGLView implementation
1407  *****************************************************************************/
1408 @implementation VLCGLView
1409
1410
1411 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1412 {
1413     char * psz_effect;
1414     p_vout = _p_vout;
1415     
1416     NSOpenGLPixelFormatAttribute attribs[] =
1417     {
1418         NSOpenGLPFAAccelerated,
1419         NSOpenGLPFANoRecovery,
1420         NSOpenGLPFADoubleBuffer,
1421         NSOpenGLPFAColorSize, 24,
1422         NSOpenGLPFAAlphaSize, 8,
1423         NSOpenGLPFADepthSize, 24,
1424         NSOpenGLPFAWindow,
1425         0
1426     };
1427
1428     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1429         initWithAttributes: attribs];
1430
1431     if( !fmt )
1432     {
1433         msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1434         return nil;
1435     }
1436
1437     self = [super initWithFrame:frame pixelFormat: fmt];
1438
1439     currentContext = [self openGLContext];
1440     [currentContext makeCurrentContext];
1441     [currentContext update];
1442
1443     /* Black background */
1444     glClearColor( 0.0, 0.0, 0.0, 0.0 );
1445
1446     /* Check if the user asked for useless visual effects */
1447     psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" );
1448     if( !psz_effect || !strcmp( psz_effect, "none" ))
1449     {
1450         i_effect = OPENGL_EFFECT_NONE;
1451     }
1452     else if( !strcmp( psz_effect, "cube" ) )
1453     {
1454         i_effect = OPENGL_EFFECT_CUBE;
1455
1456         glEnable( GL_DEPTH_TEST );
1457     }
1458     else if( !strcmp( psz_effect, "transparent-cube" ) )
1459     {
1460         i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1461
1462         glDisable( GL_DEPTH_TEST );
1463         glEnable( GL_BLEND );
1464         glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1465     }
1466     else
1467     {
1468         msg_Warn( p_vout, "no valid opengl effect provided, using "
1469                   "\"none\"" );
1470         i_effect = OPENGL_EFFECT_NONE;
1471     }
1472
1473     if( i_effect & ( OPENGL_EFFECT_CUBE |
1474                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1475     {
1476         /* Set the perpective */
1477         glMatrixMode( GL_PROJECTION );
1478         glLoadIdentity();
1479         glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1480         glMatrixMode( GL_MODELVIEW );
1481         glLoadIdentity();
1482         glTranslatef( 0.0, 0.0, - 5.0 );
1483     }
1484
1485     pi_textures[0] = 0;
1486     pi_textures[1] = 0;
1487     initDone       = 0;
1488     isFullScreen   = 0;
1489
1490     return self;
1491 }
1492
1493 - (void) reshape
1494 {
1495     if( !initDone )
1496     {
1497         return;
1498     }
1499     
1500     [currentContext makeCurrentContext];
1501
1502     NSRect bounds = [self bounds];
1503     glViewport( 0, 0, (GLint) bounds.size.width,
1504                 (GLint) bounds.size.height );
1505
1506     if( config_GetInt( p_vout, "macosx-stretch" ) )
1507     {
1508         f_x = 1.0;
1509         f_y = 1.0;
1510     }
1511     /* Quad size is set in order to preserve the aspect ratio */
1512     else if( bounds.size.height * p_vout->output.i_aspect <
1513         bounds.size.width * VOUT_ASPECT_FACTOR )
1514     {
1515         f_x = bounds.size.height * p_vout->output.i_aspect /
1516             VOUT_ASPECT_FACTOR / bounds.size.width;
1517         f_y = 1.0;
1518     }
1519     else
1520     {
1521         f_x = 1.0;
1522         f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1523             p_vout->output.i_aspect / bounds.size.height;
1524     }
1525 }
1526
1527 - (void) initTextures
1528 {
1529     int i;
1530     [currentContext makeCurrentContext];
1531
1532     /* Free previous texture if any */
1533     if( initDone )
1534     {
1535         glDeleteTextures( 2, pi_textures );
1536     }
1537     
1538     /* Create textures */
1539     glGenTextures( 2, pi_textures );
1540
1541     glEnable( GL_TEXTURE_RECTANGLE_EXT );
1542     glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1543
1544     for( i = 0; i < 2; i++ )
1545     {
1546         glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1547
1548         /* Use VRAM texturing */
1549         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1550                 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1551
1552         /* Tell the driver not to make a copy of the texture but to use
1553            our buffer */
1554         glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1555
1556         /* Linear interpolation */
1557         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1558                 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1559         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1560                 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1561
1562         /* I have no idea what this exactly does, but it seems to be
1563            necessary for scaling */
1564         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1565                 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1566         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1567                 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1568         glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
1569
1570         glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
1571                 p_vout->output.i_width, p_vout->output.i_height, 0,
1572                 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1573                 p_vout->p_sys->p_data[i] );
1574     }
1575
1576     initDone = 1;
1577 }
1578
1579 - (void)reloadTexture: (int) index
1580 {
1581     if( !initDone )
1582     {
1583         return;
1584     }
1585     
1586     [currentContext makeCurrentContext];
1587
1588     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1589
1590     /* glTexSubImage2D is faster than glTexImage2D
1591        http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1592        TextureRange/MainOpenGLView.m.htm */
1593     glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1594             p_vout->output.i_width, p_vout->output.i_height,
1595             GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1596             p_vout->p_sys->p_data[index] );
1597 }
1598
1599 - (void)goFullScreen
1600 {
1601     /* Create the new pixel format */
1602     NSOpenGLPixelFormatAttribute attribs[] =
1603     {
1604         NSOpenGLPFAAccelerated,
1605         NSOpenGLPFANoRecovery,
1606         NSOpenGLPFADoubleBuffer,
1607         NSOpenGLPFAColorSize, 24,
1608         NSOpenGLPFAAlphaSize, 8,
1609         NSOpenGLPFADepthSize, 24,
1610         NSOpenGLPFAFullScreen,
1611         NSOpenGLPFAScreenMask,
1612         /* TODO multi monitor support */
1613         CGDisplayIDToOpenGLDisplayMask( kCGDirectMainDisplay ),
1614         0
1615     };
1616     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1617         initWithAttributes: attribs];
1618     if( !fmt )
1619     {
1620         msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1621         return;
1622     }
1623
1624     /* Create the new OpenGL context */
1625     /* TODO have the shared context working so we don't have to
1626        re-init textures */
1627     fullScreenContext = [[NSOpenGLContext alloc]
1628         initWithFormat: fmt shareContext: nil];
1629     if( !fullScreenContext )
1630     {
1631         msg_Warn( p_vout, "Failed to create new NSOpenGLContext" );
1632         return;
1633     }
1634     currentContext = fullScreenContext;
1635
1636     /* Capture display, switch to fullscreen */
1637     /* XXX we should capture only the display the user wants the
1638        picture on */
1639     if( CGCaptureAllDisplays() != CGDisplayNoErr )
1640     {
1641         msg_Warn( p_vout, "CGCaptureAllDisplays() failed" );
1642         return;
1643     }
1644     [fullScreenContext setFullScreen];
1645     [fullScreenContext makeCurrentContext];
1646
1647     /* Ratio */
1648     unsigned width    = CGDisplayPixelsWide( kCGDirectMainDisplay );
1649     unsigned height   = CGDisplayPixelsHigh( kCGDirectMainDisplay );
1650     int      stretch  = config_GetInt( p_vout, "macosx-stretch" );
1651     int      fill     = config_GetInt( p_vout, "macosx-fill" );
1652     int      bigRatio = ( height * p_vout->output.i_aspect <
1653                           width * VOUT_ASPECT_FACTOR );
1654     if( stretch )
1655     {
1656         f_x = 1.0;
1657         f_y = 1.0;
1658     }
1659     else if( ( bigRatio && !fill ) || ( !bigRatio && fill ) )
1660     {
1661         f_x = (float) height * p_vout->output.i_aspect /
1662                   width / VOUT_ASPECT_FACTOR;
1663         f_y = 1.0;
1664     }
1665     else
1666     {
1667         f_x = 1.0;
1668         f_y = (float) width * VOUT_ASPECT_FACTOR /
1669                   p_vout->output.i_aspect / height;
1670     }
1671
1672     /* Update viewport, re-init textures */
1673     glViewport( 0, 0, width, height );
1674     [self initTextures];
1675
1676     /* Redraw the last picture */
1677     [self setNeedsDisplay: YES];
1678
1679     isFullScreen = 1;
1680 }
1681
1682 - (void)exitFullScreen
1683 {
1684     /* Free current OpenGL context */
1685     [NSOpenGLContext clearCurrentContext];
1686     [fullScreenContext clearDrawable];
1687     [fullScreenContext release];
1688     CGReleaseAllDisplays();
1689
1690     currentContext = [self openGLContext];
1691     [self initTextures];
1692     [self reshape];
1693
1694     /* Redraw the last picture */
1695     [self setNeedsDisplay: YES];
1696
1697     isFullScreen = 0;
1698 }
1699
1700 - (void) cleanUp
1701 {
1702     if( isFullScreen )
1703     {
1704         [self exitFullScreen];
1705     }
1706     initDone = 0;
1707 }
1708
1709 - (void) drawQuad
1710 {
1711     glBegin( GL_QUADS );
1712         /* Top left */
1713         glTexCoord2f( 0.0, 0.0 );
1714         glVertex2f( - f_x, f_y );
1715         /* Bottom left */
1716         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1717         glVertex2f( - f_x, - f_y );
1718         /* Bottom right */
1719         glTexCoord2f( (float) p_vout->output.i_width,
1720                       (float) p_vout->output.i_height );
1721         glVertex2f( f_x, - f_y );
1722         /* Top right */
1723         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1724         glVertex2f( f_x, f_y );
1725     glEnd();
1726 }
1727
1728 - (void) drawCube
1729 {
1730     glBegin( GL_QUADS );
1731         /* Front */
1732         glTexCoord2f( 0.0, 0.0 );
1733         glVertex3f( - 1.0, 1.0, 1.0 );
1734         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1735         glVertex3f( - 1.0, - 1.0, 1.0 );
1736         glTexCoord2f( (float) p_vout->output.i_width,
1737                       (float) p_vout->output.i_height );
1738         glVertex3f( 1.0, - 1.0, 1.0 );
1739         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1740         glVertex3f( 1.0, 1.0, 1.0 );
1741
1742         /* Left */
1743         glTexCoord2f( 0.0, 0.0 );
1744         glVertex3f( - 1.0, 1.0, - 1.0 );
1745         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1746         glVertex3f( - 1.0, - 1.0, - 1.0 );
1747         glTexCoord2f( (float) p_vout->output.i_width,
1748                       (float) p_vout->output.i_height );
1749         glVertex3f( - 1.0, - 1.0, 1.0 );
1750         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1751         glVertex3f( - 1.0, 1.0, 1.0 );
1752
1753         /* Back */
1754         glTexCoord2f( 0.0, 0.0 );
1755         glVertex3f( 1.0, 1.0, - 1.0 );
1756         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1757         glVertex3f( 1.0, - 1.0, - 1.0 );
1758         glTexCoord2f( (float) p_vout->output.i_width,
1759                       (float) p_vout->output.i_height );
1760         glVertex3f( - 1.0, - 1.0, - 1.0 );
1761         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1762         glVertex3f( - 1.0, 1.0, - 1.0 );
1763
1764         /* Right */
1765         glTexCoord2f( 0.0, 0.0 );
1766         glVertex3f( 1.0, 1.0, 1.0 );
1767         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1768         glVertex3f( 1.0, - 1.0, 1.0 );
1769         glTexCoord2f( (float) p_vout->output.i_width,
1770                       (float) p_vout->output.i_height );
1771         glVertex3f( 1.0, - 1.0, - 1.0 );
1772         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1773         glVertex3f( 1.0, 1.0, - 1.0 );
1774
1775         /* Top */
1776         glTexCoord2f( 0.0, 0.0 );
1777         glVertex3f( - 1.0, 1.0, - 1.0 );
1778         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1779         glVertex3f( - 1.0, 1.0, 1.0 );
1780         glTexCoord2f( (float) p_vout->output.i_width,
1781                       (float) p_vout->output.i_height );
1782         glVertex3f( 1.0, 1.0, 1.0 );
1783         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1784         glVertex3f( 1.0, 1.0, - 1.0 );
1785
1786         /* Bottom */
1787         glTexCoord2f( 0.0, 0.0 );
1788         glVertex3f( - 1.0, - 1.0, 1.0 );
1789         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1790         glVertex3f( - 1.0, - 1.0, - 1.0 );
1791         glTexCoord2f( (float) p_vout->output.i_width,
1792                       (float) p_vout->output.i_height );
1793         glVertex3f( 1.0, - 1.0, - 1.0 );
1794         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1795         glVertex3f( 1.0, - 1.0, 1.0 );
1796     glEnd();
1797 }
1798
1799 - (void) drawRect: (NSRect) rect
1800 {
1801     [currentContext makeCurrentContext];
1802
1803     /* Swap buffers only during the vertical retrace of the monitor.
1804        http://developer.apple.com/documentation/GraphicsImaging/
1805        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1806     long params[] = { 1 };
1807     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1808                      params );
1809     
1810     /* Black background */
1811     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1812
1813     if( !initDone )
1814     {
1815         [currentContext flushBuffer];
1816         return;
1817     }
1818
1819     /* Draw */
1820     glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1821                    pi_textures[p_vout->p_sys->i_cur_pic] );
1822     if( i_effect & ( OPENGL_EFFECT_CUBE |
1823                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1824     {
1825         glRotatef( 1.0, 0.3, 0.5, 0.7 );
1826         [self drawCube];
1827     }
1828     else
1829     {
1830         [self drawQuad];
1831     }
1832
1833     /* Wait for the job to be done */
1834     [currentContext flushBuffer];
1835 }
1836
1837 @end
1838
1839 /*****************************************************************************
1840  * VLCVout implementation
1841  *****************************************************************************/
1842 @implementation VLCVout
1843
1844 - (void)createWindow:(NSValue *)o_value
1845 {
1846     vlc_value_t val;
1847     VLCQTView * o_view;
1848     NSScreen * o_screen;
1849     vout_thread_t * p_vout;
1850     vlc_bool_t b_main_screen;
1851     
1852     p_vout = (vout_thread_t *)[o_value pointerValue];
1853
1854     p_vout->p_sys->o_window = [VLCWindow alloc];
1855     [p_vout->p_sys->o_window setVout: p_vout];
1856     [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1857
1858     if( var_Get( p_vout, "video-device", &val ) < 0 )
1859     {
1860         o_screen = [NSScreen mainScreen];
1861         b_main_screen = 1;
1862     }
1863     else
1864     {
1865         NSArray *o_screens = [NSScreen screens];
1866         unsigned int i_index = val.i_int;
1867         
1868         if( [o_screens count] < i_index )
1869         {
1870             o_screen = [NSScreen mainScreen];
1871             b_main_screen = 1;
1872         }
1873         else
1874         {
1875             i_index--;
1876             o_screen = [o_screens objectAtIndex: i_index];
1877             config_PutInt( p_vout, "macosx-vdev", i_index );
1878             b_main_screen = (i_index == 0);
1879         }
1880     } 
1881
1882     if( p_vout->b_fullscreen && !p_vout->p_sys->i_opengl )
1883     {
1884         NSRect screen_rect = [o_screen frame];
1885         screen_rect.origin.x = screen_rect.origin.y = 0;
1886
1887         if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1888             BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1889                              NULL, NULL, fullScreenAllowEvents );
1890
1891         [p_vout->p_sys->o_window 
1892             initWithContentRect: screen_rect
1893             styleMask: NSBorderlessWindowMask
1894             backing: NSBackingStoreBuffered
1895             defer: NO screen: o_screen];
1896
1897         //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1898         p_vout->p_sys->b_mouse_moved = YES;
1899         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1900     }
1901     else
1902     {
1903         unsigned int i_stylemask = NSTitledWindowMask |
1904                                    NSMiniaturizableWindowMask |
1905                                    NSClosableWindowMask |
1906                                    NSResizableWindowMask;
1907         
1908         if( !p_vout->p_sys->i_opengl )
1909         {
1910             if ( p_vout->p_sys->p_fullscreen_state != NULL )
1911                 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1912             p_vout->p_sys->p_fullscreen_state = NULL;
1913         }
1914
1915         [p_vout->p_sys->o_window 
1916             initWithContentRect: p_vout->p_sys->s_rect
1917             styleMask: i_stylemask
1918             backing: NSBackingStoreBuffered
1919             defer: NO screen: o_screen];
1920
1921         [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1922         
1923         if( config_GetInt( p_vout, "video-on-top" ) )
1924         {
1925             [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1926         }
1927         
1928         if( !p_vout->p_sys->b_pos_saved )   
1929         {
1930             [p_vout->p_sys->o_window center];
1931         }
1932     }
1933
1934     if( !p_vout->p_sys->i_opengl )
1935     {
1936         o_view = [[VLCQTView alloc] init];
1937         /* FIXME: [o_view setMenu:] */
1938         [p_vout->p_sys->o_window setContentView: o_view];
1939         [o_view autorelease];
1940
1941         [o_view lockFocus];
1942         p_vout->p_sys->p_qdport = [o_view qdPort];
1943         [o_view unlockFocus];
1944     }
1945     else
1946     {
1947 #define o_glview p_vout->p_sys->o_glview
1948         o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
1949         [p_vout->p_sys->o_window setContentView: o_glview];
1950         [o_glview autorelease];
1951 #undef o_glview
1952     }
1953     
1954     [p_vout->p_sys->o_window updateTitle];
1955     [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1956
1957 }
1958
1959 - (void)destroyWindow:(NSValue *)o_value
1960 {
1961     vout_thread_t * p_vout;
1962
1963     p_vout = (vout_thread_t *)[o_value pointerValue];
1964
1965     if( !p_vout->b_fullscreen )
1966     {
1967         NSRect s_rect;
1968
1969         s_rect = [[p_vout->p_sys->o_window contentView] frame];
1970         p_vout->p_sys->s_rect.size = s_rect.size;
1971
1972         s_rect = [p_vout->p_sys->o_window frame];
1973         p_vout->p_sys->s_rect.origin = s_rect.origin;
1974
1975         p_vout->p_sys->b_pos_saved = YES;
1976     }
1977     
1978     p_vout->p_sys->p_qdport = nil;
1979     [p_vout->p_sys->o_window close];
1980     p_vout->p_sys->o_window = nil;
1981 }
1982
1983 @end