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