]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
554b90a2e99821c0511de637f3815972d4e442ba
[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         int x, y;
584
585         var_GetCoords( p_vout, "mouse-moved", &x, &y );
586         var_SetCoords( p_vout, "mouse-clicked", x, y );
587
588         var_Get( p_vout, "mouse-button-down", &val );
589         val.i_int &= ~1;
590         var_Set( p_vout, "mouse-button-down", val );
591     }
592
593     [super mouseUp: o_event];
594 }
595
596 - (void)otherMouseUp:(NSEvent *)o_event
597 {
598     vlc_value_t val;
599
600     if( p_vout && [o_event type] == NSOtherMouseUp )
601     {
602         var_Get( p_vout, "mouse-button-down", &val );
603         val.i_int &= ~2;
604         var_Set( p_vout, "mouse-button-down", val );
605     }
606
607     [super mouseUp: o_event];
608 }
609
610 - (void)rightMouseUp:(NSEvent *)o_event
611 {
612     if( p_vout && [o_event type] == NSRightMouseUp )
613     {
614         /* FIXME: this isn't the appropriate place, but we can't receive
615          * NSRightMouseDown some how */
616         msg_Dbg( p_vout, "received NSRightMouseUp" );
617         [NSMenu popUpContextMenu: [[VLCMain sharedInstance] voutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] controls] voutView]];
618     }
619
620     [super mouseUp: o_event];
621 }
622
623 - (void)mouseDragged:(NSEvent *)o_event
624 {
625     [self mouseMoved: o_event];
626 }
627
628 - (void)otherMouseDragged:(NSEvent *)o_event
629 {
630     [self mouseMoved: o_event];
631 }
632
633 - (void)rightMouseDragged:(NSEvent *)o_event
634 {
635     [self mouseMoved: o_event];
636 }
637
638 - (void)mouseMoved:(NSEvent *)o_event
639 {
640     NSPoint ml;
641     NSRect s_rect;
642     BOOL b_inside;
643
644     if( p_vout )
645     {
646         s_rect = [o_view bounds];
647         ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
648         b_inside = [o_view mouse: ml inRect: s_rect];
649
650         if( b_inside )
651         {
652             int x, y;
653             unsigned int i_width, i_height, i_x, i_y;
654
655             vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
656                                        (unsigned int)s_rect.size.height,
657                                        &i_x, &i_y, &i_width, &i_height );
658
659             x = (((int)ml.x) - i_x) * p_vout->render.i_width / i_width;
660             if( [[o_view className] isEqualToString: @"VLCGLView"] )
661             {
662                 y = (((int)(s_rect.size.height - ml.y)) - i_y) *
663                             p_vout->render.i_height / i_height;
664             }
665             else
666             {
667                 y = (((int)ml.y) - i_y) * p_vout->render.i_height / i_height;
668             }
669             var_SetCoords( p_vout, "mouse-moved", x, y );
670         }
671         if( [self isFullscreen] )
672             [[[[VLCMain sharedInstance] controls] fspanel] fadeIn];
673     }
674
675     [super mouseMoved: o_event];
676 }
677
678 - (BOOL)acceptsFirstResponder
679 {
680     return YES;
681 }
682
683 - (BOOL)becomeFirstResponder
684 {
685     return YES;
686 }
687
688 - (BOOL)resignFirstResponder
689 {
690     /* We need to stay the first responder or we'll miss some
691        events */
692     return NO;
693 }
694
695 /* Class methods used by the different vout modules */
696
697 + (vout_thread_t *)realVout: (vout_thread_t *)p_vout
698 {
699     /* p_real_vout: the vout we have to use to check for video-on-top
700        and a few other things. If we are the QuickTime output, it's us.
701        It we are the OpenGL provider, it is our parent.
702        Since we can't be the QuickTime output anymore, we need to be
703        the parent.
704        FIXME: check with the caca and x11 vouts! */
705     return (vout_thread_t *) p_vout->p_parent;
706 }
707
708 + (id)voutView: (vout_thread_t *)p_vout subView: (NSView *)view
709          frame: (NSRect *)s_frame
710 {
711     int i_drawable_gl;
712     int i_timeout;
713     id o_return = nil;
714
715     i_drawable_gl = var_GetInteger( p_vout->p_libvlc, "drawable-gl" );
716
717     var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
718     var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
719     var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
720     var_Create( p_vout, "macosx-background", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
721     var_Create( p_vout, "macosx-black", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
722     var_Create( p_vout, "embedded-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
723
724     /* We only wait for NSApp to initialise if we're not embedded (as in the
725      * case of the Mozilla plugin).  We can tell whether we're embedded or not
726      * by examining the "drawable-gl" value: if it's zero, we're running in the
727      * main Mac intf; if it's non-zero, we're embedded. */
728     if( i_drawable_gl == 0 )
729     {
730         /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
731         for( i_timeout = 20 ; i_timeout-- ; )
732         {
733             if( NSApp == NULL )
734             {
735                 msleep( INTF_IDLE_SLEEP );
736             }
737         }
738
739         if( NSApp == NULL )
740         {
741             /* No MacOS X intf, unable to communicate with MT */
742             msg_Err( p_vout, "no MacOS X interface present" );
743             return nil;
744         }
745         else
746         {
747             if ( VLCIntf && !(p_vout->b_fullscreen) &&
748                         !(var_GetBool( p_vout, "macosx-background" )) &&
749                         var_GetBool( p_vout, "embedded-video") )
750             {
751                 o_return = [[[VLCMain sharedInstance] embeddedList] embeddedVout];
752             }
753         }
754     }
755
756     /* No embedded vout is available */
757     if( o_return == nil )
758     {
759         NSRect null_rect;
760         bzero( &null_rect, sizeof( NSRect ) );
761         o_return = [[VLCDetachedVoutView alloc] initWithFrame: null_rect ];
762     }
763     [o_return setVout: p_vout subView: view frame: s_frame];
764     return o_return;
765 }
766
767 - (void)enterFullscreen
768 {
769     /* Save the settings for next playing item */
770     playlist_t * p_playlist = pl_Get( p_real_vout );
771     var_SetBool( p_playlist, "fullscreen", true );
772 }
773
774 - (void)leaveFullscreen
775 {
776     /* Save the settings for next playing item */
777     playlist_t * p_playlist = pl_Get( p_real_vout );
778     var_SetBool( p_playlist, "fullscreen", false );
779 }
780
781 @end
782
783 /*****************************************************************************
784  * VLCDetachedVoutView implementation
785  *****************************************************************************/
786 @implementation VLCDetachedVoutView
787
788 - (id)initWithFrame: (NSRect)frameRect
789 {
790     [super initWithFrame: frameRect];
791     i_time_mouse_last_moved = 0;
792     return self;
793 }
794
795 - (BOOL)mouseDownCanMoveWindow
796 {
797     return YES;
798 }
799
800 - (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
801                      frame: (NSRect *) s_arg_frame
802 {
803     BOOL b_return = [super setVout: p_arg_vout subView: view frame:s_arg_frame];
804     i_time_mouse_last_moved = mdate();
805     o_window = [[VLCVoutWindow alloc] initWithVout: p_arg_vout view: self
806                                                     frame: s_arg_frame];
807     
808     [self updateTitle];
809     if([self isFullscreen])
810         [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES];
811     else
812         [view setFrame: [self frame]];
813
814     return b_return;
815 }
816
817 - (void)closeVout
818 {
819     [o_window performSelectorOnMainThread: @selector(close) withObject: NULL waitUntilDone: YES];
820     i_time_mouse_last_moved = 0;
821     [super closeVout];
822 }
823
824 - (void)mouseMoved:(NSEvent *)o_event
825 {
826     i_time_mouse_last_moved = mdate();
827     [super mouseMoved: o_event];
828 }
829
830 - (void)hideMouse:(BOOL)b_hide
831 {
832     BOOL b_inside;
833     NSPoint ml;
834     NSView *o_contents = [o_window contentView];
835
836     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
837     ml = [o_contents convertPoint:ml fromView:nil];
838     b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];
839
840     if( b_hide && b_inside )
841     {
842         [NSCursor setHiddenUntilMouseMoves: YES];
843     }
844     else if( !b_hide )
845     {
846         [NSCursor setHiddenUntilMouseMoves: NO];
847     }
848 }
849
850 - (void)manage
851 {
852     /* Dooh, why do we spend processor time doing this kind of stuff? */
853     [super manage];
854     unsigned int i_mouse_hide_timeout =
855         var_CreateGetInteger(p_vout, "mouse-hide-timeout") * 1000;
856
857     if( i_mouse_hide_timeout < 100000 )
858         i_mouse_hide_timeout = 100000;
859     if( p_vout->b_fullscreen )
860     {
861         if( mdate() - i_time_mouse_last_moved > i_mouse_hide_timeout )
862         {
863             i_time_mouse_last_moved = mdate();
864             [self hideMouse: YES];
865         }
866     }
867     else
868     {
869         [self hideMouse: NO];
870     }
871 }
872
873
874 - (void)enterFullscreen
875 {
876     [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: NO];
877     [super enterFullscreen];
878
879 }
880
881 - (void)leaveFullscreen
882 {
883     [o_window performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: NO];
884     [super leaveFullscreen];
885 }
886
887
888 - (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate
889 {
890     if( p_vout->b_fullscreen )
891         return;
892     [o_window setMovableByWindowBackground: NO];
893     [super scaleWindowWithFactor: factor animate: animate];
894     [o_window setMovableByWindowBackground: YES];
895 }
896 @end
897
898 /*****************************************************************************
899  * VLCEmbeddedVoutView implementation
900  *****************************************************************************/
901
902 @implementation VLCEmbeddedVoutView
903
904 - (void)awakeFromNib
905 {
906     o_embeddedwindow = [self window];
907 }
908
909 - (BOOL)mouseDownCanMoveWindow
910 {
911     return YES;
912 }
913
914 - (id)initWithFrame: (NSRect)frameRect
915 {
916     if(self = [super initWithFrame: frameRect])
917     {
918         b_used = NO;
919         [[[VLCMain sharedInstance] embeddedList] addEmbeddedVout: self];
920         o_embeddedwindow = nil; /* Filled later on in -awakeFromNib */
921     }
922     return self;
923 }
924
925 - (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
926                  frame: (NSRect *)s_arg_frame
927 {
928     BOOL b_return;
929
930     [NSObject cancelPreviousPerformRequestsWithTarget:o_window];
931
932     b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame];
933     if( b_return )
934     {
935         o_window = [self window];
936
937         [o_window setAcceptsMouseMovedEvents: TRUE];
938
939         if( var_CreateGetBool( p_real_vout, "video-on-top" ) )
940         {
941             [o_window setLevel: NSStatusWindowLevel];
942         }
943
944         [view setFrameSize: [self frame].size];
945     }
946
947     /* o_window needs to point to our o_embeddedwindow, super might have set it
948      * to the fullscreen window that o_embeddedwindow setups during fullscreen */
949     o_window = o_embeddedwindow;
950  
951     if( b_return )
952     {
953         [o_window lockFullscreenAnimation];
954
955         [o_window setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
956
957         [self updateTitle];
958
959         [NSObject cancelPreviousPerformRequestsWithTarget:o_window];
960
961         /* Make the window the front and key window before animating */
962         if ([o_window isVisible] && (![o_window isFullscreen]))
963             [o_window makeKeyAndOrderFront: self];
964
965         if ( [self window] != o_embeddedwindow )
966             [self scaleWindowWithFactor: 1.0 animate: [o_window isVisible] && (![o_window isFullscreen])];
967
968         [o_embeddedwindow setVideoRatio:[self voutSizeForFactor:1.0]];
969
970         /* Make sure our window is visible, if we are not in fullscreen */
971         if (![o_window isFullscreen])
972             [o_window makeKeyAndOrderFront: self];
973         [o_window unlockFullscreenAnimation];
974
975     }
976
977     return b_return;
978 }
979
980 - (void)setUsed: (BOOL)b_new_used
981 {
982     b_used = b_new_used;
983 }
984
985 - (BOOL)isUsed
986 {
987     return b_used;
988 }
989
990 - (void)closeVout
991 {
992     [super closeVout];
993
994     /* Don't close the window yet, wait a bit to see if a new input is poping up */
995     /* FIXME: Probably fade the window In and Out */
996     /* FIXME: fix core */
997     [o_embeddedwindow performSelector:@selector(orderOut:) withObject:nil afterDelay:3.];
998
999     [[[VLCMain sharedInstance] embeddedList] releaseEmbeddedVout: self];
1000 }
1001
1002 - (void)enterFullscreen
1003 {
1004     /* Save settings */
1005     [super enterFullscreen];
1006
1007     /* We are in a VLCEmbeddedWindow */
1008     [o_embeddedwindow performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES];
1009 }
1010
1011 - (void)leaveFullscreen
1012 {
1013     /* Save settings */
1014     [super leaveFullscreen];
1015
1016     /* We are in a VLCEmbeddedWindow */
1017     [o_embeddedwindow performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: YES];
1018 }
1019 @end
1020
1021 /*****************************************************************************
1022  * VLCVoutWindow implementation
1023  *****************************************************************************/
1024 @implementation VLCVoutWindow
1025
1026 - (id) initWithVout: (vout_thread_t *) vout view: (VLCVoutView *) view
1027                      frame: (NSRect *) frame
1028 {
1029     p_vout  = vout;
1030     o_view  = view;
1031     s_frame = frame;
1032     b_init_ok = NO;
1033     [self performSelectorOnMainThread: @selector(initMainThread:)
1034         withObject: NULL waitUntilDone: YES];
1035
1036     return b_init_ok ? self : nil;
1037 }
1038
1039 - (id)initMainThread: (id) sender
1040 {
1041     NSRect rect;
1042     rect.size.height = p_vout->i_window_height;
1043     rect.size.width  = p_vout->i_window_width;
1044     rect.origin.x = rect.origin.y = 70.;
1045
1046     if( self = [super initWithContentRect:rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO])
1047     {
1048         [self setBackgroundColor:[NSColor blackColor]];
1049         [self setHasShadow:YES];
1050         [self setMovableByWindowBackground: YES];
1051         [self center];
1052         [self makeKeyAndOrderFront: self];
1053         [self setReleasedWhenClosed: YES];
1054         [self setFrameUsingName:@"VLCVoutWindowDetached"];
1055         [self setFrameAutosaveName:@"VLCVoutWindowDetached"];
1056
1057         /* We'll catch mouse events */
1058         [self makeFirstResponder: o_view];
1059         [self setCanBecomeKeyWindow: YES];
1060         [self setAcceptsMouseMovedEvents: YES];
1061         [self setIgnoresMouseEvents: NO];
1062
1063         if( var_CreateGetBool( p_vout, "macosx-background" ) )
1064         {
1065             int i_device = var_GetInteger( p_vout->p_libvlc, "video-device" );
1066
1067             /* Find out on which screen to open the window */
1068             NSScreen * screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
1069             if( !screen ) screen = [NSScreen mainScreen];
1070
1071             NSRect screen_rect = [screen frame];
1072             screen_rect.origin.x = screen_rect.origin.y = 0;
1073
1074             /* Creates a window with size: screen_rect on o_screen */
1075             [self setFrame: screen_rect display: NO];
1076
1077             [self setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey)];
1078             [self setMovableByWindowBackground: NO];
1079         }
1080         if( var_CreateGetBool( p_vout, "video-on-top" ) )
1081         {
1082             [self setLevel: NSStatusWindowLevel];
1083         }
1084
1085         [self setAlphaValue: var_CreateGetFloat( p_vout, "macosx-opaqueness" )];
1086
1087         /* Add the view. It's automatically resized to fit the window */
1088         [self setContentView: o_view];
1089
1090         b_init_ok = YES;
1091     }
1092     return self;
1093 }
1094
1095 - (void)enterFullscreen
1096 {
1097     if( fullscreen ) return;
1098
1099     NSScreen *screen;
1100     int i_device;
1101     BOOL b_black = NO;
1102
1103     i_device = var_GetInteger( p_vout->p_libvlc, "video-device" );
1104     b_black = var_CreateGetBool( p_vout, "macosx-black" );
1105
1106     /* Find out on which screen to open the window */
1107     screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
1108     if( !screen ) screen = [self screen];
1109
1110     if( b_black )
1111         [screen blackoutOtherScreens];
1112
1113     [self setMovableByWindowBackground: NO];
1114
1115     if( [screen isMainScreen] )
1116         SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1117
1118     initialFrame = [self frame];
1119     [self setFrame:[screen frame] display:YES animate:YES];
1120     [self setLevel:NSNormalWindowLevel];
1121
1122     /* tell the fspanel to move itself to front next time it's triggered */
1123     [[[[VLCMain sharedInstance] controls] fspanel] setVoutWasUpdated: i_device];
1124     [[[[VLCMain sharedInstance] controls] fspanel] setActive: nil];
1125
1126     fullscreen = YES;
1127 }
1128
1129 - (void)leaveFullscreen
1130 {
1131     if( !fullscreen ) return;
1132     fullscreen = NO;
1133
1134     [NSScreen unblackoutScreens];
1135
1136     [[[[VLCMain sharedInstance] controls] fspanel] setNonActive: nil];
1137     SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1138
1139     [self setFrame:initialFrame display:YES animate:YES];
1140     [self setMovableByWindowBackground: YES];
1141     if( var_GetBool( p_vout, "video-on-top" ) )
1142         [self setLevel: NSStatusWindowLevel];
1143 }
1144
1145 - (id)voutView
1146 {
1147     return o_view;
1148 }
1149
1150 @end
1151