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