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