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