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