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