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