]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
* Updated the Vout behaviour. Still a bit coupled to p_vout.
[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     if( config_GetInt( p_vout, "video-on-top" ) )
949     {
950         config_PutInt( p_vout, "video-on-top", 0 );
951         [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
952     }
953     else
954     {
955         config_PutInt( p_vout, "video-on-top", 1 );
956         [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
957     }
958 }
959
960 - (void)toggleFullscreen
961 {
962     vlc_value_t val;
963     val.b_bool = !p_vout->b_fullscreen;
964     var_Set( p_vout, "fullscreen", val );
965 }
966
967 - (BOOL)isFullscreen
968 {
969     return( p_vout->b_fullscreen );
970 }
971
972 - (BOOL)canBecomeKeyWindow
973 {
974     return( YES );
975 }
976
977 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
978 {
979     return [(VLCApplication *) [VLCApplication sharedApplication]
980             hasDefinedShortcutKey:o_event];
981 }
982
983 - (void)keyDown:(NSEvent *)o_event
984 {
985     unichar key = 0;
986     vlc_value_t val;
987     unsigned int i_pressed_modifiers = 0;
988     val.i_int = 0;
989     
990     i_pressed_modifiers = [o_event modifierFlags];
991
992     if( i_pressed_modifiers & NSShiftKeyMask )
993         val.i_int |= KEY_MODIFIER_SHIFT;
994     if( i_pressed_modifiers & NSControlKeyMask )
995         val.i_int |= KEY_MODIFIER_CTRL;
996     if( i_pressed_modifiers & NSAlternateKeyMask )
997         val.i_int |= KEY_MODIFIER_ALT;
998     if( i_pressed_modifiers & NSCommandKeyMask )
999         val.i_int |= KEY_MODIFIER_COMMAND;
1000
1001     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1002
1003     if( key )
1004     {
1005         /* Escape should always get you out of fullscreen */
1006         if( key == (unichar) 0x1b )
1007         {
1008              if( [self isFullscreen] )
1009              {
1010                  [self toggleFullscreen];
1011              }
1012         }
1013         else if ( key == ' ' )
1014         {
1015              playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1016                                                      FIND_ANYWHERE );
1017              if ( p_playlist != NULL )
1018              {
1019                  playlist_Pause( p_playlist );
1020                  vlc_object_release( p_playlist);
1021              }
1022         }
1023         else
1024         {
1025             val.i_int |= CocoaKeyToVLC( key );
1026             var_Set( p_vout->p_vlc, "key-pressed", val );
1027         }
1028     }
1029     else
1030     {
1031         [super keyDown: o_event];
1032     }
1033 }
1034
1035 - (void)updateTitle
1036 {
1037     NSMutableString * o_title;
1038     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1039                                                        FIND_ANYWHERE );
1040     
1041     if( p_playlist == NULL )
1042     {
1043         return;
1044     }
1045
1046     vlc_mutex_lock( &p_playlist->object_lock );
1047     o_title = [NSMutableString stringWithUTF8String: 
1048         p_playlist->pp_items[p_playlist->i_index]->input.psz_uri]; 
1049     vlc_mutex_unlock( &p_playlist->object_lock );
1050
1051     vlc_object_release( p_playlist );
1052
1053     if( o_title != nil )
1054     {
1055         NSRange prefix_range = [o_title rangeOfString: @"file:"];
1056         if( prefix_range.location != NSNotFound )
1057         {
1058             [o_title deleteCharactersInRange: prefix_range];
1059         }
1060
1061         [self setTitleWithRepresentedFilename: o_title];
1062     }
1063     else
1064     {
1065         [self setTitle:
1066             [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1067     }
1068 }
1069
1070 /* This is actually the same as VLCControls::stop. */
1071 - (BOOL)windowShouldClose:(id)sender
1072 {
1073     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1074                                                        FIND_ANYWHERE );
1075     if( p_playlist == NULL )      
1076     {
1077         return NO;
1078     }
1079
1080     playlist_Stop( p_playlist );
1081     vlc_object_release( p_playlist );
1082
1083     /* The window will be closed by the intf later. */
1084     return NO;
1085 }
1086
1087 @end
1088
1089 /*****************************************************************************
1090  * VLCQTView implementation
1091  *****************************************************************************/
1092 @implementation VLCQTView
1093
1094 - (void)drawRect:(NSRect)rect
1095 {
1096     vout_thread_t * p_vout;
1097     id o_window = [self window];
1098     p_vout = (vout_thread_t *)[o_window getVout];
1099     
1100     [[NSColor blackColor] set];
1101     NSRectFill( rect );
1102     [super drawRect: rect];
1103
1104     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1105 }
1106
1107 - (BOOL)acceptsFirstResponder
1108 {
1109     return( YES );
1110 }
1111
1112 - (BOOL)becomeFirstResponder
1113 {
1114     vout_thread_t * p_vout;
1115     id o_window = [self window];
1116     p_vout = (vout_thread_t *)[o_window getVout];
1117     
1118     [o_window setAcceptsMouseMovedEvents: YES];
1119     return( YES );
1120 }
1121
1122 - (BOOL)resignFirstResponder
1123 {
1124     vout_thread_t * p_vout;
1125     id o_window = [self window];
1126     p_vout = (vout_thread_t *)[o_window getVout];
1127     
1128     [o_window setAcceptsMouseMovedEvents: NO];
1129     VLCHideMouse( p_vout, NO );
1130     return( YES );
1131 }
1132
1133 - (void)mouseDown:(NSEvent *)o_event
1134 {
1135     vout_thread_t * p_vout;
1136     id o_window = [self window];
1137     p_vout = (vout_thread_t *)[o_window getVout];
1138     vlc_value_t val;
1139
1140     switch( [o_event type] )
1141     {        
1142         case NSLeftMouseDown:
1143         {
1144             var_Get( p_vout, "mouse-button-down", &val );
1145             val.i_int |= 1;
1146             var_Set( p_vout, "mouse-button-down", val );
1147         }
1148         break;
1149         
1150         default:
1151             [super mouseDown: o_event];
1152         break;
1153     }
1154 }
1155
1156 - (void)otherMouseDown:(NSEvent *)o_event
1157 {
1158     /* This is not the the wheel button. you need to poll the
1159      * mouseWheel event for that. other is a third, forth or fifth button */
1160     vout_thread_t * p_vout;
1161     id o_window = [self window];
1162     p_vout = (vout_thread_t *)[o_window getVout];
1163     vlc_value_t val;
1164
1165     switch( [o_event type] )
1166     {
1167         case NSOtherMouseDown:
1168         {
1169             var_Get( p_vout, "mouse-button-down", &val );
1170             val.i_int |= 2;
1171             var_Set( p_vout, "mouse-button-down", val );
1172         }
1173         break;
1174         
1175         default:
1176             [super mouseDown: o_event];
1177         break;
1178     }
1179 }
1180
1181 - (void)rightMouseDown:(NSEvent *)o_event
1182 {
1183     vout_thread_t * p_vout;
1184     id o_window = [self window];
1185     p_vout = (vout_thread_t *)[o_window getVout];
1186     vlc_value_t val;
1187
1188     switch( [o_event type] )
1189     {
1190         case NSRightMouseDown:
1191         {
1192             var_Get( p_vout, "mouse-button-down", &val );
1193             val.i_int |= 4;
1194             var_Set( p_vout, "mouse-button-down", val );
1195         }
1196         break;
1197         
1198         default:
1199             [super mouseDown: o_event];
1200         break;
1201     }
1202 }
1203
1204 - (void)mouseUp:(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 NSLeftMouseUp:
1214         {
1215             vlc_value_t b_val;
1216             b_val.b_bool = VLC_TRUE;
1217             var_Set( p_vout, "mouse-clicked", b_val );
1218             
1219             var_Get( p_vout, "mouse-button-down", &val );
1220             val.i_int &= ~1;
1221             var_Set( p_vout, "mouse-button-down", val );
1222         }
1223         break;
1224                 
1225         default:
1226             [super mouseUp: o_event];
1227         break;
1228     }
1229 }
1230
1231 - (void)otherMouseUp:(NSEvent *)o_event
1232 {
1233     vout_thread_t * p_vout;
1234     id o_window = [self window];
1235     p_vout = (vout_thread_t *)[o_window getVout];
1236     vlc_value_t val;
1237
1238     switch( [o_event type] )
1239     {
1240         case NSOtherMouseUp:
1241         {
1242             var_Get( p_vout, "mouse-button-down", &val );
1243             val.i_int &= ~2;
1244             var_Set( p_vout, "mouse-button-down", val );
1245         }
1246         break;
1247                 
1248         default:
1249             [super mouseUp: o_event];
1250         break;
1251     }
1252 }
1253
1254 - (void)rightMouseUp:(NSEvent *)o_event
1255 {
1256     vout_thread_t * p_vout;
1257     id o_window = [self window];
1258     p_vout = (vout_thread_t *)[o_window getVout];
1259     vlc_value_t val;
1260
1261     switch( [o_event type] )
1262     {
1263         case NSRightMouseUp:
1264         {
1265             var_Get( p_vout, "mouse-button-down", &val );
1266             val.i_int &= ~4;
1267             var_Set( p_vout, "mouse-button-down", val );
1268         }
1269         break;
1270         
1271         default:
1272             [super mouseUp: o_event];
1273         break;
1274     }
1275 }
1276
1277 - (void)mouseDragged:(NSEvent *)o_event
1278 {
1279     [self mouseMoved:o_event];
1280 }
1281
1282 - (void)otherMouseDragged:(NSEvent *)o_event
1283 {
1284     [self mouseMoved:o_event];
1285 }
1286
1287 - (void)rightMouseDragged:(NSEvent *)o_event
1288 {
1289     [self mouseMoved:o_event];
1290 }
1291
1292 - (void)mouseMoved:(NSEvent *)o_event
1293 {
1294     NSPoint ml;
1295     NSRect s_rect;
1296     BOOL b_inside;
1297
1298     vout_thread_t * p_vout;
1299     id o_window = [self window];
1300     p_vout = (vout_thread_t *)[o_window getVout];
1301
1302     s_rect = [self bounds];
1303     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1304     b_inside = [self mouse: ml inRect: s_rect];
1305
1306     if( b_inside )
1307     {
1308         vlc_value_t val;
1309         int i_width, i_height, i_x, i_y;
1310         
1311         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1312                                    (unsigned int)s_rect.size.height,
1313                                    &i_x, &i_y, &i_width, &i_height );
1314
1315         val.i_int = ( ((int)ml.x) - i_x ) * 
1316                     p_vout->render.i_width / i_width;
1317         var_Set( p_vout, "mouse-x", val );
1318
1319         val.i_int = ( ((int)ml.y) - i_y ) * 
1320                     p_vout->render.i_height / i_height;
1321         var_Set( p_vout, "mouse-y", val );
1322             
1323         val.b_bool = VLC_TRUE;
1324         var_Set( p_vout, "mouse-moved", val );
1325         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1326         p_vout->p_sys->b_mouse_moved = YES;
1327     }
1328
1329     [super mouseMoved: o_event];
1330 }
1331
1332 @end
1333
1334 /*****************************************************************************
1335  * VLCGLView implementation
1336  *****************************************************************************/
1337 @implementation VLCGLView
1338
1339
1340 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1341 {
1342     char * psz_effect;
1343     p_vout = _p_vout;
1344     
1345     NSOpenGLPixelFormatAttribute attribs[] =
1346     {
1347         NSOpenGLPFAAccelerated,
1348         NSOpenGLPFANoRecovery,
1349         NSOpenGLPFADoubleBuffer,
1350         NSOpenGLPFAColorSize, 24,
1351         NSOpenGLPFAAlphaSize, 8,
1352         NSOpenGLPFADepthSize, 24,
1353         NSOpenGLPFAWindow,
1354         0
1355     };
1356
1357     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1358         initWithAttributes: attribs];
1359
1360     if( !fmt )
1361     {
1362         msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1363         return nil;
1364     }
1365
1366     self = [super initWithFrame:frame pixelFormat: fmt];
1367
1368     currentContext = [self openGLContext];
1369     [currentContext makeCurrentContext];
1370     [currentContext update];
1371
1372     /* Black background */
1373     glClearColor( 0.0, 0.0, 0.0, 0.0 );
1374
1375     /* Check if the user asked for useless visual effects */
1376     psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" );
1377     if( !psz_effect || !strcmp( psz_effect, "none" ))
1378     {
1379         i_effect = OPENGL_EFFECT_NONE;
1380     }
1381     else if( !strcmp( psz_effect, "cube" ) )
1382     {
1383         i_effect = OPENGL_EFFECT_CUBE;
1384
1385         glEnable( GL_DEPTH_TEST );
1386     }
1387     else if( !strcmp( psz_effect, "transparent-cube" ) )
1388     {
1389         i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1390
1391         glDisable( GL_DEPTH_TEST );
1392         glEnable( GL_BLEND );
1393         glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1394     }
1395     else
1396     {
1397         msg_Warn( p_vout, "no valid opengl effect provided, using "
1398                   "\"none\"" );
1399         i_effect = OPENGL_EFFECT_NONE;
1400     }
1401
1402     if( i_effect & ( OPENGL_EFFECT_CUBE |
1403                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1404     {
1405         /* Set the perpective */
1406         glMatrixMode( GL_PROJECTION );
1407         glLoadIdentity();
1408         glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1409         glMatrixMode( GL_MODELVIEW );
1410         glLoadIdentity();
1411         glTranslatef( 0.0, 0.0, - 5.0 );
1412     }
1413
1414     i_texture    = 0;
1415     initDone     = 0;
1416     isFullScreen = 0;
1417
1418     return self;
1419 }
1420
1421 - (void) reshape
1422 {
1423     if( !initDone )
1424     {
1425         return;
1426     }
1427     
1428     [currentContext makeCurrentContext];
1429
1430     NSRect bounds = [self bounds];
1431     glViewport( 0, 0, (GLint) bounds.size.width,
1432                 (GLint) bounds.size.height );
1433
1434     /* Quad size is set in order to preserve the aspect ratio */
1435     if( config_GetInt( p_vout, "macosx-stretch" ) )
1436     {
1437         f_x = 1.0;
1438         f_y = 1.0;
1439     }
1440     else if( bounds.size.height * p_vout->output.i_aspect <
1441         bounds.size.width * VOUT_ASPECT_FACTOR )
1442     {
1443         f_x = bounds.size.height * p_vout->output.i_aspect /
1444             VOUT_ASPECT_FACTOR / bounds.size.width;
1445         f_y = 1.0;
1446     }
1447     else
1448     {
1449         f_x = 1.0;
1450         f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1451             p_vout->output.i_aspect / bounds.size.height;
1452     }
1453 }
1454
1455 - (void) initTextures
1456 {
1457     [currentContext makeCurrentContext];
1458
1459     /* Free previous texture if any */
1460     if( i_texture )
1461     {
1462         glDeleteTextures( 1, &i_texture );
1463     }
1464     
1465     /* Create textures */
1466     glGenTextures( 1, &i_texture );
1467
1468     glEnable( GL_TEXTURE_RECTANGLE_EXT );
1469     glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1470
1471     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture );
1472
1473     /* Use VRAM texturing */
1474     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1475             GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1476
1477     /* Tell the driver not to make a copy of the texture but to use
1478        our buffer */
1479     glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1480
1481     /* Linear interpolation */
1482     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1483             GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1484     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1485             GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1486
1487     /* I have no idea what this exactly does, but it seems to be
1488        necessary for scaling */
1489     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1490             GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1491     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1492             GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1493     glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
1494
1495     glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
1496             p_vout->output.i_width, p_vout->output.i_height, 0,
1497             GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1498             PP_OUTPUTPICTURE[0]->p_data );
1499
1500     initDone = 1;
1501 }
1502
1503 - (void)reloadTexture
1504 {
1505     if( !initDone )
1506     {
1507         return;
1508     }
1509     
1510     [currentContext makeCurrentContext];
1511
1512     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture );
1513
1514     /* glTexSubImage2D is faster than glTexImage2D
1515        http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1516        TextureRange/MainOpenGLView.m.htm */
1517     glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1518             p_vout->output.i_width, p_vout->output.i_height,
1519             GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1520             PP_OUTPUTPICTURE[0]->p_data );
1521 }
1522
1523 - (void)goFullScreen
1524 {
1525     /* Create the new pixel format */
1526     NSOpenGLPixelFormatAttribute attribs[] =
1527     {
1528         NSOpenGLPFAAccelerated,
1529         NSOpenGLPFANoRecovery,
1530         NSOpenGLPFADoubleBuffer,
1531         NSOpenGLPFAColorSize, 24,
1532         NSOpenGLPFAAlphaSize, 8,
1533         NSOpenGLPFADepthSize, 24,
1534         NSOpenGLPFAFullScreen,
1535         NSOpenGLPFAScreenMask,
1536         /* TODO handle macosx-vdev */
1537         CGDisplayIDToOpenGLDisplayMask( kCGDirectMainDisplay ),
1538         0
1539     };
1540     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1541         initWithAttributes: attribs];
1542     if( !fmt )
1543     {
1544         msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1545         return;
1546     }
1547
1548     /* Create the new OpenGL context */
1549     fullScreenContext = [[NSOpenGLContext alloc]
1550         initWithFormat: fmt shareContext: nil];
1551     if( !fullScreenContext )
1552     {
1553         msg_Warn( p_vout, "Failed to create new NSOpenGLContext" );
1554         return;
1555     }
1556     currentContext = fullScreenContext;
1557
1558     /* Capture display, switch to fullscreen */
1559     if( CGCaptureAllDisplays() != CGDisplayNoErr )
1560     {
1561         msg_Warn( p_vout, "CGCaptureAllDisplays() failed" );
1562         return;
1563     }
1564     [fullScreenContext setFullScreen];
1565     [fullScreenContext makeCurrentContext];
1566
1567     /* Ratio */
1568     unsigned width    = CGDisplayPixelsWide( kCGDirectMainDisplay );
1569     unsigned height   = CGDisplayPixelsHigh( kCGDirectMainDisplay );
1570     int      stretch  = config_GetInt( p_vout, "macosx-stretch" );
1571     int      fill     = config_GetInt( p_vout, "macosx-fill" );
1572     int      bigRatio = ( height * p_vout->output.i_aspect <
1573                           width * VOUT_ASPECT_FACTOR );
1574     if( stretch )
1575     {
1576         f_x = 1.0;
1577         f_y = 1.0;
1578     }
1579     else if( ( bigRatio && !fill ) || ( !bigRatio && fill ) )
1580     {
1581         f_x = (float) height * p_vout->output.i_aspect /
1582                   width / VOUT_ASPECT_FACTOR;
1583         f_y = 1.0;
1584     }
1585     else
1586     {
1587         f_x = 1.0;
1588         f_y = (float) width * VOUT_ASPECT_FACTOR /
1589                   p_vout->output.i_aspect / height;
1590     }
1591
1592     /* Update viewport, re-init textures */
1593     glViewport( 0, 0, width, height );
1594     [self initTextures];
1595
1596     /* Redraw the last picture */
1597     [self setNeedsDisplay: YES];
1598
1599     isFullScreen = 1;
1600 }
1601
1602 - (void)exitFullScreen
1603 {
1604     /* Free current OpenGL context */
1605     [NSOpenGLContext clearCurrentContext];
1606     [fullScreenContext clearDrawable];
1607     [fullScreenContext release];
1608     CGReleaseAllDisplays();
1609
1610     currentContext = [self openGLContext];
1611     [self initTextures];
1612     [self reshape];
1613
1614     /* Redraw the last picture */
1615     [self setNeedsDisplay: YES];
1616
1617     isFullScreen = 0;
1618 }
1619
1620 - (void) cleanUp
1621 {
1622     if( isFullScreen )
1623     {
1624         [self exitFullScreen];
1625     }
1626     initDone = 0;
1627 }
1628
1629 - (void) drawQuad
1630 {
1631     glBegin( GL_QUADS );
1632         /* Top left */
1633         glTexCoord2f( 0.0, 0.0 );
1634         glVertex2f( - f_x, f_y );
1635         /* Bottom left */
1636         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1637         glVertex2f( - f_x, - f_y );
1638         /* Bottom right */
1639         glTexCoord2f( (float) p_vout->output.i_width,
1640                       (float) p_vout->output.i_height );
1641         glVertex2f( f_x, - f_y );
1642         /* Top right */
1643         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1644         glVertex2f( f_x, f_y );
1645     glEnd();
1646 }
1647
1648 - (void) drawCube
1649 {
1650     glBegin( GL_QUADS );
1651         /* Front */
1652         glTexCoord2f( 0.0, 0.0 );
1653         glVertex3f( - 1.0, 1.0, 1.0 );
1654         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1655         glVertex3f( - 1.0, - 1.0, 1.0 );
1656         glTexCoord2f( (float) p_vout->output.i_width,
1657                       (float) p_vout->output.i_height );
1658         glVertex3f( 1.0, - 1.0, 1.0 );
1659         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1660         glVertex3f( 1.0, 1.0, 1.0 );
1661
1662         /* Left */
1663         glTexCoord2f( 0.0, 0.0 );
1664         glVertex3f( - 1.0, 1.0, - 1.0 );
1665         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1666         glVertex3f( - 1.0, - 1.0, - 1.0 );
1667         glTexCoord2f( (float) p_vout->output.i_width,
1668                       (float) p_vout->output.i_height );
1669         glVertex3f( - 1.0, - 1.0, 1.0 );
1670         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1671         glVertex3f( - 1.0, 1.0, 1.0 );
1672
1673         /* Back */
1674         glTexCoord2f( 0.0, 0.0 );
1675         glVertex3f( 1.0, 1.0, - 1.0 );
1676         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1677         glVertex3f( 1.0, - 1.0, - 1.0 );
1678         glTexCoord2f( (float) p_vout->output.i_width,
1679                       (float) p_vout->output.i_height );
1680         glVertex3f( - 1.0, - 1.0, - 1.0 );
1681         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1682         glVertex3f( - 1.0, 1.0, - 1.0 );
1683
1684         /* Right */
1685         glTexCoord2f( 0.0, 0.0 );
1686         glVertex3f( 1.0, 1.0, 1.0 );
1687         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1688         glVertex3f( 1.0, - 1.0, 1.0 );
1689         glTexCoord2f( (float) p_vout->output.i_width,
1690                       (float) p_vout->output.i_height );
1691         glVertex3f( 1.0, - 1.0, - 1.0 );
1692         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1693         glVertex3f( 1.0, 1.0, - 1.0 );
1694
1695         /* Top */
1696         glTexCoord2f( 0.0, 0.0 );
1697         glVertex3f( - 1.0, 1.0, - 1.0 );
1698         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1699         glVertex3f( - 1.0, 1.0, 1.0 );
1700         glTexCoord2f( (float) p_vout->output.i_width,
1701                       (float) p_vout->output.i_height );
1702         glVertex3f( 1.0, 1.0, 1.0 );
1703         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1704         glVertex3f( 1.0, 1.0, - 1.0 );
1705
1706         /* Bottom */
1707         glTexCoord2f( 0.0, 0.0 );
1708         glVertex3f( - 1.0, - 1.0, 1.0 );
1709         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1710         glVertex3f( - 1.0, - 1.0, - 1.0 );
1711         glTexCoord2f( (float) p_vout->output.i_width,
1712                       (float) p_vout->output.i_height );
1713         glVertex3f( 1.0, - 1.0, - 1.0 );
1714         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1715         glVertex3f( 1.0, - 1.0, 1.0 );
1716     glEnd();
1717 }
1718
1719 - (void) drawRect: (NSRect) rect
1720 {
1721     [currentContext makeCurrentContext];
1722
1723     /* Swap buffers only during the vertical retrace of the monitor.
1724        http://developer.apple.com/documentation/GraphicsImaging/
1725        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1726     long params[] = { 1 };
1727     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1728                      params );
1729     
1730     /* Black background */
1731     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1732
1733     if( !initDone )
1734     {
1735         [currentContext flushBuffer];
1736         return;
1737     }
1738
1739     /* Draw */
1740     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture );
1741     if( i_effect & ( OPENGL_EFFECT_CUBE |
1742                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1743     {
1744         glRotatef( 1.0, 0.3, 0.5, 0.7 );
1745         [self drawCube];
1746     }
1747     else
1748     {
1749         [self drawQuad];
1750     }
1751
1752     /* Wait for the job to be done */
1753     [currentContext flushBuffer];
1754 }
1755
1756 @end
1757
1758 /*****************************************************************************
1759  * VLCVout implementation
1760  *****************************************************************************/
1761 @implementation VLCVout
1762
1763 - (void)createWindow:(NSValue *)o_value
1764 {
1765     vlc_value_t val;
1766     VLCQTView * o_view;
1767     NSScreen * o_screen;
1768     vout_thread_t * p_vout;
1769     vlc_bool_t b_main_screen;
1770     
1771     p_vout = (vout_thread_t *)[o_value pointerValue];
1772
1773     p_vout->p_sys->o_window = [VLCWindow alloc];
1774     [p_vout->p_sys->o_window setVout: p_vout];
1775     [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1776
1777     if( var_Get( p_vout, "video-device", &val ) < 0 )
1778     {
1779         o_screen = [NSScreen mainScreen];
1780         b_main_screen = 1;
1781     }
1782     else
1783     {
1784         NSArray *o_screens = [NSScreen screens];
1785         unsigned int i_index = val.i_int;
1786         
1787         if( [o_screens count] < i_index )
1788         {
1789             o_screen = [NSScreen mainScreen];
1790             b_main_screen = 1;
1791         }
1792         else
1793         {
1794             i_index--;
1795             o_screen = [o_screens objectAtIndex: i_index];
1796             config_PutInt( p_vout, "macosx-vdev", i_index );
1797             b_main_screen = (i_index == 0);
1798         }
1799     } 
1800
1801     if( p_vout->b_fullscreen && !p_vout->p_sys->i_opengl )
1802     {
1803         NSRect screen_rect = [o_screen frame];
1804         screen_rect.origin.x = screen_rect.origin.y = 0;
1805
1806         if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1807             BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1808                              NULL, NULL, fullScreenAllowEvents );
1809
1810         [p_vout->p_sys->o_window 
1811             initWithContentRect: screen_rect
1812             styleMask: NSBorderlessWindowMask
1813             backing: NSBackingStoreBuffered
1814             defer: NO screen: o_screen];
1815
1816         //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1817         p_vout->p_sys->b_mouse_moved = YES;
1818         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1819     }
1820     else
1821     {
1822         unsigned int i_stylemask = NSTitledWindowMask |
1823                                    NSMiniaturizableWindowMask |
1824                                    NSClosableWindowMask |
1825                                    NSResizableWindowMask;
1826         
1827         if( !p_vout->p_sys->i_opengl )
1828         {
1829             if ( p_vout->p_sys->p_fullscreen_state != NULL )
1830                 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1831             p_vout->p_sys->p_fullscreen_state = NULL;
1832         }
1833
1834         [p_vout->p_sys->o_window 
1835             initWithContentRect: p_vout->p_sys->s_rect
1836             styleMask: i_stylemask
1837             backing: NSBackingStoreBuffered
1838             defer: NO screen: o_screen];
1839
1840         [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1841         
1842         if( config_GetInt( p_vout, "video-on-top" ) )
1843         {
1844             [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1845         }
1846         
1847         if( !p_vout->p_sys->b_pos_saved )   
1848         {
1849             [p_vout->p_sys->o_window center];
1850         }
1851     }
1852
1853     if( !p_vout->p_sys->i_opengl )
1854     {
1855         o_view = [[VLCQTView alloc] init];
1856         /* FIXME: [o_view setMenu:] */
1857         [p_vout->p_sys->o_window setContentView: o_view];
1858         [o_view autorelease];
1859
1860         [o_view lockFocus];
1861         p_vout->p_sys->p_qdport = [o_view qdPort];
1862         [o_view unlockFocus];
1863     }
1864     else
1865     {
1866 #define o_glview p_vout->p_sys->o_glview
1867         o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
1868         [p_vout->p_sys->o_window setContentView: o_glview];
1869         [o_glview autorelease];
1870 #undef o_glview
1871     }
1872     
1873     [p_vout->p_sys->o_window updateTitle];
1874     [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1875
1876 }
1877
1878 - (void)destroyWindow:(NSValue *)o_value
1879 {
1880     vout_thread_t * p_vout;
1881
1882     p_vout = (vout_thread_t *)[o_value pointerValue];
1883
1884     if( !p_vout->b_fullscreen )
1885     {
1886         NSRect s_rect;
1887
1888         s_rect = [[p_vout->p_sys->o_window contentView] frame];
1889         p_vout->p_sys->s_rect.size = s_rect.size;
1890
1891         s_rect = [p_vout->p_sys->o_window frame];
1892         p_vout->p_sys->s_rect.origin = s_rect.origin;
1893
1894         p_vout->p_sys->b_pos_saved = YES;
1895     }
1896     
1897     p_vout->p_sys->p_qdport = nil;
1898     [p_vout->p_sys->o_window close];
1899     p_vout->p_sys->o_window = nil;
1900 }
1901
1902 @end