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