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