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