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