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