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