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