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