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