]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
MacOSX intf work
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id$
6  *
7  * Authors: Colin Delacroix <colin@zoy.org>
8  *          Florian G. Pflug <fgp@phlo.org>
9  *          Jon Lech Johansen <jon-vl@nanocrew.net>
10  *          Derk-Jan Hartman <thedj@users.sourceforge.net>
11  *          Eric Petit <titer@m0k.org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <errno.h>                                                 /* ENOMEM */
32 #include <stdlib.h>                                                /* free() */
33 #include <string.h>                                            /* strerror() */
34
35 #include <QuickTime/QuickTime.h>
36
37 #include <OpenGL/OpenGL.h>
38 #include <OpenGL/gl.h>
39 #include <OpenGL/glext.h>
40
41 #include <vlc_keys.h>
42
43 #include "intf.h"
44 #include "vout.h"
45
46 #define QT_MAX_DIRECTBUFFERS 10
47 #define VL_MAX_DISPLAYS 16
48
49 #define OPENGL_EFFECT_NONE             1
50 #define OPENGL_EFFECT_CUBE             2
51 #define OPENGL_EFFECT_TRANSPARENT_CUBE 4
52
53 struct picture_sys_t
54 {
55     void *p_info;
56     unsigned int i_size;
57
58     /* When using I420 output */
59     PlanarPixmapInfoYUV420 pixmap_i420;
60 };
61
62 /*****************************************************************************
63  * Local prototypes
64  *****************************************************************************/
65
66 static int  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 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1156 {
1157     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
1158 }
1159 */
1160 - (void)keyDown:(NSEvent *)o_event
1161 {
1162     unichar key = 0;
1163     vlc_value_t val;
1164     unsigned int i_pressed_modifiers = 0;
1165     val.i_int = 0;
1166     
1167     i_pressed_modifiers = [o_event modifierFlags];
1168
1169     if( i_pressed_modifiers & NSShiftKeyMask )
1170         val.i_int |= KEY_MODIFIER_SHIFT;
1171     if( i_pressed_modifiers & NSControlKeyMask )
1172         val.i_int |= KEY_MODIFIER_CTRL;
1173     if( i_pressed_modifiers & NSAlternateKeyMask )
1174         val.i_int |= KEY_MODIFIER_ALT;
1175     if( i_pressed_modifiers & NSCommandKeyMask )
1176         val.i_int |= KEY_MODIFIER_COMMAND;
1177
1178     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1179
1180     if( key )
1181     {
1182         /* Escape should always get you out of fullscreen */
1183         if( key == (unichar) 0x1b )
1184         {
1185              if( [self isFullscreen] )
1186              {
1187                  [self toggleFullscreen];
1188              }
1189         }
1190         else if ( key == ' ' )
1191         {
1192              playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1193                                                      FIND_ANYWHERE );
1194              if ( p_playlist != NULL )
1195              {
1196                  playlist_Pause( p_playlist );
1197                  vlc_object_release( p_playlist);
1198              }
1199         }
1200         else
1201         {
1202             val.i_int |= CocoaKeyToVLC( key );
1203             var_Set( p_vout->p_vlc, "key-pressed", val );
1204         }
1205     }
1206     else
1207     {
1208         [super keyDown: o_event];
1209     }
1210 }
1211
1212 - (void)updateTitle
1213 {
1214     NSMutableString * o_title;
1215     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1216                                                        FIND_ANYWHERE );
1217     
1218     if( p_playlist == NULL )
1219     {
1220         return;
1221     }
1222
1223     vlc_mutex_lock( &p_playlist->object_lock );
1224     o_title = [NSMutableString stringWithUTF8String: 
1225         p_playlist->pp_items[p_playlist->i_index]->input.psz_uri]; 
1226     vlc_mutex_unlock( &p_playlist->object_lock );
1227
1228     vlc_object_release( p_playlist );
1229
1230     if( o_title != nil )
1231     {
1232         NSRange prefix_range = [o_title rangeOfString: @"file:"];
1233         if( prefix_range.location != NSNotFound )
1234         {
1235             [o_title deleteCharactersInRange: prefix_range];
1236         }
1237
1238         [self setTitleWithRepresentedFilename: o_title];
1239     }
1240     else
1241     {
1242         [self setTitle:
1243             [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1244     }
1245 }
1246
1247 /* This is actually the same as VLCControls::stop. */
1248 - (BOOL)windowShouldClose:(id)sender
1249 {
1250     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1251                                                        FIND_ANYWHERE );
1252     if( p_playlist == NULL )      
1253     {
1254         return NO;
1255     }
1256
1257     playlist_Stop( p_playlist );
1258     vlc_object_release( p_playlist );
1259
1260     /* The window will be closed by the intf later. */
1261     return NO;
1262 }
1263
1264 @end
1265
1266 /* Common QT and OpenGL code to catch mouse events */
1267 #define CATCH_MOUSE_EVENTS \
1268 - (BOOL)acceptsFirstResponder \
1269 { \
1270     return( YES ); \
1271 } \
1272  \
1273 - (BOOL)becomeFirstResponder \
1274 { \
1275     id o_window = [self window]; \
1276      \
1277     [o_window setAcceptsMouseMovedEvents: YES]; \
1278     return( YES ); \
1279 } \
1280  \
1281 - (BOOL)resignFirstResponder \
1282 { \
1283     vout_thread_t * vout; \
1284     id o_window = [self window]; \
1285     vout = (vout_thread_t *)[o_window getVout]; \
1286      \
1287     [o_window setAcceptsMouseMovedEvents: NO]; \
1288     VLCHideMouse( vout, NO ); \
1289     return( YES ); \
1290 } \
1291  \
1292 - (void)mouseDown:(NSEvent *)o_event \
1293 { \
1294     vout_thread_t * vout; \
1295     id o_window = [self window]; \
1296     vout = (vout_thread_t *)[o_window getVout]; \
1297     vlc_value_t val; \
1298  \
1299     switch( [o_event type] ) \
1300     {         \
1301         case NSLeftMouseDown: \
1302         { \
1303             var_Get( vout, "mouse-button-down", &val ); \
1304             val.i_int |= 1; \
1305             var_Set( vout, "mouse-button-down", val ); \
1306         } \
1307         break; \
1308          \
1309         default: \
1310             [super mouseDown: o_event]; \
1311         break; \
1312     } \
1313 } \
1314  \
1315 - (void)otherMouseDown:(NSEvent *)o_event \
1316 { \
1317     vout_thread_t * vout; \
1318     id o_window = [self window]; \
1319     vout = (vout_thread_t *)[o_window getVout]; \
1320     vlc_value_t val; \
1321  \
1322     switch( [o_event type] ) \
1323     { \
1324         case NSOtherMouseDown: \
1325         { \
1326             var_Get( vout, "mouse-button-down", &val ); \
1327             val.i_int |= 2; \
1328             var_Set( vout, "mouse-button-down", val ); \
1329         } \
1330         break; \
1331          \
1332         default: \
1333             [super mouseDown: o_event]; \
1334         break; \
1335     } \
1336 } \
1337  \
1338 - (void)rightMouseDown:(NSEvent *)o_event \
1339 { \
1340     vout_thread_t * vout; \
1341     id o_window = [self window]; \
1342     vout = (vout_thread_t *)[o_window getVout]; \
1343     vlc_value_t val; \
1344  \
1345     switch( [o_event type] ) \
1346     { \
1347         case NSRightMouseDown: \
1348         { \
1349             var_Get( vout, "mouse-button-down", &val ); \
1350             val.i_int |= 4; \
1351             var_Set( vout, "mouse-button-down", val ); \
1352         } \
1353         break; \
1354          \
1355         default: \
1356             [super mouseDown: o_event]; \
1357         break; \
1358     } \
1359 } \
1360  \
1361 - (void)mouseUp:(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 NSLeftMouseUp: \
1371         { \
1372             vlc_value_t b_val; \
1373             b_val.b_bool = VLC_TRUE; \
1374             var_Set( vout, "mouse-clicked", b_val ); \
1375              \
1376             var_Get( vout, "mouse-button-down", &val ); \
1377             val.i_int &= ~1; \
1378             var_Set( vout, "mouse-button-down", val ); \
1379         } \
1380         break; \
1381                  \
1382         default: \
1383             [super mouseUp: o_event]; \
1384         break; \
1385     } \
1386 } \
1387  \
1388 - (void)otherMouseUp:(NSEvent *)o_event \
1389 { \
1390     vout_thread_t * vout; \
1391     id o_window = [self window]; \
1392     vout = (vout_thread_t *)[o_window getVout]; \
1393     vlc_value_t val; \
1394  \
1395     switch( [o_event type] ) \
1396     { \
1397         case NSOtherMouseUp: \
1398         { \
1399             var_Get( vout, "mouse-button-down", &val ); \
1400             val.i_int &= ~2; \
1401             var_Set( vout, "mouse-button-down", val ); \
1402         } \
1403         break; \
1404                  \
1405         default: \
1406             [super mouseUp: o_event]; \
1407         break; \
1408     } \
1409 } \
1410  \
1411 - (void)rightMouseUp:(NSEvent *)o_event \
1412 { \
1413     vout_thread_t * vout; \
1414     id o_window = [self window]; \
1415     vout = (vout_thread_t *)[o_window getVout]; \
1416     vlc_value_t val; \
1417  \
1418     switch( [o_event type] ) \
1419     { \
1420         case NSRightMouseUp: \
1421         { \
1422             var_Get( vout, "mouse-button-down", &val ); \
1423             val.i_int &= ~4; \
1424             var_Set( vout, "mouse-button-down", val ); \
1425         } \
1426         break; \
1427          \
1428         default: \
1429             [super mouseUp: o_event]; \
1430         break; \
1431     } \
1432 } \
1433  \
1434 - (void)mouseDragged:(NSEvent *)o_event \
1435 { \
1436     [self mouseMoved:o_event]; \
1437 } \
1438  \
1439 - (void)otherMouseDragged:(NSEvent *)o_event \
1440 { \
1441     [self mouseMoved:o_event]; \
1442 } \
1443  \
1444 - (void)rightMouseDragged:(NSEvent *)o_event \
1445 { \
1446     [self mouseMoved:o_event]; \
1447 }
1448
1449 /*****************************************************************************
1450  * VLCQTView implementation
1451  *****************************************************************************/
1452 @implementation VLCQTView
1453
1454 - (void)drawRect:(NSRect)rect
1455 {
1456     vout_thread_t * p_vout;
1457     id o_window = [self window];
1458     p_vout = (vout_thread_t *)[o_window getVout];
1459     
1460     [[NSColor blackColor] set];
1461     NSRectFill( rect );
1462     [super drawRect: rect];
1463
1464     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1465 }
1466
1467 CATCH_MOUSE_EVENTS
1468
1469 - (void)mouseMoved:(NSEvent *)o_event
1470 {
1471     NSPoint ml;
1472     NSRect s_rect;
1473     BOOL b_inside;
1474
1475     vout_thread_t * p_vout;
1476     id o_window = [self window];
1477     p_vout = (vout_thread_t *)[o_window getVout];
1478
1479     s_rect = [self bounds];
1480     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1481     b_inside = [self mouse: ml inRect: s_rect];
1482
1483     if( b_inside )
1484     {
1485         vlc_value_t val;
1486         int i_width, i_height, i_x, i_y;
1487         
1488         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1489                                    (unsigned int)s_rect.size.height,
1490                                    &i_x, &i_y, &i_width, &i_height );
1491
1492         val.i_int = ( ((int)ml.x) - i_x ) * 
1493                     p_vout->render.i_width / i_width;
1494         var_Set( p_vout, "mouse-x", val );
1495
1496         val.i_int = ( ((int)ml.y) - i_y ) * 
1497                     p_vout->render.i_height / i_height;
1498         var_Set( p_vout, "mouse-y", val );
1499             
1500         val.b_bool = VLC_TRUE;
1501         var_Set( p_vout, "mouse-moved", val );
1502         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1503         p_vout->p_sys->b_mouse_moved = YES;
1504     }
1505
1506     [super mouseMoved: o_event];
1507 }
1508
1509 @end
1510
1511 /*****************************************************************************
1512  * VLCGLView implementation
1513  *****************************************************************************/
1514 @implementation VLCGLView
1515
1516
1517 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1518 {
1519     vlc_value_t val;
1520     p_vout = _p_vout;
1521     
1522     NSOpenGLPixelFormatAttribute attribs[] =
1523     {
1524         NSOpenGLPFAAccelerated,
1525         NSOpenGLPFANoRecovery,
1526         NSOpenGLPFAColorSize, 24,
1527         NSOpenGLPFAAlphaSize, 8,
1528         NSOpenGLPFADepthSize, 24,
1529         NSOpenGLPFAWindow,
1530         0
1531     };
1532
1533     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1534         initWithAttributes: attribs];
1535
1536     if( !fmt )
1537     {
1538         msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1539         return nil;
1540     }
1541
1542     self = [super initWithFrame:frame pixelFormat: fmt];
1543     [fmt release];
1544
1545     [[self openGLContext] makeCurrentContext];
1546     [[self openGLContext] update];
1547
1548     /* Black background */
1549     glClearColor( 0.0, 0.0, 0.0, 0.0 );
1550
1551     /* Check if the user asked for useless visual effects */
1552     var_Get( p_vout, "macosx-opengl-effect", &val );
1553     if( !val.psz_string || !strcmp( val.psz_string, "none" ))
1554     {
1555         i_effect = OPENGL_EFFECT_NONE;
1556     }
1557     else if( !strcmp( val.psz_string, "cube" ) )
1558     {
1559         i_effect = OPENGL_EFFECT_CUBE;
1560
1561         glEnable( GL_DEPTH_TEST );
1562     }
1563     else if( !strcmp( val.psz_string, "transparent-cube" ) )
1564     {
1565         i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1566
1567         glDisable( GL_DEPTH_TEST );
1568         glEnable( GL_BLEND );
1569         glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1570     }
1571     else
1572     {
1573         msg_Warn( p_vout, "no valid opengl effect provided, using "
1574                   "\"none\"" );
1575         i_effect = OPENGL_EFFECT_NONE;
1576     }
1577     if( val.psz_string ) free( val.psz_string );
1578
1579     if( i_effect & ( OPENGL_EFFECT_CUBE |
1580                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1581     {
1582         /* Set the perpective */
1583         glMatrixMode( GL_PROJECTION );
1584         glLoadIdentity();
1585         glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1586         glMatrixMode( GL_MODELVIEW );
1587         glLoadIdentity();
1588         glTranslatef( 0.0, 0.0, - 5.0 );
1589     }
1590
1591     pi_textures[0] = 0;
1592     pi_textures[1] = 0;
1593     initDone       = 0;
1594
1595     return self;
1596 }
1597
1598 - (void)reshape
1599 {
1600     vlc_value_t val;
1601     NSRect bounds;
1602
1603     if( !initDone )
1604     {
1605         return;
1606     }
1607     
1608     [[self openGLContext] makeCurrentContext];
1609
1610     bounds = [self bounds];
1611     glViewport( 0, 0, (GLint) bounds.size.width,
1612                 (GLint) bounds.size.height );
1613
1614     var_Get( p_vout, "macosx-stretch", &val );
1615     if( val.b_bool )
1616     {
1617         f_x = 1.0;
1618         f_y = 1.0;
1619     }
1620     else
1621     {
1622         /* Quad size is set in order to preserve the aspect ratio */
1623         var_Get( p_vout, "macosx-fill", &val );
1624         int fill = ( val.b_bool && p_vout->b_fullscreen );
1625         int large = ( bounds.size.height * p_vout->output.i_aspect <
1626                       bounds.size.width * VOUT_ASPECT_FACTOR );
1627         if( ( large && !fill ) || ( !large && fill ) )
1628         {
1629             f_x = bounds.size.height * p_vout->output.i_aspect /
1630                 VOUT_ASPECT_FACTOR / bounds.size.width;
1631             f_y = 1.0;
1632         }
1633         else
1634         {
1635             f_x = 1.0;
1636             f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1637                 p_vout->output.i_aspect / bounds.size.height;
1638         }
1639     }
1640 }
1641
1642 - (void)initTextures
1643 {
1644     int i;
1645     [[self openGLContext] makeCurrentContext];
1646
1647     /* Free previous texture if any */
1648     if( initDone )
1649     {
1650         glDeleteTextures( 2, pi_textures );
1651     }
1652
1653     glEnable( GL_TEXTURE_RECTANGLE_EXT );
1654     glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1655
1656     glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1657     glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1658
1659     /* Tell the driver not to make a copy of the texture but to use
1660        our buffer */
1661     glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1662
1663     /* Create textures */
1664     glGenTextures( 2, pi_textures );
1665
1666     for( i = 0; i < 2; i++ )
1667     {
1668         glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1669         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1670
1671         /* Linear interpolation */
1672         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1673                 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1674         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1675                 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1676
1677         /* Use VRAM texturing */
1678         glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1679                 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1680
1681         glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB8,
1682                 p_vout->output.i_width, p_vout->output.i_height, 0,
1683                 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1684                 p_vout->p_sys->p_data[i] );
1685     }
1686
1687     /* Swap buffers only during the vertical retrace of the monitor.
1688        http://developer.apple.com/documentation/GraphicsImaging/
1689        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1690     long params[] = { 1 };
1691     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1692                      params );
1693
1694     initDone = 1;
1695 }
1696
1697 - (void)reloadTexture: (int) index
1698 {
1699     if( !initDone )
1700     {
1701         return;
1702     }
1703     
1704     [[self openGLContext] makeCurrentContext];
1705
1706     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1707     glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1708
1709     /* glTexSubImage2D is faster than glTexImage2D
1710        http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1711        TextureRange/MainOpenGLView.m.htm */
1712     glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1713             p_vout->output.i_width, p_vout->output.i_height,
1714             GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1715             p_vout->p_sys->p_data[index] );
1716 }
1717
1718 - (void) cleanUp
1719 {
1720     initDone = 0;
1721 }
1722
1723 - (void) drawQuad
1724 {
1725     glBegin( GL_QUADS );
1726         /* Top left */
1727         glTexCoord2f( 0.0, 0.0 );
1728         glVertex2f( - f_x, f_y );
1729         /* Bottom left */
1730         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1731         glVertex2f( - f_x, - f_y );
1732         /* Bottom right */
1733         glTexCoord2f( (float) p_vout->output.i_width,
1734                       (float) p_vout->output.i_height );
1735         glVertex2f( f_x, - f_y );
1736         /* Top right */
1737         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1738         glVertex2f( f_x, f_y );
1739     glEnd();
1740 }
1741
1742 - (void) drawCube
1743 {
1744     glBegin( GL_QUADS );
1745         /* Front */
1746         glTexCoord2f( 0.0, 0.0 );
1747         glVertex3f( - 1.0, 1.0, 1.0 );
1748         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1749         glVertex3f( - 1.0, - 1.0, 1.0 );
1750         glTexCoord2f( (float) p_vout->output.i_width,
1751                       (float) p_vout->output.i_height );
1752         glVertex3f( 1.0, - 1.0, 1.0 );
1753         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1754         glVertex3f( 1.0, 1.0, 1.0 );
1755
1756         /* Left */
1757         glTexCoord2f( 0.0, 0.0 );
1758         glVertex3f( - 1.0, 1.0, - 1.0 );
1759         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1760         glVertex3f( - 1.0, - 1.0, - 1.0 );
1761         glTexCoord2f( (float) p_vout->output.i_width,
1762                       (float) p_vout->output.i_height );
1763         glVertex3f( - 1.0, - 1.0, 1.0 );
1764         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1765         glVertex3f( - 1.0, 1.0, 1.0 );
1766
1767         /* Back */
1768         glTexCoord2f( 0.0, 0.0 );
1769         glVertex3f( 1.0, 1.0, - 1.0 );
1770         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1771         glVertex3f( 1.0, - 1.0, - 1.0 );
1772         glTexCoord2f( (float) p_vout->output.i_width,
1773                       (float) p_vout->output.i_height );
1774         glVertex3f( - 1.0, - 1.0, - 1.0 );
1775         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1776         glVertex3f( - 1.0, 1.0, - 1.0 );
1777
1778         /* Right */
1779         glTexCoord2f( 0.0, 0.0 );
1780         glVertex3f( 1.0, 1.0, 1.0 );
1781         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1782         glVertex3f( 1.0, - 1.0, 1.0 );
1783         glTexCoord2f( (float) p_vout->output.i_width,
1784                       (float) p_vout->output.i_height );
1785         glVertex3f( 1.0, - 1.0, - 1.0 );
1786         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1787         glVertex3f( 1.0, 1.0, - 1.0 );
1788
1789         /* Top */
1790         glTexCoord2f( 0.0, 0.0 );
1791         glVertex3f( - 1.0, 1.0, - 1.0 );
1792         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1793         glVertex3f( - 1.0, 1.0, 1.0 );
1794         glTexCoord2f( (float) p_vout->output.i_width,
1795                       (float) p_vout->output.i_height );
1796         glVertex3f( 1.0, 1.0, 1.0 );
1797         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1798         glVertex3f( 1.0, 1.0, - 1.0 );
1799
1800         /* Bottom */
1801         glTexCoord2f( 0.0, 0.0 );
1802         glVertex3f( - 1.0, - 1.0, 1.0 );
1803         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1804         glVertex3f( - 1.0, - 1.0, - 1.0 );
1805         glTexCoord2f( (float) p_vout->output.i_width,
1806                       (float) p_vout->output.i_height );
1807         glVertex3f( 1.0, - 1.0, - 1.0 );
1808         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1809         glVertex3f( 1.0, - 1.0, 1.0 );
1810     glEnd();
1811 }
1812
1813 - (void) drawRect: (NSRect) rect
1814 {
1815     [[self openGLContext] makeCurrentContext];
1816
1817     /* Black background */
1818     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1819
1820     if( !initDone )
1821     {
1822         glFlush();
1823         return;
1824     }
1825
1826     /* Draw */
1827     glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1828                    pi_textures[p_vout->p_sys->i_cur_pic] );
1829     if( i_effect & ( OPENGL_EFFECT_CUBE |
1830                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1831     {
1832         glRotatef( 1.0, 0.3, 0.5, 0.7 );
1833         [self drawCube];
1834     }
1835     else
1836     {
1837         [self drawQuad];
1838     }
1839
1840     /* Draw */
1841     glFlush();
1842 }
1843
1844 CATCH_MOUSE_EVENTS
1845
1846 - (void)mouseMoved:(NSEvent *)o_event
1847 {
1848     NSPoint ml;
1849     NSRect s_rect;
1850     BOOL b_inside;
1851
1852     s_rect = [self bounds];
1853     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1854     b_inside = [self mouse: ml inRect: s_rect];
1855
1856     if( b_inside )
1857     {
1858         vlc_value_t val;
1859         int i_width, i_height, i_x, i_y;
1860         
1861         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1862                                    (unsigned int)s_rect.size.height,
1863                                    &i_x, &i_y, &i_width, &i_height );
1864
1865         val.i_int = ( (int)ml.x - i_x ) * 
1866                     p_vout->render.i_width / i_width;
1867         var_Set( p_vout, "mouse-x", val );
1868
1869         /* Y coordinate is inverted in OpenGL */
1870         val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
1871                     p_vout->render.i_height / i_height;
1872         var_Set( p_vout, "mouse-y", val );
1873             
1874         val.b_bool = VLC_TRUE;
1875         var_Set( p_vout, "mouse-moved", val );
1876         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1877         p_vout->p_sys->b_mouse_moved = YES;
1878     }
1879
1880     [super mouseMoved: o_event];
1881 }
1882
1883 @end