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