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