]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
paintEvent is not useful anymore
[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 /* FIXME: HACK!! */
40 #ifdef __x86_64__
41 #import <CoreServices/../Frameworks/OSServices.framework/Headers/Power.h>
42 #endif
43
44 /* SystemUIMode */
45 #import <Carbon/Carbon.h>
46
47 #include <vlc_keys.h>
48
49 #include "intf.h"
50 #include "fspanel.h"
51 #include "vout.h"
52 #import "controls.h"
53 #import "embeddedwindow.h"
54
55 /*****************************************************************************
56  * DeviceCallback: Callback triggered when the video-device variable is changed
57  *****************************************************************************/
58 int DeviceCallback( vlc_object_t *p_this, const char *psz_variable,
59                      vlc_value_t old_val, vlc_value_t new_val, void *param )
60 {
61     vlc_value_t val;
62     vout_thread_t *p_vout = (vout_thread_t *)p_this;
63
64     msg_Dbg( p_vout, "set %d", new_val.i_int );
65     var_Create( p_vout->p_libvlc, "video-device", VLC_VAR_INTEGER );
66     var_Set( p_vout->p_libvlc, "video-device", new_val );
67
68     val.b_bool = true;
69     var_Set( p_vout, "intf-change", val );
70     return VLC_SUCCESS;
71 }
72
73
74 /*****************************************************************************
75  * VLCEmbeddedList implementation
76  *****************************************************************************/
77 @implementation VLCEmbeddedList
78
79 - (id)init
80 {
81     [super init];
82     o_embedded_array = [NSMutableArray array];
83     return self;
84 }
85
86 - (id)embeddedVout
87 {
88     unsigned int i;
89
90     for( i = 0; i < [o_embedded_array count]; i++ )
91     {
92         id o_vout_view = [o_embedded_array objectAtIndex: i];
93         if( ![o_vout_view isUsed] )
94         {
95             [o_vout_view setUsed: YES];
96             return o_vout_view;
97         }
98     }
99     return nil;
100 }
101
102 - (void)releaseEmbeddedVout: (id)o_vout_view
103 {
104     if( [o_embedded_array containsObject: o_vout_view] )
105     {
106         [o_vout_view setUsed: NO];
107     }
108     else
109     {
110         msg_Warn( VLCIntf, "cannot find Video Output");
111     }
112 }
113
114 - (void)addEmbeddedVout: (id)o_vout_view
115 {
116     if( ![o_embedded_array containsObject: o_vout_view] )
117     {
118         [o_embedded_array addObject: o_vout_view];
119     }
120 }
121
122 - (BOOL)windowContainsEmbedded: (id)o_window
123 {
124 /*    if( ![[o_window className] isEqualToString: @"VLCVoutWindow"] )
125     {
126         NSLog( @"We were not given a VLCVoutWindow" );
127     }*/
128     return ([self viewForWindow: o_window] == nil ? NO : YES );
129 }
130
131 - (id)viewForWindow: (id)o_window
132 {
133     if( o_embedded_array != nil )
134     {
135         id o_enumerator = [o_embedded_array objectEnumerator];
136         id o_current_embedded;
137         if( o_window != nil )
138         {
139             while( (o_current_embedded = [o_enumerator nextObject]) )
140             {
141                 if( [o_current_embedded voutWindow] == o_window )
142                 {
143                     return o_current_embedded;
144                 }
145             }
146         }
147     }
148     return nil;
149 }
150
151 @end
152
153 /*****************************************************************************
154  * VLCVoutView implementation
155  *****************************************************************************/
156 @implementation VLCVoutView
157
158 - (id)initWithFrame: (NSRect)frameRect
159 {
160     self = [super initWithFrame: frameRect];
161     p_vout = NULL;
162     o_view = nil;
163     s_frame = &frameRect;
164
165     p_real_vout = NULL;
166     o_window = nil;
167     return self;
168 }
169
170 - (BOOL)setVout: (vout_thread_t *) vout
171         subView: (NSView *) view
172           frame: (NSRect *) frame
173 {
174     int i_device;
175     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
176     NSArray *o_screens = [NSScreen screens];
177
178     p_vout  = vout;
179     o_view  = view;
180     s_frame = frame;
181
182     if( [o_screens count] <= 0 )
183     {
184         msg_Err( p_vout, "no OSX screens available" );
185         return NO;
186     }
187
188     p_real_vout = [VLCVoutView realVout: p_vout];
189
190     /* Get the pref value when this is the first time, otherwise retrieve the device from the top level video-device var */
191     if( var_Type( p_real_vout->p_libvlc, "video-device" ) == 0 )
192     {
193         i_device = var_GetInteger( p_vout, "macosx-vdev" );
194     }
195     else
196     {
197         i_device = var_GetInteger( p_real_vout->p_libvlc, "video-device" );
198     }
199
200     /* Setup the menuitem for the multiple displays. */
201     if( var_Type( p_real_vout, "video-device" ) == 0 )
202     {
203         int i = 1;
204         vlc_value_t val2, text;
205         NSScreen * o_screen;
206
207         var_Create( p_real_vout, "video-device", VLC_VAR_INTEGER |
208                                             VLC_VAR_HASCHOICE );
209         text.psz_string = _("Fullscreen Video Device");
210         var_Change( p_real_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
211
212         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
213
214         val2.i_int = 0;
215         text.psz_string = _("Default");
216         var_Change( p_real_vout, "video-device",
217                         VLC_VAR_ADDCHOICE, &val2, &text );
218         var_Set( p_real_vout, "video-device", val2 );
219
220         while( (o_screen = [o_enumerator nextObject]) != NULL )
221         {
222             char psz_temp[255];
223             NSRect s_rect = [o_screen frame];
224
225             snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
226                       "%s %d (%dx%d)", _("Screen"), i,
227                       (int)s_rect.size.width, (int)s_rect.size.height );
228
229             text.psz_string = psz_temp;
230             val2.i_int = (int)[o_screen displayID];
231             var_Change( p_real_vout, "video-device",
232                         VLC_VAR_ADDCHOICE, &val2, &text );
233             if( (int)[o_screen displayID] == i_device )
234             {
235                 var_Set( p_real_vout, "video-device", val2 );
236             }
237             i++;
238         }
239
240         var_AddCallback( p_real_vout, "video-device", DeviceCallback,
241                          NULL );
242
243         val2.b_bool = true;
244         var_Set( p_real_vout, "intf-change", val2 );
245     }
246
247     /* Add the view. It's automatically resized to fit the window */
248     [self addSubview: o_view];
249     [self setAutoresizesSubviews: YES];
250     [o_pool release];
251
252     return YES;
253 }
254
255 - (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize
256 {
257     [super resizeSubviewsWithOldSize: oldBoundsSize];
258     [o_view setFrameSize: [self frame].size];
259 }
260
261 - (void)drawRect:(NSRect)rect
262 {
263     /* When there is no subview we draw a black background */
264     [self lockFocus];
265     [[NSColor blackColor] set];
266     NSRectFill(rect);
267     [self unlockFocus];
268 }
269
270 - (void)closeVout
271 {
272     [[[[VLCMain sharedInstance] controls] fspanel] fadeOut];
273
274     /* Make sure we don't see a white flash */
275     [[self voutWindow] disableScreenUpdatesUntilFlush];
276     [o_view removeFromSuperview];
277     o_view = nil;
278     p_vout = NULL;
279     s_frame = nil;
280     o_window = nil;
281     p_real_vout = NULL;
282 }
283
284 - (void)updateTitle
285 {
286     NSString * o_title = nil; 
287     NSMutableString * o_mrl = nil;
288     input_thread_t * p_input;
289     char * psz_title;
290
291     if( !p_vout ) return;
292
293     p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
294
295     if( !p_input ) return;
296
297     input_item_t * p_item = input_GetItem( p_input );
298
299     psz_title = input_item_GetNowPlaying ( p_item );
300     if( !psz_title )
301         psz_title = input_item_GetName( p_item );
302
303     if( psz_title )
304         o_title = [NSString stringWithUTF8String: psz_title];
305
306     char *psz_uri = input_item_GetURI( p_item );
307     if( psz_uri )
308         o_mrl = [NSMutableString stringWithUTF8String: psz_uri];
309
310     free( psz_title );
311     free( psz_uri );
312
313     if( !o_title )
314         o_title = o_mrl;
315
316     if( o_mrl != nil )
317     {
318         /* FIXME once psz_access is exported, we could check if we are
319          * reading from a file in a smarter way. */
320
321         NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
322         if( prefix_range.location != NSNotFound )
323             [o_mrl deleteCharactersInRange: prefix_range];
324
325         if( [o_mrl characterAtIndex:0] == '/' )
326         {
327             /* it's a local file */
328             [o_window setRepresentedFilename: o_mrl];
329         }
330         else
331         {
332             /* it's from the network or somewhere else,
333              * we clear the previous path */
334             [o_window setRepresentedFilename: @""];
335         }
336         [o_window setTitle: o_title];
337     }
338     else
339     {
340         [o_window setTitle: [NSString stringWithUTF8String: VOUT_TITLE]];
341     }
342     vlc_object_release( p_input );
343 }
344
345 - (void)setOnTop:(BOOL)b_on_top
346 {
347     if( b_on_top )
348     {
349         [o_window setLevel: NSStatusWindowLevel];
350     }
351     else
352     {
353         [o_window setLevel: NSNormalWindowLevel];
354     }
355 }
356
357 - (NSSize)voutSizeForFactor: (float)factor
358 {
359     int i_corrected_height, i_corrected_width;
360     NSSize newsize;
361
362     if( p_vout->render.i_height * p_vout->render.i_aspect >
363                     p_vout->render.i_width * VOUT_ASPECT_FACTOR )
364     {
365         i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect /
366                                         VOUT_ASPECT_FACTOR;
367         newsize.width = (int) ( i_corrected_width * factor );
368         newsize.height = (int) ( p_vout->render.i_height * factor );
369     }
370     else
371     {
372         i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR /
373                                         p_vout->render.i_aspect;
374         newsize.width = (int) ( p_vout->render.i_width * factor );
375         newsize.height = (int) ( i_corrected_height * factor );
376     }
377
378     return newsize;
379 }
380
381 - (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate
382 {
383     if ( !p_vout->b_fullscreen )
384     {
385         NSSize newsize;
386         NSPoint topleftbase;
387         NSPoint topleftscreen;
388         NSView *mainView;
389         NSRect new_frame;
390         topleftbase.x = 0;
391         topleftbase.y = [o_window frame].size.height;
392         topleftscreen = [o_window convertBaseToScreen: topleftbase];
393
394         newsize = [self voutSizeForFactor:factor];
395
396         /* In fullscreen mode we need to use a view that is different from
397          * ourselves, with the VLCEmbeddedWindow */
398         if([o_window isKindOfClass:[VLCEmbeddedWindow class]])
399             mainView = [o_window mainView];
400         else
401             mainView = self;
402
403         /* Calculate the window's new size */
404         new_frame.size.width = [o_window frame].size.width -
405                                     [mainView frame].size.width + newsize.width;
406         new_frame.size.height = [o_window frame].size.height -
407                                     [mainView frame].size.height + newsize.height;
408
409         new_frame.origin.x = topleftscreen.x;
410         new_frame.origin.y = topleftscreen.y - new_frame.size.height;
411
412         [o_window setFrame:new_frame display:animate animate:animate];
413         p_vout->i_changes |= VOUT_SIZE_CHANGE;
414     }
415 }
416
417 - (void)toggleFloatOnTop
418 {
419     vlc_value_t val;
420
421     if( !p_real_vout ) return;
422     if( var_Get( p_real_vout, "video-on-top", &val )>=0 && val.b_bool)
423     {
424         val.b_bool = false;
425     }
426     else
427     {
428         val.b_bool = true;
429     }
430     var_Set( p_real_vout, "video-on-top", val );
431 }
432
433 - (void)toggleFullscreen
434 {
435     vlc_value_t val;
436     if( !p_real_vout ) return;
437     var_ToggleBool( p_real_vout, "fullscreen" );
438 }
439
440 - (BOOL)isFullscreen
441 {
442     vlc_value_t val;
443     if( !p_real_vout ) return NO;
444     var_Get( p_real_vout, "fullscreen", &val );
445     return( val.b_bool );
446 }
447
448 - (void)snapshot
449 {
450     var_TriggerCallback( p_real_vout, "video-snapshot" );
451 }
452
453 - (void)manage
454 {
455     /* Disable Screensaver, when we're playing something, but allow it on pause */
456     if( !VLCIntf || !VLCIntf->p_sys )
457         return;
458
459     if( VLCIntf->p_sys->i_play_status == PLAYING_S )
460         UpdateSystemActivity( UsrActivity );
461 }
462
463 - (id)voutWindow
464 {
465     return o_window;
466 }
467
468 - (void)scrollWheel:(NSEvent *)theEvent
469 {
470     VLCControls * o_controls = (VLCControls *)[[NSApp delegate] controls];
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] voutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] controls] 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] voutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] controls] 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] 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             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] controls] fspanel] 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 *)realVout: (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] embeddedList] embeddedVout];
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] embeddedList] 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] embeddedList] 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] controls] fspanel] setVoutWasUpdated: i_device];
1128     [[[[VLCMain sharedInstance] controls] fspanel] 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] controls] fspanel] setNonActive: nil];
1141     SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1142
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