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