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