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