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