]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
macosx/vout.m: fixed OpenGL aspect ratio
[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.72 2004/01/27 12:11:48 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/gl.h>
37 #include <OpenGL/glext.h>
38
39 #include <vlc_keys.h>
40
41 #include "intf.h"
42 #include "vout.h"
43
44 #define QT_MAX_DIRECTBUFFERS 10
45 #define VL_MAX_DISPLAYS 16
46
47 struct picture_sys_t
48 {
49     void *p_info;
50     unsigned int i_size;
51
52     /* When using I420 output */
53     PlanarPixmapInfoYUV420 pixmap_i420;
54 };
55
56 /*****************************************************************************
57  * Local prototypes
58  *****************************************************************************/
59
60 static int  vout_Init      ( vout_thread_t * );
61 static void vout_End       ( vout_thread_t * );
62 static int  vout_Manage    ( vout_thread_t * );
63 static void vout_Display   ( vout_thread_t *, picture_t * );
64
65 static int  CoSendRequest      ( vout_thread_t *, SEL );
66 static int  CoCreateWindow     ( vout_thread_t * );
67 static int  CoDestroyWindow    ( vout_thread_t * );
68 static int  CoToggleFullscreen ( vout_thread_t * );
69
70 static void VLCHideMouse       ( vout_thread_t *, BOOL );
71
72 static void QTScaleMatrix      ( vout_thread_t * );
73 static int  QTCreateSequence   ( vout_thread_t * );
74 static void QTDestroySequence  ( vout_thread_t * );
75 static int  QTNewPicture       ( vout_thread_t *, picture_t * );
76 static void QTFreePicture      ( vout_thread_t *, picture_t * );
77
78 /*****************************************************************************
79  * OpenVideo: allocates MacOS X video thread output method
80  *****************************************************************************
81  * This function allocates and initializes a MacOS X vout method.
82  *****************************************************************************/
83 int E_(OpenVideo) ( vlc_object_t *p_this )
84 {   
85     vout_thread_t * p_vout = (vout_thread_t *)p_this;
86     OSErr err;
87     int i_timeout;
88
89     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
90     if( p_vout->p_sys == NULL )
91     {
92         msg_Err( p_vout, "out of memory" );
93         return( 1 );
94     }
95
96     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
97
98     /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
99     for( i_timeout = 20 ; i_timeout-- ; )
100     {
101         if( NSApp == NULL )
102         {
103             msleep( INTF_IDLE_SLEEP );
104         }
105     }
106
107     if( NSApp == NULL )
108     {
109         /* no MacOS X intf, unable to communicate with MT */
110         msg_Err( p_vout, "no MacOS X interface present" );
111         free( p_vout->p_sys );
112         return( 1 );
113     }
114
115     if( [NSApp respondsToSelector: @selector(getIntf)] )
116     {
117         intf_thread_t * p_intf;
118
119         for( i_timeout = 10 ; i_timeout-- ; )
120         {
121             if( ( p_intf = [NSApp getIntf] ) == NULL )
122             {
123                 msleep( INTF_IDLE_SLEEP );
124             }
125         }
126
127         if( p_intf == NULL )
128         {
129             msg_Err( p_vout, "MacOS X intf has getIntf, but is NULL" );
130             free( p_vout->p_sys );
131             return( 1 );
132         }
133     }
134
135     p_vout->p_sys->b_mouse_moved = VLC_TRUE;
136     p_vout->p_sys->i_time_mouse_last_moved = mdate();
137
138     /* set window size */
139     p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
140     p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
141
142     /* Check if we should use QuickTime or OpenGL */
143     p_vout->p_sys->i_opengl = config_GetInt( p_vout, "macosx-opengl" );
144
145     if( !p_vout->p_sys->i_opengl )
146     {
147         /* Initialize QuickTime */
148         p_vout->p_sys->h_img_descr = 
149             (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
150         p_vout->p_sys->p_matrix =
151             (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
152         p_vout->p_sys->p_fullscreen_state = NULL;
153
154         if( ( err = EnterMovies() ) != noErr )
155         {
156             msg_Err( p_vout, "EnterMovies failed: %d", err );
157             free( p_vout->p_sys->p_matrix );
158             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
159             free( p_vout->p_sys );
160             return( 1 );
161         } 
162
163         /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
164         vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
165
166         err = FindCodec( kYUV420CodecType, bestSpeedCodec,
167                             nil, &p_vout->p_sys->img_dc );
168         
169         vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
170         if( err == noErr && p_vout->p_sys->img_dc != 0 )
171         {
172             p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
173             p_vout->p_sys->i_codec = kYUV420CodecType;
174         }
175         else
176         {
177             msg_Err( p_vout, "failed to find an appropriate codec" );
178         }
179
180         if( p_vout->p_sys->img_dc == 0 )
181         {
182             free( p_vout->p_sys->p_matrix );
183             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
184             free( p_vout->p_sys );
185             return VLC_EGENERIC;        
186         }
187     }
188
189     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
190     NSArray * o_screens = [NSScreen screens];
191     if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
192     {
193         int i = 1;
194         vlc_value_t val, text;
195         NSScreen * o_screen;
196
197         int i_option = config_GetInt( p_vout, "macosx-vdev" );
198
199         var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
200                                             VLC_VAR_HASCHOICE ); 
201         text.psz_string = _("Video device");
202         var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
203         
204         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
205
206         while( (o_screen = [o_enumerator nextObject]) != NULL )
207         {
208             char psz_temp[255];
209             NSRect s_rect = [o_screen frame];
210
211             snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1, 
212                       "%s %d (%dx%d)", _("Screen"), i,
213                       (int)s_rect.size.width, (int)s_rect.size.height ); 
214
215             text.psz_string = psz_temp;
216             val.i_int = i;
217             var_Change( p_vout, "video-device",
218                         VLC_VAR_ADDCHOICE, &val, &text );
219
220             if( ( i - 1 ) == i_option )
221             {
222                 var_Set( p_vout, "video-device", val );
223             }
224             i++;
225         }
226
227         var_AddCallback( p_vout, "video-device", vout_VarCallback,
228                          NULL );
229
230         val.b_bool = VLC_TRUE;
231         var_Set( p_vout, "intf-change", val );
232     }
233     [o_pool release];
234
235     if( CoCreateWindow( p_vout ) )
236     {
237         msg_Err( p_vout, "unable to create window" );
238         if( !p_vout->p_sys->i_opengl )
239         {
240             free( p_vout->p_sys->p_matrix );
241             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
242         }
243         free( p_vout->p_sys ); 
244         return( 1 );
245     }
246
247     p_vout->pf_init = vout_Init;
248     p_vout->pf_end = vout_End;
249     p_vout->pf_manage = vout_Manage;
250     p_vout->pf_render = NULL;
251     p_vout->pf_display = vout_Display;
252
253     return( 0 );
254 }
255
256 /*****************************************************************************
257  * vout_Init: initialize video thread output method
258  *****************************************************************************/
259 static int vout_Init( vout_thread_t *p_vout )
260 {
261     int i_index;
262     picture_t *p_pic;
263
264     I_OUTPUTPICTURES = 0;
265
266     /* Initialize the output structure; we already found a codec,
267      * and the corresponding chroma we will be using. Since we can
268      * arbitrary scale, stick to the coordinates and aspect. */
269     p_vout->output.i_width  = p_vout->render.i_width;
270     p_vout->output.i_height = p_vout->render.i_height;
271     p_vout->output.i_aspect = p_vout->render.i_aspect;
272
273     if( !p_vout->p_sys->i_opengl )
274     {
275         SetPort( p_vout->p_sys->p_qdport );
276         QTScaleMatrix( p_vout );
277
278         if( QTCreateSequence( p_vout ) )
279         {
280             msg_Err( p_vout, "unable to create sequence" );
281             return( 1 );
282         }
283     }
284     else
285     {
286         p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
287         p_vout->output.i_rmask  = 0xFF0000;
288         p_vout->output.i_gmask  = 0x00FF00;
289         p_vout->output.i_bmask  = 0x0000FF;
290     }
291
292     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
293     while( I_OUTPUTPICTURES <
294            ( p_vout->p_sys->i_opengl ? 3 : 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             vout_AllocatePicture( p_vout, p_pic, p_vout->output.i_chroma,
324                     p_vout->output.i_width, p_vout->output.i_height,
325                     p_vout->output.i_aspect );
326         }
327
328         p_pic->i_status = DESTROYED_PICTURE;
329         p_pic->i_type   = DIRECT_PICTURE;
330
331         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
332
333         I_OUTPUTPICTURES++;
334     }
335
336     if( p_vout->p_sys->i_opengl )
337     {
338         p_vout->p_sys->o_glview->i_index = -1;
339         p_vout->p_sys->o_glview->i_init_done = 1;
340     }
341
342     return( 0 );
343 }
344
345 /*****************************************************************************
346  * vout_End: terminate video thread output method
347  *****************************************************************************/
348 static void vout_End( vout_thread_t *p_vout )
349 {
350     int i_index;
351
352     if( !p_vout->p_sys->i_opengl )
353     {
354         QTDestroySequence( p_vout );
355     }
356
357     /* Free the direct buffers we allocated */
358     for( i_index = I_OUTPUTPICTURES; i_index; )
359     {
360         i_index--;
361         if( !p_vout->p_sys->i_opengl )
362         {
363             QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
364         }
365         else
366         {
367             free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
368         }
369     }
370 }
371
372 /*****************************************************************************
373  * CloseVideo: destroy video thread output method
374  *****************************************************************************/
375 void E_(CloseVideo) ( vlc_object_t *p_this )
376 {       
377     vout_thread_t * p_vout = (vout_thread_t *)p_this;     
378
379     if( CoDestroyWindow( p_vout ) )
380     {
381         msg_Err( p_vout, "unable to destroy window" );
382     }
383
384     if( !p_vout->p_sys->i_opengl )
385     {
386         if ( p_vout->p_sys->p_fullscreen_state != NULL )
387             EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
388
389         ExitMovies();
390
391         free( p_vout->p_sys->p_matrix );
392         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
393     }
394
395     free( p_vout->p_sys );
396 }
397
398 /*****************************************************************************
399  * vout_Manage: handle events
400  *****************************************************************************
401  * This function should be called regularly by video output thread. It manages
402  * console events. It returns a non null value on error.
403  *****************************************************************************/
404 static int vout_Manage( vout_thread_t *p_vout )
405 {
406     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
407     {
408         if( CoToggleFullscreen( p_vout ) )  
409         {
410             return( 1 );
411         }
412
413         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
414     }
415
416     if( p_vout->i_changes & VOUT_SIZE_CHANGE ) 
417     {
418         if( !p_vout->p_sys->i_opengl )
419         {
420             QTScaleMatrix( p_vout );
421             SetDSequenceMatrix( p_vout->p_sys->i_seq, 
422                                 p_vout->p_sys->p_matrix );
423         }
424  
425         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
426     }
427
428     /* hide/show mouse cursor 
429      * this code looks unnecessarily complicated, but is necessary like this.
430      * it has to deal with multiple monitors and therefore checks a lot */
431     if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
432     {
433         if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
434         {
435             VLCHideMouse( p_vout, YES );
436         }
437     }
438     else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
439     {
440         VLCHideMouse( p_vout, NO );
441     }
442     
443     /* disable screen saver */
444     UpdateSystemActivity( UsrActivity );
445     
446     return( 0 );
447 }
448
449 /*****************************************************************************
450  * vout_Display: displays previously rendered output
451  *****************************************************************************
452  * This function sends the currently rendered image to the display.
453  *****************************************************************************/
454 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
455 {
456     if( !p_vout->p_sys->i_opengl )
457     {
458         OSErr err;
459         CodecFlags flags;
460
461         if( ( err = DecompressSequenceFrameS( 
462                         p_vout->p_sys->i_seq,
463                         p_pic->p_sys->p_info,
464                         p_pic->p_sys->i_size,                    
465                         codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
466         {
467             msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
468         }
469         else
470         {
471             QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
472         }
473     }
474     else
475     {
476         p_vout->p_sys->o_glview->i_index++; 
477         p_vout->p_sys->o_glview->i_index %= 3;
478         [p_vout->p_sys->o_glview setNeedsDisplay: YES];
479         p_pic->p->p_pixels =
480             PP_OUTPUTPICTURE[(p_vout->p_sys->o_glview->i_index+1)%3]->p_data;
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     self = [super initWithFrame:frame pixelFormat: fmt];
1281
1282     [[self openGLContext] makeCurrentContext];
1283     [[self openGLContext] update];
1284
1285     glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1286     glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1287     glClearColor( 0.0, 0.0, 0.0, 0.0 );
1288
1289     i_init_done       = 0;
1290     i_textures_loaded = 0;
1291
1292     return self;
1293 }
1294
1295 - (void) reshape
1296 {
1297     [[self openGLContext] update];
1298     NSRect bounds = [self bounds];
1299     glViewport( 0, 0, (GLint) bounds.size.width, (GLint) bounds.size.height );
1300 }
1301
1302 - (void)drawRect:(NSRect)rect
1303 {
1304     vout_thread_t * p_vout;
1305     id o_window = [self window];
1306     p_vout = (vout_thread_t *)[o_window getVout];
1307
1308
1309     /* Make this current context */
1310     [[self openGLContext] makeCurrentContext];
1311
1312     /* Black background */
1313     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1314
1315     if( !i_init_done )
1316     {
1317         return;
1318     }
1319
1320     if( !i_textures_loaded )
1321     {
1322         glGenTextures( 3, pi_textures );
1323         i_textures_loaded = 1;
1324     }
1325     else 
1326     {
1327         glDisable( GL_TEXTURE_2D );
1328         glEnable( GL_TEXTURE_RECTANGLE_EXT );
1329         glBindTexture(GL_TEXTURE_RECTANGLE_EXT, pi_textures[i_index]);
1330
1331         /* Map our buffer to the texture */
1332         glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
1333                 p_vout->output.i_width, p_vout->output.i_height, 0,
1334                 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1335                 PP_OUTPUTPICTURE[i_index]->p_data );
1336     }
1337
1338     glDisable( GL_TEXTURE_2D );
1339     glEnable( GL_TEXTURE_RECTANGLE_EXT );
1340     glBindTexture(GL_TEXTURE_RECTANGLE_EXT, pi_textures[i_index] );
1341
1342     /* Turn on AGP transferts */
1343     glTexParameterf( GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_PRIORITY, 0.0 );
1344
1345     /* Use AGP texturing */
1346     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1347             GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1348
1349     /* Tell the driver not to make a copy of the texture but to use out
1350        buffer */
1351     glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1352
1353     /* Linear interpolation */
1354     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1355             GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1356     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1357             GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1358
1359     /* I have no idea what this exactly does, but it seems to be
1360        necessary for scaling */
1361     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1362             GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE );
1363     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1364             GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1365     glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
1366
1367
1368     /* Draw a quad with our texture on it */
1369     NSRect bounds = [self bounds];
1370     float  f_x, f_y;
1371     if( bounds.size.height * p_vout->i_window_width <
1372         bounds.size.width * p_vout->i_window_height )
1373     {
1374         f_x = bounds.size.height * p_vout->i_window_width /
1375               bounds.size.width / p_vout->i_window_height;
1376         f_y = 1.0;
1377     }
1378     else
1379     {
1380         f_x = 1.0;
1381         f_y = bounds.size.width * p_vout->i_window_height /
1382               bounds.size.height / p_vout->i_window_width;
1383     }
1384
1385     glBegin( GL_QUADS );
1386         /* Top left */
1387         glTexCoord2f( 0.0f, 0.0f );
1388         glVertex2f( - f_x, f_y );
1389         /* Bottom left */
1390         glTexCoord2f( 0.0f, (float) p_vout->output.i_height );
1391         glVertex2f( - f_x, - f_y );
1392         /* Bottom right */
1393         glTexCoord2f( (float) p_vout->output.i_width,
1394                       (float) p_vout->output.i_height );
1395         glVertex2f( f_x, - f_y );
1396         /* Top right */
1397         glTexCoord2f( (float) p_vout->output.i_width, 0.0f );
1398         glVertex2f( f_x, f_y );
1399     glEnd();
1400
1401     /* Wait for the job to be done */
1402     [[self openGLContext] flushBuffer];
1403 }
1404
1405 @end
1406
1407 /*****************************************************************************
1408  * VLCVout implementation
1409  *****************************************************************************/
1410 @implementation VLCVout
1411
1412 - (void)createWindow:(NSValue *)o_value
1413 {
1414     vlc_value_t val;
1415     VLCQTView * o_view;
1416     NSScreen * o_screen;
1417     vout_thread_t * p_vout;
1418     vlc_bool_t b_main_screen;
1419     
1420     p_vout = (vout_thread_t *)[o_value pointerValue];
1421
1422     p_vout->p_sys->o_window = [VLCWindow alloc];
1423     [p_vout->p_sys->o_window setVout: p_vout];
1424     [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1425
1426     if( var_Get( p_vout, "video-device", &val ) < 0 )
1427     {
1428         o_screen = [NSScreen mainScreen];
1429         b_main_screen = 1;
1430     }
1431     else
1432     {
1433         NSArray *o_screens = [NSScreen screens];
1434         unsigned int i_index = val.i_int;
1435         
1436         if( [o_screens count] < i_index )
1437         {
1438             o_screen = [NSScreen mainScreen];
1439             b_main_screen = 1;
1440         }
1441         else
1442         {
1443             i_index--;
1444             o_screen = [o_screens objectAtIndex: i_index];
1445             config_PutInt( p_vout, "macosx-vdev", i_index );
1446             b_main_screen = (i_index == 0);
1447         }
1448     } 
1449
1450     if( p_vout->p_sys->i_opengl )
1451     {
1452         /* XXX Fix fullscreen mode */
1453         p_vout->b_fullscreen = 0;
1454     }
1455
1456     if( p_vout->b_fullscreen )
1457     {
1458         NSRect screen_rect = [o_screen frame];
1459         screen_rect.origin.x = screen_rect.origin.y = 0;
1460
1461         if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1462             BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1463                              NULL, NULL, fullScreenAllowEvents );
1464
1465         [p_vout->p_sys->o_window 
1466             initWithContentRect: screen_rect
1467             styleMask: NSBorderlessWindowMask
1468             backing: NSBackingStoreBuffered
1469             defer: NO screen: o_screen];
1470
1471         //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1472         p_vout->p_sys->b_mouse_moved = YES;
1473         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1474     }
1475     else
1476     {
1477         unsigned int i_stylemask = NSTitledWindowMask |
1478                                    NSMiniaturizableWindowMask |
1479                                    NSClosableWindowMask |
1480                                    NSResizableWindowMask;
1481         
1482         if( !p_vout->p_sys->i_opengl )
1483         {
1484             if ( p_vout->p_sys->p_fullscreen_state != NULL )
1485                 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1486             p_vout->p_sys->p_fullscreen_state = NULL;
1487         }
1488
1489         [p_vout->p_sys->o_window 
1490             initWithContentRect: p_vout->p_sys->s_rect
1491             styleMask: i_stylemask
1492             backing: NSBackingStoreBuffered
1493             defer: NO screen: o_screen];
1494
1495         [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1496         
1497         if( config_GetInt( p_vout, "video-on-top" ) )
1498         {
1499             [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1500         }
1501         
1502         if( !p_vout->p_sys->b_pos_saved )   
1503         {
1504             [p_vout->p_sys->o_window center];
1505         }
1506     }
1507
1508     if( !p_vout->p_sys->i_opengl )
1509     {
1510         o_view = [[VLCQTView alloc] init];
1511         /* FIXME: [o_view setMenu:] */
1512         [p_vout->p_sys->o_window setContentView: o_view];
1513         [o_view autorelease];
1514
1515         [o_view lockFocus];
1516         p_vout->p_sys->p_qdport = [o_view qdPort];
1517         [o_view unlockFocus];
1518     }
1519     else
1520     {
1521 #define o_glview p_vout->p_sys->o_glview
1522         o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect];
1523         [p_vout->p_sys->o_window setContentView: o_glview];
1524         [o_glview autorelease];
1525 #undef o_glview
1526     }
1527     
1528     [p_vout->p_sys->o_window updateTitle];
1529     [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1530
1531 }
1532
1533 - (void)destroyWindow:(NSValue *)o_value
1534 {
1535     vout_thread_t * p_vout;
1536
1537     p_vout = (vout_thread_t *)[o_value pointerValue];
1538
1539     if( !p_vout->b_fullscreen )
1540     {
1541         NSRect s_rect;
1542
1543         s_rect = [[p_vout->p_sys->o_window contentView] frame];
1544         p_vout->p_sys->s_rect.size = s_rect.size;
1545
1546         s_rect = [p_vout->p_sys->o_window frame];
1547         p_vout->p_sys->s_rect.origin = s_rect.origin;
1548
1549         p_vout->p_sys->b_pos_saved = YES;
1550     }
1551     
1552     p_vout->p_sys->p_qdport = nil;
1553     [p_vout->p_sys->o_window close];
1554     p_vout->p_sys->o_window = nil;
1555 }
1556
1557 @end