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