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