]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
macosx: Ignore attachment:// in Art uri for now.
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2009 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 /* prevent system sleep */
38 #import <CoreServices/CoreServices.h>
39 /* FIXME: HACK!! */
40 #ifdef __x86_64__
41 #import <CoreServices/../Frameworks/OSServices.framework/Headers/Power.h>
42 #endif
43
44 /* SystemUIMode */
45 #import <Carbon/Carbon.h>
46
47 #include <vlc_keys.h>
48
49 #include "intf.h"
50 #include "fspanel.h"
51 #include "vout.h"
52 #import "controls.h"
53 #import "embeddedwindow.h"
54
55 /*****************************************************************************
56  * DeviceCallback: Callback triggered when the video-device variable is changed
57  *****************************************************************************/
58 int DeviceCallback( vlc_object_t *p_this, const char *psz_variable,
59                      vlc_value_t old_val, vlc_value_t new_val, void *param )
60 {
61     vlc_value_t val;
62     vout_thread_t *p_vout = (vout_thread_t *)p_this;
63
64     msg_Dbg( p_vout, "set %d", new_val.i_int );
65     var_Create( p_vout->p_libvlc, "video-device", VLC_VAR_INTEGER );
66     var_Set( p_vout->p_libvlc, "video-device", new_val );
67
68     val.b_bool = true;
69     var_Set( p_vout, "intf-change", val );
70     return VLC_SUCCESS;
71 }
72
73
74 /*****************************************************************************
75  * VLCEmbeddedList implementation
76  *****************************************************************************/
77 @implementation VLCEmbeddedList
78
79 - (id)init
80 {
81     [super init];
82     o_embedded_array = [NSMutableArray array];
83     return self;
84 }
85
86 - (id)embeddedVout
87 {
88     unsigned int i;
89
90     for( i = 0; i < [o_embedded_array count]; i++ )
91     {
92         id o_vout_view = [o_embedded_array objectAtIndex: i];
93         if( ![o_vout_view isUsed] )
94         {
95             [o_vout_view setUsed: YES];
96             return o_vout_view;
97         }
98     }
99     return nil;
100 }
101
102 - (void)releaseEmbeddedVout: (id)o_vout_view
103 {
104     if( [o_embedded_array containsObject: o_vout_view] )
105     {
106         [o_vout_view setUsed: NO];
107     }
108     else
109     {
110         msg_Warn( VLCIntf, "cannot find Video Output");
111     }
112 }
113
114 - (void)addEmbeddedVout: (id)o_vout_view
115 {
116     if( ![o_embedded_array containsObject: o_vout_view] )
117     {
118         [o_embedded_array addObject: o_vout_view];
119     }
120 }
121
122 - (BOOL)windowContainsEmbedded: (id)o_window
123 {
124 /*    if( ![[o_window className] isEqualToString: @"VLCVoutWindow"] )
125     {
126         NSLog( @"We were not given a VLCVoutWindow" );
127     }*/
128     return ([self viewForWindow: o_window] == nil ? NO : YES );
129 }
130
131 - (id)viewForWindow: (id)o_window
132 {
133     if( o_embedded_array != nil )
134     {
135         id o_enumerator = [o_embedded_array objectEnumerator];
136         id o_current_embedded;
137         if( o_window != nil )
138         {
139             while( (o_current_embedded = [o_enumerator nextObject]) )
140             {
141                 if( [o_current_embedded voutWindow] == o_window )
142                 {
143                     return o_current_embedded;
144                 }
145             }
146         }
147     }
148     return nil;
149 }
150
151 @end
152
153 /*****************************************************************************
154  * VLCVoutView implementation
155  *****************************************************************************/
156 @implementation VLCVoutView
157
158 - (id)initWithFrame: (NSRect)frameRect
159 {
160     self = [super initWithFrame: frameRect];
161     p_vout = NULL;
162     o_view = nil;
163     s_frame = &frameRect;
164
165     p_real_vout = NULL;
166     o_window = nil;
167     return self;
168 }
169
170 - (BOOL)setVout: (vout_thread_t *) vout
171         subView: (NSView *) view
172           frame: (NSRect *) frame
173 {
174     int i_device;
175     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
176     NSArray *o_screens = [NSScreen screens];
177
178     p_vout  = vout;
179     o_view  = view;
180     s_frame = frame;
181
182     if( [o_screens count] <= 0 )
183     {
184         msg_Err( p_vout, "no OSX screens available" );
185         return NO;
186     }
187
188     p_real_vout = [VLCVoutView realVout: p_vout];
189
190     /* Get the pref value when this is the first time, otherwise retrieve the device from the top level video-device var */
191     if( var_Type( p_real_vout->p_libvlc, "video-device" ) == 0 )
192     {
193         i_device = var_GetInteger( p_vout, "macosx-vdev" );
194     }
195     else
196     {
197         i_device = var_GetInteger( p_real_vout->p_libvlc, "video-device" );
198     }
199
200     /* Setup the menuitem for the multiple displays. */
201     if( var_Type( p_real_vout, "video-device" ) == 0 )
202     {
203         int i = 1;
204         vlc_value_t val2, text;
205         NSScreen * o_screen;
206
207         var_Create( p_real_vout, "video-device", VLC_VAR_INTEGER |
208                                             VLC_VAR_HASCHOICE );
209         text.psz_string = _("Fullscreen Video Device");
210         var_Change( p_real_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
211
212         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
213
214         val2.i_int = 0;
215         text.psz_string = _("Default");
216         var_Change( p_real_vout, "video-device",
217                         VLC_VAR_ADDCHOICE, &val2, &text );
218         var_Set( p_real_vout, "video-device", val2 );
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             val2.i_int = (int)[o_screen displayID];
231             var_Change( p_real_vout, "video-device",
232                         VLC_VAR_ADDCHOICE, &val2, &text );
233             if( (int)[o_screen displayID] == i_device )
234             {
235                 var_Set( p_real_vout, "video-device", val2 );
236             }
237             i++;
238         }
239
240         var_AddCallback( p_real_vout, "video-device", DeviceCallback,
241                          NULL );
242
243         val2.b_bool = true;
244         var_Set( p_real_vout, "intf-change", val2 );
245     }
246
247     /* Add the view. It's automatically resized to fit the window */
248     [self addSubview: o_view];
249     [self setAutoresizesSubviews: YES];
250     [o_pool release];
251
252     return YES;
253 }
254
255 - (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize
256 {
257     [super resizeSubviewsWithOldSize: oldBoundsSize];
258     [o_view setFrameSize: [self frame].size];
259 }
260
261 - (void)drawRect:(NSRect)rect
262 {
263     /* When there is no subview we draw a black background */
264     [self lockFocus];
265     [[NSColor blackColor] set];
266     NSRectFill(rect);
267     [self unlockFocus];
268 }
269
270 - (void)closeVout
271 {
272     [[[[VLCMain sharedInstance] controls] fspanel] fadeOut];
273
274     /* Make sure we don't see a white flash */
275     [[self voutWindow] disableScreenUpdatesUntilFlush];
276     [o_view removeFromSuperview];
277     o_view = nil;
278     p_vout = NULL;
279     s_frame = nil;
280     o_window = nil;
281     p_real_vout = NULL;
282 }
283
284 - (void)updateTitle
285 {
286     NSString * o_title = nil; 
287     NSMutableString * o_mrl = nil;
288     input_thread_t * p_input;
289     char * psz_title;
290
291     if( !p_vout ) return;
292
293     p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
294
295     if( !p_input ) return;
296
297     input_item_t * p_item = input_GetItem( p_input );
298
299     psz_title = input_item_GetNowPlaying ( p_item );
300     if( !psz_title )
301         psz_title = input_item_GetName( p_item );
302
303     if( psz_title )
304         o_title = [NSString stringWithUTF8String: psz_title];
305
306     char *psz_uri = input_item_GetURI( p_item );
307     if( psz_uri )
308         o_mrl = [NSMutableString stringWithUTF8String: psz_uri];
309
310     free( psz_title );
311     free( psz_uri );
312
313     if( !o_title )
314         o_title = o_mrl;
315
316     if( o_mrl != nil )
317     {
318         /* FIXME once psz_access is exported, we could check if we are
319          * reading from a file in a smarter way. */
320
321         NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
322         if( prefix_range.location != NSNotFound )
323             [o_mrl deleteCharactersInRange: prefix_range];
324
325         if( [o_mrl characterAtIndex:0] == '/' )
326         {
327             /* it's a local file */
328             [o_window setRepresentedFilename: o_mrl];
329         }
330         else
331         {
332             /* it's from the network or somewhere else,
333              * we clear the previous path */
334             [o_window setRepresentedFilename: @""];
335         }
336         [o_window setTitle: o_title];
337     }
338     else
339     {
340         [o_window setTitle: [NSString stringWithUTF8String: VOUT_TITLE]];
341     }
342     vlc_object_release( p_input );
343 }
344
345 - (void)setOnTop:(BOOL)b_on_top
346 {
347     if( b_on_top )
348     {
349         [o_window setLevel: NSStatusWindowLevel];
350     }
351     else
352     {
353         [o_window setLevel: NSNormalWindowLevel];
354     }
355 }
356
357 - (NSSize)voutSizeForFactor: (float)factor
358 {
359     int i_corrected_height, i_corrected_width;
360     NSSize newsize;
361
362     if( p_vout->render.i_height * p_vout->render.i_aspect >
363                     p_vout->render.i_width * VOUT_ASPECT_FACTOR )
364     {
365         i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect /
366                                         VOUT_ASPECT_FACTOR;
367         newsize.width = (int) ( i_corrected_width * factor );
368         newsize.height = (int) ( p_vout->render.i_height * factor );
369     }
370     else
371     {
372         i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR /
373                                         p_vout->render.i_aspect;
374         newsize.width = (int) ( p_vout->render.i_width * factor );
375         newsize.height = (int) ( i_corrected_height * factor );
376     }
377
378     return newsize;
379 }
380
381 - (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate
382 {
383     if ( !p_vout->b_fullscreen )
384     {
385         NSSize newsize;
386         NSPoint topleftbase;
387         NSPoint topleftscreen;
388         NSView *mainView;
389         NSRect new_frame;
390         topleftbase.x = 0;
391         topleftbase.y = [o_window frame].size.height;
392         topleftscreen = [o_window convertBaseToScreen: topleftbase];
393
394         newsize = [self voutSizeForFactor:factor];
395
396         /* In fullscreen mode we need to use a view that is different from
397          * ourselves, with the VLCEmbeddedWindow */
398         if([o_window isKindOfClass:[VLCEmbeddedWindow class]])
399             mainView = [o_window mainView];
400         else
401             mainView = self;
402
403         /* Calculate the window's new size */
404         new_frame.size.width = [o_window frame].size.width -
405                                     [mainView frame].size.width + newsize.width;
406         new_frame.size.height = [o_window frame].size.height -
407                                     [mainView frame].size.height + newsize.height;
408
409         new_frame.origin.x = topleftscreen.x;
410         new_frame.origin.y = topleftscreen.y - new_frame.size.height;
411
412         [o_window setFrame:new_frame display:animate animate:animate];
413         p_vout->i_changes |= VOUT_SIZE_CHANGE;
414     }
415 }
416
417 - (void)toggleFloatOnTop
418 {
419     vlc_value_t val;
420
421     if( !p_real_vout ) return;
422     if( var_Get( p_real_vout, "video-on-top", &val )>=0 && val.b_bool)
423     {
424         val.b_bool = false;
425     }
426     else
427     {
428         val.b_bool = true;
429     }
430     var_Set( p_real_vout, "video-on-top", val );
431 }
432
433 - (void)toggleFullscreen
434 {
435     vlc_value_t val;
436     if( !p_real_vout ) return;
437     var_Get( p_real_vout, "fullscreen", &val );
438     val.b_bool = !val.b_bool;
439     var_Set( p_real_vout, "fullscreen", val );
440 }
441
442 - (BOOL)isFullscreen
443 {
444     vlc_value_t val;
445     if( !p_real_vout ) return NO;
446     var_Get( p_real_vout, "fullscreen", &val );
447     return( val.b_bool );
448 }
449
450 - (void)snapshot
451 {
452     var_TriggerCallback( p_real_vout, "video-snapshot" );
453 }
454
455 - (void)manage
456 {
457     /* Disable Screensaver, when we're playing something, but allow it on pause */
458     if( !VLCIntf || !VLCIntf->p_sys )
459         return;
460
461     if( VLCIntf->p_sys->i_play_status == PLAYING_S )
462         UpdateSystemActivity( UsrActivity );
463 }
464
465 - (id)voutWindow
466 {
467     return o_window;
468 }
469
470 - (void)scrollWheel:(NSEvent *)theEvent
471 {
472     VLCControls * o_controls = (VLCControls *)[[NSApp delegate] controls];
473     [o_controls scrollWheel: theEvent];
474 }
475
476 - (void)keyDown:(NSEvent *)o_event
477 {
478     unichar key = 0;
479     vlc_value_t val;
480     unsigned int i_pressed_modifiers = 0;
481     val.i_int = 0;
482
483     i_pressed_modifiers = [o_event modifierFlags];
484
485     if( i_pressed_modifiers & NSShiftKeyMask )
486         val.i_int |= KEY_MODIFIER_SHIFT;
487     if( i_pressed_modifiers & NSControlKeyMask )
488         val.i_int |= KEY_MODIFIER_CTRL;
489     if( i_pressed_modifiers & NSAlternateKeyMask )
490         val.i_int |= KEY_MODIFIER_ALT;
491     if( i_pressed_modifiers & NSCommandKeyMask )
492         val.i_int |= KEY_MODIFIER_COMMAND;
493
494     key = [[[o_event charactersIgnoringModifiers] lowercaseString] characterAtIndex: 0];
495
496     if( key )
497     {
498         /* Escape should always get you out of fullscreen */
499         if( key == (unichar) 0x1b )
500         {
501              if( p_real_vout && [self isFullscreen] )
502              {
503                  [self toggleFullscreen];
504              }
505         }
506         else if ( p_vout )
507         {
508             if( key == ' ')
509                 val.i_int = config_GetInt( p_vout, "key-play-pause" );
510             else
511                 val.i_int |= (int)CocoaKeyToVLC( key );
512             var_Set( p_vout->p_libvlc, "key-pressed", val );
513         }
514         else NSLog( @"Could not send keyevent to VLC core" );
515     }
516     else
517         [super keyDown: o_event];
518 }
519
520 - (void)mouseDown:(NSEvent *)o_event
521 {
522     vlc_value_t val;
523     if( p_vout )
524     {
525         if( ( [o_event type] == NSLeftMouseDown ) &&
526           ( ! ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
527         {
528             if( [o_event clickCount] <= 1 )
529             {
530                 /* single clicking */
531                 var_Get( p_vout, "mouse-button-down", &val );
532                 val.i_int |= 1;
533                 var_Set( p_vout, "mouse-button-down", val );
534             }
535             else
536             {
537                 /* multiple clicking */
538                 [self toggleFullscreen];
539             }
540         }
541         else if( ( [o_event type] == NSRightMouseDown ) ||
542                ( ( [o_event type] == NSLeftMouseDown ) &&
543                  ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
544         {
545             msg_Dbg( p_vout, "received NSRightMouseDown (generic method) or Ctrl clic" );
546             [NSMenu popUpContextMenu: [[VLCMain sharedInstance] voutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] controls] voutView]];
547         }
548     }
549
550     [super mouseDown: o_event];
551 }
552
553 - (void)otherMouseDown:(NSEvent *)o_event
554 {
555     vlc_value_t val;
556
557     if( p_vout && [o_event type] == NSOtherMouseDown )
558     {
559         var_Get( p_vout, "mouse-button-down", &val );
560         val.i_int |= 2;
561         var_Set( p_vout, "mouse-button-down", val );
562     }
563
564     [super mouseDown: o_event];
565 }
566
567 - (void)rightMouseDown:(NSEvent *)o_event
568 {
569     if( p_vout && [o_event type] == NSRightMouseDown )
570     {
571         msg_Dbg( p_vout, "received NSRightMouseDown (specific method)" );
572         [NSMenu popUpContextMenu: [[VLCMain sharedInstance] voutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] controls] voutView]];
573     }
574
575     [super mouseDown: o_event];
576 }
577
578 - (void)mouseUp:(NSEvent *)o_event
579 {
580     vlc_value_t val;
581
582     if( p_vout && [o_event type] == NSLeftMouseUp )
583     {
584         var_SetBool( p_vout, "mouse-clicked", true );
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] voutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] controls] 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] controls] fspanel] 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 *)realVout: (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     int i_drawable_gl;
717     int i_timeout;
718     id o_return = nil;
719
720     i_drawable_gl = var_GetInteger( p_vout->p_libvlc, "drawable-gl" );
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-gl" value: if it's zero, we're running in the
732      * main Mac intf; if it's non-zero, we're embedded. */
733     if( i_drawable_gl == 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] embeddedList] embeddedVout];
757             }
758         }
759     }
760
761     /* No embedded vout is available */
762     if( o_return == nil )
763     {
764         NSRect null_rect;
765         bzero( &null_rect, sizeof( NSRect ) );
766         o_return = [[VLCDetachedVoutView alloc] initWithFrame: null_rect ];
767     }
768     [o_return setVout: p_vout subView: view frame: s_frame];
769     return o_return;
770 }
771
772 - (void)enterFullscreen
773 {
774     /* Save the settings for next playing item */
775     playlist_t * p_playlist = pl_Hold( p_real_vout );
776     var_SetBool( p_playlist, "fullscreen", true );
777     pl_Release( p_real_vout );
778 }
779
780 - (void)leaveFullscreen
781 {
782     /* Save the settings for next playing item */
783     playlist_t * p_playlist = pl_Hold( p_real_vout );
784     var_SetBool( p_playlist, "fullscreen", false );
785     pl_Release( p_real_vout );
786 }
787
788 @end
789
790 /*****************************************************************************
791  * VLCDetachedVoutView implementation
792  *****************************************************************************/
793 @implementation VLCDetachedVoutView
794
795 - (id)initWithFrame: (NSRect)frameRect
796 {
797     [super initWithFrame: frameRect];
798     i_time_mouse_last_moved = 0;
799     return self;
800 }
801
802 - (BOOL)mouseDownCanMoveWindow
803 {
804     return YES;
805 }
806
807 - (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
808                      frame: (NSRect *) s_arg_frame
809 {
810     BOOL b_return = [super setVout: p_arg_vout subView: view frame:s_arg_frame];
811     i_time_mouse_last_moved = mdate();
812     o_window = [[VLCVoutWindow alloc] initWithVout: p_arg_vout view: self
813                                                     frame: s_arg_frame];
814     
815     [self updateTitle];
816     if([self isFullscreen])
817         [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES];
818     else
819         [view setFrame: [self frame]];
820
821     return b_return;
822 }
823
824 - (void)closeVout
825 {
826     [o_window performSelectorOnMainThread: @selector(close) withObject: NULL waitUntilDone: YES];
827     i_time_mouse_last_moved = 0;
828     [super closeVout];
829 }
830
831 - (void)mouseMoved:(NSEvent *)o_event
832 {
833     i_time_mouse_last_moved = mdate();
834     [super mouseMoved: o_event];
835 }
836
837 - (void)hideMouse:(BOOL)b_hide
838 {
839     BOOL b_inside;
840     NSPoint ml;
841     NSView *o_contents = [o_window contentView];
842
843     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
844     ml = [o_contents convertPoint:ml fromView:nil];
845     b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];
846
847     if( b_hide && b_inside )
848     {
849         [NSCursor setHiddenUntilMouseMoves: YES];
850     }
851     else if( !b_hide )
852     {
853         [NSCursor setHiddenUntilMouseMoves: NO];
854     }
855 }
856
857 - (void)manage
858 {
859     /* Dooh, why do we spend processor time doing this kind of stuff? */
860     [super manage];
861     unsigned int i_mouse_hide_timeout =
862         var_CreateGetInteger(p_vout, "mouse-hide-timeout") * 1000;
863
864     if( i_mouse_hide_timeout < 100000 )
865         i_mouse_hide_timeout = 100000;
866     if( p_vout->b_fullscreen )
867     {
868         if( mdate() - i_time_mouse_last_moved > i_mouse_hide_timeout )
869         {
870             i_time_mouse_last_moved = mdate();
871             [self hideMouse: YES];
872         }
873     }
874     else
875     {
876         [self hideMouse: NO];
877     }
878 }
879
880
881 - (void)enterFullscreen
882 {
883     [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: NO];
884     [super enterFullscreen];
885
886 }
887
888 - (void)leaveFullscreen
889 {
890     [o_window performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: NO];
891     [super leaveFullscreen];
892 }
893
894
895 - (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate
896 {
897     if( p_vout->b_fullscreen )
898         return;
899     [o_window setMovableByWindowBackground: NO];
900     [super scaleWindowWithFactor: factor animate: animate];
901     [o_window setMovableByWindowBackground: YES];
902 }
903 @end
904
905 /*****************************************************************************
906  * VLCEmbeddedVoutView implementation
907  *****************************************************************************/
908
909 @implementation VLCEmbeddedVoutView
910
911 - (void)awakeFromNib
912 {
913     o_embeddedwindow = [self window];
914 }
915
916 - (BOOL)mouseDownCanMoveWindow
917 {
918     return YES;
919 }
920
921 - (id)initWithFrame: (NSRect)frameRect
922 {
923     if(self = [super initWithFrame: frameRect])
924     {
925         b_used = NO;
926         [[[VLCMain sharedInstance] embeddedList] addEmbeddedVout: self];
927         o_embeddedwindow = nil; /* Filled later on in -awakeFromNib */
928     }
929     return self;
930 }
931
932 - (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
933                  frame: (NSRect *)s_arg_frame
934 {
935     BOOL b_return;
936
937     [NSObject cancelPreviousPerformRequestsWithTarget:o_window];
938
939     b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame];
940     if( b_return )
941     {
942         o_window = [self window];
943
944         [o_window setAcceptsMouseMovedEvents: TRUE];
945
946         if( var_CreateGetBool( p_real_vout, "video-on-top" ) )
947         {
948             [o_window setLevel: NSStatusWindowLevel];
949         }
950
951         [view setFrameSize: [self frame].size];
952     }
953
954     /* o_window needs to point to our o_embeddedwindow, super might have set it
955      * to the fullscreen window that o_embeddedwindow setups during fullscreen */
956     o_window = o_embeddedwindow;
957  
958     if( b_return )
959     {
960         [o_window lockFullscreenAnimation];
961
962         [o_window setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
963
964         [self updateTitle];
965
966         [NSObject cancelPreviousPerformRequestsWithTarget:o_window];
967
968         /* Make the window the front and key window before animating */
969         if ([o_window isVisible] && (![o_window isFullscreen]))
970             [o_window makeKeyAndOrderFront: self];
971
972         [self scaleWindowWithFactor: 1.0 animate: [o_window isVisible] && (![o_window isFullscreen])];
973
974         [o_embeddedwindow setVideoRatio:[self voutSizeForFactor:1.0]];
975
976         /* Make sure our window is visible, if we are not in fullscreen */
977         if (![o_window isFullscreen])
978             [o_window makeKeyAndOrderFront: self];
979         [o_window unlockFullscreenAnimation];
980
981     }
982
983     return b_return;
984 }
985
986 - (void)setUsed: (BOOL)b_new_used
987 {
988     b_used = b_new_used;
989 }
990
991 - (BOOL)isUsed
992 {
993     return b_used;
994 }
995
996 - (void)closeVout
997 {
998     [super closeVout];
999
1000     /* Don't close the window yet, wait a bit to see if a new input is poping up */
1001     /* FIXME: Probably fade the window In and Out */
1002     /* FIXME: fix core */
1003     [o_embeddedwindow performSelector:@selector(orderOut:) withObject:nil afterDelay:3.];
1004
1005     [[[VLCMain sharedInstance] embeddedList] releaseEmbeddedVout: self];
1006 }
1007
1008 - (void)enterFullscreen
1009 {
1010     /* Save settings */
1011     [super enterFullscreen];
1012
1013     /* We are in a VLCEmbeddedWindow */
1014     [o_embeddedwindow performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES];
1015 }
1016
1017 - (void)leaveFullscreen
1018 {
1019     /* Save settings */
1020     [super leaveFullscreen];
1021
1022     /* We are in a VLCEmbeddedWindow */
1023     [o_embeddedwindow performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: YES];
1024 }
1025 @end
1026
1027 /*****************************************************************************
1028  * VLCVoutWindow implementation
1029  *****************************************************************************/
1030 @implementation VLCVoutWindow
1031
1032 - (id) initWithVout: (vout_thread_t *) vout view: (VLCVoutView *) view
1033                      frame: (NSRect *) frame
1034 {
1035     p_vout  = vout;
1036     o_view  = view;
1037     s_frame = frame;
1038     b_init_ok = NO;
1039     [self performSelectorOnMainThread: @selector(initMainThread:)
1040         withObject: NULL waitUntilDone: YES];
1041
1042     return b_init_ok ? self : nil;
1043 }
1044
1045 - (id)initMainThread: (id) sender
1046 {
1047     NSRect rect;
1048     rect.size.height = p_vout->i_window_height;
1049     rect.size.width  = p_vout->i_window_width;
1050     rect.origin.x = rect.origin.y = 70.;
1051
1052     if( self = [super initWithContentRect:rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO])
1053     {
1054         [self setBackgroundColor:[NSColor blackColor]];
1055         [self setHasShadow:YES];
1056         [self setMovableByWindowBackground: YES];
1057         [self center];
1058         [self makeKeyAndOrderFront: self];
1059         [self setReleasedWhenClosed: YES];
1060         [self setFrameUsingName:@"VLCVoutWindowDetached"];
1061         [self setFrameAutosaveName:@"VLCVoutWindowDetached"];
1062
1063         /* We'll catch mouse events */
1064         [self makeFirstResponder: o_view];
1065         [self setCanBecomeKeyWindow: YES];
1066         [self setAcceptsMouseMovedEvents: YES];
1067         [self setIgnoresMouseEvents: NO];
1068
1069         if( var_CreateGetBool( p_vout, "macosx-background" ) )
1070         {
1071             int i_device = var_GetInteger( p_vout->p_libvlc, "video-device" );
1072
1073             /* Find out on which screen to open the window */
1074             NSScreen * screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
1075             if( !screen ) screen = [NSScreen mainScreen];
1076
1077             NSRect screen_rect = [screen frame];
1078             screen_rect.origin.x = screen_rect.origin.y = 0;
1079
1080             /* Creates a window with size: screen_rect on o_screen */
1081             [self setFrame: screen_rect display: NO];
1082
1083             [self setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey)];
1084             [self setMovableByWindowBackground: NO];
1085         }
1086         if( var_CreateGetBool( p_vout, "video-on-top" ) )
1087         {
1088             [self setLevel: NSStatusWindowLevel];
1089         }
1090
1091         [self setAlphaValue: var_CreateGetFloat( p_vout, "macosx-opaqueness" )];
1092
1093         /* Add the view. It's automatically resized to fit the window */
1094         [self setContentView: o_view];
1095
1096         b_init_ok = YES;
1097     }
1098     return self;
1099 }
1100
1101 - (void)enterFullscreen
1102 {
1103     if( fullscreen ) return;
1104
1105     NSScreen *screen;
1106     int i_device;
1107     BOOL b_black = NO;
1108
1109     i_device = var_GetInteger( p_vout->p_libvlc, "video-device" );
1110     b_black = var_CreateGetBool( p_vout, "macosx-black" );
1111
1112     /* Find out on which screen to open the window */
1113     screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
1114     if( !screen ) screen = [self screen];
1115
1116     if( b_black )
1117         [screen blackoutOtherScreens];
1118
1119     [self setMovableByWindowBackground: NO];
1120
1121     if( [screen isMainScreen] )
1122         SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1123
1124     initialFrame = [self frame];
1125     [self setFrame:[screen frame] display:YES animate:YES];
1126     [self setLevel:NSNormalWindowLevel];
1127
1128     /* tell the fspanel to move itself to front next time it's triggered */
1129     [[[[VLCMain sharedInstance] controls] fspanel] setVoutWasUpdated: i_device];
1130     [[[[VLCMain sharedInstance] controls] fspanel] setActive: nil];
1131
1132     fullscreen = YES;
1133 }
1134
1135 - (void)leaveFullscreen
1136 {
1137     if( !fullscreen ) return;
1138     fullscreen = NO;
1139
1140     [NSScreen unblackoutScreens];
1141
1142     [[[[VLCMain sharedInstance] controls] fspanel] setNonActive: nil];
1143     SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1144
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