]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
Clean up here and there
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2008 the VideoLAN team
5  * $Id$
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 <hartman at videolan dot org>
11  *          Eric Petit <titer@m0k.org>
12  *          Benjamin Pracht <bigben at videolan dot org>
13  *          Felix Paul Kühne <fkuehne at videolan dot org>
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
28  *****************************************************************************/
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <stdlib.h>                                                /* free() */
35 #include <string.h>
36
37 /* BeginFullScreen, EndFullScreen */
38 #include <QuickTime/QuickTime.h>
39
40 /* prevent system sleep */
41 #import <CoreServices/CoreServices.h>
42
43 #include <vlc_keys.h>
44
45 #include "intf.h"
46 #include "fspanel.h"
47 #include "vout.h"
48 #import "controls.h"
49 #import "embeddedwindow.h"
50
51 /*****************************************************************************
52  * DeviceCallback: Callback triggered when the video-device variable is changed
53  *****************************************************************************/
54 int DeviceCallback( vlc_object_t *p_this, const char *psz_variable,
55                      vlc_value_t old_val, vlc_value_t new_val, void *param )
56 {
57     vlc_value_t val;
58     vout_thread_t *p_vout = (vout_thread_t *)p_this;
59
60     msg_Dbg( p_vout, "set %d", new_val.i_int );
61     var_Create( p_vout->p_libvlc, "video-device", VLC_VAR_INTEGER );
62     var_Set( p_vout->p_libvlc, "video-device", new_val );
63
64     val.b_bool = true;
65     var_Set( p_vout, "intf-change", val );
66     return VLC_SUCCESS;
67 }
68
69
70 /*****************************************************************************
71  * VLCEmbeddedList implementation
72  *****************************************************************************/
73 @implementation VLCEmbeddedList
74
75 - (id)init
76 {
77     [super init];
78     o_embedded_array = [NSMutableArray array];
79     return self;
80 }
81
82 - (id)getEmbeddedVout
83 {
84     unsigned int i;
85
86     for( i = 0; i < [o_embedded_array count]; i++ )
87     {
88         id o_vout_view = [o_embedded_array objectAtIndex: i];
89         if( ![o_vout_view isUsed] )
90         {
91             [o_vout_view setUsed: YES];
92             return o_vout_view;
93         }
94     }
95     return nil;
96 }
97
98 - (void)releaseEmbeddedVout: (id)o_vout_view
99 {
100     if( [o_embedded_array containsObject: o_vout_view] )
101     {
102         [o_vout_view setUsed: NO];
103     }
104     else
105     {
106         msg_Warn( VLCIntf, "cannot find Video Output");
107     }
108 }
109
110 - (void)addEmbeddedVout: (id)o_vout_view
111 {
112     if( ![o_embedded_array containsObject: o_vout_view] )
113     {
114         [o_embedded_array addObject: o_vout_view];
115     }
116 }
117
118 - (BOOL)windowContainsEmbedded: (id)o_window
119 {
120 /*    if( ![[o_window className] isEqualToString: @"VLCVoutWindow"] )
121     {
122         NSLog( @"We were not given a VLCVoutWindow" );
123     }*/
124     return ([self getViewForWindow: o_window] == nil ? NO : YES );
125 }
126
127 - (id)getViewForWindow: (id)o_window
128 {
129     if( o_embedded_array != nil )
130     {
131         id o_enumerator = [o_embedded_array objectEnumerator];
132         id o_current_embedded;
133         if( o_window != nil )
134         {
135             while( (o_current_embedded = [o_enumerator nextObject]) )
136             {
137                 if( [o_current_embedded getWindow] == o_window )
138                 {
139                     return o_current_embedded;
140                 }
141             }
142         }
143     }
144     return nil;
145 }
146
147 @end
148
149 /*****************************************************************************
150  * VLCVoutView implementation
151  *****************************************************************************/
152 @implementation VLCVoutView
153
154 - (id)initWithFrame: (NSRect)frameRect
155 {
156     self = [super initWithFrame: frameRect];
157     p_vout = NULL;
158     o_view = nil;
159     s_frame = &frameRect;
160
161     p_real_vout = NULL;
162     o_window = nil;
163     return self;
164 }
165
166 - (BOOL)setVout: (vout_thread_t *) vout
167         subView: (NSView *) view
168           frame: (NSRect *) frame
169 {
170     int i_device;
171     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
172     NSArray *o_screens = [NSScreen screens];
173
174     p_vout  = vout;
175     o_view  = view;
176     s_frame = frame;
177
178     if( [o_screens count] <= 0 )
179     {
180         msg_Err( p_vout, "no OSX screens available" );
181         return NO;
182     }
183
184     p_real_vout = [VLCVoutView getRealVout: p_vout];
185
186     /* Get the pref value when this is the first time, otherwise retrieve the device from the top level video-device var */
187     if( var_Type( p_real_vout->p_libvlc, "video-device" ) == 0 )
188     {
189         i_device = var_GetInteger( p_vout, "macosx-vdev" );
190     }
191     else
192     {
193         i_device = var_GetInteger( p_real_vout->p_libvlc, "video-device" );
194     }
195
196     /* Setup the menuitem for the multiple displays. */
197     if( var_Type( p_real_vout, "video-device" ) == 0 )
198     {
199         int i = 1;
200         vlc_value_t val2, text;
201         NSScreen * o_screen;
202
203         var_Create( p_real_vout, "video-device", VLC_VAR_INTEGER |
204                                             VLC_VAR_HASCHOICE );
205         text.psz_string = _("Fullscreen Video Device");
206         var_Change( p_real_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
207
208         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
209
210         val2.i_int = 0;
211         text.psz_string = _("Default");
212         var_Change( p_real_vout, "video-device",
213                         VLC_VAR_ADDCHOICE, &val2, &text );
214         var_Set( p_real_vout, "video-device", val2 );
215
216         while( (o_screen = [o_enumerator nextObject]) != NULL )
217         {
218             char psz_temp[255];
219             NSRect s_rect = [o_screen frame];
220
221             snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
222                       "%s %d (%dx%d)", _("Screen"), i,
223                       (int)s_rect.size.width, (int)s_rect.size.height );
224
225             text.psz_string = psz_temp;
226             val2.i_int = (int)[o_screen displayID];
227             var_Change( p_real_vout, "video-device",
228                         VLC_VAR_ADDCHOICE, &val2, &text );
229             if( (int)[o_screen displayID] == i_device )
230             {
231                 var_Set( p_real_vout, "video-device", val2 );
232             }
233             i++;
234         }
235
236         var_AddCallback( p_real_vout, "video-device", DeviceCallback,
237                          NULL );
238
239         val2.b_bool = true;
240         var_Set( p_real_vout, "intf-change", val2 );
241     }
242
243     /* Add the view. It's automatically resized to fit the window */
244     [self addSubview: o_view];
245     [self setAutoresizesSubviews: YES];
246     [o_pool release];
247
248     return YES;
249 }
250
251 - (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize
252 {
253     [super resizeSubviewsWithOldSize: oldBoundsSize];
254     [o_view setFrameSize: [self frame].size];
255 }
256
257 - (void)drawRect:(NSRect)rect
258 {
259     /* When there is no subview we draw a black background */
260     [self lockFocus];
261     [[NSColor blackColor] set];
262     NSRectFill(rect);
263     [self unlockFocus];
264 }
265
266 - (void)closeVout
267 {
268     [[[[VLCMain sharedInstance] getControls] getFSPanel] fadeOut];
269
270     /* Make sure we don't see a white flash */
271     [[self window] disableScreenUpdatesUntilFlush];
272     [o_view removeFromSuperview];
273     o_view = nil;
274     p_vout = NULL;
275     s_frame = nil;
276     o_window = nil;
277     p_real_vout = NULL;
278 }
279
280 - (void)updateTitle
281 {
282     NSString * o_title = nil; 
283     NSMutableString * o_mrl = nil;
284     input_thread_t * p_input;
285     char * psz_title;
286
287     if( !p_vout ) return;
288
289     p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
290
291     if( !p_input ) return;
292
293     input_item_t * p_item = input_GetItem( p_input );
294
295     psz_title = input_item_GetNowPlaying ( p_item );
296     if( !psz_title )
297         psz_title = input_item_GetName( p_item );
298
299     if( psz_title )
300         o_title = [NSString stringWithUTF8String: psz_title];
301
302     char *psz_uri = input_item_GetURI( p_item );
303     if( psz_uri )
304         o_mrl = [NSMutableString stringWithUTF8String: psz_uri];
305
306     free( psz_title );
307     free( psz_uri );
308
309     if( !o_title )
310         o_title = o_mrl;
311
312     if( o_mrl != nil )
313     {
314         /* FIXME once psz_access is exported, we could check if we are
315          * reading from a file in a smarter way. */
316
317         NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
318         if( prefix_range.location != NSNotFound )
319             [o_mrl deleteCharactersInRange: prefix_range];
320
321         if( [o_mrl characterAtIndex:0] == '/' )
322         {
323             /* it's a local file */
324             [o_window setRepresentedFilename: o_mrl];
325         }
326         else
327         {
328             /* it's from the network or somewhere else,
329              * we clear the previous path */
330             [o_window setRepresentedFilename: @""];
331         }
332         [o_window setTitle: o_title];
333     }
334     else
335     {
336         [o_window setTitle: [NSString stringWithUTF8String: VOUT_TITLE]];
337     }
338     vlc_object_release( p_input );
339 }
340
341
342 - (void)setOnTop:(BOOL)b_on_top
343 {
344     if( b_on_top )
345     {
346         [o_window setLevel: NSStatusWindowLevel];
347     }
348     else
349     {
350         [o_window setLevel: NSNormalWindowLevel];
351     }
352 }
353
354 - (NSSize)voutSizeForFactor: (float)factor
355 {
356     int i_corrected_height, i_corrected_width;
357     NSSize newsize;
358
359     if( p_vout->render.i_height * p_vout->render.i_aspect >
360                     p_vout->render.i_width * VOUT_ASPECT_FACTOR )
361     {
362         i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect /
363                                         VOUT_ASPECT_FACTOR;
364         newsize.width = (int) ( i_corrected_width * factor );
365         newsize.height = (int) ( p_vout->render.i_height * factor );
366     }
367     else
368     {
369         i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR /
370                                         p_vout->render.i_aspect;
371         newsize.width = (int) ( p_vout->render.i_width * factor );
372         newsize.height = (int) ( i_corrected_height * factor );
373     }
374
375     return newsize;
376 }
377
378 - (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate
379 {
380     if ( !p_vout->b_fullscreen )
381     {
382         NSSize newsize;
383         NSPoint topleftbase;
384         NSPoint topleftscreen;
385         NSView *mainView;
386         NSRect new_frame;
387         topleftbase.x = 0;
388         topleftbase.y = [o_window frame].size.height;
389         topleftscreen = [o_window convertBaseToScreen: topleftbase];
390
391         newsize = [self voutSizeForFactor:factor];
392
393         /* In fullscreen mode we need to use a view that is different from
394          * ourselves, with the VLCEmbeddedWindow */
395         if([o_window isKindOfClass:[VLCEmbeddedWindow class]])
396             mainView = [o_window mainView];
397         else
398             mainView = self;
399
400         /* Calculate the window's new size */
401         new_frame.size.width = [o_window frame].size.width -
402                                     [mainView frame].size.width + newsize.width;
403         new_frame.size.height = [o_window frame].size.height -
404                                     [mainView frame].size.height + newsize.height;
405
406         new_frame.origin.x = topleftscreen.x;
407         new_frame.origin.y = topleftscreen.y - new_frame.size.height;
408
409         [o_window setFrame:new_frame display:animate animate:animate];
410         p_vout->i_changes |= VOUT_SIZE_CHANGE;
411     }
412 }
413
414 - (void)toggleFloatOnTop
415 {
416     vlc_value_t val;
417
418     if( !p_real_vout ) return;
419     if( var_Get( p_real_vout, "video-on-top", &val )>=0 && val.b_bool)
420     {
421         val.b_bool = false;
422     }
423     else
424     {
425         val.b_bool = true;
426     }
427     var_Set( p_real_vout, "video-on-top", val );
428 }
429
430 - (void)toggleFullscreen
431 {
432     vlc_value_t val;
433     if( !p_real_vout ) return;
434     var_Get( p_real_vout, "fullscreen", &val );
435     val.b_bool = !val.b_bool;
436     var_Set( p_real_vout, "fullscreen", val );
437 }
438
439 - (BOOL)isFullscreen
440 {
441     vlc_value_t val;
442     if( !p_real_vout ) return NO;
443     var_Get( p_real_vout, "fullscreen", &val );
444     return( val.b_bool );
445 }
446
447 - (void)snapshot
448 {
449     vout_Control( p_real_vout, VOUT_SNAPSHOT );
450 }
451
452 - (void)manage
453 {
454     /* Disable Screensaver, when we're playing something, but allow it on pause */
455     if( !VLCIntf || !VLCIntf->p_sys )
456         return;
457
458     UInt8 UsrActivity;
459     if( VLCIntf->p_sys->i_play_status == PLAYING_S )
460         UpdateSystemActivity( UsrActivity );
461 }
462
463 - (id)getWindow
464 {
465     return o_window;
466 }
467
468 - (void)scrollWheel:(NSEvent *)theEvent
469 {
470     VLCControls * o_controls = (VLCControls *)[[NSApp delegate] getControls];
471     [o_controls scrollWheel: theEvent];
472 }
473
474 - (void)keyDown:(NSEvent *)o_event
475 {
476     unichar key = 0;
477     vlc_value_t val;
478     unsigned int i_pressed_modifiers = 0;
479     val.i_int = 0;
480
481     i_pressed_modifiers = [o_event modifierFlags];
482
483     if( i_pressed_modifiers & NSShiftKeyMask )
484         val.i_int |= KEY_MODIFIER_SHIFT;
485     if( i_pressed_modifiers & NSControlKeyMask )
486         val.i_int |= KEY_MODIFIER_CTRL;
487     if( i_pressed_modifiers & NSAlternateKeyMask )
488         val.i_int |= KEY_MODIFIER_ALT;
489     if( i_pressed_modifiers & NSCommandKeyMask )
490         val.i_int |= KEY_MODIFIER_COMMAND;
491
492     key = [[[o_event charactersIgnoringModifiers] lowercaseString] characterAtIndex: 0];
493
494     if( key )
495     {
496         /* Escape should always get you out of fullscreen */
497         if( key == (unichar) 0x1b )
498         {
499              if( p_real_vout && [self isFullscreen] )
500              {
501                  [self toggleFullscreen];
502              }
503         }
504         else if ( p_vout )
505         {
506             if( key == ' ')
507                 val.i_int = config_GetInt( p_vout, "key-play-pause" );
508             else
509                 val.i_int |= (int)CocoaKeyToVLC( key );
510             var_Set( p_vout->p_libvlc, "key-pressed", val );
511         }
512         else NSLog( @"Could not send keyevent to VLC core" );
513     }
514     else
515         [super keyDown: o_event];
516 }
517
518 - (void)mouseDown:(NSEvent *)o_event
519 {
520     vlc_value_t val;
521     if( p_vout )
522     {
523         if( ( [o_event type] == NSLeftMouseDown ) &&
524           ( ! ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
525         {
526             if( [o_event clickCount] <= 1 )
527             {
528                 /* single clicking */
529                 var_Get( p_vout, "mouse-button-down", &val );
530                 val.i_int |= 1;
531                 var_Set( p_vout, "mouse-button-down", val );
532             }
533             else
534             {
535                 /* multiple clicking */
536                 [self toggleFullscreen];
537             }
538         }
539         else if( ( [o_event type] == NSRightMouseDown ) ||
540                ( ( [o_event type] == NSLeftMouseDown ) &&
541                  ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
542         {
543             msg_Dbg( p_vout, "received NSRightMouseDown (generic method) or Ctrl clic" );
544             [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] voutView]];
545         }
546     }
547
548     [super mouseDown: o_event];
549 }
550
551 - (void)otherMouseDown:(NSEvent *)o_event
552 {
553     vlc_value_t val;
554
555     if( p_vout && [o_event type] == NSOtherMouseDown )
556     {
557         var_Get( p_vout, "mouse-button-down", &val );
558         val.i_int |= 2;
559         var_Set( p_vout, "mouse-button-down", val );
560     }
561
562     [super mouseDown: o_event];
563 }
564
565 - (void)rightMouseDown:(NSEvent *)o_event
566 {
567     if( p_vout && [o_event type] == NSRightMouseDown )
568     {
569         msg_Dbg( p_vout, "received NSRightMouseDown (specific method)" );
570         [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] voutView]];
571     }
572
573     [super mouseDown: o_event];
574 }
575
576 - (void)mouseUp:(NSEvent *)o_event
577 {
578     vlc_value_t val;
579
580     if( p_vout && [o_event type] == NSLeftMouseUp )
581     {
582         vlc_value_t b_val;
583         b_val.b_bool = true;
584         var_Set( p_vout, "mouse-clicked", b_val );
585
586         var_Get( p_vout, "mouse-button-down", &val );
587         val.i_int &= ~1;
588         var_Set( p_vout, "mouse-button-down", val );
589     }
590
591     [super mouseUp: o_event];
592 }
593
594 - (void)otherMouseUp:(NSEvent *)o_event
595 {
596     vlc_value_t val;
597
598     if( p_vout && [o_event type] == NSOtherMouseUp )
599     {
600         var_Get( p_vout, "mouse-button-down", &val );
601         val.i_int &= ~2;
602         var_Set( p_vout, "mouse-button-down", val );
603     }
604
605     [super mouseUp: o_event];
606 }
607
608 - (void)rightMouseUp:(NSEvent *)o_event
609 {
610     if( p_vout && [o_event type] == NSRightMouseUp )
611     {
612         /* FIXME: this isn't the appropriate place, but we can't receive
613          * NSRightMouseDown some how */
614         msg_Dbg( p_vout, "received NSRightMouseUp" );
615         [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] voutView]];
616     }
617
618     [super mouseUp: o_event];
619 }
620
621 - (void)mouseDragged:(NSEvent *)o_event
622 {
623     [self mouseMoved: o_event];
624 }
625
626 - (void)otherMouseDragged:(NSEvent *)o_event
627 {
628     [self mouseMoved: o_event];
629 }
630
631 - (void)rightMouseDragged:(NSEvent *)o_event
632 {
633     [self mouseMoved: o_event];
634 }
635
636 - (void)mouseMoved:(NSEvent *)o_event
637 {
638     NSPoint ml;
639     NSRect s_rect;
640     BOOL b_inside;
641
642     if( p_vout )
643     {
644         s_rect = [o_view bounds];
645         ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
646         b_inside = [o_view mouse: ml inRect: s_rect];
647
648         if( b_inside )
649         {
650             vlc_value_t val;
651             unsigned int i_width, i_height, i_x, i_y;
652
653             vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
654                                        (unsigned int)s_rect.size.height,
655                                        &i_x, &i_y, &i_width, &i_height );
656
657             val.i_int = ( ((int)ml.x) - i_x ) *
658                         p_vout->render.i_width / i_width;
659             var_Set( p_vout, "mouse-x", val );
660
661             if( [[o_view className] isEqualToString: @"VLCGLView"] )
662             {
663                 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
664                             p_vout->render.i_height / i_height;
665             }
666             else
667             {
668                 val.i_int = ( ((int)ml.y) - i_y ) *
669                             p_vout->render.i_height / i_height;
670             }
671             var_Set( p_vout, "mouse-y", val );
672
673             val.b_bool = true;
674             var_Set( p_vout, "mouse-moved", val );
675         }
676         if( [self isFullscreen] )
677             [[[[VLCMain sharedInstance] getControls] getFSPanel] fadeIn];
678     }
679
680     [super mouseMoved: o_event];
681 }
682
683 - (BOOL)acceptsFirstResponder
684 {
685     return YES;
686 }
687
688 - (BOOL)becomeFirstResponder
689 {
690     return YES;
691 }
692
693 - (BOOL)resignFirstResponder
694 {
695     /* We need to stay the first responder or we'll miss some
696        events */
697     return NO;
698 }
699
700 /* Class methods used by the different vout modules */
701
702 + (vout_thread_t *)getRealVout: (vout_thread_t *)p_vout
703 {
704     /* p_real_vout: the vout we have to use to check for video-on-top
705        and a few other things. If we are the QuickTime output, it's us.
706        It we are the OpenGL provider, it is our parent.
707        Since we can't be the QuickTime output anymore, we need to be
708        the parent.
709        FIXME: check with the caca and x11 vouts! */
710     return (vout_thread_t *) p_vout->p_parent;
711 }
712
713 + (id)voutView: (vout_thread_t *)p_vout subView: (NSView *)view
714          frame: (NSRect *)s_frame
715 {
716     vlc_value_t value_drawable;
717     int i_timeout;
718     id o_return = nil;
719
720     var_Get( p_vout->p_libvlc, "drawable", &value_drawable );
721
722     var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
723     var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
724     var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
725     var_Create( p_vout, "macosx-background", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
726     var_Create( p_vout, "macosx-black", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
727     var_Create( p_vout, "embedded-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
728
729     /* We only wait for NSApp to initialise if we're not embedded (as in the
730      * case of the Mozilla plugin).  We can tell whether we're embedded or not
731      * by examining the "drawable" value: if it's zero, we're running in the
732      * main Mac intf; if it's non-zero, we're embedded. */
733     if( value_drawable.i_int == 0 )
734     {
735         /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
736         for( i_timeout = 20 ; i_timeout-- ; )
737         {
738             if( NSApp == NULL )
739             {
740                 msleep( INTF_IDLE_SLEEP );
741             }
742         }
743
744         if( NSApp == NULL )
745         {
746             /* No MacOS X intf, unable to communicate with MT */
747             msg_Err( p_vout, "no MacOS X interface present" );
748             return nil;
749         }
750         else
751         {
752             if ( VLCIntf && !(p_vout->b_fullscreen) &&
753                         !(var_GetBool( p_vout, "macosx-background" )) &&
754                         var_GetBool( p_vout, "embedded-video") )
755             {
756                 o_return = [[[VLCMain sharedInstance] getEmbeddedList]
757                                                             getEmbeddedVout];
758             }
759         }
760     }
761
762     /* No embedded vout is available */
763     if( o_return == nil )
764     {
765         NSRect null_rect;
766         bzero( &null_rect, sizeof( NSRect ) );
767         o_return = [[VLCDetachedVoutView alloc] initWithFrame: null_rect ];
768     }
769     [o_return setVout: p_vout subView: view frame: s_frame];
770     return o_return;
771 }
772
773 - (void)enterFullscreen
774 {
775     /* Save the settings for next playing item */
776     playlist_t * p_playlist = pl_Hold( p_real_vout );
777     var_SetBool( p_playlist, "fullscreen", true );
778     pl_Release( p_real_vout );
779 }
780
781 - (void)leaveFullscreen
782 {
783     /* Save the settings for next playing item */
784     playlist_t * p_playlist = pl_Hold( p_real_vout );
785     var_SetBool( p_playlist, "fullscreen", false );
786     pl_Release( p_real_vout );
787 }
788
789 @end
790
791 /*****************************************************************************
792  * VLCDetachedVoutView implementation
793  *****************************************************************************/
794 @implementation VLCDetachedVoutView
795
796 - (id)initWithFrame: (NSRect)frameRect
797 {
798     [super initWithFrame: frameRect];
799     i_time_mouse_last_moved = 0;
800     return self;
801 }
802
803 - (BOOL)mouseDownCanMoveWindow
804 {
805     return YES;
806 }
807
808 - (bool)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
809                      frame: (NSRect *) s_arg_frame
810 {
811     BOOL b_return = [super setVout: p_arg_vout subView: view frame:s_arg_frame];
812     i_time_mouse_last_moved = mdate();
813     o_window = [[VLCVoutWindow alloc] initWithVout: p_arg_vout view: self
814                                                     frame: s_arg_frame];
815     
816     [self updateTitle];
817     if([self isFullscreen])
818         [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES];
819     else
820         [view setFrame: [self frame]];
821
822     return b_return;
823 }
824
825 - (void)closeVout
826 {
827     [o_window performSelectorOnMainThread: @selector(close) withObject: NULL waitUntilDone: YES];
828     i_time_mouse_last_moved = 0;
829     [super closeVout];
830 }
831
832 - (void)mouseMoved:(NSEvent *)o_event
833 {
834     i_time_mouse_last_moved = mdate();
835     [super mouseMoved: o_event];
836 }
837
838 - (void)hideMouse:(BOOL)b_hide
839 {
840     BOOL b_inside;
841     NSPoint ml;
842     NSView *o_contents = [o_window contentView];
843
844     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
845     ml = [o_contents convertPoint:ml fromView:nil];
846     b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];
847
848     if( b_hide && b_inside )
849     {
850         [NSCursor setHiddenUntilMouseMoves: YES];
851     }
852     else if( !b_hide )
853     {
854         [NSCursor setHiddenUntilMouseMoves: NO];
855     }
856 }
857
858 - (void)manage
859 {
860     /* Dooh, why do we spend processor time doing this kind of stuff? */
861     [super manage];
862     unsigned int i_mouse_hide_timeout =
863         var_CreateGetInteger(p_vout, "mouse-hide-timeout") * 1000;
864
865     if( i_mouse_hide_timeout < 100000 )
866         i_mouse_hide_timeout = 100000;
867     if( p_vout->b_fullscreen )
868     {
869         if( mdate() - i_time_mouse_last_moved > i_mouse_hide_timeout )
870         {
871             i_time_mouse_last_moved = mdate();
872             [self hideMouse: YES];
873         }
874     }
875     else
876     {
877         [self hideMouse: NO];
878     }
879 }
880
881
882 - (void)enterFullscreen
883 {
884     [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: NO];
885     [super enterFullscreen];
886
887 }
888
889 - (void)leaveFullscreen
890 {
891     [o_window performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: NO];
892     [super leaveFullscreen];
893 }
894
895
896 - (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate
897 {
898     if( p_vout->b_fullscreen )
899         return;
900     [o_window setMovableByWindowBackground: NO];
901     [super scaleWindowWithFactor: factor animate: animate];
902     [o_window setMovableByWindowBackground: YES];
903 }
904 @end
905
906 /*****************************************************************************
907  * VLCEmbeddedVoutView implementation
908  *****************************************************************************/
909
910 @implementation VLCEmbeddedVoutView
911
912 - (void)awakeFromNib
913 {
914     o_embeddedwindow = [self window];
915 }
916
917 - (BOOL)mouseDownCanMoveWindow
918 {
919     return YES;
920 }
921
922 - (id)initWithFrame: (NSRect)frameRect
923 {
924     if(self = [super initWithFrame: frameRect])
925     {
926         b_used = NO;
927         [[[VLCMain sharedInstance] getEmbeddedList] addEmbeddedVout: self];
928         o_embeddedwindow = nil; /* Filled later on in -awakeFromNib */
929     }
930     return self;
931 }
932
933 - (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
934                  frame: (NSRect *)s_arg_frame
935 {
936     BOOL b_return;
937
938     [NSObject cancelPreviousPerformRequestsWithTarget:o_window];
939
940     b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame];
941     if( b_return )
942     {
943         o_window = [self window];
944
945         [o_window setAcceptsMouseMovedEvents: TRUE];
946
947         if( var_CreateGetBool( p_real_vout, "video-on-top" ) )
948         {
949             [o_window setLevel: NSStatusWindowLevel];
950         }
951
952         [view setFrameSize: [self frame].size];
953     }
954
955     /* o_window needs to point to our o_embeddedwindow, super might have set it
956      * to the fullscreen window that o_embeddedwindow setups during fullscreen */
957     o_window = o_embeddedwindow;
958  
959     if( b_return )
960     {
961         [o_window lockFullscreenAnimation];
962
963         [o_window setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
964
965         [self updateTitle];
966
967         [NSObject cancelPreviousPerformRequestsWithTarget:o_window];
968
969         /* Make the window the front and key window before animating */
970         if ([o_window isVisible] && (![o_window isFullscreen]))
971             [o_window makeKeyAndOrderFront: self];
972
973         [self scaleWindowWithFactor: 1.0 animate: [o_window isVisible] && (![o_window isFullscreen])];
974
975         [o_embeddedwindow setVideoRatio:[self voutSizeForFactor:1.0]];
976
977         /* Make sure our window is visible, if we are not in fullscreen */
978         if (![o_window isFullscreen])
979             [o_window makeKeyAndOrderFront: self];
980         [o_window unlockFullscreenAnimation];
981
982     }
983
984     return b_return;
985 }
986
987 - (void)setUsed: (BOOL)b_new_used
988 {
989     b_used = b_new_used;
990 }
991
992 - (BOOL)isUsed
993 {
994     return b_used;
995 }
996
997 - (void)closeVout
998 {
999     [super closeVout];
1000
1001     /* Don't close the window yet, wait a bit to see if a new input is poping up */
1002     /* FIXME: Probably fade the window In and Out */
1003     /* FIXME: fix core */
1004     [o_embeddedwindow performSelector:@selector(orderOut:) withObject:nil afterDelay:3.];
1005
1006     [[[VLCMain sharedInstance] getEmbeddedList] releaseEmbeddedVout: self];
1007 }
1008
1009 - (void)enterFullscreen
1010 {
1011     /* Save settings */
1012     [super enterFullscreen];
1013
1014     /* We are in a VLCEmbeddedWindow */
1015     [o_embeddedwindow performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES];
1016 }
1017
1018 - (void)leaveFullscreen
1019 {
1020     /* Save settings */
1021     [super leaveFullscreen];
1022
1023     /* We are in a VLCEmbeddedWindow */
1024     [o_embeddedwindow performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: YES];
1025 }
1026 @end
1027
1028 /*****************************************************************************
1029  * VLCVoutWindow implementation
1030  *****************************************************************************/
1031 @implementation VLCVoutWindow
1032
1033 - (id) initWithVout: (vout_thread_t *) vout view: (VLCVoutView *) view
1034                      frame: (NSRect *) frame
1035 {
1036     p_vout  = vout;
1037     o_view  = view;
1038     s_frame = frame;
1039     b_init_ok = NO;
1040     [self performSelectorOnMainThread: @selector(initMainThread:)
1041         withObject: NULL waitUntilDone: YES];
1042
1043     return b_init_ok ? self : nil;
1044 }
1045
1046 - (id)initMainThread: (id) sender
1047 {
1048     NSRect rect;
1049     rect.size.height = p_vout->i_window_height;
1050     rect.size.width  = p_vout->i_window_width;
1051     rect.origin.x = rect.origin.y = 70.;
1052
1053     if( self = [super initWithContentRect:rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO])
1054     {
1055         [self setBackgroundColor:[NSColor blackColor]];
1056         [self setHasShadow:YES];
1057         [self setMovableByWindowBackground: YES];
1058         [self center];
1059         [self makeKeyAndOrderFront: self];
1060         [self setReleasedWhenClosed: YES];
1061         [self setFrameUsingName:@"VLCVoutWindowDetached"];
1062         [self setFrameAutosaveName:@"VLCVoutWindowDetached"];
1063
1064         /* We'll catch mouse events */
1065         [self makeFirstResponder: o_view];
1066         [self setCanBecomeKeyWindow: YES];
1067         [self setAcceptsMouseMovedEvents: YES];
1068         [self setIgnoresMouseEvents: NO];
1069
1070         if( var_CreateGetBool( p_vout, "macosx-background" ) )
1071         {
1072             int i_device = var_GetInteger( p_vout->p_libvlc, "video-device" );
1073
1074             /* Find out on which screen to open the window */
1075             NSScreen * screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
1076             if( !screen ) screen = [NSScreen mainScreen];
1077
1078             NSRect screen_rect = [screen frame];
1079             screen_rect.origin.x = screen_rect.origin.y = 0;
1080
1081             /* Creates a window with size: screen_rect on o_screen */
1082             [self setFrame: screen_rect display: NO];
1083
1084             [self setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey)];
1085             [self setMovableByWindowBackground: NO];
1086         }
1087         if( var_CreateGetBool( p_vout, "video-on-top" ) )
1088         {
1089             [self setLevel: NSStatusWindowLevel];
1090         }
1091
1092         [self setAlphaValue: var_CreateGetFloat( p_vout, "macosx-opaqueness" )];
1093
1094         /* Add the view. It's automatically resized to fit the window */
1095         [self setContentView: o_view];
1096
1097         b_init_ok = YES;
1098     }
1099     return self;
1100 }
1101
1102 - (void)enterFullscreen
1103 {
1104     if( fullscreen ) return;
1105
1106     NSScreen *screen;
1107     int i_device;
1108     BOOL b_black = NO;
1109
1110     i_device = var_GetInteger( p_vout->p_libvlc, "video-device" );
1111     b_black = var_CreateGetBool( p_vout, "macosx-black" );
1112
1113     /* Find out on which screen to open the window */
1114     screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
1115     if( !screen ) screen = [self screen];
1116
1117     if( b_black )
1118         [screen blackoutOtherScreens];
1119
1120     [self setMovableByWindowBackground: NO];
1121
1122     if( [screen isMainScreen] )
1123         SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1124
1125     initialFrame = [self frame];
1126     [self setFrame:[screen frame] display:YES animate:YES];
1127     [self setLevel:NSNormalWindowLevel];
1128
1129     /* tell the fspanel to move itself to front next time it's triggered */
1130     [[[[VLCMain sharedInstance] getControls] getFSPanel] setVoutWasUpdated: i_device];
1131     [[[[VLCMain sharedInstance] getControls] getFSPanel] setActive: nil];
1132
1133     fullscreen = YES;
1134 }
1135
1136 - (void)leaveFullscreen
1137 {
1138     if( !fullscreen ) return;
1139     fullscreen = NO;
1140
1141     [NSScreen unblackoutScreens];
1142
1143     [[[[VLCMain sharedInstance] getControls] getFSPanel] setNonActive: nil];
1144     SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1145     [self setFrame:initialFrame display:YES animate:YES];
1146     [self setMovableByWindowBackground: YES];
1147     if( var_GetBool( p_vout, "video-on-top" ) )
1148         [self setLevel: NSStatusWindowLevel];
1149 }
1150
1151 - (id)voutView
1152 {
1153     return o_view;
1154 }
1155
1156 @end
1157