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