]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
macosx: experimental 64bit support
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Colin Delacroix <colin@zoy.org>
8  *          Florian G. Pflug <fgp@phlo.org>
9  *          Jon Lech Johansen <jon-vl@nanocrew.net>
10  *          Derk-Jan Hartman <hartman at videolan dot org>
11  *          Eric Petit <titer@m0k.org>
12  *          Benjamin Pracht <bigben at videolan dot org>
13  *          Felix Paul Kühne <fkuehne at videolan dot org>
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
28  *****************************************************************************/
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <stdlib.h>                                                /* free() */
35 #include <string.h>
36
37 /* prevent system sleep */
38 #import <CoreServices/CoreServices.h>
39 #import <CoreServices/../Frameworks/OSServices.framework/Headers/Power.h>
40
41 /* SystemUIMode */
42 #import <Carbon/Carbon.h>
43
44 #include <vlc_keys.h>
45
46 #include "intf.h"
47 #include "fspanel.h"
48 #include "vout.h"
49 #import "controls.h"
50 #import "embeddedwindow.h"
51
52 /*****************************************************************************
53  * DeviceCallback: Callback triggered when the video-device variable is changed
54  *****************************************************************************/
55 int DeviceCallback( vlc_object_t *p_this, const char *psz_variable,
56                      vlc_value_t old_val, vlc_value_t new_val, void *param )
57 {
58     vlc_value_t val;
59     vout_thread_t *p_vout = (vout_thread_t *)p_this;
60
61     msg_Dbg( p_vout, "set %d", new_val.i_int );
62     var_Create( p_vout->p_libvlc, "video-device", VLC_VAR_INTEGER );
63     var_Set( p_vout->p_libvlc, "video-device", new_val );
64
65     val.b_bool = true;
66     var_Set( p_vout, "intf-change", val );
67     return VLC_SUCCESS;
68 }
69
70
71 /*****************************************************************************
72  * VLCEmbeddedList implementation
73  *****************************************************************************/
74 @implementation VLCEmbeddedList
75
76 - (id)init
77 {
78     [super init];
79     o_embedded_array = [NSMutableArray array];
80     return self;
81 }
82
83 - (id)getEmbeddedVout
84 {
85     unsigned int i;
86
87     for( i = 0; i < [o_embedded_array count]; i++ )
88     {
89         id o_vout_view = [o_embedded_array objectAtIndex: i];
90         if( ![o_vout_view isUsed] )
91         {
92             [o_vout_view setUsed: YES];
93             return o_vout_view;
94         }
95     }
96     return nil;
97 }
98
99 - (void)releaseEmbeddedVout: (id)o_vout_view
100 {
101     if( [o_embedded_array containsObject: o_vout_view] )
102     {
103         [o_vout_view setUsed: NO];
104     }
105     else
106     {
107         msg_Warn( VLCIntf, "cannot find Video Output");
108     }
109 }
110
111 - (void)addEmbeddedVout: (id)o_vout_view
112 {
113     if( ![o_embedded_array containsObject: o_vout_view] )
114     {
115         [o_embedded_array addObject: o_vout_view];
116     }
117 }
118
119 - (BOOL)windowContainsEmbedded: (id)o_window
120 {
121 /*    if( ![[o_window className] isEqualToString: @"VLCVoutWindow"] )
122     {
123         NSLog( @"We were not given a VLCVoutWindow" );
124     }*/
125     return ([self getViewForWindow: o_window] == nil ? NO : YES );
126 }
127
128 - (id)getViewForWindow: (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 getWindow] == 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 getRealVout: 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] getControls] getFSPanel] fadeOut];
270
271     /* Make sure we don't see a white flash */
272     [[self window] 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 = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
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_Get( p_real_vout, "fullscreen", &val );
435     val.b_bool = !val.b_bool;
436     var_Set( p_real_vout, "fullscreen", val );
437 }
438
439 - (BOOL)isFullscreen
440 {
441     vlc_value_t val;
442     if( !p_real_vout ) return NO;
443     var_Get( p_real_vout, "fullscreen", &val );
444     return( val.b_bool );
445 }
446
447 - (void)snapshot
448 {
449     var_TriggerCallback( p_real_vout, "video-snapshot" );
450 }
451
452 - (void)manage
453 {
454     /* Disable Screensaver, when we're playing something, but allow it on pause */
455     if( !VLCIntf || !VLCIntf->p_sys )
456         return;
457
458     UInt8 UsrActivity;
459     if( VLCIntf->p_sys->i_play_status == PLAYING_S )
460         UpdateSystemActivity( UsrActivity );
461 }
462
463 - (id)getWindow
464 {
465     return o_window;
466 }
467
468 - (void)scrollWheel:(NSEvent *)theEvent
469 {
470     VLCControls * o_controls = (VLCControls *)[[NSApp delegate] getControls];
471     [o_controls scrollWheel: theEvent];
472 }
473
474 - (void)keyDown:(NSEvent *)o_event
475 {
476     unichar key = 0;
477     vlc_value_t val;
478     unsigned int i_pressed_modifiers = 0;
479     val.i_int = 0;
480
481     i_pressed_modifiers = [o_event modifierFlags];
482
483     if( i_pressed_modifiers & NSShiftKeyMask )
484         val.i_int |= KEY_MODIFIER_SHIFT;
485     if( i_pressed_modifiers & NSControlKeyMask )
486         val.i_int |= KEY_MODIFIER_CTRL;
487     if( i_pressed_modifiers & NSAlternateKeyMask )
488         val.i_int |= KEY_MODIFIER_ALT;
489     if( i_pressed_modifiers & NSCommandKeyMask )
490         val.i_int |= KEY_MODIFIER_COMMAND;
491
492     key = [[[o_event charactersIgnoringModifiers] lowercaseString] characterAtIndex: 0];
493
494     if( key )
495     {
496         /* Escape should always get you out of fullscreen */
497         if( key == (unichar) 0x1b )
498         {
499              if( p_real_vout && [self isFullscreen] )
500              {
501                  [self toggleFullscreen];
502              }
503         }
504         else if ( p_vout )
505         {
506             if( key == ' ')
507                 val.i_int = config_GetInt( p_vout, "key-play-pause" );
508             else
509                 val.i_int |= (int)CocoaKeyToVLC( key );
510             var_Set( p_vout->p_libvlc, "key-pressed", val );
511         }
512         else NSLog( @"Could not send keyevent to VLC core" );
513     }
514     else
515         [super keyDown: o_event];
516 }
517
518 - (void)mouseDown:(NSEvent *)o_event
519 {
520     vlc_value_t val;
521     if( p_vout )
522     {
523         if( ( [o_event type] == NSLeftMouseDown ) &&
524           ( ! ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
525         {
526             if( [o_event clickCount] <= 1 )
527             {
528                 /* single clicking */
529                 var_Get( p_vout, "mouse-button-down", &val );
530                 val.i_int |= 1;
531                 var_Set( p_vout, "mouse-button-down", val );
532             }
533             else
534             {
535                 /* multiple clicking */
536                 [self toggleFullscreen];
537             }
538         }
539         else if( ( [o_event type] == NSRightMouseDown ) ||
540                ( ( [o_event type] == NSLeftMouseDown ) &&
541                  ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
542         {
543             msg_Dbg( p_vout, "received NSRightMouseDown (generic method) or Ctrl clic" );
544             [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] voutView]];
545         }
546     }
547
548     [super mouseDown: o_event];
549 }
550
551 - (void)otherMouseDown:(NSEvent *)o_event
552 {
553     vlc_value_t val;
554
555     if( p_vout && [o_event type] == NSOtherMouseDown )
556     {
557         var_Get( p_vout, "mouse-button-down", &val );
558         val.i_int |= 2;
559         var_Set( p_vout, "mouse-button-down", val );
560     }
561
562     [super mouseDown: o_event];
563 }
564
565 - (void)rightMouseDown:(NSEvent *)o_event
566 {
567     if( p_vout && [o_event type] == NSRightMouseDown )
568     {
569         msg_Dbg( p_vout, "received NSRightMouseDown (specific method)" );
570         [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] voutView]];
571     }
572
573     [super mouseDown: o_event];
574 }
575
576 - (void)mouseUp:(NSEvent *)o_event
577 {
578     vlc_value_t val;
579
580     if( p_vout && [o_event type] == NSLeftMouseUp )
581     {
582         var_SetBool( p_vout, "mouse-clicked", true );
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] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] 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             vlc_value_t val;
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             val.i_int = ( ((int)ml.x) - i_x ) *
656                         p_vout->render.i_width / i_width;
657             var_Set( p_vout, "mouse-x", val );
658
659             if( [[o_view className] isEqualToString: @"VLCGLView"] )
660             {
661                 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
662                             p_vout->render.i_height / i_height;
663             }
664             else
665             {
666                 val.i_int = ( ((int)ml.y) - i_y ) *
667                             p_vout->render.i_height / i_height;
668             }
669             var_Set( p_vout, "mouse-y", val );
670
671             val.b_bool = true;
672             var_Set( p_vout, "mouse-moved", val );
673         }
674         if( [self isFullscreen] )
675             [[[[VLCMain sharedInstance] getControls] getFSPanel] fadeIn];
676     }
677
678     [super mouseMoved: o_event];
679 }
680
681 - (BOOL)acceptsFirstResponder
682 {
683     return YES;
684 }
685
686 - (BOOL)becomeFirstResponder
687 {
688     return YES;
689 }
690
691 - (BOOL)resignFirstResponder
692 {
693     /* We need to stay the first responder or we'll miss some
694        events */
695     return NO;
696 }
697
698 /* Class methods used by the different vout modules */
699
700 + (vout_thread_t *)getRealVout: (vout_thread_t *)p_vout
701 {
702     /* p_real_vout: the vout we have to use to check for video-on-top
703        and a few other things. If we are the QuickTime output, it's us.
704        It we are the OpenGL provider, it is our parent.
705        Since we can't be the QuickTime output anymore, we need to be
706        the parent.
707        FIXME: check with the caca and x11 vouts! */
708     return (vout_thread_t *) p_vout->p_parent;
709 }
710
711 + (id)voutView: (vout_thread_t *)p_vout subView: (NSView *)view
712          frame: (NSRect *)s_frame
713 {
714     int i_drawable_gl;
715     int i_timeout;
716     id o_return = nil;
717
718     i_drawable_gl = var_GetInteger( p_vout->p_libvlc, "drawable-gl" );
719
720     var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
721     var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
722     var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
723     var_Create( p_vout, "macosx-background", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
724     var_Create( p_vout, "macosx-black", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
725     var_Create( p_vout, "embedded-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
726
727     /* We only wait for NSApp to initialise if we're not embedded (as in the
728      * case of the Mozilla plugin).  We can tell whether we're embedded or not
729      * by examining the "drawable-gl" value: if it's zero, we're running in the
730      * main Mac intf; if it's non-zero, we're embedded. */
731     if( i_drawable_gl == 0 )
732     {
733         /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
734         for( i_timeout = 20 ; i_timeout-- ; )
735         {
736             if( NSApp == NULL )
737             {
738                 msleep( INTF_IDLE_SLEEP );
739             }
740         }
741
742         if( NSApp == NULL )
743         {
744             /* No MacOS X intf, unable to communicate with MT */
745             msg_Err( p_vout, "no MacOS X interface present" );
746             return nil;
747         }
748         else
749         {
750             if ( VLCIntf && !(p_vout->b_fullscreen) &&
751                         !(var_GetBool( p_vout, "macosx-background" )) &&
752                         var_GetBool( p_vout, "embedded-video") )
753             {
754                 o_return = [[[VLCMain sharedInstance] getEmbeddedList]
755                                                             getEmbeddedVout];
756             }
757         }
758     }
759
760     /* No embedded vout is available */
761     if( o_return == nil )
762     {
763         NSRect null_rect;
764         bzero( &null_rect, sizeof( NSRect ) );
765         o_return = [[VLCDetachedVoutView alloc] initWithFrame: null_rect ];
766     }
767     [o_return setVout: p_vout subView: view frame: s_frame];
768     return o_return;
769 }
770
771 - (void)enterFullscreen
772 {
773     /* Save the settings for next playing item */
774     playlist_t * p_playlist = pl_Hold( p_real_vout );
775     var_SetBool( p_playlist, "fullscreen", true );
776     pl_Release( p_real_vout );
777 }
778
779 - (void)leaveFullscreen
780 {
781     /* Save the settings for next playing item */
782     playlist_t * p_playlist = pl_Hold( p_real_vout );
783     var_SetBool( p_playlist, "fullscreen", false );
784     pl_Release( p_real_vout );
785 }
786
787 @end
788
789 /*****************************************************************************
790  * VLCDetachedVoutView implementation
791  *****************************************************************************/
792 @implementation VLCDetachedVoutView
793
794 - (id)initWithFrame: (NSRect)frameRect
795 {
796     [super initWithFrame: frameRect];
797     i_time_mouse_last_moved = 0;
798     return self;
799 }
800
801 - (BOOL)mouseDownCanMoveWindow
802 {
803     return YES;
804 }
805
806 - (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
807                      frame: (NSRect *) s_arg_frame
808 {
809     BOOL b_return = [super setVout: p_arg_vout subView: view frame:s_arg_frame];
810     i_time_mouse_last_moved = mdate();
811     o_window = [[VLCVoutWindow alloc] initWithVout: p_arg_vout view: self
812                                                     frame: s_arg_frame];
813     
814     [self updateTitle];
815     if([self isFullscreen])
816         [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES];
817     else
818         [view setFrame: [self frame]];
819
820     return b_return;
821 }
822
823 - (void)closeVout
824 {
825     [o_window performSelectorOnMainThread: @selector(close) withObject: NULL waitUntilDone: YES];
826     i_time_mouse_last_moved = 0;
827     [super closeVout];
828 }
829
830 - (void)mouseMoved:(NSEvent *)o_event
831 {
832     i_time_mouse_last_moved = mdate();
833     [super mouseMoved: o_event];
834 }
835
836 - (void)hideMouse:(BOOL)b_hide
837 {
838     BOOL b_inside;
839     NSPoint ml;
840     NSView *o_contents = [o_window contentView];
841
842     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
843     ml = [o_contents convertPoint:ml fromView:nil];
844     b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];
845
846     if( b_hide && b_inside )
847     {
848         [NSCursor setHiddenUntilMouseMoves: YES];
849     }
850     else if( !b_hide )
851     {
852         [NSCursor setHiddenUntilMouseMoves: NO];
853     }
854 }
855
856 - (void)manage
857 {
858     /* Dooh, why do we spend processor time doing this kind of stuff? */
859     [super manage];
860     unsigned int i_mouse_hide_timeout =
861         var_CreateGetInteger(p_vout, "mouse-hide-timeout") * 1000;
862
863     if( i_mouse_hide_timeout < 100000 )
864         i_mouse_hide_timeout = 100000;
865     if( p_vout->b_fullscreen )
866     {
867         if( mdate() - i_time_mouse_last_moved > i_mouse_hide_timeout )
868         {
869             i_time_mouse_last_moved = mdate();
870             [self hideMouse: YES];
871         }
872     }
873     else
874     {
875         [self hideMouse: NO];
876     }
877 }
878
879
880 - (void)enterFullscreen
881 {
882     [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: NO];
883     [super enterFullscreen];
884
885 }
886
887 - (void)leaveFullscreen
888 {
889     [o_window performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: NO];
890     [super leaveFullscreen];
891 }
892
893
894 - (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate
895 {
896     if( p_vout->b_fullscreen )
897         return;
898     [o_window setMovableByWindowBackground: NO];
899     [super scaleWindowWithFactor: factor animate: animate];
900     [o_window setMovableByWindowBackground: YES];
901 }
902 @end
903
904 /*****************************************************************************
905  * VLCEmbeddedVoutView implementation
906  *****************************************************************************/
907
908 @implementation VLCEmbeddedVoutView
909
910 - (void)awakeFromNib
911 {
912     o_embeddedwindow = [self window];
913 }
914
915 - (BOOL)mouseDownCanMoveWindow
916 {
917     return YES;
918 }
919
920 - (id)initWithFrame: (NSRect)frameRect
921 {
922     if(self = [super initWithFrame: frameRect])
923     {
924         b_used = NO;
925         [[[VLCMain sharedInstance] getEmbeddedList] addEmbeddedVout: self];
926         o_embeddedwindow = nil; /* Filled later on in -awakeFromNib */
927     }
928     return self;
929 }
930
931 - (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
932                  frame: (NSRect *)s_arg_frame
933 {
934     BOOL b_return;
935
936     [NSObject cancelPreviousPerformRequestsWithTarget:o_window];
937
938     b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame];
939     if( b_return )
940     {
941         o_window = [self window];
942
943         [o_window setAcceptsMouseMovedEvents: TRUE];
944
945         if( var_CreateGetBool( p_real_vout, "video-on-top" ) )
946         {
947             [o_window setLevel: NSStatusWindowLevel];
948         }
949
950         [view setFrameSize: [self frame].size];
951     }
952
953     /* o_window needs to point to our o_embeddedwindow, super might have set it
954      * to the fullscreen window that o_embeddedwindow setups during fullscreen */
955     o_window = o_embeddedwindow;
956  
957     if( b_return )
958     {
959         [o_window lockFullscreenAnimation];
960
961         [o_window setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
962
963         [self updateTitle];
964
965         [NSObject cancelPreviousPerformRequestsWithTarget:o_window];
966
967         /* Make the window the front and key window before animating */
968         if ([o_window isVisible] && (![o_window isFullscreen]))
969             [o_window makeKeyAndOrderFront: self];
970
971         [self scaleWindowWithFactor: 1.0 animate: [o_window isVisible] && (![o_window isFullscreen])];
972
973         [o_embeddedwindow setVideoRatio:[self voutSizeForFactor:1.0]];
974
975         /* Make sure our window is visible, if we are not in fullscreen */
976         if (![o_window isFullscreen])
977             [o_window makeKeyAndOrderFront: self];
978         [o_window unlockFullscreenAnimation];
979
980     }
981
982     return b_return;
983 }
984
985 - (void)setUsed: (BOOL)b_new_used
986 {
987     b_used = b_new_used;
988 }
989
990 - (BOOL)isUsed
991 {
992     return b_used;
993 }
994
995 - (void)closeVout
996 {
997     [super closeVout];
998
999     /* Don't close the window yet, wait a bit to see if a new input is poping up */
1000     /* FIXME: Probably fade the window In and Out */
1001     /* FIXME: fix core */
1002     [o_embeddedwindow performSelector:@selector(orderOut:) withObject:nil afterDelay:3.];
1003
1004     [[[VLCMain sharedInstance] getEmbeddedList] releaseEmbeddedVout: self];
1005 }
1006
1007 - (void)enterFullscreen
1008 {
1009     /* Save settings */
1010     [super enterFullscreen];
1011
1012     /* We are in a VLCEmbeddedWindow */
1013     [o_embeddedwindow performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES];
1014 }
1015
1016 - (void)leaveFullscreen
1017 {
1018     /* Save settings */
1019     [super leaveFullscreen];
1020
1021     /* We are in a VLCEmbeddedWindow */
1022     [o_embeddedwindow performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: YES];
1023 }
1024 @end
1025
1026 /*****************************************************************************
1027  * VLCVoutWindow implementation
1028  *****************************************************************************/
1029 @implementation VLCVoutWindow
1030
1031 - (id) initWithVout: (vout_thread_t *) vout view: (VLCVoutView *) view
1032                      frame: (NSRect *) frame
1033 {
1034     p_vout  = vout;
1035     o_view  = view;
1036     s_frame = frame;
1037     b_init_ok = NO;
1038     [self performSelectorOnMainThread: @selector(initMainThread:)
1039         withObject: NULL waitUntilDone: YES];
1040
1041     return b_init_ok ? self : nil;
1042 }
1043
1044 - (id)initMainThread: (id) sender
1045 {
1046     NSRect rect;
1047     rect.size.height = p_vout->i_window_height;
1048     rect.size.width  = p_vout->i_window_width;
1049     rect.origin.x = rect.origin.y = 70.;
1050
1051     if( self = [super initWithContentRect:rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO])
1052     {
1053         [self setBackgroundColor:[NSColor blackColor]];
1054         [self setHasShadow:YES];
1055         [self setMovableByWindowBackground: YES];
1056         [self center];
1057         [self makeKeyAndOrderFront: self];
1058         [self setReleasedWhenClosed: YES];
1059         [self setFrameUsingName:@"VLCVoutWindowDetached"];
1060         [self setFrameAutosaveName:@"VLCVoutWindowDetached"];
1061
1062         /* We'll catch mouse events */
1063         [self makeFirstResponder: o_view];
1064         [self setCanBecomeKeyWindow: YES];
1065         [self setAcceptsMouseMovedEvents: YES];
1066         [self setIgnoresMouseEvents: NO];
1067
1068         if( var_CreateGetBool( p_vout, "macosx-background" ) )
1069         {
1070             int i_device = var_GetInteger( p_vout->p_libvlc, "video-device" );
1071
1072             /* Find out on which screen to open the window */
1073             NSScreen * screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
1074             if( !screen ) screen = [NSScreen mainScreen];
1075
1076             NSRect screen_rect = [screen frame];
1077             screen_rect.origin.x = screen_rect.origin.y = 0;
1078
1079             /* Creates a window with size: screen_rect on o_screen */
1080             [self setFrame: screen_rect display: NO];
1081
1082             [self setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey)];
1083             [self setMovableByWindowBackground: NO];
1084         }
1085         if( var_CreateGetBool( p_vout, "video-on-top" ) )
1086         {
1087             [self setLevel: NSStatusWindowLevel];
1088         }
1089
1090         [self setAlphaValue: var_CreateGetFloat( p_vout, "macosx-opaqueness" )];
1091
1092         /* Add the view. It's automatically resized to fit the window */
1093         [self setContentView: o_view];
1094
1095         b_init_ok = YES;
1096     }
1097     return self;
1098 }
1099
1100 - (void)enterFullscreen
1101 {
1102     if( fullscreen ) return;
1103
1104     NSScreen *screen;
1105     int i_device;
1106     BOOL b_black = NO;
1107
1108     i_device = var_GetInteger( p_vout->p_libvlc, "video-device" );
1109     b_black = var_CreateGetBool( p_vout, "macosx-black" );
1110
1111     /* Find out on which screen to open the window */
1112     screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
1113     if( !screen ) screen = [self screen];
1114
1115     if( b_black )
1116         [screen blackoutOtherScreens];
1117
1118     [self setMovableByWindowBackground: NO];
1119
1120     if( [screen isMainScreen] )
1121         SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1122
1123     initialFrame = [self frame];
1124     [self setFrame:[screen frame] display:YES animate:YES];
1125     [self setLevel:NSNormalWindowLevel];
1126
1127     /* tell the fspanel to move itself to front next time it's triggered */
1128     [[[[VLCMain sharedInstance] getControls] getFSPanel] setVoutWasUpdated: i_device];
1129     [[[[VLCMain sharedInstance] getControls] getFSPanel] setActive: nil];
1130
1131     fullscreen = YES;
1132 }
1133
1134 - (void)leaveFullscreen
1135 {
1136     if( !fullscreen ) return;
1137     fullscreen = NO;
1138
1139     [NSScreen unblackoutScreens];
1140
1141     [[[[VLCMain sharedInstance] getControls] getFSPanel] setNonActive: nil];
1142     SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1143     [self setFrame:initialFrame display:YES animate:YES];
1144     [self setMovableByWindowBackground: YES];
1145     if( var_GetBool( p_vout, "video-on-top" ) )
1146         [self setLevel: NSStatusWindowLevel];
1147 }
1148
1149 - (id)voutView
1150 {
1151     return o_view;
1152 }
1153
1154 @end
1155