cd4c5880b6bd1a2df067f35f3481c7c10f06e2d2
[vlc.git] / modules / gui / macosx / vout.m
1
2 /* vout.m: MacOS X video output plugin
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id: vout.m,v 1.51 2003/08/14 12:38:03 garf 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 "intf.h"
37 #include "vout.h"
38
39 #define QT_MAX_DIRECTBUFFERS 10
40 #define VL_MAX_DISPLAYS 16
41
42 struct picture_sys_t
43 {
44     void *p_info;
45     unsigned int i_size;
46
47     /* When using I420 output */
48     PlanarPixmapInfoYUV420 pixmap_i420;
49 };
50
51 /*****************************************************************************
52  * Local prototypes
53  *****************************************************************************/
54
55 static int  vout_Init      ( vout_thread_t * );
56 static void vout_End       ( vout_thread_t * );
57 static int  vout_Manage    ( vout_thread_t * );
58 static void vout_Display   ( vout_thread_t *, picture_t * );
59
60 static int  CoSendRequest      ( vout_thread_t *, SEL );
61 static int  CoCreateWindow     ( vout_thread_t * );
62 static int  CoDestroyWindow    ( vout_thread_t * );
63 static int  CoToggleFullscreen ( vout_thread_t * );
64
65 static void VLCHideMouse       ( vout_thread_t *, BOOL );
66
67 static void QTScaleMatrix      ( vout_thread_t * );
68 static int  QTCreateSequence   ( vout_thread_t * );
69 static void QTDestroySequence  ( vout_thread_t * );
70 static int  QTNewPicture       ( vout_thread_t *, picture_t * );
71 static void QTFreePicture      ( vout_thread_t *, picture_t * );
72
73 /*****************************************************************************
74  * OpenVideo: allocates MacOS X video thread output method
75  *****************************************************************************
76  * This function allocates and initializes a MacOS X vout method.
77  *****************************************************************************/
78 int E_(OpenVideo) ( vlc_object_t *p_this )
79 {   
80     vout_thread_t * p_vout = (vout_thread_t *)p_this;
81     OSErr err;
82     int i_timeout;
83     vlc_value_t value_drawable;
84
85     var_Get( p_vout->p_vlc, "drawable", &value_drawable );
86
87     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
88     if( p_vout->p_sys == NULL )
89     {
90         msg_Err( p_vout, "out of memory" );
91         return( 1 );
92     }
93
94     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
95
96     /* We don't need an intf in mozilla plugin */
97     if( value_drawable.i_int == 0 )
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     
117         if( [NSApp respondsToSelector: @selector(getIntf)] )
118         {
119             intf_thread_t * p_intf;
120     
121             for( i_timeout = 10 ; i_timeout-- ; )
122             {
123                 if( ( p_intf = [NSApp getIntf] ) == NULL )
124                 {
125                     msleep( INTF_IDLE_SLEEP );
126                 }
127             }
128     
129             if( p_intf == NULL )
130             {
131                 msg_Err( p_vout, "MacOS X intf has getIntf, but is NULL" );
132                 free( p_vout->p_sys );
133                 return( 1 );
134             }
135         }
136     }
137
138     p_vout->p_sys->h_img_descr = 
139         (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
140     p_vout->p_sys->p_matrix = (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
141     p_vout->p_sys->p_fullscreen_state = NULL;
142
143     p_vout->p_sys->b_mouse_pointer_visible = YES;
144     p_vout->p_sys->b_mouse_moved = YES;
145     p_vout->p_sys->i_time_mouse_last_moved = mdate();
146
147     if( value_drawable.i_int != 0 )
148     {
149         p_vout->p_sys->mask = NewRgn();
150         p_vout->p_sys->isplugin = 1 ;
151     
152     } else
153     {
154         p_vout->p_sys->mask = NULL;
155         p_vout->p_sys->isplugin = 0 ;
156     }
157
158     /* set window size */
159     p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
160     p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
161
162     if( ( err = EnterMovies() ) != noErr )
163     {
164         msg_Err( p_vout, "EnterMovies failed: %d", err );
165         free( p_vout->p_sys->p_matrix );
166         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
167         free( p_vout->p_sys );
168         return( 1 );
169     } 
170
171     /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
172     vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
173
174     err = FindCodec( kYUV420CodecType, bestSpeedCodec,
175                         nil, &p_vout->p_sys->img_dc );
176     
177     vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
178     if( err == noErr && p_vout->p_sys->img_dc != 0 )
179     {
180         p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
181         p_vout->p_sys->i_codec = kYUV420CodecType;
182     }
183     else
184     {
185         msg_Err( p_vout, "failed to find an appropriate codec" );
186     }
187
188     if( p_vout->p_sys->img_dc == 0 )
189     {
190         free( p_vout->p_sys->p_matrix );
191         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
192         free( p_vout->p_sys );
193         return VLC_EGENERIC;        
194     }
195
196     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
197     NSArray * o_screens = [NSScreen screens];
198     if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
199     {
200         int i = 1;
201         vlc_value_t val, text;
202         NSScreen * o_screen;
203
204         int i_option = config_GetInt( p_vout, "macosx-vdev" );
205
206         var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
207                                             VLC_VAR_HASCHOICE ); 
208         text.psz_string = _("Video device");
209         var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
210         
211         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
212
213         while( (o_screen = [o_enumerator nextObject]) != NULL )
214         {
215             char psz_temp[255];
216             NSRect s_rect = [o_screen frame];
217
218             snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1, 
219                       "%s %d (%dx%d)", _("Screen"), i,
220                       (int)s_rect.size.width, (int)s_rect.size.height ); 
221
222             text.psz_string = psz_temp;
223             val.i_int = i;
224             var_Change( p_vout, "video-device",
225                         VLC_VAR_ADDCHOICE, &val, &text );
226
227             if( ( i - 1 ) == i_option )
228             {
229                 var_Set( p_vout, "video-device", val );
230             }
231             i++;
232         }
233
234         var_AddCallback( p_vout, "video-device", vout_VarCallback,
235                          NULL );
236
237         val.b_bool = VLC_TRUE;
238         var_Set( p_vout, "intf-change", val );
239     }
240     [o_pool release];
241
242     /* We don't need a window either in the mozilla plugin */
243     if( p_vout->p_sys->isplugin == 0 )
244     {
245         if( CoCreateWindow( p_vout ) )
246         {
247             msg_Err( p_vout, "unable to create window" );
248             free( p_vout->p_sys->p_matrix );
249             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
250             free( p_vout->p_sys ); 
251             return( 1 );
252         }
253     }
254
255     p_vout->pf_init = vout_Init;
256     p_vout->pf_end = vout_End;
257     p_vout->pf_manage = vout_Manage;
258     p_vout->pf_render = NULL;
259     p_vout->pf_display = vout_Display;
260
261     return( 0 );
262 }
263
264 /*****************************************************************************
265  * vout_Init: initialize video thread output method
266  *****************************************************************************/
267 static int vout_Init( vout_thread_t *p_vout )
268 {
269     int i_index;
270     picture_t *p_pic;
271     vlc_value_t val;
272
273     I_OUTPUTPICTURES = 0;
274
275     /* Initialize the output structure; we already found a codec,
276      * and the corresponding chroma we will be using. Since we can
277      * arbitrary scale, stick to the coordinates and aspect. */
278     p_vout->output.i_width  = p_vout->render.i_width;
279     p_vout->output.i_height = p_vout->render.i_height;
280     p_vout->output.i_aspect = p_vout->render.i_aspect;
281
282     var_Get( p_vout->p_vlc, "drawable", &val );
283
284     if( p_vout->p_sys->isplugin )
285     {
286         p_vout->p_sys->p_qdport = val.i_int;
287     }
288
289     SetPort( p_vout->p_sys->p_qdport );
290     QTScaleMatrix( p_vout );
291
292     if( QTCreateSequence( p_vout ) )
293     {
294         msg_Err( p_vout, "unable to create sequence" );
295         return( 1 );
296     }
297
298     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
299     while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
300     {
301         p_pic = NULL;
302
303         /* Find an empty picture slot */
304         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
305         {
306             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
307             {
308                 p_pic = p_vout->p_picture + i_index;
309                 break;
310             }
311         }
312
313         /* Allocate the picture */
314         if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
315         {
316             break;
317         }
318
319         p_pic->i_status = DESTROYED_PICTURE;
320         p_pic->i_type   = DIRECT_PICTURE;
321
322         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
323
324         I_OUTPUTPICTURES++;
325     }
326
327     return( 0 );
328 }
329
330 /*****************************************************************************
331  * vout_End: terminate video thread output method
332  *****************************************************************************/
333 static void vout_End( vout_thread_t *p_vout )
334 {
335     int i_index;
336
337     QTDestroySequence( p_vout );
338
339     /* Free the direct buffers we allocated */
340     for( i_index = I_OUTPUTPICTURES; i_index; )
341     {
342         i_index--;
343         QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
344     }
345 }
346
347 /*****************************************************************************
348  * CloseVideo: destroy video thread output method
349  *****************************************************************************/
350 void E_(CloseVideo) ( vlc_object_t *p_this )
351 {       
352     vout_thread_t * p_vout = (vout_thread_t *)p_this;
353
354     if ( p_vout->p_sys->isplugin == 0)
355     {
356         if( CoDestroyWindow( p_vout ) )
357         {
358             msg_Err( p_vout, "unable to destroy window" );
359         }
360     }
361
362     if ( p_vout->p_sys->p_fullscreen_state != NULL )
363         EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
364
365     ExitMovies();
366
367     free( p_vout->p_sys->p_matrix );
368     DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
369
370     free( p_vout->p_sys );
371 }
372
373 /*****************************************************************************
374  * vout_Manage: handle events
375  *****************************************************************************
376  * This function should be called regularly by video output thread. It manages
377  * console events. It returns a non null value on error.
378  *****************************************************************************/
379 static int vout_Manage( vout_thread_t *p_vout )
380 {
381     vlc_value_t val1;
382     var_Get( p_vout->p_vlc, "drawableredraw", &val1 );
383
384     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
385     {
386         if( CoToggleFullscreen( p_vout ) )  
387         {
388             return( 1 );
389         }
390
391         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
392     }
393
394     if( (p_vout->i_changes & VOUT_SIZE_CHANGE) || (val1.i_int == 1))
395     {
396         if (val1.i_int == 1) 
397         {
398         val1.i_int = 0;
399         var_Set( p_vout->p_vlc, "drawableredraw", val1 );
400         SetDSequenceMask( p_vout->p_sys->i_seq , p_vout->p_sys->mask );
401         } else if (p_vout->i_changes & VOUT_SIZE_CHANGE)
402         {
403                 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
404         }
405     
406         QTScaleMatrix( p_vout );
407         SetDSequenceMatrix( p_vout->p_sys->i_seq, 
408                             p_vout->p_sys->p_matrix );
409     }
410
411     /* hide/show mouse cursor 
412      * this code looks unnecessarily complicated, but is necessary like this.
413      * it has to deal with multiple monitors and therefore checks a lot */
414     if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
415     {
416         if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 && 
417                    p_vout->p_sys->b_mouse_pointer_visible )
418         {
419             VLCHideMouse( p_vout, YES );
420         }
421         else if ( !p_vout->p_sys->b_mouse_pointer_visible )
422         {
423             vlc_bool_t b_playing = NO;
424             playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
425                                                                     FIND_ANYWHERE );
426
427             if ( p_playlist != nil )
428             {
429                 vlc_mutex_lock( &p_playlist->object_lock );
430                 if( p_playlist->p_input != NULL )
431                 {
432                     vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock );
433                     b_playing = p_playlist->p_input->stream.control.i_status != PAUSE_S;
434                     vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
435                 }
436                 vlc_mutex_unlock( &p_playlist->object_lock );
437                 vlc_object_release( p_playlist );
438             }
439             if ( !b_playing )
440             {
441                 VLCHideMouse( p_vout, NO );
442             }
443         }
444     }
445     else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
446     {
447         if( !p_vout->p_sys->b_mouse_pointer_visible )
448         {
449             VLCHideMouse( p_vout, NO );
450         }
451         else
452         {
453             p_vout->p_sys->b_mouse_moved = NO;
454         }
455     }
456     
457     return( 0 );
458 }
459
460 /*****************************************************************************
461  * vout_Display: displays previously rendered output
462  *****************************************************************************
463  * This function sends the currently rendered image to the display.
464  *****************************************************************************/
465 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
466 {
467     OSErr err;
468     CodecFlags flags;
469
470     if( p_vout->p_sys->isplugin )
471     {
472
473         /* In mozilla plugin, mozilla browser also draws things in
474          * the windows. So we have to update the port/Origin for each
475          * picture. FIXME : the vout should lock something ! */
476         GetPort( &p_vout->p_sys->p_qdportold );
477         SetPort( p_vout->p_sys->p_qdport );
478         SetOrigin( p_vout->p_sys->portx , p_vout->p_sys->porty );
479     
480         if( ( err = DecompressSequenceFrameS( 
481                         p_vout->p_sys->i_seq,
482                         p_pic->p_sys->p_info,
483                         p_pic->p_sys->i_size,                    
484                         codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
485         {
486             msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
487         }
488         else
489         {
490             QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
491         }
492     
493         SetPort( p_vout->p_sys->p_qdportold );
494     }
495     else
496     { 
497         if( ( err = DecompressSequenceFrameS(
498                         p_vout->p_sys->i_seq,
499                         p_pic->p_sys->p_info,
500                         p_pic->p_sys->i_size,
501                         codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
502         {
503             msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
504         }
505         else
506         {
507             QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
508         }
509     }
510 }
511
512 /*****************************************************************************
513  * CoSendRequest: send request to interface thread
514  *****************************************************************************
515  * Returns 0 on success, 1 otherwise
516  *****************************************************************************/
517 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
518 {
519     int i_ret = 0;
520
521     VLCVout * o_vlv = [[VLCVout alloc] init];
522
523     if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
524     {
525         msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
526     }
527
528     [o_vlv release];
529
530     return( i_ret );
531 }
532
533 /*****************************************************************************
534  * CoCreateWindow: create new window 
535  *****************************************************************************
536  * Returns 0 on success, 1 otherwise
537  *****************************************************************************/
538 static int CoCreateWindow( vout_thread_t *p_vout )
539 {
540     if( CoSendRequest( p_vout, @selector(createWindow:) ) )
541     {
542         msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
543         return( 1 );
544     }
545
546     return( 0 );
547 }
548
549 /*****************************************************************************
550  * CoDestroyWindow: destroy window 
551  *****************************************************************************
552  * Returns 0 on success, 1 otherwise
553  *****************************************************************************/
554 static int CoDestroyWindow( vout_thread_t *p_vout )
555 {
556     if( !p_vout->p_sys->b_mouse_pointer_visible )
557     {
558         VLCHideMouse( p_vout, NO );
559     }
560
561     if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
562     {
563         msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
564         return( 1 );
565     }
566
567     return( 0 );
568 }
569
570 /*****************************************************************************
571  * CoToggleFullscreen: toggle fullscreen 
572  *****************************************************************************
573  * Returns 0 on success, 1 otherwise
574  *****************************************************************************/
575 static int CoToggleFullscreen( vout_thread_t *p_vout )
576 {
577     QTDestroySequence( p_vout );
578
579     if( CoDestroyWindow( p_vout ) )
580     {
581         msg_Err( p_vout, "unable to destroy window" );
582         return( 1 );
583     }
584     
585     p_vout->b_fullscreen = !p_vout->b_fullscreen;
586
587     config_PutInt( p_vout, "fullscreen", p_vout->b_fullscreen );
588
589     if( CoCreateWindow( p_vout ) )
590     {
591         msg_Err( p_vout, "unable to create window" );
592         return( 1 );
593     }
594
595     if( QTCreateSequence( p_vout ) )
596     {
597         msg_Err( p_vout, "unable to create sequence" );
598         return( 1 ); 
599     } 
600
601     return( 0 );
602 }
603
604 /*****************************************************************************
605  * VLCHideMouse: if b_hide then hide the cursor
606  *****************************************************************************/
607 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
608 {
609     BOOL b_inside;
610     NSRect s_rect;
611     NSPoint ml;
612     NSWindow *o_window = p_vout->p_sys->o_window;
613     NSView *o_contents = [o_window contentView];
614     
615     s_rect = [o_contents bounds];
616     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
617     ml = [o_contents convertPoint:ml fromView:nil];
618     b_inside = [o_contents mouse: ml inRect: s_rect];
619     
620     if ( b_hide && b_inside )
621     {
622         /* only hide if mouse over VLCView */
623         [NSCursor hide];
624         p_vout->p_sys->b_mouse_pointer_visible = 0;
625     }
626     else if ( !b_hide )
627     {
628         [NSCursor unhide];
629         p_vout->p_sys->b_mouse_pointer_visible = 1;
630     }
631     p_vout->p_sys->b_mouse_moved = NO;
632     p_vout->p_sys->i_time_mouse_last_moved = mdate();
633     return;
634 }
635
636 /*****************************************************************************
637  * QTScaleMatrix: scale matrix 
638  *****************************************************************************/
639 static void QTScaleMatrix( vout_thread_t *p_vout )
640 {
641     Rect s_rect;
642     unsigned int i_width, i_height;
643     Fixed factor_x, factor_y;
644     unsigned int i_offset_x = 0;
645     unsigned int i_offset_y = 0;
646     vlc_value_t val;
647     vlc_value_t valt;
648     vlc_value_t vall;
649     vlc_value_t valb;
650     vlc_value_t valr;
651     vlc_value_t valx;
652     vlc_value_t valy;
653     vlc_value_t valw;
654     vlc_value_t valh;
655     vlc_value_t valportx;
656     vlc_value_t valporty;
657
658     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
659     i_width = s_rect.right - s_rect.left;
660     i_height = s_rect.bottom - s_rect.top;
661
662     var_Get( p_vout->p_vlc, "drawable", &val );
663     var_Get( p_vout->p_vlc, "drawablet", &valt );
664     var_Get( p_vout->p_vlc, "drawablel", &vall );
665     var_Get( p_vout->p_vlc, "drawableb", &valb );
666     var_Get( p_vout->p_vlc, "drawabler", &valr );
667     var_Get( p_vout->p_vlc, "drawablex", &valx );
668     var_Get( p_vout->p_vlc, "drawabley", &valy );
669     var_Get( p_vout->p_vlc, "drawablew", &valw );
670     var_Get( p_vout->p_vlc, "drawableh", &valh );
671     var_Get( p_vout->p_vlc, "drawableportx", &valportx );
672     var_Get( p_vout->p_vlc, "drawableporty", &valporty );
673
674     if( p_vout->p_sys->isplugin )
675     {
676         p_vout->p_sys->portx = valportx.i_int;
677         p_vout->p_sys->porty = valporty.i_int;
678         p_vout->p_sys->p_qdport = val.i_int;
679         i_width = valw.i_int;
680         i_height = valh.i_int;
681
682         SetRectRgn( p_vout->p_sys->mask , 0 , 0 , valr.i_int - vall.i_int , valb.i_int - valt.i_int );
683     }
684
685     if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
686     {
687         int i_adj_width = i_height * p_vout->output.i_aspect /
688                           VOUT_ASPECT_FACTOR;
689
690         factor_x = FixDiv( Long2Fix( i_adj_width ),
691                            Long2Fix( p_vout->output.i_width ) );
692         factor_y = FixDiv( Long2Fix( i_height ),
693                            Long2Fix( p_vout->output.i_height ) );
694
695         i_offset_x = (i_width - i_adj_width) / 2 + i_offset_x;
696     }
697     else
698     {
699         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
700                            p_vout->output.i_aspect;
701
702         factor_x = FixDiv( Long2Fix( i_width ),
703                            Long2Fix( p_vout->output.i_width ) );
704         factor_y = FixDiv( Long2Fix( i_adj_height ),
705                            Long2Fix( p_vout->output.i_height ) );
706
707         i_offset_y = (i_height - i_adj_height) / 2 + i_offset_y;
708     }
709     
710     SetIdentityMatrix( p_vout->p_sys->p_matrix );
711
712     ScaleMatrix( p_vout->p_sys->p_matrix,
713                  factor_x, factor_y,
714                  Long2Fix(0), Long2Fix(0) );            
715
716     TranslateMatrix( p_vout->p_sys->p_matrix, 
717                      Long2Fix(i_offset_x), 
718                      Long2Fix(i_offset_y) );
719
720 }
721
722 /*****************************************************************************
723  * QTCreateSequence: create a new sequence 
724  *****************************************************************************
725  * Returns 0 on success, 1 otherwise
726  *****************************************************************************/
727 static int QTCreateSequence( vout_thread_t *p_vout )
728 {
729     OSErr err;
730     ImageDescriptionPtr p_descr;
731
732     HLock( (Handle)p_vout->p_sys->h_img_descr );
733     p_descr = *p_vout->p_sys->h_img_descr;
734
735     p_descr->idSize = sizeof(ImageDescription);
736     p_descr->cType = p_vout->p_sys->i_codec;
737     p_descr->version = 1;
738     p_descr->revisionLevel = 0;
739     p_descr->vendor = 'appl';
740     p_descr->width = p_vout->output.i_width;
741     p_descr->height = p_vout->output.i_height;
742     p_descr->hRes = Long2Fix(72);
743     p_descr->vRes = Long2Fix(72);
744     p_descr->spatialQuality = codecLosslessQuality;
745     p_descr->frameCount = 1;
746     p_descr->clutID = -1;
747     p_descr->dataSize = 0;
748     p_descr->depth = 24;
749
750
751     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
752
753
754     if( ( err = DecompressSequenceBeginS( 
755                               &p_vout->p_sys->i_seq,
756                               p_vout->p_sys->h_img_descr,
757                               NULL, 0,
758                               p_vout->p_sys->p_qdport,
759                               NULL, NULL,
760                               p_vout->p_sys->p_matrix,
761                               0, p_vout->p_sys->mask,
762                               codecFlagUseImageBuffer,
763                               codecLosslessQuality,
764                               p_vout->p_sys->img_dc ) ) )
765     {
766         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
767         return( 1 );
768     }
769
770
771     return( 0 );
772 }
773
774 /*****************************************************************************
775  * QTDestroySequence: destroy sequence 
776  *****************************************************************************/
777 static void QTDestroySequence( vout_thread_t *p_vout )
778 {
779     CDSequenceEnd( p_vout->p_sys->i_seq );
780 }
781
782 /*****************************************************************************
783  * QTNewPicture: allocate a picture
784  *****************************************************************************
785  * Returns 0 on success, 1 otherwise
786  *****************************************************************************/
787 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
788 {
789     int i_width  = p_vout->output.i_width;
790     int i_height = p_vout->output.i_height;
791
792     /* We know the chroma, allocate a buffer which will be used
793      * directly by the decoder */
794     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
795
796     if( p_pic->p_sys == NULL )
797     {
798         return( -1 );
799     }
800
801     switch( p_vout->output.i_chroma )
802     {
803         case VLC_FOURCC('I','4','2','0'):
804
805             p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
806             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
807
808             /* Allocate the memory buffer */
809             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
810                                           16, i_width * i_height * 3 / 2 );
811
812             /* Y buffer */
813             p_pic->Y_PIXELS = p_pic->p_data; 
814             p_pic->p[Y_PLANE].i_lines = i_height;
815             p_pic->p[Y_PLANE].i_pitch = i_width;
816             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
817             p_pic->p[Y_PLANE].i_visible_pitch = i_width;
818
819             /* U buffer */
820             p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
821             p_pic->p[U_PLANE].i_lines = i_height / 2;
822             p_pic->p[U_PLANE].i_pitch = i_width / 2;
823             p_pic->p[U_PLANE].i_pixel_pitch = 1;
824             p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
825
826             /* V buffer */
827             p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
828             p_pic->p[V_PLANE].i_lines = i_height / 2;
829             p_pic->p[V_PLANE].i_pitch = i_width / 2;
830             p_pic->p[V_PLANE].i_pixel_pitch = 1;
831             p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
832
833             /* We allocated 3 planes */
834             p_pic->i_planes = 3;
835
836 #define P p_pic->p_sys->pixmap_i420
837             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
838                                        - p_pic->p_sys->p_info;
839             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
840                                         - p_pic->p_sys->p_info;
841             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
842                                         - p_pic->p_sys->p_info;
843
844             P.componentInfoY.rowBytes = i_width;
845             P.componentInfoCb.rowBytes = i_width / 2;
846             P.componentInfoCr.rowBytes = i_width / 2;
847 #undef P
848
849             break;
850
851     default:
852         /* Unknown chroma, tell the guy to get lost */
853         free( p_pic->p_sys );
854         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
855                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
856         p_pic->i_planes = 0;
857         return( -1 );
858     }
859
860     return( 0 );
861 }
862
863 /*****************************************************************************
864  * QTFreePicture: destroy a picture allocated with QTNewPicture
865  *****************************************************************************/
866 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
867 {
868     switch( p_vout->output.i_chroma )
869     {
870         case VLC_FOURCC('I','4','2','0'):
871             free( p_pic->p_data_orig );
872             break;
873     }
874
875     free( p_pic->p_sys );
876 }
877
878 /*****************************************************************************
879  * VLCWindow implementation
880  *****************************************************************************/
881 @implementation VLCWindow
882
883 - (void)setVout:(vout_thread_t *)_p_vout
884 {
885     p_vout = _p_vout;
886 }
887
888 - (vout_thread_t *)getVout
889 {
890     return( p_vout );
891 }
892
893 - (void)scaleWindowWithFactor: (float)factor
894 {
895     NSSize newsize;
896     int i_corrected_height, i_corrected_width;
897     NSPoint topleftbase;
898     NSPoint topleftscreen;
899     
900     if ( !p_vout->b_fullscreen )
901     {
902         topleftbase.x = 0;
903         topleftbase.y = [self frame].size.height;
904         topleftscreen = [self convertBaseToScreen: topleftbase];
905         
906         if( p_vout->output.i_height * p_vout->output.i_aspect > 
907                         p_vout->output.i_width * VOUT_ASPECT_FACTOR )
908         {
909             i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
910                                             VOUT_ASPECT_FACTOR;
911             newsize.width = (int) ( i_corrected_width * factor );
912             newsize.height = (int) ( p_vout->render.i_height * factor );
913         }
914         else
915         {
916             i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
917                                             p_vout->output.i_aspect;
918             newsize.width = (int) ( p_vout->render.i_width * factor );
919             newsize.height = (int) ( i_corrected_height * factor );
920         }
921
922         [self setContentSize: newsize];
923         
924         [self setFrameTopLeftPoint: topleftscreen];
925         p_vout->i_changes |= VOUT_SIZE_CHANGE;
926     }
927 }
928
929 - (void)toggleFloatOnTop
930 {
931     if( config_GetInt( p_vout, "macosx-float" ) )
932     {
933         config_PutInt( p_vout, "macosx-float", 0 );
934         [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
935     }
936     else
937     {
938         config_PutInt( p_vout, "macosx-float", 1 );
939         [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
940     }
941 }
942
943 - (void)toggleFullscreen
944 {
945     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
946 }
947
948 - (BOOL)isFullscreen
949 {
950     return( p_vout->b_fullscreen );
951 }
952
953 - (BOOL)canBecomeKeyWindow
954 {
955     return( YES );
956 }
957
958 - (void)keyDown:(NSEvent *)o_event
959 {
960     unichar key = 0;
961     vlc_value_t val;
962
963     if( [[o_event characters] length] )
964     {
965         key = [[o_event characters] characterAtIndex: 0];
966     }
967
968     switch( key )
969     {
970         case 'f': case 'F':
971             [self toggleFullscreen];
972             break;
973
974         case (unichar)0x1b: /* escape */
975             if( [self isFullscreen] )
976             {
977                 [self toggleFullscreen];
978             }
979             break;
980
981         case 'q': case 'Q':
982             p_vout->p_vlc->b_die = VLC_TRUE;
983             break;
984
985         case ' ':
986             input_SetStatus( p_vout, INPUT_STATUS_PAUSE );
987             break;
988
989         case (unichar)0xf700: /* arrow up */
990             val.psz_string = "UP";
991             var_Set( p_vout, "key-pressed", val );
992             break;
993
994         case (unichar)0xf701: /* arrow down */
995             val.psz_string = "DOWN";
996             var_Set( p_vout, "key-pressed", val );
997             break;
998
999         case (unichar)0xf702: /* arrow left */
1000             val.psz_string = "LEFT";
1001             var_Set( p_vout, "key-pressed", val );
1002             break;
1003
1004         case (unichar)0xf703: /* arrow right */
1005             val.psz_string = "RIGHT";
1006             var_Set( p_vout, "key-pressed", val );
1007             break;
1008
1009         case (unichar)0xd: /* return */
1010         case (unichar)0x3: /* enter */
1011             val.psz_string = "ENTER";
1012             var_Set( p_vout, "key-pressed", val );
1013             break;
1014
1015
1016         default:
1017             [super keyDown: o_event];
1018             break;
1019     }
1020 }
1021
1022 - (void)updateTitle
1023 {
1024     NSMutableString * o_title;
1025     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1026                                                        FIND_ANYWHERE );
1027     
1028     if( p_playlist == NULL )
1029     {
1030         return;
1031     }
1032
1033     vlc_mutex_lock( &p_playlist->object_lock );
1034     o_title = [NSMutableString stringWithUTF8String: 
1035         p_playlist->pp_items[p_playlist->i_index]->psz_name]; 
1036     vlc_mutex_unlock( &p_playlist->object_lock );
1037
1038     vlc_object_release( p_playlist );
1039
1040     if( o_title != nil )
1041     {
1042         NSRange prefix_range = [o_title rangeOfString: @"file:"];
1043         if( prefix_range.location != NSNotFound )
1044         {
1045             [o_title deleteCharactersInRange: prefix_range];
1046         }
1047
1048         [self setTitleWithRepresentedFilename: o_title];
1049     }
1050     else
1051     {
1052         [self setTitle:
1053             [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1054     }
1055 }
1056
1057 /* This is actually the same as VLCControls::stop. */
1058 - (BOOL)windowShouldClose:(id)sender
1059 {
1060     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1061                                                        FIND_ANYWHERE );
1062     if( p_playlist == NULL )      
1063     {
1064         return NO;
1065     }
1066
1067     playlist_Stop( p_playlist );
1068     vlc_object_release( p_playlist );
1069
1070     /* The window will be closed by the intf later. */
1071     return NO;
1072 }
1073
1074 @end
1075
1076 /*****************************************************************************
1077  * VLCView implementation
1078  *****************************************************************************/
1079 @implementation VLCView
1080
1081 - (void)drawRect:(NSRect)rect
1082 {
1083     vout_thread_t * p_vout;
1084     id o_window = [self window];
1085     p_vout = (vout_thread_t *)[o_window getVout];
1086     
1087     [[NSColor blackColor] set];
1088     NSRectFill( rect );
1089     [super drawRect: rect];
1090
1091     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1092 }
1093
1094 - (BOOL)acceptsFirstResponder
1095 {
1096     return( YES );
1097 }
1098
1099 - (BOOL)becomeFirstResponder
1100 {
1101     vout_thread_t * p_vout;
1102     id o_window = [self window];
1103     p_vout = (vout_thread_t *)[o_window getVout];
1104     
1105     [o_window setAcceptsMouseMovedEvents: YES];
1106     return( YES );
1107 }
1108
1109 - (BOOL)resignFirstResponder
1110 {
1111     vout_thread_t * p_vout;
1112     id o_window = [self window];
1113     p_vout = (vout_thread_t *)[o_window getVout];
1114     
1115     [o_window setAcceptsMouseMovedEvents: NO];
1116     VLCHideMouse( p_vout, NO );
1117     return( YES );
1118 }
1119
1120 - (void)mouseDown:(NSEvent *)o_event
1121 {
1122     vout_thread_t * p_vout;
1123     id o_window = [self window];
1124     p_vout = (vout_thread_t *)[o_window getVout];
1125     vlc_value_t val;
1126
1127     switch( [o_event type] )
1128     {        
1129         case NSLeftMouseDown:
1130         {
1131             var_Get( p_vout, "mouse-button-down", &val );
1132             val.i_int |= 1;
1133             var_Set( p_vout, "mouse-button-down", val );
1134         }
1135         break;
1136         
1137         default:
1138             [super mouseDown: o_event];
1139         break;
1140     }
1141 }
1142
1143 - (void)otherMouseDown:(NSEvent *)o_event
1144 {
1145     /* This is not the the wheel button. you need to poll the
1146      * mouseWheel event for that. other is a third, forth or fifth button */
1147     vout_thread_t * p_vout;
1148     id o_window = [self window];
1149     p_vout = (vout_thread_t *)[o_window getVout];
1150     vlc_value_t val;
1151
1152     switch( [o_event type] )
1153     {
1154         case NSOtherMouseDown:
1155         {
1156             var_Get( p_vout, "mouse-button-down", &val );
1157             val.i_int |= 2;
1158             var_Set( p_vout, "mouse-button-down", val );
1159         }
1160         break;
1161         
1162         default:
1163             [super mouseDown: o_event];
1164         break;
1165     }
1166 }
1167
1168 - (void)rightMouseDown:(NSEvent *)o_event
1169 {
1170     vout_thread_t * p_vout;
1171     id o_window = [self window];
1172     p_vout = (vout_thread_t *)[o_window getVout];
1173     vlc_value_t val;
1174
1175     switch( [o_event type] )
1176     {
1177         case NSRightMouseDown:
1178         {
1179             var_Get( p_vout, "mouse-button-down", &val );
1180             val.i_int |= 4;
1181             var_Set( p_vout, "mouse-button-down", val );
1182         }
1183         break;
1184         
1185         default:
1186             [super mouseDown: o_event];
1187         break;
1188     }
1189 }
1190
1191 - (void)mouseUp:(NSEvent *)o_event
1192 {
1193     vout_thread_t * p_vout;
1194     id o_window = [self window];
1195     p_vout = (vout_thread_t *)[o_window getVout];
1196     vlc_value_t val;
1197
1198     switch( [o_event type] )
1199     {
1200         case NSLeftMouseUp:
1201         {
1202             vlc_value_t b_val;
1203             b_val.b_bool = VLC_TRUE;
1204             var_Set( p_vout, "mouse-clicked", b_val );
1205             
1206             var_Get( p_vout, "mouse-button-down", &val );
1207             val.i_int &= ~1;
1208             var_Set( p_vout, "mouse-button-down", val );
1209         }
1210         break;
1211                 
1212         default:
1213             [super mouseUp: o_event];
1214         break;
1215     }
1216 }
1217
1218 - (void)otherMouseUp:(NSEvent *)o_event
1219 {
1220     vout_thread_t * p_vout;
1221     id o_window = [self window];
1222     p_vout = (vout_thread_t *)[o_window getVout];
1223     vlc_value_t val;
1224
1225     switch( [o_event type] )
1226     {
1227         case NSOtherMouseUp:
1228         {
1229             var_Get( p_vout, "mouse-button-down", &val );
1230             val.i_int &= ~2;
1231             var_Set( p_vout, "mouse-button-down", val );
1232         }
1233         break;
1234                 
1235         default:
1236             [super mouseUp: o_event];
1237         break;
1238     }
1239 }
1240
1241 - (void)rightMouseUp:(NSEvent *)o_event
1242 {
1243     vout_thread_t * p_vout;
1244     id o_window = [self window];
1245     p_vout = (vout_thread_t *)[o_window getVout];
1246     vlc_value_t val;
1247
1248     switch( [o_event type] )
1249     {
1250         case NSRightMouseUp:
1251         {
1252             var_Get( p_vout, "mouse-button-down", &val );
1253             val.i_int &= ~4;
1254             var_Set( p_vout, "mouse-button-down", val );
1255         }
1256         break;
1257         
1258         default:
1259             [super mouseUp: o_event];
1260         break;
1261     }
1262 }
1263
1264 - (void)mouseDragged:(NSEvent *)o_event
1265 {
1266     [self mouseMoved:o_event];
1267 }
1268
1269 - (void)otherMouseDragged:(NSEvent *)o_event
1270 {
1271     [self mouseMoved:o_event];
1272 }
1273
1274 - (void)rightMouseDragged:(NSEvent *)o_event
1275 {
1276     [self mouseMoved:o_event];
1277 }
1278
1279 - (void)mouseMoved:(NSEvent *)o_event
1280 {
1281     NSPoint ml;
1282     NSRect s_rect;
1283     BOOL b_inside;
1284
1285     vout_thread_t * p_vout;
1286     id o_window = [self window];
1287     p_vout = (vout_thread_t *)[o_window getVout];
1288
1289     s_rect = [self bounds];
1290     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1291     b_inside = [self mouse: ml inRect: s_rect];
1292
1293     if( b_inside )
1294     {
1295         vlc_value_t val;
1296         int i_width, i_height, i_x, i_y;
1297         
1298         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1299                                    (unsigned int)s_rect.size.height,
1300                                    &i_x, &i_y, &i_width, &i_height );
1301
1302         val.i_int = ( ((int)ml.x) - i_x ) * 
1303                     p_vout->render.i_width / i_width;
1304         var_Set( p_vout, "mouse-x", val );
1305
1306         val.i_int = ( ((int)ml.y) - i_y ) * 
1307                     p_vout->render.i_height / i_height;
1308         var_Set( p_vout, "mouse-y", val );
1309             
1310         val.b_bool = VLC_TRUE;
1311         var_Set( p_vout, "mouse-moved", val );
1312         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1313         p_vout->p_sys->b_mouse_moved = YES;
1314     }
1315     else if ( !b_inside && !p_vout->p_sys->b_mouse_pointer_visible )
1316     {
1317         /* people with multiple monitors need their mouse,
1318          * even if VLCView in fullscreen. */
1319         VLCHideMouse( p_vout, NO );
1320     }
1321     
1322     [super mouseMoved: o_event];
1323 }
1324
1325 @end
1326
1327 /*****************************************************************************
1328  * VLCVout implementation
1329  *****************************************************************************/
1330 @implementation VLCVout
1331
1332 - (void)createWindow:(NSValue *)o_value
1333 {
1334     vlc_value_t val;
1335     VLCView * o_view;
1336     NSScreen * o_screen;
1337     vout_thread_t * p_vout;
1338     vlc_bool_t b_main_screen;
1339     
1340     p_vout = (vout_thread_t *)[o_value pointerValue];
1341
1342     p_vout->p_sys->o_window = [VLCWindow alloc];
1343     [p_vout->p_sys->o_window setVout: p_vout];
1344     [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1345
1346     if( var_Get( p_vout, "video-device", &val ) < 0 )
1347     {
1348         o_screen = [NSScreen mainScreen];
1349         b_main_screen = 1;
1350     }
1351     else
1352     {
1353         NSArray *o_screens = [NSScreen screens];
1354         unsigned int i_index = val.i_int;
1355         
1356         if( [o_screens count] < i_index )
1357         {
1358             o_screen = [NSScreen mainScreen];
1359             b_main_screen = 1;
1360         }
1361         else
1362         {
1363             i_index--;
1364             o_screen = [o_screens objectAtIndex: i_index];
1365             config_PutInt( p_vout, "macosx-vdev", i_index );
1366             b_main_screen = (i_index == 0);
1367         }
1368     } 
1369
1370     if( p_vout->b_fullscreen )
1371     {
1372         NSRect screen_rect = [o_screen frame];
1373         screen_rect.origin.x = screen_rect.origin.y = 0;
1374
1375         if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1376             BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1377                              NULL, NULL, fullScreenAllowEvents );
1378
1379         [p_vout->p_sys->o_window 
1380             initWithContentRect: screen_rect
1381             styleMask: NSBorderlessWindowMask
1382             backing: NSBackingStoreBuffered
1383             defer: NO screen: o_screen];
1384
1385         [p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1386         p_vout->p_sys->b_mouse_moved = YES;
1387         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1388     }
1389     else
1390     {
1391         unsigned int i_stylemask = NSTitledWindowMask |
1392                                    NSMiniaturizableWindowMask |
1393                                    NSClosableWindowMask |
1394                                    NSResizableWindowMask;
1395         
1396         if ( p_vout->p_sys->p_fullscreen_state != NULL )
1397             EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1398         p_vout->p_sys->p_fullscreen_state = NULL;
1399
1400         [p_vout->p_sys->o_window 
1401             initWithContentRect: p_vout->p_sys->s_rect
1402             styleMask: i_stylemask
1403             backing: NSBackingStoreBuffered
1404             defer: NO screen: o_screen];
1405
1406         [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1407         
1408         if( config_GetInt( p_vout, "macosx-float" ) )
1409         {
1410             [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1411         }
1412         
1413         if( !p_vout->p_sys->b_pos_saved )   
1414         {
1415             [p_vout->p_sys->o_window center];
1416         }
1417     }
1418
1419     o_view = [[VLCView alloc] init];
1420
1421     /* FIXME: [o_view setMenu:] */
1422     [p_vout->p_sys->o_window setContentView: o_view];
1423     [o_view autorelease];
1424
1425     [o_view lockFocus];
1426     p_vout->p_sys->p_qdport = [o_view qdPort];
1427
1428     [o_view unlockFocus];
1429     
1430     [p_vout->p_sys->o_window updateTitle];
1431     [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1432 }
1433
1434 - (void)destroyWindow:(NSValue *)o_value
1435 {
1436     vout_thread_t * p_vout;
1437
1438     p_vout = (vout_thread_t *)[o_value pointerValue];
1439
1440     if( !p_vout->b_fullscreen )
1441     {
1442         NSRect s_rect;
1443
1444         s_rect = [[p_vout->p_sys->o_window contentView] frame];
1445         p_vout->p_sys->s_rect.size = s_rect.size;
1446
1447         s_rect = [p_vout->p_sys->o_window frame];
1448         p_vout->p_sys->s_rect.origin = s_rect.origin;
1449
1450         p_vout->p_sys->b_pos_saved = YES;
1451     }
1452     
1453     p_vout->p_sys->p_qdport = nil;
1454     [p_vout->p_sys->o_window close];
1455     p_vout->p_sys->o_window = nil;
1456 }
1457
1458 @end