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