]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
* New Fullscreen controller panel
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2006 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_vlc, "video-device", VLC_VAR_INTEGER );
58     var_Set( p_vout->p_vlc, "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_vlc, "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_vlc, "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     [o_view removeFromSuperview];
250     o_view = nil;
251     p_vout = NULL;
252     s_frame = nil;
253     o_window = nil;
254     p_real_vout = NULL;
255 }
256
257 - (void)updateTitle
258 {
259     NSMutableString * o_title = nil, * o_mrl = nil;
260     input_thread_t * p_input;
261
262     if( p_vout == NULL )
263     {
264         return;
265     }
266
267     p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
268
269     if( p_input == NULL )
270     {
271         return;
272     }
273
274     if( p_input->input.p_item->psz_name != NULL )
275         o_title = [NSMutableString stringWithUTF8String:
276             p_input->input.p_item->psz_name];
277     if( p_input->input.p_item->psz_uri != NULL )
278         o_mrl = [NSMutableString stringWithUTF8String:
279             p_input->input.p_item->psz_uri];
280     if( o_title == nil )
281         o_title = o_mrl;
282
283     if( o_mrl != nil )
284     {
285         if( p_input->input.p_access && !strcmp( p_input->input.p_access->p_module->psz_shortname, "File" ) )
286         {
287             NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
288             if( prefix_range.location != NSNotFound )
289                 [o_mrl deleteCharactersInRange: prefix_range];
290             [o_window setRepresentedFilename: o_mrl];
291         }
292         [o_window setTitle: o_title];
293     }
294     else
295     {
296         [o_window setTitle: [NSString stringWithCString: VOUT_TITLE]];
297     }
298     vlc_object_release( p_input );
299 }
300
301
302 - (void)setOnTop:(BOOL)b_on_top
303 {
304     if( b_on_top )
305     {
306         [o_window setLevel: NSStatusWindowLevel];
307     }
308     else
309     {
310         [o_window setLevel: NSNormalWindowLevel];
311     }
312 }
313
314 - (void)scaleWindowWithFactor: (float)factor
315 {
316     NSSize newsize;
317     int i_corrected_height, i_corrected_width;
318     NSPoint topleftbase;
319     NSPoint topleftscreen;
320
321     if ( !p_vout->b_fullscreen )
322     {
323         NSRect new_frame;
324         topleftbase.x = 0;
325         topleftbase.y = [o_window frame].size.height;
326         topleftscreen = [o_window convertBaseToScreen: topleftbase];
327
328         if( p_vout->render.i_height * p_vout->render.i_aspect >
329                         p_vout->render.i_width * VOUT_ASPECT_FACTOR )
330         {
331             i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect /
332                                             VOUT_ASPECT_FACTOR;
333             newsize.width = (int) ( i_corrected_width * factor );
334             newsize.height = (int) ( p_vout->render.i_height * factor );
335         }
336         else
337         {
338             i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR /
339                                             p_vout->render.i_aspect;
340             newsize.width = (int) ( p_vout->render.i_width * factor );
341             newsize.height = (int) ( i_corrected_height * factor );
342         }
343
344         /* Calculate the window's new size */
345         new_frame.size.width = [o_window frame].size.width -
346                                     [self frame].size.width + newsize.width;
347         new_frame.size.height = [o_window frame].size.height -
348                                     [self frame].size.height + newsize.height;
349
350         new_frame.origin.x = topleftscreen.x;
351         new_frame.origin.y = topleftscreen.y - new_frame.size.height;
352
353         [o_window setFrame: new_frame display: YES];
354
355         p_vout->i_changes |= VOUT_SIZE_CHANGE;
356     }
357 }
358
359 - (void)toggleFloatOnTop
360 {
361     vlc_value_t val;
362
363     if( !p_real_vout ) return;
364     if( var_Get( p_real_vout, "video-on-top", &val )>=0 && val.b_bool)
365     {
366         val.b_bool = VLC_FALSE;
367     }
368     else
369     {
370         val.b_bool = VLC_TRUE;
371     }
372     var_Set( p_real_vout, "video-on-top", val );
373 }
374
375 - (void)toggleFullscreen
376 {
377     vlc_value_t val;
378     if( !p_real_vout ) return;
379     var_Get( p_real_vout, "fullscreen", &val );
380     val.b_bool = !val.b_bool;
381     var_Set( p_real_vout, "fullscreen", val );
382     if( [self isFullscreen] )
383         [[[[VLCMain sharedInstance] getControls] getFSPanel] orderFront: self];
384     else
385         [[[[VLCMain sharedInstance] getControls] getFSPanel] orderOut: self];
386 }
387
388 - (BOOL)isFullscreen
389 {
390     vlc_value_t val;
391     var_Get( p_real_vout, "fullscreen", &val );
392     return( val.b_bool );
393 }
394
395 - (void)snapshot
396 {
397     vout_Control( p_real_vout, VOUT_SNAPSHOT );
398 }
399
400 - (void)manage
401 {
402     /* Disable Screensaver */
403     UpdateSystemActivity( UsrActivity );
404 }
405
406 - (id)getWindow
407 {
408     return o_window;
409 }
410
411 - (void)keyDown:(NSEvent *)o_event
412 {
413     unichar key = 0;
414     vlc_value_t val;
415     unsigned int i_pressed_modifiers = 0;
416     val.i_int = 0;
417
418     i_pressed_modifiers = [o_event modifierFlags];
419
420     if( i_pressed_modifiers & NSShiftKeyMask )
421         val.i_int |= KEY_MODIFIER_SHIFT;
422     if( i_pressed_modifiers & NSControlKeyMask )
423         val.i_int |= KEY_MODIFIER_CTRL;
424     if( i_pressed_modifiers & NSAlternateKeyMask )
425         val.i_int |= KEY_MODIFIER_ALT;
426     if( i_pressed_modifiers & NSCommandKeyMask )
427         val.i_int |= KEY_MODIFIER_COMMAND;
428
429     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
430
431     if( key )
432     {
433         /* Escape should always get you out of fullscreen */
434         if( key == (unichar) 0x1b )
435         {
436              if( p_real_vout && [self isFullscreen] )
437              {
438                  [self toggleFullscreen];
439              }
440         }
441         else if ( key == ' ' )
442         {
443             vlc_value_t val;
444             val.i_int = config_GetInt( p_vout, "key-play-pause" );
445             var_Set( p_vout->p_vlc, "key-pressed", val );
446         }
447         else
448         {
449             val.i_int |= CocoaKeyToVLC( key );
450             var_Set( p_vout->p_vlc, "key-pressed", val );
451         }
452     }
453     else
454     {
455         [super keyDown: o_event];
456     }
457 }
458
459 - (void)mouseDown:(NSEvent *)o_event
460 {
461     vlc_value_t val;
462
463     if( p_vout )
464     {
465         if( ( [o_event type] == NSLeftMouseDown ) &&
466           ( ! ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
467         {
468             if( [o_event clickCount] <= 1 )
469             {
470                 /* single clicking */
471                 var_Get( p_vout, "mouse-button-down", &val );
472                 val.i_int |= 1;
473                 var_Set( p_vout, "mouse-button-down", val );
474             }
475             else
476             {
477                 /* multiple clicking */
478                 [self toggleFullscreen];
479             }
480         }
481         else if( ( [o_event type] == NSRightMouseDown ) ||
482                ( ( [o_event type] == NSLeftMouseDown ) &&
483                  ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
484         {
485             msg_Dbg( p_vout, "received NSRightMouseDown (generic method) or Ctrl clic" );
486             [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] getVoutView]];
487         }
488     }
489
490     [super mouseDown: o_event];
491 }
492
493 - (void)otherMouseDown:(NSEvent *)o_event
494 {
495     vlc_value_t val;
496
497     if( p_vout && [o_event type] == NSOtherMouseDown )
498     {
499         var_Get( p_vout, "mouse-button-down", &val );
500         val.i_int |= 2;
501         var_Set( p_vout, "mouse-button-down", val );
502     }
503
504     [super mouseDown: o_event];
505 }
506
507 - (void)rightMouseDown:(NSEvent *)o_event
508 {
509     if( p_vout && [o_event type] == NSRightMouseDown )
510     {
511         msg_Dbg( p_vout, "received NSRightMouseDown (specific method)" );
512         [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] getVoutView]];
513     }
514
515     [super mouseDown: o_event];
516 }
517
518 - (void)mouseUp:(NSEvent *)o_event
519 {
520     vlc_value_t val;
521
522     if( p_vout && [o_event type] == NSLeftMouseUp )
523     {
524         vlc_value_t b_val;
525         b_val.b_bool = VLC_TRUE;
526         var_Set( p_vout, "mouse-clicked", b_val );
527
528         var_Get( p_vout, "mouse-button-down", &val );
529         val.i_int &= ~1;
530         var_Set( p_vout, "mouse-button-down", val );
531     }
532
533     [super mouseUp: o_event];
534 }
535
536 - (void)otherMouseUp:(NSEvent *)o_event
537 {
538     vlc_value_t val;
539
540     if( p_vout && [o_event type] == NSOtherMouseUp )
541     {
542         var_Get( p_vout, "mouse-button-down", &val );
543         val.i_int &= ~2;
544         var_Set( p_vout, "mouse-button-down", val );
545     }
546
547     [super mouseUp: o_event];
548 }
549
550 - (void)rightMouseUp:(NSEvent *)o_event
551 {
552     if( p_vout && [o_event type] == NSRightMouseUp )
553     {
554         /* FIXME: this isn't the appropriate place, but we can't receive
555          * NSRightMouseDown some how */
556         msg_Dbg( p_vout, "received NSRightMouseUp" ); 
557         [NSMenu popUpContextMenu: [[VLCMain sharedInstance] getVoutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] getControls] getVoutView]];
558     }
559
560     [super mouseUp: o_event];
561 }
562
563 - (void)mouseDragged:(NSEvent *)o_event
564 {
565     [self mouseMoved: o_event];
566 }
567
568 - (void)otherMouseDragged:(NSEvent *)o_event
569 {
570     [self mouseMoved: o_event];
571 }
572
573 - (void)rightMouseDragged:(NSEvent *)o_event
574 {
575     [self mouseMoved: o_event];
576 }
577
578 - (void)mouseMoved:(NSEvent *)o_event
579 {
580     NSPoint ml;
581     NSRect s_rect;
582     BOOL b_inside;
583
584     if( p_vout )
585     {
586         s_rect = [o_view bounds];
587         ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
588         b_inside = [o_view mouse: ml inRect: s_rect];
589
590         if( b_inside )
591         {
592             vlc_value_t val;
593             unsigned int i_width, i_height, i_x, i_y;
594
595             vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
596                                        (unsigned int)s_rect.size.height,
597                                        &i_x, &i_y, &i_width, &i_height );
598
599             val.i_int = ( ((int)ml.x) - i_x ) *
600                         p_vout->render.i_width / i_width;
601             var_Set( p_vout, "mouse-x", val );
602
603             if( [[o_view className] isEqualToString: @"VLCGLView"] )
604             {
605                 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
606                             p_vout->render.i_height / i_height;
607             }
608             else
609             {
610                 val.i_int = ( ((int)ml.y) - i_y ) *
611                             p_vout->render.i_height / i_height;
612             }
613             var_Set( p_vout, "mouse-y", val );
614
615             val.b_bool = VLC_TRUE;
616             var_Set( p_vout, "mouse-moved", val );
617         }
618         if( [self isFullscreen] )
619             [[[[VLCMain sharedInstance] getControls] getFSPanel] fadeIn];
620     }
621
622     [super mouseMoved: o_event];
623 }
624
625 - (BOOL)acceptsFirstResponder
626 {
627     return YES;
628 }
629
630 - (BOOL)becomeFirstResponder
631 {
632     return YES;
633 }
634
635 - (BOOL)resignFirstResponder
636 {
637     /* We need to stay the first responder or we'll miss some
638        events */
639     return NO;
640 }
641
642 /* Class methods used by the different vout modules */
643
644 + (vout_thread_t *)getRealVout: (vout_thread_t *)p_vout
645 {
646     /* p_real_vout: the vout we have to use to check for video-on-top
647        and a few other things. If we are the QuickTime output, it's us.
648        It we are the OpenGL provider, it is our parent. */
649     if( p_vout->i_object_type == VLC_OBJECT_OPENGL )
650     {
651         return (vout_thread_t *) p_vout->p_parent;
652     }
653     else
654     {
655         return p_vout;
656     }
657
658 }
659
660 + (id)getVoutView: (vout_thread_t *)p_vout subView: (NSView *)view
661                                     frame: (NSRect *)s_frame
662 {
663     vlc_value_t value_drawable;
664     int i_timeout;
665     id o_return = nil;
666     vout_thread_t * p_real_vout = [VLCVoutView getRealVout: p_vout];
667
668     var_Get( p_vout->p_vlc, "drawable", &value_drawable );
669
670     var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
671     var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
672     var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
673     var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
674     var_Create( p_vout, "macosx-background", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
675     var_Create( p_vout, "macosx-black", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
676     var_Create( p_vout, "macosx-embedded", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
677
678
679     /* We only wait for NSApp to initialise if we're not embedded (as in the
680      * case of the Mozilla plugin).  We can tell whether we're embedded or not
681      * by examining the "drawable" value: if it's zero, we're running in the
682      * main Mac intf; if it's non-zero, we're embedded. */
683     if( value_drawable.i_int == 0 )
684     {
685         /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
686         for( i_timeout = 20 ; i_timeout-- ; )
687         {
688             if( NSApp == NULL )
689             {
690                 msleep( INTF_IDLE_SLEEP );
691             }
692         }
693
694         if( NSApp == NULL )
695         {
696             /* No MacOS X intf, unable to communicate with MT */
697             msg_Err( p_vout, "no MacOS X interface present" );
698             return nil;
699         }
700         else
701         {
702             if ( VLCIntf && !(p_vout->b_fullscreen) &&
703                         !(var_GetBool( p_real_vout, "macosx-background" )) &&
704                         var_GetBool( p_vout, "macosx-embedded") )
705             {
706                 o_return = [[[VLCMain sharedInstance] getEmbeddedList]
707                                                             getEmbeddedVout];
708             }
709         }
710     }
711
712     /* No embedded vout is available */
713     if( o_return == nil )
714     {
715         NSRect null_rect;
716         bzero( &null_rect, sizeof( NSRect ) );
717         o_return = [[VLCDetachedVoutView alloc] initWithFrame: null_rect ];
718     }
719     [o_return setVout: p_vout subView: view frame: s_frame];
720     return o_return;
721 }
722
723 @end
724
725 /*****************************************************************************
726  * VLCDetachedVoutView implementation
727  *****************************************************************************/
728 @implementation VLCDetachedVoutView
729
730 - (id)initWithFrame: (NSRect)frameRect
731 {
732     [super initWithFrame: frameRect];
733     i_time_mouse_last_moved = 0;
734     return self;
735 }
736
737 - (bool)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
738                      frame: (NSRect *) s_arg_frame
739 {
740     BOOL b_return = [super setVout: p_arg_vout subView: view frame:s_arg_frame];
741     i_time_mouse_last_moved = mdate();
742     o_window = [[VLCWindow alloc] initWithVout: p_arg_vout view: self
743                                                     frame: s_arg_frame];
744     [self updateTitle];
745     [view setFrame: [self frame]];
746
747     if( var_GetBool( p_real_vout, "video-on-top" ) )
748     {
749         [o_window setLevel: NSStatusWindowLevel];
750     }
751
752
753     [o_window setAcceptsMouseMovedEvents: TRUE];
754     return b_return;
755 }
756
757 - (void)closeVout
758 {
759     [o_window closeWindow];
760     [o_window setAcceptsMouseMovedEvents: NO];
761     i_time_mouse_last_moved = 0;
762     [super closeVout];
763 }
764
765 - (void)mouseMoved:(NSEvent *)o_event
766 {
767     i_time_mouse_last_moved = mdate();
768     [super mouseMoved: o_event];
769 }
770
771 - (void)hideMouse:(BOOL)b_hide
772 {
773     BOOL b_inside;
774     NSPoint ml;
775     NSView *o_contents = [o_window contentView];
776
777     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
778     ml = [o_contents convertPoint:ml fromView:nil];
779     b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];
780
781     if( b_hide && b_inside )
782     {
783         [NSCursor setHiddenUntilMouseMoves: YES];
784     }
785     else if( !b_hide )
786     {
787         [NSCursor setHiddenUntilMouseMoves: NO];
788     }
789 }
790
791 - (void)manage
792 {
793     [super manage];
794     if( p_vout->b_fullscreen )
795     {
796         if( mdate() - i_time_mouse_last_moved > 3000000 )
797         {
798             [self hideMouse: YES];
799         }
800     }
801     else
802     {
803         [self hideMouse: NO];
804     }
805 }
806
807 @end
808
809 /*****************************************************************************
810  * VLCEmbeddedVoutView implementation
811  *****************************************************************************/
812
813 @implementation VLCEmbeddedVoutView
814
815 - (id)initWithFrame: (NSRect)frameRect
816 {
817     [super initWithFrame: frameRect];
818     b_used = NO;
819     [[[VLCMain sharedInstance] getEmbeddedList] addEmbeddedVout: self];
820     return self;
821 }
822
823 - (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
824                      frame: (NSRect *) s_arg_frame
825
826 {
827     BOOL b_return;
828     b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame];
829     if( b_return )
830     {
831         o_window = [self window];
832         [o_window makeKeyAndOrderFront: self];
833         [o_window setAcceptsMouseMovedEvents: TRUE];
834
835         if( var_GetBool( p_real_vout, "video-on-top" ) )
836         {
837             [o_window setLevel: NSStatusWindowLevel];
838         }
839
840         [view setFrameSize: [self frame].size];
841     }
842     return b_return;
843 }
844
845 - (void)setUsed: (BOOL)b_new_used
846 {
847     b_used = b_new_used;
848 }
849
850 - (BOOL)isUsed
851 {
852     return b_used;
853 }
854
855 - (void)closeVout
856 {
857     [super closeVout];
858     [o_window setAcceptsMouseMovedEvents: NO];
859     [[[VLCMain sharedInstance] getEmbeddedList] releaseEmbeddedVout: self];
860 }
861
862
863 @end
864
865 @implementation VLCDetachedEmbeddedVoutView
866
867 - (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
868                      frame: (NSRect *) s_arg_frame
869 {
870     BOOL b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame];
871
872     if( b_return )
873     {
874         [o_window setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
875         [self updateTitle];
876         [self scaleWindowWithFactor: 1.0];
877         [o_window makeKeyAndOrderFront: self];
878     }
879     return b_return;
880 }
881
882 - (void)closeVout
883 {
884     [o_window orderOut: self];
885     [super closeVout];
886 }
887
888 @end
889
890 /*****************************************************************************
891  * VLCWindow implementation
892  *****************************************************************************/
893 @implementation VLCWindow
894
895 - (id) initWithVout: (vout_thread_t *) vout view: (VLCVoutView *) view
896                      frame: (NSRect *) frame
897 {
898     p_vout  = vout;
899     o_view  = view;
900     s_frame = frame;
901
902     [self performSelectorOnMainThread: @selector(initReal:)
903         withObject: NULL waitUntilDone: YES];
904
905     if( !b_init_ok )
906     {
907         return NULL;
908     }
909
910     return self;
911 }
912
913 - (id)initReal: (id) sender
914 {
915     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
916     NSArray *o_screens = [NSScreen screens];
917     NSScreen *o_screen;
918     vlc_bool_t b_menubar_screen = VLC_FALSE;
919     int i_device;
920
921     b_init_ok = VLC_FALSE;
922
923     p_real_vout = [VLCVoutView getRealVout: p_vout];
924     i_device = var_GetInteger( p_real_vout->p_vlc, "video-device" );
925     b_black = var_GetBool( p_real_vout->p_vlc, "macosx-black" );
926
927     /* Find out on which screen to open the window */
928     if( i_device <= 0 || i_device > (int)[o_screens count] )
929     {
930          /* No preference specified. Use the main screen */
931         o_screen = [NSScreen mainScreen];
932         i_device = [o_screens indexOfObject: o_screen];
933         if( o_screen == [o_screens objectAtIndex: 0] )
934             b_menubar_screen = VLC_TRUE;
935     }
936     else
937     {
938         i_device--;
939         o_screen = [o_screens objectAtIndex: i_device];
940         b_menubar_screen = ( i_device == 0 );
941     }
942
943     if( p_vout->b_fullscreen )
944     {
945         CGDisplayFadeReservationToken token;
946         NSRect screen_rect = [o_screen frame];
947         screen_rect.origin.x = screen_rect.origin.y = 0;
948
949         /* Creates a window with size: screen_rect on o_screen */
950         [self initWithContentRect: screen_rect
951               styleMask: NSBorderlessWindowMask
952               backing: NSBackingStoreBuffered
953               defer: YES screen: o_screen];
954         
955         if( var_GetBool( p_real_vout, "macosx-black" ) )
956         if( b_black == VLC_TRUE )
957         {
958             CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
959             CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, true );
960             CGReleaseDisplayFadeReservation( token );
961             unsigned int i;
962             for( i = 0 ; i < [o_screens count]; i++)
963             {
964                 struct
965                 {
966                     CGDirectDisplayID displayID;
967                     CGGammaValue redMin, redMax, redGamma,
968                                  greenMin, greenMax, greenGamma,
969                                  blueMin, blueMax, blueGamma;
970                 } dispSettings;
971                 CGDisplayCount dspyCnt;
972                 CGPoint gPoint;
973
974                 if( i == (unsigned int)i_device ) continue;
975
976                 screen_rect = [[o_screens objectAtIndex: i] frame];
977
978                 gPoint.x = screen_rect.origin.x;
979                 gPoint.y = screen_rect.origin.y;
980                 CGGetDisplaysWithPoint( gPoint, 1, &(dispSettings.displayID), &dspyCnt);
981                 CGGetDisplayTransferByFormula(
982                     dispSettings.displayID,
983                     &dispSettings.redMin, &dispSettings.redMax, &dispSettings.redGamma,
984                     &dispSettings.greenMin, &dispSettings.greenMax, &dispSettings.greenGamma,
985                     &dispSettings.blueMin, &dispSettings.blueMax, &dispSettings.blueGamma );
986                 CGSetDisplayTransferByFormula(
987                     dispSettings.displayID,
988                     dispSettings.redMin,   0, dispSettings.redGamma,
989                     dispSettings.greenMin, 0, dispSettings.greenGamma,
990                     dispSettings.blueMin,  0, dispSettings.blueGamma );
991             }
992         }
993         if( b_menubar_screen )
994         {
995             SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
996         }
997         if( b_black == VLC_TRUE )
998         {
999             CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
1000             CGDisplayFade( token, 2 , kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, false );
1001             CGReleaseDisplayFadeReservation( token);
1002         }
1003     }
1004     else if( var_GetBool( p_real_vout, "macosx-background" ) )
1005     {
1006         NSRect screen_rect = [o_screen frame];
1007         screen_rect.origin.x = screen_rect.origin.y = 0;
1008
1009         /* Creates a window with size: screen_rect on o_screen */
1010         [self initWithContentRect: screen_rect
1011               styleMask: NSBorderlessWindowMask
1012               backing: NSBackingStoreBuffered
1013               defer: YES screen: o_screen];
1014
1015         [self setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey)];
1016     }
1017     else
1018     {
1019         unsigned int i_stylemask = NSTitledWindowMask |
1020                                    NSMiniaturizableWindowMask |
1021                                    NSClosableWindowMask |
1022                                    NSResizableWindowMask;
1023
1024         NSRect s_rect;
1025         if( !s_frame )
1026         {
1027             s_rect.size.width  = p_vout->i_window_width;
1028             s_rect.size.height = p_vout->i_window_height;
1029         }
1030         else
1031         {
1032             s_rect = *s_frame;
1033         }
1034
1035         [self initWithContentRect: s_rect
1036               styleMask: i_stylemask
1037               backing: NSBackingStoreBuffered
1038               defer: YES screen: o_screen];
1039
1040         [self setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
1041
1042         if( !s_frame )
1043         {
1044             [self center];
1045         }
1046     }
1047
1048     [self makeKeyAndOrderFront: nil];
1049     [self setReleasedWhenClosed: YES];
1050
1051     /* We'll catch mouse events */
1052     [self makeFirstResponder: o_view];
1053
1054     /* Add the view. It's automatically resized to fit the window */
1055     [self setContentView: o_view];
1056
1057     [o_pool release];
1058
1059     b_init_ok = VLC_TRUE;
1060     return self;
1061 }
1062
1063 - (void)close
1064 {
1065     [o_view closeVout];
1066 }
1067
1068 - (void) closeWindow
1069 {
1070     /* XXX waitUntilDone = NO to avoid a possible deadlock when hitting
1071        Command-Q */
1072     [self setContentView: NULL];
1073     [self performSelectorOnMainThread: @selector(closeReal:)
1074         withObject: NULL waitUntilDone: NO];
1075 }
1076
1077 - (id) closeReal: (id) sender
1078 {
1079     if( b_black == VLC_TRUE )
1080     {
1081         CGDisplayFadeReservationToken token;
1082         CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
1083         CGDisplayFade( token, 2, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, false );
1084         CGReleaseDisplayFadeReservation( token);
1085         CGDisplayRestoreColorSyncSettings();
1086     }
1087     SetSystemUIMode( kUIModeNormal, 0);
1088     [super close];
1089     return NULL;
1090 }
1091
1092 - (id)getVoutView
1093 {
1094     return o_view;
1095 }
1096
1097 - (BOOL)canBecomeKeyWindow
1098 {
1099     return YES;
1100 }
1101
1102 /* Sometimes crashes VLC....
1103 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1104 {
1105         return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
1106 }*/
1107
1108 /* This is actually the same as VLCControls::stop. */
1109
1110 - (BOOL)windowShouldClose:(id)sender
1111 {
1112     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1113                                                        FIND_ANYWHERE );
1114     if( p_playlist == NULL )
1115     {
1116         return NO;
1117     }
1118
1119     playlist_Stop( p_playlist );
1120     vlc_object_release( p_playlist );
1121
1122     /* The window will be closed by the intf later. */
1123     return NO;
1124 }
1125
1126
1127 @end