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