421957c61a3075a29ff88383f3bb0c000a3e7ed4
[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.75 2004/01/29 02:01:49 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         /* Do this now instead of initWithFrame because we need a
341            first buffer for the glTexImage2D call */
342         [p_vout->p_sys->o_glview initTexture];
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         /* Update the texture with our new picture */
480         [p_vout->p_sys->o_glview reloadTexture: p_pic->p->p_pixels];
481     }
482 }
483
484 /*****************************************************************************
485  * CoSendRequest: send request to interface thread
486  *****************************************************************************
487  * Returns 0 on success, 1 otherwise
488  *****************************************************************************/
489 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
490 {
491     int i_ret = 0;
492     vlc_value_t val;
493     intf_thread_t * p_intf;
494
495     VLCVout * o_vlv = [[VLCVout alloc] init];
496
497     if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
498     {
499         msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
500     }
501
502     [o_vlv release];
503
504     /*This makes this function dependant of the presence of a macosx 
505     interface. We do not check if this interface exists, since it has 
506     already been done before.*/
507
508     p_intf = [NSApp getIntf];
509
510     val.b_bool = VLC_TRUE;
511     var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
512     var_Set(p_intf, "intf-change",val);
513
514     return( i_ret );
515 }
516
517 /*****************************************************************************
518  * CoCreateWindow: create new window 
519  *****************************************************************************
520  * Returns 0 on success, 1 otherwise
521  *****************************************************************************/
522 static int CoCreateWindow( vout_thread_t *p_vout )
523 {
524     if( CoSendRequest( p_vout, @selector(createWindow:) ) )
525     {
526         msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
527         return( 1 );
528     }
529     
530     return( 0 );
531 }
532
533 /*****************************************************************************
534  * CoDestroyWindow: destroy window 
535  *****************************************************************************
536  * Returns 0 on success, 1 otherwise
537  *****************************************************************************/
538 static int CoDestroyWindow( vout_thread_t *p_vout )
539 {
540
541     VLCHideMouse( p_vout, NO );
542
543     if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
544     {
545         msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
546         return( 1 );
547     }
548
549     return( 0 );
550 }
551
552 /*****************************************************************************
553  * CoToggleFullscreen: toggle fullscreen 
554  *****************************************************************************
555  * Returns 0 on success, 1 otherwise
556  *****************************************************************************/
557 static int CoToggleFullscreen( vout_thread_t *p_vout )
558 {
559     if( p_vout->p_sys->i_opengl )
560     {
561         /* TODO */
562         return 0;
563     }
564     
565     QTDestroySequence( p_vout );
566
567     if( CoDestroyWindow( p_vout ) )
568     {
569         msg_Err( p_vout, "unable to destroy window" );
570         return( 1 );
571     }
572     
573     p_vout->b_fullscreen = !p_vout->b_fullscreen;
574
575     if( CoCreateWindow( p_vout ) )
576     {
577         msg_Err( p_vout, "unable to create window" );
578         return( 1 );
579     }
580
581     SetPort( p_vout->p_sys->p_qdport );
582     QTScaleMatrix( p_vout );
583
584     if( QTCreateSequence( p_vout ) )
585     {
586         msg_Err( p_vout, "unable to create sequence" );
587         return( 1 ); 
588     } 
589
590     return( 0 );
591 }
592
593 /*****************************************************************************
594  * VLCHideMouse: if b_hide then hide the cursor
595  *****************************************************************************/
596 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
597 {
598     BOOL b_inside;
599     NSRect s_rect;
600     NSPoint ml;
601     NSWindow *o_window = p_vout->p_sys->o_window;
602     NSView *o_contents = [o_window contentView];
603     
604     s_rect = [o_contents bounds];
605     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
606     ml = [o_contents convertPoint:ml fromView:nil];
607     b_inside = [o_contents mouse: ml inRect: s_rect];
608     
609     if ( b_hide && b_inside )
610     {
611         /* only hide if mouse over VLCQTView */
612         [NSCursor setHiddenUntilMouseMoves: YES];
613     }
614     else if ( !b_hide )
615     {
616         [NSCursor setHiddenUntilMouseMoves: NO];
617     }
618     p_vout->p_sys->b_mouse_moved = NO;
619     p_vout->p_sys->i_time_mouse_last_moved = mdate();
620     return;
621 }
622
623 /*****************************************************************************
624  * QTScaleMatrix: scale matrix 
625  *****************************************************************************/
626 static void QTScaleMatrix( vout_thread_t *p_vout )
627 {
628     Rect s_rect;
629     unsigned int i_width, i_height;
630     Fixed factor_x, factor_y;
631     unsigned int i_offset_x = 0;
632     unsigned int i_offset_y = 0;
633
634     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
635
636     i_width = s_rect.right - s_rect.left;
637     i_height = s_rect.bottom - s_rect.top;
638
639     if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
640     {
641         int i_adj_width = i_height * p_vout->output.i_aspect /
642                           VOUT_ASPECT_FACTOR;
643
644         factor_x = FixDiv( Long2Fix( i_adj_width ),
645                            Long2Fix( p_vout->output.i_width ) );
646         factor_y = FixDiv( Long2Fix( i_height ),
647                            Long2Fix( p_vout->output.i_height ) );
648
649         i_offset_x = (i_width - i_adj_width) / 2;
650     }
651     else
652     {
653         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
654                            p_vout->output.i_aspect;
655
656         factor_x = FixDiv( Long2Fix( i_width ),
657                            Long2Fix( p_vout->output.i_width ) );
658         factor_y = FixDiv( Long2Fix( i_adj_height ),
659                            Long2Fix( p_vout->output.i_height ) );
660
661         i_offset_y = (i_height - i_adj_height) / 2;
662     }
663     
664     SetIdentityMatrix( p_vout->p_sys->p_matrix );
665
666     ScaleMatrix( p_vout->p_sys->p_matrix,
667                  factor_x, factor_y,
668                  Long2Fix(0), Long2Fix(0) );            
669
670     TranslateMatrix( p_vout->p_sys->p_matrix, 
671                      Long2Fix(i_offset_x), 
672                      Long2Fix(i_offset_y) );
673
674 }
675
676 /*****************************************************************************
677  * QTCreateSequence: create a new sequence 
678  *****************************************************************************
679  * Returns 0 on success, 1 otherwise
680  *****************************************************************************/
681 static int QTCreateSequence( vout_thread_t *p_vout )
682 {
683     OSErr err;
684     ImageDescriptionPtr p_descr;
685
686     HLock( (Handle)p_vout->p_sys->h_img_descr );
687     p_descr = *p_vout->p_sys->h_img_descr;
688
689     p_descr->idSize = sizeof(ImageDescription);
690     p_descr->cType = p_vout->p_sys->i_codec;
691     p_descr->version = 1;
692     p_descr->revisionLevel = 0;
693     p_descr->vendor = 'appl';
694     p_descr->width = p_vout->output.i_width;
695     p_descr->height = p_vout->output.i_height;
696     p_descr->hRes = Long2Fix(72);
697     p_descr->vRes = Long2Fix(72);
698     p_descr->spatialQuality = codecLosslessQuality;
699     p_descr->frameCount = 1;
700     p_descr->clutID = -1;
701     p_descr->dataSize = 0;
702     p_descr->depth = 24;
703
704     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
705
706     if( ( err = DecompressSequenceBeginS( 
707                               &p_vout->p_sys->i_seq,
708                               p_vout->p_sys->h_img_descr,
709                               NULL, 0,
710                               p_vout->p_sys->p_qdport,
711                               NULL, NULL,
712                               p_vout->p_sys->p_matrix,
713                               0, NULL,
714                               codecFlagUseImageBuffer,
715                               codecLosslessQuality,
716                               p_vout->p_sys->img_dc ) ) )
717     {
718         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
719         return( 1 );
720     }
721
722     return( 0 );
723 }
724
725 /*****************************************************************************
726  * QTDestroySequence: destroy sequence 
727  *****************************************************************************/
728 static void QTDestroySequence( vout_thread_t *p_vout )
729 {
730     CDSequenceEnd( p_vout->p_sys->i_seq );
731 }
732
733 /*****************************************************************************
734  * QTNewPicture: allocate a picture
735  *****************************************************************************
736  * Returns 0 on success, 1 otherwise
737  *****************************************************************************/
738 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
739 {
740     int i_width  = p_vout->output.i_width;
741     int i_height = p_vout->output.i_height;
742
743     /* We know the chroma, allocate a buffer which will be used
744      * directly by the decoder */
745     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
746
747     if( p_pic->p_sys == NULL )
748     {
749         return( -1 );
750     }
751
752     switch( p_vout->output.i_chroma )
753     {
754         case VLC_FOURCC('I','4','2','0'):
755
756             p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
757             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
758
759             /* Allocate the memory buffer */
760             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
761                                           16, i_width * i_height * 3 / 2 );
762
763             /* Y buffer */
764             p_pic->Y_PIXELS = p_pic->p_data; 
765             p_pic->p[Y_PLANE].i_lines = i_height;
766             p_pic->p[Y_PLANE].i_pitch = i_width;
767             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
768             p_pic->p[Y_PLANE].i_visible_pitch = i_width;
769
770             /* U buffer */
771             p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
772             p_pic->p[U_PLANE].i_lines = i_height / 2;
773             p_pic->p[U_PLANE].i_pitch = i_width / 2;
774             p_pic->p[U_PLANE].i_pixel_pitch = 1;
775             p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
776
777             /* V buffer */
778             p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
779             p_pic->p[V_PLANE].i_lines = i_height / 2;
780             p_pic->p[V_PLANE].i_pitch = i_width / 2;
781             p_pic->p[V_PLANE].i_pixel_pitch = 1;
782             p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
783
784             /* We allocated 3 planes */
785             p_pic->i_planes = 3;
786
787 #define P p_pic->p_sys->pixmap_i420
788             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
789                                        - p_pic->p_sys->p_info;
790             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
791                                         - p_pic->p_sys->p_info;
792             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
793                                         - p_pic->p_sys->p_info;
794
795             P.componentInfoY.rowBytes = i_width;
796             P.componentInfoCb.rowBytes = i_width / 2;
797             P.componentInfoCr.rowBytes = i_width / 2;
798 #undef P
799
800             break;
801
802     default:
803         /* Unknown chroma, tell the guy to get lost */
804         free( p_pic->p_sys );
805         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
806                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
807         p_pic->i_planes = 0;
808         return( -1 );
809     }
810
811     return( 0 );
812 }
813
814 /*****************************************************************************
815  * QTFreePicture: destroy a picture allocated with QTNewPicture
816  *****************************************************************************/
817 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
818 {
819     switch( p_vout->output.i_chroma )
820     {
821         case VLC_FOURCC('I','4','2','0'):
822             free( p_pic->p_data_orig );
823             break;
824     }
825
826     free( p_pic->p_sys );
827 }
828
829 /*****************************************************************************
830  * VLCWindow implementation
831  *****************************************************************************/
832 @implementation VLCWindow
833
834 - (void)setVout:(vout_thread_t *)_p_vout
835 {
836     p_vout = _p_vout;
837 }
838
839 - (vout_thread_t *)getVout
840 {
841     return( p_vout );
842 }
843
844 - (void)scaleWindowWithFactor: (float)factor
845 {
846     NSSize newsize;
847     int i_corrected_height, i_corrected_width;
848     NSPoint topleftbase;
849     NSPoint topleftscreen;
850     
851     if ( !p_vout->b_fullscreen )
852     {
853         topleftbase.x = 0;
854         topleftbase.y = [self frame].size.height;
855         topleftscreen = [self convertBaseToScreen: topleftbase];
856         
857         if( p_vout->output.i_height * p_vout->output.i_aspect > 
858                         p_vout->output.i_width * VOUT_ASPECT_FACTOR )
859         {
860             i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
861                                             VOUT_ASPECT_FACTOR;
862             newsize.width = (int) ( i_corrected_width * factor );
863             newsize.height = (int) ( p_vout->render.i_height * factor );
864         }
865         else
866         {
867             i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
868                                             p_vout->output.i_aspect;
869             newsize.width = (int) ( p_vout->render.i_width * factor );
870             newsize.height = (int) ( i_corrected_height * factor );
871         }
872     
873         [self setContentSize: newsize];
874         
875         [self setFrameTopLeftPoint: topleftscreen];
876         p_vout->i_changes |= VOUT_SIZE_CHANGE;
877     }
878 }
879
880 - (void)toggleFloatOnTop
881 {
882     if( config_GetInt( p_vout, "video-on-top" ) )
883     {
884         config_PutInt( p_vout, "video-on-top", 0 );
885         [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
886     }
887     else
888     {
889         config_PutInt( p_vout, "video-on-top", 1 );
890         [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
891     }
892 }
893
894 - (void)toggleFullscreen
895 {
896     config_PutInt(p_vout, "fullscreen", !p_vout->b_fullscreen);
897     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
898 }
899
900 - (BOOL)isFullscreen
901 {
902     return( p_vout->b_fullscreen );
903 }
904
905 - (BOOL)canBecomeKeyWindow
906 {
907     return( YES );
908 }
909
910 - (void)keyDown:(NSEvent *)o_event
911 {
912     unichar key = 0;
913     vlc_value_t val;
914     unsigned int i_pressed_modifiers = 0;
915     val.i_int = 0;
916     
917     i_pressed_modifiers = [o_event modifierFlags];
918
919     if( i_pressed_modifiers & NSShiftKeyMask )
920         val.i_int |= KEY_MODIFIER_SHIFT;
921     if( i_pressed_modifiers & NSControlKeyMask )
922         val.i_int |= KEY_MODIFIER_CTRL;
923     if( i_pressed_modifiers & NSAlternateKeyMask )
924         val.i_int |= KEY_MODIFIER_ALT;
925     if( i_pressed_modifiers & NSCommandKeyMask )
926         val.i_int |= KEY_MODIFIER_COMMAND;
927
928     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
929
930     if( key )
931     {
932         /* Escape should always get you out of fullscreen */
933         if( key == (unichar) 0x1b )
934         {
935              if( [self isFullscreen] )
936              {
937                  [self toggleFullscreen];
938              }
939         }
940         else if ( key == ' ' )
941         {
942              playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
943                                                      FIND_ANYWHERE );
944              if ( p_playlist != NULL )
945              {
946                  playlist_Pause( p_playlist );
947                  vlc_object_release( p_playlist);
948              }
949         }
950         else
951         {
952             val.i_int |= CocoaKeyToVLC( key );
953             var_Set( p_vout->p_vlc, "key-pressed", val );
954         }
955     }
956     else
957     {
958         [super keyDown: o_event];
959     }
960 }
961
962 - (void)updateTitle
963 {
964     NSMutableString * o_title;
965     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
966                                                        FIND_ANYWHERE );
967     
968     if( p_playlist == NULL )
969     {
970         return;
971     }
972
973     vlc_mutex_lock( &p_playlist->object_lock );
974     o_title = [NSMutableString stringWithUTF8String: 
975         p_playlist->pp_items[p_playlist->i_index]->psz_uri]; 
976     vlc_mutex_unlock( &p_playlist->object_lock );
977
978     vlc_object_release( p_playlist );
979
980     if( o_title != nil )
981     {
982         NSRange prefix_range = [o_title rangeOfString: @"file:"];
983         if( prefix_range.location != NSNotFound )
984         {
985             [o_title deleteCharactersInRange: prefix_range];
986         }
987
988         [self setTitleWithRepresentedFilename: o_title];
989     }
990     else
991     {
992         [self setTitle:
993             [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
994     }
995 }
996
997 /* This is actually the same as VLCControls::stop. */
998 - (BOOL)windowShouldClose:(id)sender
999 {
1000     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1001                                                        FIND_ANYWHERE );
1002     if( p_playlist == NULL )      
1003     {
1004         return NO;
1005     }
1006
1007     playlist_Stop( p_playlist );
1008     vlc_object_release( p_playlist );
1009
1010     /* The window will be closed by the intf later. */
1011     return NO;
1012 }
1013
1014 @end
1015
1016 /*****************************************************************************
1017  * VLCQTView implementation
1018  *****************************************************************************/
1019 @implementation VLCQTView
1020
1021 - (void)drawRect:(NSRect)rect
1022 {
1023     vout_thread_t * p_vout;
1024     id o_window = [self window];
1025     p_vout = (vout_thread_t *)[o_window getVout];
1026     
1027     [[NSColor blackColor] set];
1028     NSRectFill( rect );
1029     [super drawRect: rect];
1030
1031     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1032 }
1033
1034 - (BOOL)acceptsFirstResponder
1035 {
1036     return( YES );
1037 }
1038
1039 - (BOOL)becomeFirstResponder
1040 {
1041     vout_thread_t * p_vout;
1042     id o_window = [self window];
1043     p_vout = (vout_thread_t *)[o_window getVout];
1044     
1045     [o_window setAcceptsMouseMovedEvents: YES];
1046     return( YES );
1047 }
1048
1049 - (BOOL)resignFirstResponder
1050 {
1051     vout_thread_t * p_vout;
1052     id o_window = [self window];
1053     p_vout = (vout_thread_t *)[o_window getVout];
1054     
1055     [o_window setAcceptsMouseMovedEvents: NO];
1056     VLCHideMouse( p_vout, NO );
1057     return( YES );
1058 }
1059
1060 - (void)mouseDown:(NSEvent *)o_event
1061 {
1062     vout_thread_t * p_vout;
1063     id o_window = [self window];
1064     p_vout = (vout_thread_t *)[o_window getVout];
1065     vlc_value_t val;
1066
1067     switch( [o_event type] )
1068     {        
1069         case NSLeftMouseDown:
1070         {
1071             var_Get( p_vout, "mouse-button-down", &val );
1072             val.i_int |= 1;
1073             var_Set( p_vout, "mouse-button-down", val );
1074         }
1075         break;
1076         
1077         default:
1078             [super mouseDown: o_event];
1079         break;
1080     }
1081 }
1082
1083 - (void)otherMouseDown:(NSEvent *)o_event
1084 {
1085     /* This is not the the wheel button. you need to poll the
1086      * mouseWheel event for that. other is a third, forth or fifth button */
1087     vout_thread_t * p_vout;
1088     id o_window = [self window];
1089     p_vout = (vout_thread_t *)[o_window getVout];
1090     vlc_value_t val;
1091
1092     switch( [o_event type] )
1093     {
1094         case NSOtherMouseDown:
1095         {
1096             var_Get( p_vout, "mouse-button-down", &val );
1097             val.i_int |= 2;
1098             var_Set( p_vout, "mouse-button-down", val );
1099         }
1100         break;
1101         
1102         default:
1103             [super mouseDown: o_event];
1104         break;
1105     }
1106 }
1107
1108 - (void)rightMouseDown:(NSEvent *)o_event
1109 {
1110     vout_thread_t * p_vout;
1111     id o_window = [self window];
1112     p_vout = (vout_thread_t *)[o_window getVout];
1113     vlc_value_t val;
1114
1115     switch( [o_event type] )
1116     {
1117         case NSRightMouseDown:
1118         {
1119             var_Get( p_vout, "mouse-button-down", &val );
1120             val.i_int |= 4;
1121             var_Set( p_vout, "mouse-button-down", val );
1122         }
1123         break;
1124         
1125         default:
1126             [super mouseDown: o_event];
1127         break;
1128     }
1129 }
1130
1131 - (void)mouseUp:(NSEvent *)o_event
1132 {
1133     vout_thread_t * p_vout;
1134     id o_window = [self window];
1135     p_vout = (vout_thread_t *)[o_window getVout];
1136     vlc_value_t val;
1137
1138     switch( [o_event type] )
1139     {
1140         case NSLeftMouseUp:
1141         {
1142             vlc_value_t b_val;
1143             b_val.b_bool = VLC_TRUE;
1144             var_Set( p_vout, "mouse-clicked", b_val );
1145             
1146             var_Get( p_vout, "mouse-button-down", &val );
1147             val.i_int &= ~1;
1148             var_Set( p_vout, "mouse-button-down", val );
1149         }
1150         break;
1151                 
1152         default:
1153             [super mouseUp: o_event];
1154         break;
1155     }
1156 }
1157
1158 - (void)otherMouseUp:(NSEvent *)o_event
1159 {
1160     vout_thread_t * p_vout;
1161     id o_window = [self window];
1162     p_vout = (vout_thread_t *)[o_window getVout];
1163     vlc_value_t val;
1164
1165     switch( [o_event type] )
1166     {
1167         case NSOtherMouseUp:
1168         {
1169             var_Get( p_vout, "mouse-button-down", &val );
1170             val.i_int &= ~2;
1171             var_Set( p_vout, "mouse-button-down", val );
1172         }
1173         break;
1174                 
1175         default:
1176             [super mouseUp: o_event];
1177         break;
1178     }
1179 }
1180
1181 - (void)rightMouseUp:(NSEvent *)o_event
1182 {
1183     vout_thread_t * p_vout;
1184     id o_window = [self window];
1185     p_vout = (vout_thread_t *)[o_window getVout];
1186     vlc_value_t val;
1187
1188     switch( [o_event type] )
1189     {
1190         case NSRightMouseUp:
1191         {
1192             var_Get( p_vout, "mouse-button-down", &val );
1193             val.i_int &= ~4;
1194             var_Set( p_vout, "mouse-button-down", val );
1195         }
1196         break;
1197         
1198         default:
1199             [super mouseUp: o_event];
1200         break;
1201     }
1202 }
1203
1204 - (void)mouseDragged:(NSEvent *)o_event
1205 {
1206     [self mouseMoved:o_event];
1207 }
1208
1209 - (void)otherMouseDragged:(NSEvent *)o_event
1210 {
1211     [self mouseMoved:o_event];
1212 }
1213
1214 - (void)rightMouseDragged:(NSEvent *)o_event
1215 {
1216     [self mouseMoved:o_event];
1217 }
1218
1219 - (void)mouseMoved:(NSEvent *)o_event
1220 {
1221     NSPoint ml;
1222     NSRect s_rect;
1223     BOOL b_inside;
1224
1225     vout_thread_t * p_vout;
1226     id o_window = [self window];
1227     p_vout = (vout_thread_t *)[o_window getVout];
1228
1229     s_rect = [self bounds];
1230     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1231     b_inside = [self mouse: ml inRect: s_rect];
1232
1233     if( b_inside )
1234     {
1235         vlc_value_t val;
1236         int i_width, i_height, i_x, i_y;
1237         
1238         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1239                                    (unsigned int)s_rect.size.height,
1240                                    &i_x, &i_y, &i_width, &i_height );
1241
1242         val.i_int = ( ((int)ml.x) - i_x ) * 
1243                     p_vout->render.i_width / i_width;
1244         var_Set( p_vout, "mouse-x", val );
1245
1246         val.i_int = ( ((int)ml.y) - i_y ) * 
1247                     p_vout->render.i_height / i_height;
1248         var_Set( p_vout, "mouse-y", val );
1249             
1250         val.b_bool = VLC_TRUE;
1251         var_Set( p_vout, "mouse-moved", val );
1252         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1253         p_vout->p_sys->b_mouse_moved = YES;
1254     }
1255
1256     [super mouseMoved: o_event];
1257 }
1258
1259 @end
1260
1261 /*****************************************************************************
1262  * VLCGLView implementation
1263  *****************************************************************************/
1264 @implementation VLCGLView
1265
1266
1267 - (id) initWithFrame: (NSRect) frame
1268 {
1269     NSOpenGLPixelFormatAttribute attribs[] =
1270     {
1271         NSOpenGLPFAAccelerated,
1272         NSOpenGLPFANoRecovery,
1273         NSOpenGLPFADoubleBuffer,
1274         0
1275     };
1276
1277     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1278         initWithAttributes: attribs];
1279
1280     if( !fmt )
1281     {
1282         fprintf( stderr, "Cannot create NSOpenGLPixelFormat\n" );
1283         return nil;
1284     }
1285
1286     self = [super initWithFrame:frame pixelFormat: fmt];
1287
1288     [[self openGLContext] makeCurrentContext];
1289     [[self openGLContext] update];
1290
1291     glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1292     glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1293     glClearColor( 0.0, 0.0, 0.0, 0.0 );
1294
1295     i_init_done = 0;
1296
1297     return self;
1298 }
1299
1300 - (void) reshape
1301 {
1302     [[self openGLContext] update];
1303     NSRect bounds = [self bounds];
1304     glViewport( 0, 0, (GLint) bounds.size.width,
1305                 (GLint) bounds.size.height );
1306 }
1307
1308 - (void) initTexture
1309 {
1310     vout_thread_t * p_vout;
1311     id o_window = [self window];
1312     p_vout = (vout_thread_t *)[o_window getVout];
1313
1314     [self lockFocus];
1315
1316     [[self openGLContext] makeCurrentContext];
1317
1318     /* Swap buffers only during the vertical retrace of the monitor.
1319        http://developer.apple.com/documentation/GraphicsImaging/
1320        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1321     long params[] = { 1 };
1322     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1323                      params );
1324
1325     /* Create the texture */
1326     glGenTextures( 1, &i_texture );
1327     glEnable( GL_TEXTURE_RECTANGLE_EXT );
1328     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture );
1329
1330     /* Turn on AGP transferts */
1331     glTexParameterf( GL_TEXTURE_RECTANGLE_EXT,
1332                      GL_TEXTURE_PRIORITY, 0.0 );
1333
1334     /* Use AGP texturing */
1335     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1336             GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1337
1338     /* Tell the driver not to make a copy of the texture but to use
1339        our buffer */
1340     glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1341
1342     /* Linear interpolation */
1343     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1344             GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1345     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1346             GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1347
1348     /* I have no idea what this exactly does, but it seems to be
1349        necessary for scaling */
1350     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1351             GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE );
1352     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1353             GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1354     glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
1355
1356     glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
1357             p_vout->output.i_width, p_vout->output.i_height, 0,
1358             GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1359             PP_OUTPUTPICTURE[0]->p_data );
1360
1361     i_init_done = 1;
1362     
1363     [self unlockFocus];
1364 }
1365
1366 - (void) reloadTexture: (uint8_t *) buffer
1367 {
1368     vout_thread_t * p_vout;
1369     id o_window = [self window];
1370     p_vout = (vout_thread_t *)[o_window getVout];
1371     
1372     [self lockFocus];
1373
1374     [[self openGLContext] makeCurrentContext];
1375     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture );
1376
1377     /* glTexSubImage2D is usually faster than glTexImage2D */
1378     glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1379             p_vout->output.i_width, p_vout->output.i_height,
1380             GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1381             buffer );
1382     [self drawRect: [self bounds]];
1383
1384     [self unlockFocus];
1385 }
1386
1387 - (void) drawRect: (NSRect) rect
1388 {
1389     vout_thread_t * p_vout;
1390     id o_window = [self window];
1391     p_vout = (vout_thread_t *)[o_window getVout];
1392
1393     /* Black background */
1394     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1395
1396     if( !i_init_done )
1397         return;
1398
1399     /* Draw a quad with our texture on it. Quad size is set in order
1400        to preserve the aspect ratio */
1401     NSRect bounds = [self bounds];
1402     float  f_x, f_y;
1403     if( bounds.size.height * p_vout->output.i_aspect <
1404         bounds.size.width * VOUT_ASPECT_FACTOR )
1405     {
1406         f_x = bounds.size.height * p_vout->output.i_aspect /
1407             VOUT_ASPECT_FACTOR / bounds.size.width;
1408         f_y = 1.0;
1409     }
1410     else
1411     {
1412         f_x = 1.0;
1413         f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1414             p_vout->output.i_aspect / bounds.size.height;
1415     }
1416
1417     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture );
1418     glBegin( GL_QUADS );
1419         /* Top left */
1420         glTexCoord2f( 0.0f, 0.0f );
1421         glVertex2f( - f_x, f_y );
1422         /* Bottom left */
1423         glTexCoord2f( 0.0f, (float) p_vout->output.i_height );
1424         glVertex2f( - f_x, - f_y );
1425         /* Bottom right */
1426         glTexCoord2f( (float) p_vout->output.i_width,
1427                       (float) p_vout->output.i_height );
1428         glVertex2f( f_x, - f_y );
1429         /* Top right */
1430         glTexCoord2f( (float) p_vout->output.i_width, 0.0f );
1431         glVertex2f( f_x, f_y );
1432     glEnd();
1433
1434     /* Wait for the job to be done */
1435     [[self openGLContext] flushBuffer];
1436 }
1437
1438 @end
1439
1440 /*****************************************************************************
1441  * VLCVout implementation
1442  *****************************************************************************/
1443 @implementation VLCVout
1444
1445 - (void)createWindow:(NSValue *)o_value
1446 {
1447     vlc_value_t val;
1448     VLCQTView * o_view;
1449     NSScreen * o_screen;
1450     vout_thread_t * p_vout;
1451     vlc_bool_t b_main_screen;
1452     
1453     p_vout = (vout_thread_t *)[o_value pointerValue];
1454
1455     p_vout->p_sys->o_window = [VLCWindow alloc];
1456     [p_vout->p_sys->o_window setVout: p_vout];
1457     [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1458
1459     if( var_Get( p_vout, "video-device", &val ) < 0 )
1460     {
1461         o_screen = [NSScreen mainScreen];
1462         b_main_screen = 1;
1463     }
1464     else
1465     {
1466         NSArray *o_screens = [NSScreen screens];
1467         unsigned int i_index = val.i_int;
1468         
1469         if( [o_screens count] < i_index )
1470         {
1471             o_screen = [NSScreen mainScreen];
1472             b_main_screen = 1;
1473         }
1474         else
1475         {
1476             i_index--;
1477             o_screen = [o_screens objectAtIndex: i_index];
1478             config_PutInt( p_vout, "macosx-vdev", i_index );
1479             b_main_screen = (i_index == 0);
1480         }
1481     } 
1482
1483     if( p_vout->p_sys->i_opengl )
1484     {
1485         /* XXX Fix fullscreen mode */
1486         p_vout->b_fullscreen = 0;
1487     }
1488
1489     if( p_vout->b_fullscreen )
1490     {
1491         NSRect screen_rect = [o_screen frame];
1492         screen_rect.origin.x = screen_rect.origin.y = 0;
1493
1494         if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1495             BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1496                              NULL, NULL, fullScreenAllowEvents );
1497
1498         [p_vout->p_sys->o_window 
1499             initWithContentRect: screen_rect
1500             styleMask: NSBorderlessWindowMask
1501             backing: NSBackingStoreBuffered
1502             defer: NO screen: o_screen];
1503
1504         //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1505         p_vout->p_sys->b_mouse_moved = YES;
1506         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1507     }
1508     else
1509     {
1510         unsigned int i_stylemask = NSTitledWindowMask |
1511                                    NSMiniaturizableWindowMask |
1512                                    NSClosableWindowMask |
1513                                    NSResizableWindowMask;
1514         
1515         if( !p_vout->p_sys->i_opengl )
1516         {
1517             if ( p_vout->p_sys->p_fullscreen_state != NULL )
1518                 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1519             p_vout->p_sys->p_fullscreen_state = NULL;
1520         }
1521
1522         [p_vout->p_sys->o_window 
1523             initWithContentRect: p_vout->p_sys->s_rect
1524             styleMask: i_stylemask
1525             backing: NSBackingStoreBuffered
1526             defer: NO screen: o_screen];
1527
1528         [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1529         
1530         if( config_GetInt( p_vout, "video-on-top" ) )
1531         {
1532             [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1533         }
1534         
1535         if( !p_vout->p_sys->b_pos_saved )   
1536         {
1537             [p_vout->p_sys->o_window center];
1538         }
1539     }
1540
1541     if( !p_vout->p_sys->i_opengl )
1542     {
1543         o_view = [[VLCQTView alloc] init];
1544         /* FIXME: [o_view setMenu:] */
1545         [p_vout->p_sys->o_window setContentView: o_view];
1546         [o_view autorelease];
1547
1548         [o_view lockFocus];
1549         p_vout->p_sys->p_qdport = [o_view qdPort];
1550         [o_view unlockFocus];
1551     }
1552     else
1553     {
1554 #define o_glview p_vout->p_sys->o_glview
1555         o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect];
1556         [p_vout->p_sys->o_window setContentView: o_glview];
1557         [o_glview autorelease];
1558 #undef o_glview
1559     }
1560     
1561     [p_vout->p_sys->o_window updateTitle];
1562     [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1563
1564 }
1565
1566 - (void)destroyWindow:(NSValue *)o_value
1567 {
1568     vout_thread_t * p_vout;
1569
1570     p_vout = (vout_thread_t *)[o_value pointerValue];
1571
1572     if( !p_vout->b_fullscreen )
1573     {
1574         NSRect s_rect;
1575
1576         s_rect = [[p_vout->p_sys->o_window contentView] frame];
1577         p_vout->p_sys->s_rect.size = s_rect.size;
1578
1579         s_rect = [p_vout->p_sys->o_window frame];
1580         p_vout->p_sys->s_rect.origin = s_rect.origin;
1581
1582         p_vout->p_sys->b_pos_saved = YES;
1583     }
1584     
1585     p_vout->p_sys->p_qdport = nil;
1586     [p_vout->p_sys->o_window close];
1587     p_vout->p_sys->o_window = nil;
1588 }
1589
1590 @end