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